Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[native_toolchain_c] Compile with -Os by default #1744

Merged
merged 3 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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