Skip to content

Commit

Permalink
[native_toolchain_c] Compile with -Os by default (#1744)
Browse files Browse the repository at this point in the history
Closes: #1267
  • Loading branch information
dcharkes authored Nov 25, 2024
1 parent 7afad0b commit 579688b
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 7 deletions.
2 changes: 2 additions & 0 deletions pkgs/native_toolchain_c/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

- For Android, produce dylibs with page-size set to 16kb by default.
https://github.com/dart-lang/native/issues/1611
- Make optimization level configurable. Defaults to `-Os` and `/Os`.
https://github.com/dart-lang/native/issues/1267

## 0.6.0

Expand Down
1 change: 1 addition & 0 deletions pkgs/native_toolchain_c/lib/native_toolchain_c.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export 'src/cbuilder/cbuilder.dart' show CBuilder;
export 'src/cbuilder/clinker.dart' show CLinker;
export 'src/cbuilder/language.dart' show Language;
export 'src/cbuilder/linker_options.dart' show LinkerOptions;
export 'src/cbuilder/optimization_level.dart';
export 'src/cbuilder/output_type.dart' show OutputType;
export 'src/utils/env_from_bat.dart';
4 changes: 4 additions & 0 deletions pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:native_assets_cli/code_assets_builder.dart';
import 'ctool.dart';
import 'language.dart';
import 'linkmode.dart';
import 'optimization_level.dart';
import 'output_type.dart';
import 'run_cbuilder.dart';

Expand Down Expand Up @@ -67,6 +68,7 @@ class CBuilder extends CTool implements Builder {
super.language = Language.c,
super.cppLinkStdLib,
super.linkModePreference,
super.optimizationLevel = OptimizationLevel.oS,
}) : super(type: OutputType.library);

CBuilder.executable({
Expand All @@ -87,6 +89,7 @@ class CBuilder extends CTool implements Builder {
super.std,
super.language = Language.c,
super.cppLinkStdLib,
super.optimizationLevel = OptimizationLevel.oS,
}) : super(
type: OutputType.executable,
assetName: null,
Expand Down Expand Up @@ -158,6 +161,7 @@ class CBuilder extends CTool implements Builder {
std: std,
language: language,
cppLinkStdLib: cppLinkStdLib,
optimizationLevel: optimizationLevel,
);
await task.run();
}
Expand Down
3 changes: 3 additions & 0 deletions pkgs/native_toolchain_c/lib/src/cbuilder/clinker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'ctool.dart';
import 'language.dart';
import 'linker_options.dart';
import 'linkmode.dart';
import 'optimization_level.dart';
import 'output_type.dart';
import 'run_cbuilder.dart';

Expand All @@ -36,6 +37,7 @@ class CLinker extends CTool implements Linker {
super.language = Language.c,
super.cppLinkStdLib,
super.linkModePreference,
super.optimizationLevel = OptimizationLevel.oS,
}) : super(type: OutputType.library);

/// Runs the C Linker with on this C build spec.
Expand Down Expand Up @@ -84,6 +86,7 @@ class CLinker extends CTool implements Linker {
std: std,
language: language,
cppLinkStdLib: cppLinkStdLib,
optimizationLevel: optimizationLevel,
);
await task.run();

Expand Down
5 changes: 5 additions & 0 deletions pkgs/native_toolchain_c/lib/src/cbuilder/ctool.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:native_assets_cli/code_assets.dart';

import 'cbuilder.dart';
import 'language.dart';
import 'optimization_level.dart';
import 'output_type.dart';

abstract class CTool {
Expand Down Expand Up @@ -120,6 +121,9 @@ abstract class CTool {
/// the value is instead retrieved from the [LinkConfig].
final LinkModePreference? linkModePreference;

/// What optimization level should be used for compiling.
final OptimizationLevel optimizationLevel;

CTool({
required this.name,
required this.assetName,
Expand All @@ -135,5 +139,6 @@ abstract class CTool {
required this.cppLinkStdLib,
required this.linkModePreference,
required this.type,
required this.optimizationLevel,
});
}
56 changes: 56 additions & 0 deletions pkgs/native_toolchain_c/lib/src/cbuilder/optimization_level.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

/// Optimization level for code compilation.
///
/// For more information refer to compiler documentation:
/// * https://clang.llvm.org/docs/CommandGuide/clang.html#code-generation-options
/// * https://learn.microsoft.com/en-us/cpp/build/reference/o-options-optimize-code?view=msvc-170
final class OptimizationLevel {
/// The optimization level.
final String _level;

const OptimizationLevel._(this._level);

/// No optimization; prioritize fast compilation.
static const OptimizationLevel o0 = OptimizationLevel._('O0');

/// Basic optimizations; balance compilation speed and code size.
static const OptimizationLevel o1 = OptimizationLevel._('O1');

/// More aggressive optimizations; prioritize code size reduction.
static const OptimizationLevel o2 = OptimizationLevel._('O2');

/// The most aggressive optimizations; prioritize runtime performance.
///
/// Not supported in MSVC, defaults to [o2] for MSVC.
static const OptimizationLevel o3 = OptimizationLevel._('O3');

/// Optimize for code size, even if it impacts runtime performance.
static const OptimizationLevel oS = OptimizationLevel._('Os');

/// Unspecified optimization level; the default or compiler-chosen level.
static const OptimizationLevel unspecified =
OptimizationLevel._('unspecified');

/// Returns the string representation of the optimization level.
@override
String toString() => _level;

String clangFlag() => '-$_level';

String msvcFlag() => switch (this) {
o3 => o2.msvcFlag(),
_ => '/$_level',
};

static const List<OptimizationLevel> values = [
o0,
o1,
o2,
o3,
oS,
unspecified,
];
}
7 changes: 7 additions & 0 deletions pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import '../utils/run_process.dart';
import 'compiler_resolver.dart';
import 'language.dart';
import 'linker_options.dart';
import 'optimization_level.dart';

class RunCBuilder {
/// The options are for linking only, so this will be non-null iff a linker
Expand Down Expand Up @@ -45,6 +46,7 @@ class RunCBuilder {
final String? std;
final Language language;
final String? cppLinkStdLib;
final OptimizationLevel optimizationLevel;

RunCBuilder({
required this.config,
Expand All @@ -64,6 +66,7 @@ class RunCBuilder {
this.std,
this.language = Language.c,
this.cppLinkStdLib,
required this.optimizationLevel,
}) : outDir = config.outputDirectory,
assert([executable, dynamicLibrary, staticLibrary]
.whereType<Uri>()
Expand Down Expand Up @@ -275,6 +278,8 @@ class RunCBuilder {
'-l',
cppLinkStdLib ?? defaultCppLinkStdLib[config.targetOS]!
],
if (optimizationLevel != OptimizationLevel.unspecified)
optimizationLevel.clangFlag(),
...linkerOptions?.preSourcesFlags(toolInstance.tool, sourceFiles) ?? [],
// Support Android 15 page size by default, can be overridden by
// passing [flags].
Expand Down Expand Up @@ -327,6 +332,8 @@ class RunCBuilder {
final result = await runProcess(
executable: tool.uri,
arguments: [
if (optimizationLevel != OptimizationLevel.unspecified)
optimizationLevel.msvcFlag(),
if (std != null) '/std:$std',
if (language == Language.cpp) '/TP',
...flags,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,30 @@ void main() {
/// From https://docs.flutter.dev/reference/supported-platforms.
const flutterAndroidNdkVersionHighestSupported = 34;

const optimizationLevels = OptimizationLevel.values;
var selectOptimizationLevel = 0;

for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) {
for (final target in targets) {
for (final apiLevel in [
flutterAndroidNdkVersionLowestBestEffort,
flutterAndroidNdkVersionLowestSupported,
flutterAndroidNdkVersionHighestSupported,
]) {
test('CBuilder $linkMode library $target minSdkVersion $apiLevel',
() async {
// Cycle through all optimization levels.
final optimizationLevel = optimizationLevels[selectOptimizationLevel];
selectOptimizationLevel =
(selectOptimizationLevel + 1) % optimizationLevels.length;
test(
'CBuilder $linkMode library $target minSdkVersion $apiLevel '
'$optimizationLevel', () async {
final tempUri = await tempDirForTest();
final libUri = await buildLib(
tempUri,
target,
apiLevel,
linkMode,
optimizationLevel: optimizationLevel,
);
if (Platform.isLinux) {
final machine = await readelfMachine(libUri.path);
Expand Down Expand Up @@ -128,6 +137,7 @@ Future<Uri> buildLib(
int androidNdkApi,
LinkMode linkMode, {
List<String> flags = const [],
OptimizationLevel optimizationLevel = OptimizationLevel.o3,
}) async {
final addCUri = packageUri.resolve('test/cbuilder/testfiles/add/src/add.c');
const name = 'add';
Expand Down
12 changes: 10 additions & 2 deletions pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_ios_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,30 @@ void main() {

const name = 'add';

const optimizationLevels = OptimizationLevel.values;
var selectOptimizationLevel = 0;

for (final language in [Language.c, Language.objectiveC]) {
for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) {
for (final targetIOSSdk in IOSSdk.values) {
for (final target in targets) {
if (target == Architecture.x64 && targetIOSSdk == IOSSdk.iPhoneOS) {
continue;
}

final libName = OS.iOS.libraryFileName(name, linkMode);
for (final installName in [
null,
if (linkMode == DynamicLoadingBundled())
Uri.file('@executable_path/Frameworks/$libName'),
]) {
// Cycle through all optimization levels.
final optimizationLevel =
optimizationLevels[selectOptimizationLevel];
selectOptimizationLevel =
(selectOptimizationLevel + 1) % optimizationLevels.length;
test(
'CBuilder $linkMode $language library $targetIOSSdk $target'
' ${installName ?? ''}'
' ${installName ?? ''} $optimizationLevel'
.trim(), () async {
final tempUri = await tempDirForTest();
final tempUri2 = await tempDirForTest();
Expand Down Expand Up @@ -97,6 +104,7 @@ void main() {
sources: [sourceUri.toFilePath()],
installName: installName,
language: language,
optimizationLevel: optimizationLevel,
);
await cbuilder.run(
config: buildConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,16 @@ void main() {
Architecture.riscv64,
];

const optimizationLevels = OptimizationLevel.values;
var selectOptimizationLevel = 0;

for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) {
for (final target in targets) {
test('CBuilder $linkMode library $target', () async {
// Cycle through all optimization levels.
final optimizationLevel = optimizationLevels[selectOptimizationLevel];
selectOptimizationLevel =
(selectOptimizationLevel + 1) % optimizationLevels.length;
test('CBuilder $linkMode library $target $optimizationLevel', () async {
final tempUri = await tempDirForTest();
final tempUri2 = await tempDirForTest();
final addCUri =
Expand Down Expand Up @@ -66,6 +73,7 @@ void main() {
name: name,
assetName: name,
sources: [addCUri.toFilePath()],
optimizationLevel: optimizationLevel,
);
await cbuilder.run(
config: buildConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,19 @@ void main() {
Architecture.x64: '64-bit x86-64',
};

const optimizationLevels = OptimizationLevel.values;
var selectOptimizationLevel = 0;

for (final language in [Language.c, Language.objectiveC]) {
for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) {
for (final target in targets) {
test('CBuilder $linkMode $language library $target', () async {
// Cycle through all optimization levels.
final optimizationLevel = optimizationLevels[selectOptimizationLevel];
selectOptimizationLevel =
(selectOptimizationLevel + 1) % optimizationLevels.length;

test('CBuilder $linkMode $language library $target $optimizationLevel',
() async {
final tempUri = await tempDirForTest();
final tempUri2 = await tempDirForTest();
final sourceUri = switch (language) {
Expand Down Expand Up @@ -79,6 +88,7 @@ void main() {
assetName: name,
sources: [sourceUri.toFilePath()],
language: language,
optimizationLevel: optimizationLevel,
);
await cbuilder.run(
config: buildConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,21 @@ void main() {
Architecture.x64: 'x64',
};

const optimizationLevels = OptimizationLevel.values;
var selectOptimizationLevel = 0;

final dumpbinFileType = {
DynamicLoadingBundled(): 'DLL',
StaticLinking(): 'LIBRARY',
};

for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) {
for (final target in targets) {
test('CBuilder $linkMode library $target', () async {
// Cycle through all optimization levels.
final optimizationLevel = optimizationLevels[selectOptimizationLevel];
selectOptimizationLevel =
(selectOptimizationLevel + 1) % optimizationLevels.length;
test('CBuilder $linkMode library $target $optimizationLevel', () async {
final tempUri = await tempDirForTest();
final tempUri2 = await tempDirForTest();
final addCUri =
Expand Down Expand Up @@ -85,6 +92,7 @@ void main() {
name: name,
assetName: name,
sources: [addCUri.toFilePath()],
optimizationLevel: optimizationLevel,
);
await cbuilder.run(
config: buildConfig,
Expand Down

0 comments on commit 579688b

Please sign in to comment.