Unverified Commit 2269dc10 authored by Jackson Gardner's avatar Jackson Gardner Committed by GitHub

Add option to run wasm-opt on module output. (#124831)

This fixes https://github.com/flutter/flutter/issues/124159

Adds the command line argument `--wasm-opt` to optimize the web assembly output.
parent aa8cc44e
...@@ -43,6 +43,8 @@ enum Artifact { ...@@ -43,6 +43,8 @@ enum Artifact {
dart2jsSnapshot, dart2jsSnapshot,
/// The dart snapshot of the dart2wasm compiler. /// The dart snapshot of the dart2wasm compiler.
dart2wasmSnapshot, dart2wasmSnapshot,
/// The wasm-opt binary that ships with the dart-sdk
wasmOptBinary,
/// The root of the Linux desktop sources. /// The root of the Linux desktop sources.
linuxDesktopPath, linuxDesktopPath,
...@@ -184,6 +186,8 @@ String? _artifactToFileName(Artifact artifact, Platform hostPlatform, [ BuildMod ...@@ -184,6 +186,8 @@ String? _artifactToFileName(Artifact artifact, Platform hostPlatform, [ BuildMod
return 'dart2js.dart.snapshot'; return 'dart2js.dart.snapshot';
case Artifact.dart2wasmSnapshot: case Artifact.dart2wasmSnapshot:
return 'dart2wasm_product.snapshot'; return 'dart2wasm_product.snapshot';
case Artifact.wasmOptBinary:
return 'wasm-opt$exe';
case Artifact.frontendServerSnapshotForEngineDartSdk: case Artifact.frontendServerSnapshotForEngineDartSdk:
return 'frontend_server.dart.snapshot'; return 'frontend_server.dart.snapshot';
case Artifact.linuxDesktopPath: case Artifact.linuxDesktopPath:
...@@ -510,6 +514,7 @@ class CachedArtifacts implements Artifacts { ...@@ -510,6 +514,7 @@ class CachedArtifacts implements Artifacts {
case Artifact.engineDartAotRuntime: case Artifact.engineDartAotRuntime:
case Artifact.dart2jsSnapshot: case Artifact.dart2jsSnapshot:
case Artifact.dart2wasmSnapshot: case Artifact.dart2wasmSnapshot:
case Artifact.wasmOptBinary:
case Artifact.frontendServerSnapshotForEngineDartSdk: case Artifact.frontendServerSnapshotForEngineDartSdk:
case Artifact.constFinder: case Artifact.constFinder:
case Artifact.flutterFramework: case Artifact.flutterFramework:
...@@ -550,6 +555,7 @@ class CachedArtifacts implements Artifacts { ...@@ -550,6 +555,7 @@ class CachedArtifacts implements Artifacts {
case Artifact.engineDartAotRuntime: case Artifact.engineDartAotRuntime:
case Artifact.dart2jsSnapshot: case Artifact.dart2jsSnapshot:
case Artifact.dart2wasmSnapshot: case Artifact.dart2wasmSnapshot:
case Artifact.wasmOptBinary:
case Artifact.frontendServerSnapshotForEngineDartSdk: case Artifact.frontendServerSnapshotForEngineDartSdk:
case Artifact.constFinder: case Artifact.constFinder:
case Artifact.flutterMacOSFramework: case Artifact.flutterMacOSFramework:
...@@ -608,6 +614,7 @@ class CachedArtifacts implements Artifacts { ...@@ -608,6 +614,7 @@ class CachedArtifacts implements Artifacts {
case Artifact.engineDartAotRuntime: case Artifact.engineDartAotRuntime:
case Artifact.dart2jsSnapshot: case Artifact.dart2jsSnapshot:
case Artifact.dart2wasmSnapshot: case Artifact.dart2wasmSnapshot:
case Artifact.wasmOptBinary:
case Artifact.frontendServerSnapshotForEngineDartSdk: case Artifact.frontendServerSnapshotForEngineDartSdk:
case Artifact.icuData: case Artifact.icuData:
case Artifact.isolateSnapshotData: case Artifact.isolateSnapshotData:
...@@ -646,6 +653,11 @@ class CachedArtifacts implements Artifacts { ...@@ -646,6 +653,11 @@ class CachedArtifacts implements Artifacts {
_dartSdkPath(_cache), 'bin', 'snapshots', _dartSdkPath(_cache), 'bin', 'snapshots',
_artifactToFileName(artifact, _platform), _artifactToFileName(artifact, _platform),
); );
case Artifact.wasmOptBinary:
return _fileSystem.path.join(
_dartSdkPath(_cache), 'bin', 'utils',
_artifactToFileName(artifact, _platform),
);
case Artifact.flutterTester: case Artifact.flutterTester:
case Artifact.vmSnapshotData: case Artifact.vmSnapshotData:
case Artifact.isolateSnapshotData: case Artifact.isolateSnapshotData:
...@@ -963,6 +975,8 @@ class CachedLocalEngineArtifacts implements Artifacts { ...@@ -963,6 +975,8 @@ class CachedLocalEngineArtifacts implements Artifacts {
case Artifact.dart2wasmSnapshot: case Artifact.dart2wasmSnapshot:
case Artifact.frontendServerSnapshotForEngineDartSdk: case Artifact.frontendServerSnapshotForEngineDartSdk:
return _fileSystem.path.join(_getDartSdkPath(), 'bin', 'snapshots', artifactFileName); return _fileSystem.path.join(_getDartSdkPath(), 'bin', 'snapshots', artifactFileName);
case Artifact.wasmOptBinary:
return _fileSystem.path.join(_getDartSdkPath(), 'bin', 'utils', artifactFileName);
case Artifact.flutterToolsFileGenerators: case Artifact.flutterToolsFileGenerators:
return _getFileGeneratorsPath(); return _getFileGeneratorsPath();
} }
...@@ -1092,6 +1106,11 @@ class CachedLocalWebSdkArtifacts implements Artifacts { ...@@ -1092,6 +1106,11 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
_getDartSdkPath(), 'bin', 'snapshots', _getDartSdkPath(), 'bin', 'snapshots',
_artifactToFileName(artifact, _platform, mode), _artifactToFileName(artifact, _platform, mode),
); );
case Artifact.wasmOptBinary:
return _fileSystem.path.join(
_getDartSdkPath(), 'bin', 'snapshots',
_artifactToFileName(artifact, _platform, mode),
);
case Artifact.genSnapshot: case Artifact.genSnapshot:
case Artifact.flutterTester: case Artifact.flutterTester:
case Artifact.flutterFramework: case Artifact.flutterFramework:
......
...@@ -242,9 +242,12 @@ class Dart2WasmTarget extends Dart2WebTarget { ...@@ -242,9 +242,12 @@ class Dart2WasmTarget extends Dart2WebTarget {
if (buildModeEnvironment == null) { if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, name); throw MissingDefineException(kBuildMode, name);
} }
final WasmCompilerConfig compilerConfig = WasmCompilerConfig.fromBuildSystemEnvironment(environment.defines);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment); final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final Artifacts artifacts = globals.artifacts!; final Artifacts artifacts = globals.artifacts!;
final File outputWasmFile = environment.buildDir.childFile('main.dart.wasm'); final File outputWasmFile = environment.buildDir.childFile(
compilerConfig.runWasmOpt ? 'main.dart.unopt.wasm' : 'main.dart.wasm'
);
final File depFile = environment.buildDir.childFile('dart2wasm.d'); final File depFile = environment.buildDir.childFile('dart2wasm.d');
final String dartSdkPath = artifacts.getArtifactPath(Artifact.engineDartSdkPath, platform: TargetPlatform.web_javascript); final String dartSdkPath = artifacts.getArtifactPath(Artifact.engineDartSdkPath, platform: TargetPlatform.web_javascript);
final String dartSdkRoot = environment.fileSystem.directory(dartSdkPath).parent.path; final String dartSdkRoot = environment.fileSystem.directory(dartSdkPath).parent.path;
...@@ -260,7 +263,7 @@ class Dart2WasmTarget extends Dart2WebTarget { ...@@ -260,7 +263,7 @@ class Dart2WasmTarget extends Dart2WebTarget {
...decodeCommaSeparated(environment.defines, kExtraFrontEndOptions), ...decodeCommaSeparated(environment.defines, kExtraFrontEndOptions),
for (final String dartDefine in decodeDartDefines(environment.defines, kDartDefines)) for (final String dartDefine in decodeDartDefines(environment.defines, kDartDefines))
'-D$dartDefine', '-D$dartDefine',
...WasmCompilerConfig.fromBuildSystemEnvironment(environment.defines).toCommandOptions(), ...compilerConfig.toCommandOptions(),
'--packages=.dart_tool/package_config.json', '--packages=.dart_tool/package_config.json',
'--dart-sdk=$dartSdkPath', '--dart-sdk=$dartSdkPath',
'--multi-root-scheme', '--multi-root-scheme',
...@@ -286,6 +289,35 @@ class Dart2WasmTarget extends Dart2WebTarget { ...@@ -286,6 +289,35 @@ class Dart2WasmTarget extends Dart2WebTarget {
if (compileResult.exitCode != 0) { if (compileResult.exitCode != 0) {
throw Exception(_collectOutput(compileResult)); throw Exception(_collectOutput(compileResult));
} }
if (compilerConfig.runWasmOpt) {
final String wasmOptBinary = artifacts.getArtifactPath(
Artifact.wasmOptBinary,
platform: TargetPlatform.web_javascript
);
final File optimizedOutput = environment.buildDir.childFile('main.dart.wasm');
final List<String> optimizeArgs = <String>[
wasmOptBinary,
'-all',
'--closed-world',
'-tnh',
'-O3',
'--type-ssa',
'--gufa',
'-O3',
'--type-merging',
outputWasmFile.path,
'-o',
optimizedOutput.path,
];
final ProcessResult optimizeResult = await globals.processManager.run(optimizeArgs);
if (optimizeResult.exitCode != 0) {
throw Exception(_collectOutput(optimizeResult));
}
// Rename the .mjs file not to have the `.unopt` bit
final File jsRuntimeFile = environment.buildDir.childFile('main.dart.unopt.mjs');
await jsRuntimeFile.rename(environment.buildDir.childFile('main.dart.mjs').path);
}
} }
@override @override
......
...@@ -101,6 +101,12 @@ class BuildWebCommand extends BuildSubCommand { ...@@ -101,6 +101,12 @@ class BuildWebCommand extends BuildSubCommand {
negatable: false, negatable: false,
hide: !featureFlags.isFlutterWebWasmEnabled, hide: !featureFlags.isFlutterWebWasmEnabled,
); );
argParser.addFlag(
'wasm-opt',
help: 'Run wasm-opt on the output wasm module.',
negatable: false,
hide: !featureFlags.isFlutterWebWasmEnabled,
);
} }
final FileSystem _fileSystem; final FileSystem _fileSystem;
...@@ -133,6 +139,7 @@ class BuildWebCommand extends BuildSubCommand { ...@@ -133,6 +139,7 @@ class BuildWebCommand extends BuildSubCommand {
} }
compilerConfig = WasmCompilerConfig( compilerConfig = WasmCompilerConfig(
omitTypeChecks: boolArg('omit-type-checks'), omitTypeChecks: boolArg('omit-type-checks'),
runWasmOpt: boolArg('wasm-opt'),
); );
} else { } else {
compilerConfig = JsCompilerConfig( compilerConfig = JsCompilerConfig(
......
...@@ -130,6 +130,7 @@ class JsCompilerConfig extends WebCompilerConfig { ...@@ -130,6 +130,7 @@ class JsCompilerConfig extends WebCompilerConfig {
class WasmCompilerConfig extends WebCompilerConfig { class WasmCompilerConfig extends WebCompilerConfig {
const WasmCompilerConfig({ const WasmCompilerConfig({
required this.omitTypeChecks, required this.omitTypeChecks,
required this.runWasmOpt,
}); });
/// Creates a new [WasmCompilerConfig] from build system environment values. /// Creates a new [WasmCompilerConfig] from build system environment values.
...@@ -139,20 +140,26 @@ class WasmCompilerConfig extends WebCompilerConfig { ...@@ -139,20 +140,26 @@ class WasmCompilerConfig extends WebCompilerConfig {
Map<String, String> defines) => Map<String, String> defines) =>
WasmCompilerConfig( WasmCompilerConfig(
omitTypeChecks: defines[kOmitTypeChecks] == 'true', omitTypeChecks: defines[kOmitTypeChecks] == 'true',
runWasmOpt: defines[kRunWasmOpt] == 'true',
); );
/// Build environment for [omitTypeChecks]; /// Build environment for [omitTypeChecks];
static const String kOmitTypeChecks = 'WasmOmitTypeChecks'; static const String kOmitTypeChecks = 'WasmOmitTypeChecks';
static const String kRunWasmOpt = 'RunWasmOpt';
/// If `omit-type-checks` should be passed to `dart2wasm`. /// If `omit-type-checks` should be passed to `dart2wasm`.
final bool omitTypeChecks; final bool omitTypeChecks;
// Run wasm-opt on the resulting module.
final bool runWasmOpt;
@override @override
bool get isWasm => true; bool get isWasm => true;
@override @override
Map<String, String> toBuildSystemEnvironment() => <String, String>{ Map<String, String> toBuildSystemEnvironment() => <String, String>{
kOmitTypeChecks: omitTypeChecks.toString(), kOmitTypeChecks: omitTypeChecks.toString(),
kRunWasmOpt: runWasmOpt.toString(),
}; };
List<String> toCommandOptions() => <String>[ List<String> toCommandOptions() => <String>[
......
...@@ -845,6 +845,62 @@ void main() { ...@@ -845,6 +845,62 @@ void main() {
ProcessManager: () => processManager, ProcessManager: () => processManager,
})); }));
test('Dart2WasmTarget invokes dart2wasm and wasm-opt when RunWasmOpt is enabled', () => testbed.run(() async {
environment.defines[kBuildMode] = 'release';
environment.defines[WasmCompilerConfig.kRunWasmOpt] = 'true';
final File depFile = environment.buildDir.childFile('dart2wasm.d');
final File outputJsFile = environment.buildDir.childFile('main.dart.unopt.mjs');
processManager.addCommand(FakeCommand(
command: <String>[
'bin/cache/dart-sdk/bin/dartaotruntime',
'--disable-dart-dev',
'bin/cache/dart-sdk/bin/snapshots/dart2wasm_product.snapshot',
'-Ddart.vm.product=true',
'--packages=.dart_tool/package_config.json',
'--dart-sdk=bin/cache/dart-sdk',
'--multi-root-scheme',
'org-dartlang-sdk',
'--multi-root',
'bin/cache/flutter_web_sdk',
'--multi-root',
'bin/cache',
'--libraries-spec',
'bin/cache/flutter_web_sdk/libraries.json',
'--depfile=${depFile.absolute.path}',
environment.buildDir.childFile('main.dart').absolute.path,
environment.buildDir.childFile('main.dart.unopt.wasm').absolute.path,
], onRun: () => outputJsFile..createSync()..writeAsStringSync('foo')));
processManager.addCommand(FakeCommand(
command: <String>[
'bin/cache/dart-sdk/bin/utils/wasm-opt',
'-all',
'--closed-world',
'-tnh',
'-O3',
'--type-ssa',
'--gufa',
'-O3',
'--type-merging',
environment.buildDir.childFile('main.dart.unopt.wasm').absolute.path,
'-o',
environment.buildDir.childFile('main.dart.wasm').absolute.path,
]));
await Dart2WasmTarget(WebRendererMode.canvaskit).build(environment);
expect(outputJsFile.existsSync(), isFalse);
final File movedJsFile = environment.buildDir.childFile('main.dart.mjs');
expect(movedJsFile.existsSync(), isTrue);
expect(movedJsFile.readAsStringSync(), 'foo');
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
test('Dart2WasmTarget with skwasm renderer adds extra flags', () => testbed.run(() async { test('Dart2WasmTarget with skwasm renderer adds extra flags', () => testbed.run(() async {
environment.defines[kBuildMode] = 'release'; environment.defines[kBuildMode] = 'release';
final File depFile = environment.buildDir.childFile('dart2wasm.d'); final File depFile = environment.buildDir.childFile('dart2wasm.d');
......
...@@ -47,6 +47,7 @@ void main() { ...@@ -47,6 +47,7 @@ void main() {
'HasWebPlugins': 'false', 'HasWebPlugins': 'false',
'ServiceWorkerStrategy': ServiceWorkerStrategy.offlineFirst.cliName, 'ServiceWorkerStrategy': ServiceWorkerStrategy.offlineFirst.cliName,
'WasmOmitTypeChecks': 'false', 'WasmOmitTypeChecks': 'false',
'RunWasmOpt': 'false',
'BuildMode': 'debug', 'BuildMode': 'debug',
'DartObfuscation': 'false', 'DartObfuscation': 'false',
'TrackWidgetCreation': 'true', 'TrackWidgetCreation': 'true',
...@@ -70,7 +71,10 @@ void main() { ...@@ -70,7 +71,10 @@ void main() {
'target', 'target',
BuildInfo.debug, BuildInfo.debug,
ServiceWorkerStrategy.offlineFirst, ServiceWorkerStrategy.offlineFirst,
compilerConfig: const WasmCompilerConfig(omitTypeChecks: false), compilerConfig: const WasmCompilerConfig(
omitTypeChecks: false,
runWasmOpt: false
),
); );
expect(logger.statusText, contains('Compiling target for the Web...')); expect(logger.statusText, contains('Compiling target for the Web...'));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment