Skip to content

Commit 579688b

Browse files
authored
[native_toolchain_c] Compile with -Os by default (#1744)
Closes: #1267
1 parent 7afad0b commit 579688b

12 files changed

+129
-7
lines changed

pkgs/native_toolchain_c/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

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

68
## 0.6.0
79

pkgs/native_toolchain_c/lib/native_toolchain_c.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ export 'src/cbuilder/cbuilder.dart' show CBuilder;
99
export 'src/cbuilder/clinker.dart' show CLinker;
1010
export 'src/cbuilder/language.dart' show Language;
1111
export 'src/cbuilder/linker_options.dart' show LinkerOptions;
12+
export 'src/cbuilder/optimization_level.dart';
1213
export 'src/cbuilder/output_type.dart' show OutputType;
1314
export 'src/utils/env_from_bat.dart';

pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:native_assets_cli/code_assets_builder.dart';
1111
import 'ctool.dart';
1212
import 'language.dart';
1313
import 'linkmode.dart';
14+
import 'optimization_level.dart';
1415
import 'output_type.dart';
1516
import 'run_cbuilder.dart';
1617

@@ -67,6 +68,7 @@ class CBuilder extends CTool implements Builder {
6768
super.language = Language.c,
6869
super.cppLinkStdLib,
6970
super.linkModePreference,
71+
super.optimizationLevel = OptimizationLevel.oS,
7072
}) : super(type: OutputType.library);
7173

7274
CBuilder.executable({
@@ -87,6 +89,7 @@ class CBuilder extends CTool implements Builder {
8789
super.std,
8890
super.language = Language.c,
8991
super.cppLinkStdLib,
92+
super.optimizationLevel = OptimizationLevel.oS,
9093
}) : super(
9194
type: OutputType.executable,
9295
assetName: null,
@@ -158,6 +161,7 @@ class CBuilder extends CTool implements Builder {
158161
std: std,
159162
language: language,
160163
cppLinkStdLib: cppLinkStdLib,
164+
optimizationLevel: optimizationLevel,
161165
);
162166
await task.run();
163167
}

pkgs/native_toolchain_c/lib/src/cbuilder/clinker.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'ctool.dart';
1212
import 'language.dart';
1313
import 'linker_options.dart';
1414
import 'linkmode.dart';
15+
import 'optimization_level.dart';
1516
import 'output_type.dart';
1617
import 'run_cbuilder.dart';
1718

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

4143
/// Runs the C Linker with on this C build spec.
@@ -84,6 +86,7 @@ class CLinker extends CTool implements Linker {
8486
std: std,
8587
language: language,
8688
cppLinkStdLib: cppLinkStdLib,
89+
optimizationLevel: optimizationLevel,
8790
);
8891
await task.run();
8992

pkgs/native_toolchain_c/lib/src/cbuilder/ctool.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:native_assets_cli/code_assets.dart';
77

88
import 'cbuilder.dart';
99
import 'language.dart';
10+
import 'optimization_level.dart';
1011
import 'output_type.dart';
1112

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

124+
/// What optimization level should be used for compiling.
125+
final OptimizationLevel optimizationLevel;
126+
123127
CTool({
124128
required this.name,
125129
required this.assetName,
@@ -135,5 +139,6 @@ abstract class CTool {
135139
required this.cppLinkStdLib,
136140
required this.linkModePreference,
137141
required this.type,
142+
required this.optimizationLevel,
138143
});
139144
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// Optimization level for code compilation.
6+
///
7+
/// For more information refer to compiler documentation:
8+
/// * https://clang.llvm.org/docs/CommandGuide/clang.html#code-generation-options
9+
/// * https://learn.microsoft.com/en-us/cpp/build/reference/o-options-optimize-code?view=msvc-170
10+
final class OptimizationLevel {
11+
/// The optimization level.
12+
final String _level;
13+
14+
const OptimizationLevel._(this._level);
15+
16+
/// No optimization; prioritize fast compilation.
17+
static const OptimizationLevel o0 = OptimizationLevel._('O0');
18+
19+
/// Basic optimizations; balance compilation speed and code size.
20+
static const OptimizationLevel o1 = OptimizationLevel._('O1');
21+
22+
/// More aggressive optimizations; prioritize code size reduction.
23+
static const OptimizationLevel o2 = OptimizationLevel._('O2');
24+
25+
/// The most aggressive optimizations; prioritize runtime performance.
26+
///
27+
/// Not supported in MSVC, defaults to [o2] for MSVC.
28+
static const OptimizationLevel o3 = OptimizationLevel._('O3');
29+
30+
/// Optimize for code size, even if it impacts runtime performance.
31+
static const OptimizationLevel oS = OptimizationLevel._('Os');
32+
33+
/// Unspecified optimization level; the default or compiler-chosen level.
34+
static const OptimizationLevel unspecified =
35+
OptimizationLevel._('unspecified');
36+
37+
/// Returns the string representation of the optimization level.
38+
@override
39+
String toString() => _level;
40+
41+
String clangFlag() => '-$_level';
42+
43+
String msvcFlag() => switch (this) {
44+
o3 => o2.msvcFlag(),
45+
_ => '/$_level',
46+
};
47+
48+
static const List<OptimizationLevel> values = [
49+
o0,
50+
o1,
51+
o2,
52+
o3,
53+
oS,
54+
unspecified,
55+
];
56+
}

pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import '../utils/run_process.dart';
1616
import 'compiler_resolver.dart';
1717
import 'language.dart';
1818
import 'linker_options.dart';
19+
import 'optimization_level.dart';
1920

2021
class RunCBuilder {
2122
/// The options are for linking only, so this will be non-null iff a linker
@@ -45,6 +46,7 @@ class RunCBuilder {
4546
final String? std;
4647
final Language language;
4748
final String? cppLinkStdLib;
49+
final OptimizationLevel optimizationLevel;
4850

4951
RunCBuilder({
5052
required this.config,
@@ -64,6 +66,7 @@ class RunCBuilder {
6466
this.std,
6567
this.language = Language.c,
6668
this.cppLinkStdLib,
69+
required this.optimizationLevel,
6770
}) : outDir = config.outputDirectory,
6871
assert([executable, dynamicLibrary, staticLibrary]
6972
.whereType<Uri>()
@@ -275,6 +278,8 @@ class RunCBuilder {
275278
'-l',
276279
cppLinkStdLib ?? defaultCppLinkStdLib[config.targetOS]!
277280
],
281+
if (optimizationLevel != OptimizationLevel.unspecified)
282+
optimizationLevel.clangFlag(),
278283
...linkerOptions?.preSourcesFlags(toolInstance.tool, sourceFiles) ?? [],
279284
// Support Android 15 page size by default, can be overridden by
280285
// passing [flags].
@@ -327,6 +332,8 @@ class RunCBuilder {
327332
final result = await runProcess(
328333
executable: tool.uri,
329334
arguments: [
335+
if (optimizationLevel != OptimizationLevel.unspecified)
336+
optimizationLevel.msvcFlag(),
330337
if (std != null) '/std:$std',
331338
if (language == Language.cpp) '/TP',
332339
...flags,

pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_android_test.dart

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,30 @@ void main() {
3636
/// From https://docs.flutter.dev/reference/supported-platforms.
3737
const flutterAndroidNdkVersionHighestSupported = 34;
3838

39+
const optimizationLevels = OptimizationLevel.values;
40+
var selectOptimizationLevel = 0;
41+
3942
for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) {
4043
for (final target in targets) {
4144
for (final apiLevel in [
4245
flutterAndroidNdkVersionLowestBestEffort,
4346
flutterAndroidNdkVersionLowestSupported,
4447
flutterAndroidNdkVersionHighestSupported,
4548
]) {
46-
test('CBuilder $linkMode library $target minSdkVersion $apiLevel',
47-
() async {
49+
// Cycle through all optimization levels.
50+
final optimizationLevel = optimizationLevels[selectOptimizationLevel];
51+
selectOptimizationLevel =
52+
(selectOptimizationLevel + 1) % optimizationLevels.length;
53+
test(
54+
'CBuilder $linkMode library $target minSdkVersion $apiLevel '
55+
'$optimizationLevel', () async {
4856
final tempUri = await tempDirForTest();
4957
final libUri = await buildLib(
5058
tempUri,
5159
target,
5260
apiLevel,
5361
linkMode,
62+
optimizationLevel: optimizationLevel,
5463
);
5564
if (Platform.isLinux) {
5665
final machine = await readelfMachine(libUri.path);
@@ -128,6 +137,7 @@ Future<Uri> buildLib(
128137
int androidNdkApi,
129138
LinkMode linkMode, {
130139
List<String> flags = const [],
140+
OptimizationLevel optimizationLevel = OptimizationLevel.o3,
131141
}) async {
132142
final addCUri = packageUri.resolve('test/cbuilder/testfiles/add/src/add.c');
133143
const name = 'add';

pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_ios_test.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,30 @@ void main() {
3535

3636
const name = 'add';
3737

38+
const optimizationLevels = OptimizationLevel.values;
39+
var selectOptimizationLevel = 0;
40+
3841
for (final language in [Language.c, Language.objectiveC]) {
3942
for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) {
4043
for (final targetIOSSdk in IOSSdk.values) {
4144
for (final target in targets) {
4245
if (target == Architecture.x64 && targetIOSSdk == IOSSdk.iPhoneOS) {
4346
continue;
4447
}
45-
4648
final libName = OS.iOS.libraryFileName(name, linkMode);
4749
for (final installName in [
4850
null,
4951
if (linkMode == DynamicLoadingBundled())
5052
Uri.file('@executable_path/Frameworks/$libName'),
5153
]) {
54+
// Cycle through all optimization levels.
55+
final optimizationLevel =
56+
optimizationLevels[selectOptimizationLevel];
57+
selectOptimizationLevel =
58+
(selectOptimizationLevel + 1) % optimizationLevels.length;
5259
test(
5360
'CBuilder $linkMode $language library $targetIOSSdk $target'
54-
' ${installName ?? ''}'
61+
' ${installName ?? ''} $optimizationLevel'
5562
.trim(), () async {
5663
final tempUri = await tempDirForTest();
5764
final tempUri2 = await tempDirForTest();
@@ -97,6 +104,7 @@ void main() {
97104
sources: [sourceUri.toFilePath()],
98105
installName: installName,
99106
language: language,
107+
optimizationLevel: optimizationLevel,
100108
);
101109
await cbuilder.run(
102110
config: buildConfig,

pkgs/native_toolchain_c/test/cbuilder/cbuilder_cross_linux_host_test.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,16 @@ void main() {
2626
Architecture.riscv64,
2727
];
2828

29+
const optimizationLevels = OptimizationLevel.values;
30+
var selectOptimizationLevel = 0;
31+
2932
for (final linkMode in [DynamicLoadingBundled(), StaticLinking()]) {
3033
for (final target in targets) {
31-
test('CBuilder $linkMode library $target', () async {
34+
// Cycle through all optimization levels.
35+
final optimizationLevel = optimizationLevels[selectOptimizationLevel];
36+
selectOptimizationLevel =
37+
(selectOptimizationLevel + 1) % optimizationLevels.length;
38+
test('CBuilder $linkMode library $target $optimizationLevel', () async {
3239
final tempUri = await tempDirForTest();
3340
final tempUri2 = await tempDirForTest();
3441
final addCUri =
@@ -66,6 +73,7 @@ void main() {
6673
name: name,
6774
assetName: name,
6875
sources: [addCUri.toFilePath()],
76+
optimizationLevel: optimizationLevel,
6977
);
7078
await cbuilder.run(
7179
config: buildConfig,

0 commit comments

Comments
 (0)