From fdeb9dc5757ca9bf88c319f003a355e7d4acf5ad Mon Sep 17 00:00:00 2001 From: Nate Biggs Date: Mon, 1 Dec 2025 21:50:29 -0800 Subject: [PATCH] [dart2wasm] Introduce an opt phase to dart2wasm. To help facilitate this we move all IO into a separate helper library/class. This makes it easier to have symmetric read/write functions and to do IO within compile.dart where necessary. Adding the new `opt` phase allows us to remove the duplicated code between dartdev and compile_benchmark simplifying those two files a lot. It will also allow us to more easily invoke wasm-opt within our internal build pipeline. For compile_benchmark we still run the opt phase independently (but through dart2wasm) to keep the benchmark data as consistent as possible. Change-Id: Iaa855dbc3a05abfedbc3eea4af32e3ba27e84600 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/464640 Commit-Queue: Nate Biggs Reviewed-by: Martin Kustermann --- .../benchmark/self_compile_benchmark.dart | 57 +++- pkg/dart2wasm/lib/compile.dart | 261 +++++++++++------- pkg/dart2wasm/lib/compiler_options.dart | 36 ++- pkg/dart2wasm/lib/dart2wasm.dart | 16 +- .../lib/dynamic_module_kernel_metadata.dart | 27 +- pkg/dart2wasm/lib/generate_wasm.dart | 55 +--- pkg/dart2wasm/lib/io_util.dart | 184 ++++++++++++ pkg/dart2wasm/lib/modules.dart | 16 +- pkg/dart2wasm/lib/option.dart | 12 +- pkg/dart2wasm/lib/translator.dart | 35 ++- pkg/dart2wasm/test/phases/phases_test.dart | 96 +++++-- pkg/dart2wasm/test/util.dart | 3 + pkg/dart2wasm/tool/compile_benchmark | 106 +------ pkg/dartdev/lib/src/commands/compile.dart | 244 ++-------------- 14 files changed, 606 insertions(+), 542 deletions(-) create mode 100644 pkg/dart2wasm/lib/io_util.dart diff --git a/pkg/dart2wasm/benchmark/self_compile_benchmark.dart b/pkg/dart2wasm/benchmark/self_compile_benchmark.dart index 7b37b69fb8b..c56ef60bc18 100644 --- a/pkg/dart2wasm/benchmark/self_compile_benchmark.dart +++ b/pkg/dart2wasm/benchmark/self_compile_benchmark.dart @@ -2,41 +2,70 @@ // 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. +import 'dart:typed_data'; + import 'package:_fe_analyzer_shared/src/util/colors.dart' as colors; import 'package:dart2wasm/compile.dart'; import 'package:dart2wasm/compiler_options.dart'; +import 'package:dart2wasm/io_util.dart'; import 'filesystem_io.dart' if (dart.library.js_interop) 'filesystem_js.dart'; +class _BenchmarkIOManager extends CompilerPhaseInputOutputManager { + final WasmCompilerFileSystem benchmarkFileSystem; + Uint8List? moduleBytes; + + _BenchmarkIOManager(this.benchmarkFileSystem, WasmCompilerOptions options) + : super(benchmarkFileSystem, options); + + @override + Future writeWasmModule(Uint8List wasmModule, String moduleName) async { + moduleBytes = wasmModule; + } + + @override + Future writeWasmSourceMap(String sourceMap, String moduleName) async {} + + @override + Future writeJsRuntime(String jsRuntime) async {} + + @override + Future writeSupportJs(String supportJs) async {} + + void flushWasmModules(String wasmFile) { + benchmarkFileSystem.writeBytesSync(wasmFile, moduleBytes!); + } +} + Future main(List args) async { final sw = Stopwatch()..start(); final fileSystem = WasmCompilerFileSystem(); - final result = await compileBenchmark( - fileSystem, 'pkg/dart2wasm/benchmark/self_compile_benchmark.dart'); + final mainFile = 'pkg/dart2wasm/benchmark/self_compile_benchmark.dart'; + final main = Uri.file('${fileSystem.sdkRoot}/$mainFile'); + + final options = WasmCompilerOptions(mainUri: main, outputFile: 'out.wasm'); + final ioManager = _BenchmarkIOManager(fileSystem, options); + + options.librariesSpecPath = + Uri.file('${fileSystem.sdkRoot}/sdk/lib/libraries.json'); + + await compileBenchmark(options, ioManager); print('Dart2WasmSelfCompile(RunTimeRaw): ${sw.elapsed.inMilliseconds} ms.'); if (args.isNotEmpty) { - final module = result.wasmModules.values.single; final wasmFile = args.single; - fileSystem.writeBytesSync(wasmFile, module.moduleBytes); + ioManager.flushWasmModules(wasmFile); } } -Future compileBenchmark( - WasmCompilerFileSystem fileSystem, String mainFile) async { +Future compileBenchmark(WasmCompilerOptions options, + CompilerPhaseInputOutputManager ioManager) async { // Avoid CFE self-detecting whether `stdout`/`stderr` is terminal and supports // colors (as we don't have `dart:io` available when we run dart2wasm in a // wasm runtime). colors.enableColors = false; - final main = Uri.file('${fileSystem.sdkRoot}/$mainFile'); - - final options = WasmCompilerOptions(mainUri: main, outputFile: 'out.wasm'); - options.librariesSpecPath = - Uri.file('${fileSystem.sdkRoot}/sdk/lib/libraries.json'); - - final result = await compile( - options, fileSystem, (mod) => Uri.parse('$mod.maps'), (diag) { + final result = await compile(options, ioManager, (diag) { print('Diagnostics: ${diag.severity} ${diag.plainTextFormatted}'); }); if (result is! CompilationSuccess) { diff --git a/pkg/dart2wasm/lib/compile.dart b/pkg/dart2wasm/lib/compile.dart index c9ec7426bc8..a76fa34ad18 100644 --- a/pkg/dart2wasm/lib/compile.dart +++ b/pkg/dart2wasm/lib/compile.dart @@ -2,11 +2,6 @@ // 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. -import 'dart:io' show File; -import 'dart:typed_data'; - -import 'package:build_integration/file_system/multi_root.dart' - show MultiRootFileSystem, MultiRootFileSystemEntity; import 'package:front_end/src/api_prototype/dynamic_module_validator.dart' show DynamicInterfaceYamlFile; import 'package:front_end/src/api_prototype/file_system.dart' show FileSystem; @@ -18,14 +13,9 @@ import 'package:front_end/src/api_unstable/vm.dart' kernelForProgram, CfeSeverity; import 'package:kernel/ast.dart'; -import 'package:kernel/binary/ast_from_binary.dart' - show BinaryBuilderWithMetadata; import 'package:kernel/class_hierarchy.dart'; import 'package:kernel/core_types.dart'; -import 'package:kernel/kernel.dart' - show writeComponentToText, loadComponentFromBytes; import 'package:kernel/library_index.dart'; -import 'package:kernel/text/ast_to_text.dart'; import 'package:kernel/type_environment.dart'; import 'package:kernel/verifier.dart'; import 'package:vm/kernel_front_end.dart' show writeDepfile; @@ -46,6 +36,7 @@ import 'deferred_loading.dart'; import 'dry_run.dart'; import 'dynamic_module_kernel_metadata.dart'; import 'dynamic_modules.dart'; +import 'io_util.dart'; import 'js/method_collector.dart' show JSMethods; import 'js/runtime_generator.dart' as js; import 'modules.dart'; @@ -93,11 +84,17 @@ class TfaResult extends CompilationSuccess { } class CodegenResult extends CompilationSuccess { - final Map wasmModules; - final String jsRuntime; - final String supportJs; + final String mainWasmFile; + final int numModules; - CodegenResult(this.wasmModules, this.jsRuntime, this.supportJs); + CodegenResult(this.mainWasmFile, this.numModules); +} + +class OptResult extends CompilationSuccess { + final String mainWasmFile; + final int numModules; + + OptResult(this.mainWasmFile, this.numModules); } abstract class CompilationError extends CompilationResult {} @@ -148,6 +145,43 @@ const List _librariesToIndex = [ "dart:typed_data", ]; +const List _binaryenFlags = [ + '--enable-gc', + '--enable-reference-types', + '--enable-multivalue', + '--enable-exception-handling', + '--enable-nontrapping-float-to-int', + '--enable-sign-ext', + '--enable-bulk-memory', + '--enable-threads', + '--no-inline=**', + '--closed-world', + '--traps-never-happen', + '--type-unfinalizing', + '-Os', + '--type-ssa', + '--gufa', + '-Os', + '--type-merging', + '-Os', + '--type-finalizing', + '--minimize-rec-groups', +]; + +const List _binaryenFlagsMultiModule = [ + '--enable-gc', + '--enable-reference-types', + '--enable-multivalue', + '--enable-exception-handling', + '--enable-nontrapping-float-to-int', + '--enable-sign-ext', + '--enable-bulk-memory', + '--enable-threads', + '--no-inline=**', + '--traps-never-happen', + '-Os', +]; + /// Compile a Dart file into a Wasm module. /// /// Returns `null` if an error occurred during compilation. The @@ -161,8 +195,7 @@ const List _librariesToIndex = [ /// mappings. Future compile( compiler.WasmCompilerOptions options, - FileSystem fileSystem, - Uri Function(String moduleName)? sourceMapUrlGenerator, + CompilerPhaseInputOutputManager ioManager, void Function(CfeDiagnosticMessage) handleDiagnosticMessage) async { final wasm.Mode mode; if (options.translatorOptions.jsCompatibility) { @@ -177,15 +210,9 @@ Future compile( removeAsserts: !options.translatorOptions.enableAsserts, mode: mode); - if (options.multiRootScheme != null) { - fileSystem = MultiRootFileSystem( - options.multiRootScheme!, - options.multiRoots.isEmpty ? [Uri.base] : options.multiRoots, - fileSystem); - } - CfeResult? cfeResult; TfaResult? tfaResult; + CodegenResult? codegenResult; CompilationResult? lastResult; for (final phase in options.phases) { @@ -194,7 +221,8 @@ Future compile( lastResult = await _runCfePhase( options, target, - fileSystem, + ioManager.fileSystem, + ioManager, handleDiagnosticMessage, ); if (lastResult is! CfeResult) return lastResult; @@ -202,31 +230,40 @@ Future compile( case compiler.CompilerPhase.tfa: lastResult = await _runTfaPhase( - cfeResult ?? await _loadCfeResult(options, fileSystem), + cfeResult ?? await _loadCfeResult(options, ioManager), options, target, - fileSystem, + ioManager, ); if (lastResult is! TfaResult) return lastResult; tfaResult = lastResult; case compiler.CompilerPhase.codegen: lastResult = await _runCodegenPhase( - tfaResult ?? await _loadTfaResult(options, target, fileSystem), + tfaResult ?? await _loadTfaResult(options, target, ioManager), options, - fileSystem, - sourceMapUrlGenerator); + ioManager); + + if (lastResult is! CodegenResult) return lastResult; + codegenResult = lastResult; + + case compiler.CompilerPhase.opt: + lastResult = await _runOptPhase( + codegenResult ?? await _loadCodegenResult(options, ioManager), + options, + ioManager); + + if (lastResult is! OptResult) return lastResult; } } return lastResult!; } -Future _loadCfeResult( - compiler.WasmCompilerOptions options, FileSystem fileSystem) async { - final component = loadComponentFromBytes( - await File.fromUri((await _resolveUri(fileSystem, options.mainUri))!) - .readAsBytes()); +Future _loadCfeResult(compiler.WasmCompilerOptions options, + CompilerPhaseInputOutputManager ioManager) async { + final component = Component(); + await ioManager.readComponent(options.mainUri, component); final coreTypes = CoreTypes(component); return CfeResult(component, coreTypes); } @@ -235,6 +272,7 @@ Future _runCfePhase( compiler.WasmCompilerOptions options, WasmTarget target, FileSystem fileSystem, + CompilerPhaseInputOutputManager ioManager, void Function(CfeDiagnosticMessage) handleDiagnosticMessage) async { var hadCompileTimeError = false; void diagnosticMessageHandler(CfeDiagnosticMessage message) { @@ -276,10 +314,7 @@ Future _runCfePhase( if (isDynamicMainModule) { final dynamicInterfaceUri = options.dynamicInterfaceUri; if (dynamicInterfaceUri != null) { - final resolvedDynamicInterfaceUri = - await _resolveUri(fileSystem, dynamicInterfaceUri); - final contents = - File.fromUri(resolvedDynamicInterfaceUri!).readAsStringSync(); + final contents = await ioManager.readString(dynamicInterfaceUri); final dynamicInterfaceYamlFile = DynamicInterfaceYamlFile(contents); additionalSources = dynamicInterfaceYamlFile .getUserLibraryUris(dynamicInterfaceUri) @@ -288,7 +323,7 @@ Future _runCfePhase( } final dynamicMainModuleUri = - await _resolveUri(fileSystem, options.dynamicMainModuleUri); + await ioManager.resolveUri(options.dynamicMainModuleUri); final isDynamicSubmodule = options.dynamicModuleType == DynamicModuleType.submodule; if (isDynamicSubmodule) { @@ -324,35 +359,43 @@ Future _runCfePhase( final component = compilerResult!.component!; if (options.dumpKernelAfterCfe != null) { - writeComponentToText(component, - path: options.dumpKernelAfterCfe!, showMetadata: true); + ioManager.writeComponentAsText(component, options.dumpKernelAfterCfe!); + } + + if (options.emitCfe) { + await ioManager.writeComponent(component, options.outputFile); } return CfeResult(component, compilerResult.coreTypes!); } Future _loadTfaResult(compiler.WasmCompilerOptions options, - WasmTarget target, FileSystem fileSystem) async { + WasmTarget target, CompilerPhaseInputOutputManager ioManager) async { final component = createEmptyComponent(); final recordClassesRepository = _RecordClassesRepository(); final interopMethodsRepository = _InteropMethodsRepository(); component.addMetadataRepository(recordClassesRepository); component.addMetadataRepository(interopMethodsRepository); - BinaryBuilderWithMetadata( - await File.fromUri((await _resolveUri(fileSystem, options.mainUri))!) - .readAsBytes()) - .readComponent(component); + await ioManager.readComponent(options.mainUri, component); + final coreTypes = CoreTypes(component); final libraryIndex = LibraryIndex(component, _librariesToIndex); final classHierarchy = ClassHierarchy(component, coreTypes); final dynamicMainModuleUri = - await _resolveUri(fileSystem, options.dynamicMainModuleUri); + await ioManager.resolveUri(options.dynamicMainModuleUri); final dynamicInterfaceUri = - await _resolveUri(fileSystem, options.dynamicInterfaceUri); + await ioManager.resolveUri(options.dynamicInterfaceUri); - final moduleStrategy = _createModuleStrategy(options, component, coreTypes, - target, classHierarchy, dynamicMainModuleUri, dynamicInterfaceUri); + final moduleStrategy = await _createModuleStrategy( + options, + ioManager, + component, + coreTypes, + target, + classHierarchy, + dynamicMainModuleUri, + dynamicInterfaceUri); final recordClasses = {}; recordClassesRepository.mapping.forEach((cls, shape) { @@ -368,7 +411,7 @@ Future _loadTfaResult(compiler.WasmCompilerOptions options, if (isDynamicSubmodule) { mainModuleMetadata = - await deserializeMainModuleMetadata(component, options); + await deserializeMainModuleMetadata(component, ioManager); mainModuleMetadata.verifyDynamicSubmoduleOptions(options); } else if (isDynamicMainModule) { MainModuleMetadata.verifyMainModuleOptions(options); @@ -382,7 +425,7 @@ Future _runTfaPhase( CfeResult cfeResult, compiler.WasmCompilerOptions options, WasmTarget target, - FileSystem fileSystem) async { + CompilerPhaseInputOutputManager ioManager) async { var CfeResult(:component, :coreTypes) = cfeResult; ClosedWorldClassHierarchy classHierarchy = @@ -400,9 +443,9 @@ Future _runTfaPhase( classHierarchy); final dynamicMainModuleUri = - await _resolveUri(fileSystem, options.dynamicMainModuleUri); + await ioManager.resolveUri(options.dynamicMainModuleUri); final dynamicInterfaceUri = - await _resolveUri(fileSystem, options.dynamicInterfaceUri); + await ioManager.resolveUri(options.dynamicInterfaceUri); final isDynamicMainModule = options.dynamicModuleType == DynamicModuleType.main; final isDynamicSubmodule = @@ -435,12 +478,18 @@ Future _runTfaPhase( target.recordClasses = recordClasses; if (options.dumpKernelBeforeTfa != null) { - writeComponentToText(component, - path: options.dumpKernelBeforeTfa!, showMetadata: true); + ioManager.writeComponentAsText(component, options.dumpKernelBeforeTfa!); } - final moduleStrategy = _createModuleStrategy(options, component, coreTypes, - target, classHierarchy, dynamicMainModuleUri, dynamicInterfaceUri); + final moduleStrategy = await _createModuleStrategy( + options, + ioManager, + component, + coreTypes, + target, + classHierarchy, + dynamicMainModuleUri, + dynamicInterfaceUri); // Ensure we annotate AST nodes as entry points prior to other transformations // looking at pragmas (such as mixin_deduplication and TFA). @@ -469,11 +518,12 @@ Future _runTfaPhase( if (isDynamicSubmodule) { mainModuleMetadata = - await deserializeMainModuleMetadata(component, options); + await deserializeMainModuleMetadata(component, ioManager); mainModuleMetadata.verifyDynamicSubmoduleOptions(options); } else if (isDynamicMainModule) { MainModuleMetadata.verifyMainModuleOptions(options); - await serializeMainModuleComponent(component, dynamicMainModuleUri!, + await serializeMainModuleComponent( + ioManager, component, dynamicMainModuleUri!, optimized: false); } @@ -498,7 +548,7 @@ Future _runTfaPhase( libraryIndex = LibraryIndex(component, _librariesToIndex); } - if (options.phases.last == compiler.CompilerPhase.tfa) { + if (options.emitTfa) { // Store metadata needed for codegen so that it can be serialized. final recordClassesRepo = _RecordClassesRepository(); recordClasses.forEach((shape, cls) { @@ -520,19 +570,27 @@ Future _runTfaPhase( }()); if (options.dumpKernelAfterTfa != null) { - writeComponentToText(component, - path: options.dumpKernelAfterTfa!, showMetadata: true); + ioManager.writeComponentAsText(component, options.dumpKernelAfterTfa!); + } + + if (options.emitTfa) { + await ioManager.writeComponent(component, options.outputFile); } return TfaResult(component, coreTypes, libraryIndex, moduleStrategy, mainModuleMetadata, jsInteropMethods, recordClasses); } +Future _loadCodegenResult(compiler.WasmCompilerOptions options, + CompilerPhaseInputOutputManager ioManager) async { + return CodegenResult(options.mainUri.toFilePath(), + await ioManager.getModuleCount(options.mainUri)); +} + Future _runCodegenPhase( TfaResult tfaSuccess, compiler.WasmCompilerOptions options, - FileSystem fileSystem, - Uri Function(String moduleName)? sourceMapUrlGenerator) async { + CompilerPhaseInputOutputManager ioManager) async { final TfaResult( :component, :coreTypes, @@ -558,29 +616,32 @@ Future _runCodegenPhase( String? depFile = options.depFile; if (depFile != null) { - writeDepfile( - fileSystem, component.uriToSource.keys, options.outputFile, depFile); + writeDepfile(ioManager.fileSystem, component.uriToSource.keys, + options.outputFile, depFile); } final generateSourceMaps = options.translatorOptions.generateSourceMaps; - final modules = translator.translate(sourceMapUrlGenerator); - final wasmModules = {}; + final modules = translator.translate(ioManager.sourceMapUrlGenerator); + final writeFutures = >[]; modules.forEach((moduleOutput, module) { if (moduleOutput.skipEmit) return; final serializer = Serializer(); module.serialize(serializer); - final wasmModuleSerialized = serializer.data; + writeFutures.add( + ioManager.writeWasmModule(serializer.data, moduleOutput.moduleName)); - final sourceMap = - generateSourceMaps ? serializer.sourceMapSerializer.serialize() : null; - wasmModules[moduleOutput.moduleName] = - (moduleBytes: wasmModuleSerialized, sourceMap: sourceMap); + if (generateSourceMaps) { + final sourceMap = serializer.sourceMapSerializer.serialize(); + writeFutures.add( + ioManager.writeWasmSourceMap(sourceMap, moduleOutput.moduleName)); + } }); + await Future.wait(writeFutures); final jsRuntimeFinalizer = js.RuntimeFinalizer(jsInteropMethods); final dynamicMainModuleUri = - await _resolveUri(fileSystem, options.dynamicMainModuleUri); + await ioManager.resolveUri(options.dynamicMainModuleUri); final isDynamicMainModule = options.dynamicModuleType == DynamicModuleType.main; final isDynamicSubmodule = @@ -602,8 +663,9 @@ Future _runCodegenPhase( final supportJs = _generateSupportJs(options.translatorOptions); if (isDynamicMainModule) { - await serializeMainModuleMetadata(component, translator, options); - await serializeMainModuleComponent(component, dynamicMainModuleUri!, + await serializeMainModuleMetadata(component, translator, ioManager); + await serializeMainModuleComponent( + ioManager, component, dynamicMainModuleUri!, optimized: true); } @@ -612,17 +674,39 @@ Future _runCodegenPhase( await writeLoadIdsFile(component, coreTypes, options, loadingMap); } - return CodegenResult(wasmModules, jsRuntime, supportJs); + await ioManager.writeJsRuntime(jsRuntime); + await ioManager.writeSupportJs(supportJs); + + return CodegenResult(options.outputFile, modules.length); } -ModuleStrategy _createModuleStrategy( +Future _runOptPhase( + CodegenResult codegenResult, compiler.WasmCompilerOptions options, + CompilerPhaseInputOutputManager ioManager) async { + final futures = >[]; + final numModules = codegenResult.numModules; + for (int i = 0; i < numModules; i++) { + futures.add(ioManager.runWasmOpt( + codegenResult.mainWasmFile, + i, + options.useMultiModuleOpt + ? _binaryenFlagsMultiModule + : _binaryenFlags)); + } + await Future.wait(futures); + return OptResult(options.outputFile, numModules); +} + +Future _createModuleStrategy( + compiler.WasmCompilerOptions options, + CompilerPhaseInputOutputManager ioManager, Component component, CoreTypes coreTypes, WasmTarget target, ClassHierarchy classHierarchy, Uri? dynamicMainModuleUri, - Uri? dynamicInterfaceUri) { + Uri? dynamicInterfaceUri) async { final isDynamicMainModule = options.dynamicModuleType == DynamicModuleType.main; final isDynamicSubmodule = @@ -637,7 +721,7 @@ ModuleStrategy _createModuleStrategy( component, coreTypes, options, - File.fromUri(dynamicInterfaceUri!).readAsStringSync(), + await ioManager.readString(dynamicInterfaceUri!), options.dynamicInterfaceUri!); } else if (isDynamicSubmodule) { return DynamicSubmoduleStrategy( @@ -677,15 +761,6 @@ void _patchMainTearOffs(CoreTypes coreTypes, Component component) { if (mainHasType(mainArg0Type)) return patchToReturnMainTearOff(mainTearOff0); } -Future _resolveUri(FileSystem fileSystem, Uri? uri) async { - if (uri == null) return null; - var fileSystemEntity = fileSystem.entityForUri(uri); - if (fileSystemEntity is MultiRootFileSystemEntity) { - fileSystemEntity = await fileSystemEntity.delegate; - } - return fileSystemEntity.uri; -} - class _RecordClassesRepository extends MetadataRepository { static const String _tag = 'dart2wasm.recordClasses'; @override @@ -780,9 +855,3 @@ String _generateSupportJs(TranslatorOptions options) { ]; return '(${requiredFeatures.join('&&')})'; } - -String writeComponentToString(Component component) { - final buffer = StringBuffer(); - Printer(buffer).writeComponentFile(component); - return '$buffer'; -} diff --git a/pkg/dart2wasm/lib/compiler_options.dart b/pkg/dart2wasm/lib/compiler_options.dart index dcb81c77529..bd03cd66805 100644 --- a/pkg/dart2wasm/lib/compiler_options.dart +++ b/pkg/dart2wasm/lib/compiler_options.dart @@ -20,7 +20,8 @@ import 'translator.dart'; enum CompilerPhase { cfe, tfa, - codegen; + codegen, + opt; static CompilerPhase parse(String name) { for (final phase in values) { @@ -31,6 +32,7 @@ enum CompilerPhase { } class WasmCompilerOptions { + static const int mainModuleId = 0; final TranslatorOptions translatorOptions = TranslatorOptions(); Uri? platformPath; @@ -54,6 +56,9 @@ class WasmCompilerOptions { String? dumpKernelBeforeTfa; String? dumpKernelAfterTfa; bool dryRun = false; + Uri? wasmOptPath; + bool saveUnopt = false; + bool stripWasm = true; List phases = const [ CompilerPhase.cfe, CompilerPhase.tfa, @@ -67,6 +72,21 @@ class WasmCompilerOptions { bool get enableDynamicModules => dynamicModuleType != null; + bool get useMultiModuleOpt => + translatorOptions.enableDeferredLoading || + translatorOptions.enableMultiModuleStressTestMode || + enableDynamicModules; + + String moduleNameForId(String filePath, int id, {bool emitAsMain = false}) => + emitAsMain || id == mainModuleId + ? path.basename(filePath) + : path.basename(path.setExtension(filePath, '_module$id.wasm')); + + bool get emitCfe => phases.last == CompilerPhase.cfe; + bool get emitTfa => phases.last == CompilerPhase.tfa; + bool get emitCodegen => phases.last == CompilerPhase.tfa; + bool get readCodegen => phases.first == CompilerPhase.codegen; + void validate() { if (translatorOptions.importSharedMemory && translatorOptions.sharedMemoryMaxPages == null) { @@ -125,6 +145,10 @@ class WasmCompilerOptions { if (inputExtension != '.dill') { throw ArgumentError('Input to codegen phase must be a .dill file.'); } + case CompilerPhase.opt: + if (inputExtension != '.wasm') { + throw ArgumentError('Input to opt phase must be a .wasm file.'); + } } // Ensure correct output file type @@ -143,6 +167,16 @@ class WasmCompilerOptions { throw ArgumentError( 'Output from codegen phase must be a .wasm file.'); } + case CompilerPhase.opt: + if (outputExtension != '.wasm') { + throw ArgumentError('Output from opt phase must be a .wasm file.'); + } + } + + if (phases.contains(CompilerPhase.opt) && + translatorOptions.optimizationLevel == 0) { + throw ArgumentError( + 'Cannot specify "opt" phase with optimization level 0'); } } } diff --git a/pkg/dart2wasm/lib/dart2wasm.dart b/pkg/dart2wasm/lib/dart2wasm.dart index ce1ee02cff5..56875d89dbc 100644 --- a/pkg/dart2wasm/lib/dart2wasm.dart +++ b/pkg/dart2wasm/lib/dart2wasm.dart @@ -19,13 +19,14 @@ final WasmCompilerOptions _d = WasmCompilerOptions.defaultOptions(); final List