[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 <natebiggs@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
@@ -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<void> writeWasmModule(Uint8List wasmModule, String moduleName) async {
|
||||
moduleBytes = wasmModule;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> writeWasmSourceMap(String sourceMap, String moduleName) async {}
|
||||
|
||||
@override
|
||||
Future<void> writeJsRuntime(String jsRuntime) async {}
|
||||
|
||||
@override
|
||||
Future<void> writeSupportJs(String supportJs) async {}
|
||||
|
||||
void flushWasmModules(String wasmFile) {
|
||||
benchmarkFileSystem.writeBytesSync(wasmFile, moduleBytes!);
|
||||
}
|
||||
}
|
||||
|
||||
Future main(List<String> 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<CodegenResult> compileBenchmark(
|
||||
WasmCompilerFileSystem fileSystem, String mainFile) async {
|
||||
Future<CodegenResult> 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) {
|
||||
|
||||
+165
-96
@@ -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<String, ({Uint8List moduleBytes, String? sourceMap})> 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<String> _librariesToIndex = [
|
||||
"dart:typed_data",
|
||||
];
|
||||
|
||||
const List<String> _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=*<noInline>*',
|
||||
'--closed-world',
|
||||
'--traps-never-happen',
|
||||
'--type-unfinalizing',
|
||||
'-Os',
|
||||
'--type-ssa',
|
||||
'--gufa',
|
||||
'-Os',
|
||||
'--type-merging',
|
||||
'-Os',
|
||||
'--type-finalizing',
|
||||
'--minimize-rec-groups',
|
||||
];
|
||||
|
||||
const List<String> _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=*<noInline>*',
|
||||
'--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<String> _librariesToIndex = [
|
||||
/// mappings.
|
||||
Future<CompilationResult> 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<CompilationResult> 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<CompilationResult> compile(
|
||||
lastResult = await _runCfePhase(
|
||||
options,
|
||||
target,
|
||||
fileSystem,
|
||||
ioManager.fileSystem,
|
||||
ioManager,
|
||||
handleDiagnosticMessage,
|
||||
);
|
||||
if (lastResult is! CfeResult) return lastResult;
|
||||
@@ -202,31 +230,40 @@ Future<CompilationResult> 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<CfeResult> _loadCfeResult(
|
||||
compiler.WasmCompilerOptions options, FileSystem fileSystem) async {
|
||||
final component = loadComponentFromBytes(
|
||||
await File.fromUri((await _resolveUri(fileSystem, options.mainUri))!)
|
||||
.readAsBytes());
|
||||
Future<CfeResult> _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<CompilationResult> _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<CompilationResult> _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<CompilationResult> _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<CompilationResult> _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<TfaResult> _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 = <RecordShape, Class>{};
|
||||
recordClassesRepository.mapping.forEach((cls, shape) {
|
||||
@@ -368,7 +411,7 @@ Future<TfaResult> _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<CompilationResult> _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<CompilationResult> _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<CompilationResult> _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<CompilationResult> _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<CompilationResult> _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<CompilationResult> _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<CodegenResult> _loadCodegenResult(compiler.WasmCompilerOptions options,
|
||||
CompilerPhaseInputOutputManager ioManager) async {
|
||||
return CodegenResult(options.mainUri.toFilePath(),
|
||||
await ioManager.getModuleCount(options.mainUri));
|
||||
}
|
||||
|
||||
Future<CompilationResult> _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<CompilationResult> _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 = <String, ({Uint8List moduleBytes, String? sourceMap})>{};
|
||||
final modules = translator.translate(ioManager.sourceMapUrlGenerator);
|
||||
final writeFutures = <Future<void>>[];
|
||||
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<CompilationResult> _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<CompilationResult> _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<CompilationResult> _runOptPhase(
|
||||
CodegenResult codegenResult,
|
||||
compiler.WasmCompilerOptions options,
|
||||
CompilerPhaseInputOutputManager ioManager) async {
|
||||
final futures = <Future<void>>[];
|
||||
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<ModuleStrategy> _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<Uri?> _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<RecordShape> {
|
||||
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';
|
||||
}
|
||||
|
||||
@@ -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<CompilerPhase> 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,14 @@ final WasmCompilerOptions _d = WasmCompilerOptions.defaultOptions();
|
||||
|
||||
final List<Option> options = [
|
||||
Flag("help", (o, _) {}, abbr: "h", negatable: false, defaultsTo: false),
|
||||
IntOption("optimization-level",
|
||||
(o, value) => o.translatorOptions.optimizationLevel = value,
|
||||
abbr: "O"),
|
||||
Flag("import-shared-memory",
|
||||
(o, value) => o.translatorOptions.importSharedMemory = value,
|
||||
defaultsTo: _d.translatorOptions.importSharedMemory),
|
||||
Flag("inlining", (o, value) => o.translatorOptions.inlining = value,
|
||||
defaultsTo: _d.translatorOptions.inlining),
|
||||
Flag("minify", (o, value) => o.translatorOptions.minify = value,
|
||||
defaultsTo: _d.translatorOptions.minify),
|
||||
Flag("inlining", (o, value) => o.translatorOptions.inliningOverride = value),
|
||||
Flag("minify", (o, value) => o.translatorOptions.minifyOverride = value),
|
||||
Flag("dry-run", (o, value) => o.dryRun = value, defaultsTo: _d.dryRun),
|
||||
StringMultiOption(
|
||||
"phases",
|
||||
@@ -49,10 +50,10 @@ final List<Option> options = [
|
||||
(o, value) => o.translatorOptions.omitExplicitTypeChecks = value,
|
||||
defaultsTo: _d.translatorOptions.omitExplicitTypeChecks),
|
||||
Flag("omit-implicit-checks",
|
||||
(o, value) => o.translatorOptions.omitImplicitTypeChecks = value,
|
||||
(o, value) => o.translatorOptions.omitImplicitTypeChecksOverride = value,
|
||||
defaultsTo: _d.translatorOptions.omitImplicitTypeChecks),
|
||||
Flag("omit-bounds-checks", (o, value) {
|
||||
o.translatorOptions.omitBoundsChecks = value;
|
||||
o.translatorOptions.omitBoundsChecksOverride = value;
|
||||
}, defaultsTo: _d.translatorOptions.omitBoundsChecks),
|
||||
Flag("verbose", (o, value) => o.translatorOptions.verbose = value,
|
||||
defaultsTo: _d.translatorOptions.verbose),
|
||||
@@ -141,6 +142,9 @@ final List<Option> options = [
|
||||
Flag("validate-dynamic-modules",
|
||||
(o, value) => o.validateDynamicModules = value,
|
||||
defaultsTo: true, negatable: true),
|
||||
UriOption("wasm-opt", (o, value) => o.wasmOptPath = value),
|
||||
Flag("save-unopt", (o, value) => o.saveUnopt = value),
|
||||
Flag("strip-wasm", (o, value) => o.stripWasm = value, negatable: true),
|
||||
];
|
||||
|
||||
Map<fe.ExperimentalFlag, bool> processFeExperimentalFlags(
|
||||
|
||||
@@ -9,15 +9,14 @@ import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/binary/ast_from_binary.dart'
|
||||
show BinaryBuilderWithMetadata;
|
||||
import 'package:kernel/core_types.dart';
|
||||
import 'package:kernel/kernel.dart'
|
||||
show writeComponentToBinary, writeComponentToBytes;
|
||||
import 'package:kernel/kernel.dart' show writeComponentToBytes;
|
||||
import 'package:kernel/library_index.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'class_info.dart';
|
||||
import 'compiler_options.dart';
|
||||
import 'dispatch_table.dart';
|
||||
import 'dynamic_modules.dart';
|
||||
import 'io_util.dart';
|
||||
import 'js/method_collector.dart' show JSMethods;
|
||||
import 'serialization.dart';
|
||||
import 'translator.dart';
|
||||
@@ -460,11 +459,13 @@ String _makeOptDillPath(String path) =>
|
||||
'${path.substring(0, path.length - '.dill'.length)}.opt.dill';
|
||||
|
||||
Future<void> serializeMainModuleComponent(
|
||||
Component component, Uri dynamicModuleMainUri,
|
||||
CompilerPhaseInputOutputManager ioManager,
|
||||
Component component,
|
||||
Uri dynamicModuleMainUri,
|
||||
{required bool optimized}) async {
|
||||
// TODO(natebiggs): Serialize as a summary and filter to only necessary
|
||||
// libraries.
|
||||
await writeComponentToBinary(
|
||||
await ioManager.writeComponent(
|
||||
component,
|
||||
optimized
|
||||
? _makeOptDillPath(dynamicModuleMainUri.path)
|
||||
@@ -505,21 +506,15 @@ Future<(Component, JSMethods)> generateDynamicSubmoduleComponent(
|
||||
}
|
||||
|
||||
Future<MainModuleMetadata> deserializeMainModuleMetadata(
|
||||
Component component, WasmCompilerOptions options) async {
|
||||
final filename = options.dynamicModuleMetadataFile ??
|
||||
Uri.parse(path.setExtension(
|
||||
options.dynamicMainModuleUri!.toFilePath(), '.dyndata'));
|
||||
final dynamicModuleMetadataBytes = await File.fromUri(filename).readAsBytes();
|
||||
final source = DataDeserializer(dynamicModuleMetadataBytes, component);
|
||||
Component component, CompilerPhaseInputOutputManager ioManager) async {
|
||||
final source = DataDeserializer(
|
||||
await ioManager.readMainDynModuleMetadataBytes(), component);
|
||||
return MainModuleMetadata.deserialize(source);
|
||||
}
|
||||
|
||||
Future<void> serializeMainModuleMetadata(Component component,
|
||||
Translator translator, WasmCompilerOptions options) async {
|
||||
final filename = options.dynamicModuleMetadataFile ??
|
||||
Uri.parse(path.setExtension(
|
||||
options.dynamicMainModuleUri!.toFilePath(), '.dyndata'));
|
||||
Translator translator, CompilerPhaseInputOutputManager ioManager) async {
|
||||
final serializer = DataSerializer(component);
|
||||
translator.dynamicModuleInfo!.metadata.serialize(serializer, translator);
|
||||
await File.fromUri(filename).writeAsBytes(serializer.takeBytes());
|
||||
await ioManager.writeMainDynModuleMetadataBytes(serializer.takeBytes());
|
||||
}
|
||||
|
||||
@@ -2,16 +2,13 @@
|
||||
// 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';
|
||||
|
||||
import 'package:front_end/src/api_prototype/standard_file_system.dart'
|
||||
show StandardFileSystem;
|
||||
import 'package:front_end/src/api_unstable/vm.dart' show printDiagnosticMessage;
|
||||
import 'package:kernel/kernel.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'compile.dart';
|
||||
import 'compiler_options.dart';
|
||||
import 'io_util.dart';
|
||||
|
||||
export 'package:dart2wasm/compiler_options.dart';
|
||||
|
||||
@@ -61,25 +58,9 @@ Future<int> generateWasm(WasmCompilerOptions options,
|
||||
' - watch points = [${translatorOptions.watchPoints.map((p) => p.toString()).join(',')}]');
|
||||
}
|
||||
|
||||
String moduleNameToWasmOutputFile(String moduleName) {
|
||||
return path.join(path.dirname(options.outputFile), moduleName);
|
||||
}
|
||||
|
||||
String moduleNameToSourceMapFile(String moduleName) {
|
||||
return '${moduleNameToWasmOutputFile(moduleName)}.map';
|
||||
}
|
||||
|
||||
Uri moduleNameToRelativeSourceMapUri(String moduleName) {
|
||||
return Uri.file(path.basename(moduleNameToSourceMapFile(moduleName)));
|
||||
}
|
||||
|
||||
final relativeSourceMapUrlMapper = translatorOptions.generateSourceMaps
|
||||
? moduleNameToRelativeSourceMapUri
|
||||
: null;
|
||||
|
||||
final fileSystem = StandardFileSystem.instance;
|
||||
CompilationResult result = await compile(
|
||||
options, StandardFileSystem.instance, relativeSourceMapUrlMapper,
|
||||
(message) {
|
||||
options, CompilerPhaseInputOutputManager(fileSystem, options), (message) {
|
||||
if (!options.dryRun) printDiagnosticMessage(message, errorPrinter);
|
||||
});
|
||||
|
||||
@@ -109,35 +90,5 @@ Future<int> generateWasm(WasmCompilerOptions options,
|
||||
return 255;
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case CfeResult(:final component):
|
||||
await File(options.outputFile)
|
||||
.writeAsBytes(writeComponentToBytes(component));
|
||||
case TfaResult(:final component):
|
||||
await File(options.outputFile)
|
||||
.writeAsBytes(writeComponentToBytes(component));
|
||||
case CodegenResult(:final wasmModules, :final jsRuntime, :final supportJs):
|
||||
final writeFutures = <Future>[];
|
||||
wasmModules.forEach((moduleName, moduleInfo) {
|
||||
final (:moduleBytes, :sourceMap) = moduleInfo;
|
||||
final File outFile = File(moduleNameToWasmOutputFile(moduleName));
|
||||
outFile.parent.createSync(recursive: true);
|
||||
writeFutures.add(outFile.writeAsBytes(moduleBytes));
|
||||
|
||||
if (sourceMap != null) {
|
||||
writeFutures.add(File(moduleNameToSourceMapFile(moduleName))
|
||||
.writeAsString(sourceMap));
|
||||
}
|
||||
});
|
||||
await Future.wait(writeFutures);
|
||||
|
||||
final jsFile = path.setExtension(options.outputFile, '.mjs');
|
||||
await File(jsFile).writeAsString(jsRuntime);
|
||||
|
||||
final supportJsFile =
|
||||
path.setExtension(options.outputFile, '.support.js');
|
||||
await File(supportJsFile).writeAsString(supportJs);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
// Copyright (c) 2025, 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.
|
||||
|
||||
import 'dart:io' show File, Directory, Process, ProcessResult;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:build_integration/file_system/multi_root.dart'
|
||||
show MultiRootFileSystemEntity, MultiRootFileSystem;
|
||||
import 'package:front_end/src/api_prototype/file_system.dart' show FileSystem;
|
||||
import 'package:kernel/ast.dart' show Component;
|
||||
import 'package:kernel/binary/ast_from_binary.dart'
|
||||
show BinaryBuilderWithMetadata;
|
||||
import 'package:kernel/kernel.dart'
|
||||
show writeComponentToBinary, writeComponentToText;
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'compiler_options.dart';
|
||||
|
||||
class CompilerPhaseInputOutputManager {
|
||||
final FileSystem fileSystem;
|
||||
final WasmCompilerOptions options;
|
||||
|
||||
CompilerPhaseInputOutputManager(FileSystem fileSystem, this.options)
|
||||
: fileSystem = options.multiRootScheme != null
|
||||
? MultiRootFileSystem(
|
||||
options.multiRootScheme!,
|
||||
options.multiRoots.isEmpty ? [Uri.base] : options.multiRoots,
|
||||
fileSystem)
|
||||
: fileSystem;
|
||||
|
||||
String _moduleNameToWasmFile(String prefix, String moduleName) {
|
||||
return path.join(path.dirname(prefix), moduleName);
|
||||
}
|
||||
|
||||
String _moduleNameToSourceMapFile(String prefix, String moduleName) {
|
||||
return '${_moduleNameToWasmFile(prefix, moduleName)}.map';
|
||||
}
|
||||
|
||||
Uri _moduleNameToRelativeSourceMapUri(String moduleName) {
|
||||
return Uri.file(path
|
||||
.basename(_moduleNameToSourceMapFile(options.outputFile, moduleName)));
|
||||
}
|
||||
|
||||
Uri Function(String)? get sourceMapUrlGenerator =>
|
||||
options.translatorOptions.generateSourceMaps
|
||||
? _moduleNameToRelativeSourceMapUri
|
||||
: null;
|
||||
|
||||
Future<String> readString(Uri uri) async {
|
||||
return await File.fromUri((await resolveUri(uri))!).readAsString();
|
||||
}
|
||||
|
||||
Future<List<int>> readBytes(Uri uri) async {
|
||||
return await File.fromUri((await resolveUri(uri))!).readAsBytes();
|
||||
}
|
||||
|
||||
Future<void> readComponent(Uri componentUri, Component component) async {
|
||||
BinaryBuilderWithMetadata(
|
||||
await File.fromUri((await resolveUri(componentUri))!).readAsBytes())
|
||||
.readComponent(component);
|
||||
}
|
||||
|
||||
Future<void> writeComponent(Component component, String path,
|
||||
{bool includeSource = true}) {
|
||||
return writeComponentToBinary(component, path,
|
||||
includeSource: includeSource);
|
||||
}
|
||||
|
||||
void writeComponentAsText(Component component, String path) {
|
||||
writeComponentToText(component, path: path, showMetadata: true);
|
||||
}
|
||||
|
||||
Future<void> writeWasmModule(Uint8List wasmModule, String moduleName) {
|
||||
final wasmFileName = _moduleNameToWasmFile(options.outputFile, moduleName);
|
||||
final Directory dir = Directory(path.dirname(wasmFileName));
|
||||
// Do this synchronously to make sure it happens before subsequent async
|
||||
// operations.
|
||||
if (!dir.existsSync()) {
|
||||
dir.createSync(recursive: true);
|
||||
}
|
||||
|
||||
return File(wasmFileName).writeAsBytes(wasmModule);
|
||||
}
|
||||
|
||||
Future<void> writeWasmSourceMap(String sourceMap, String moduleName) {
|
||||
return File(_moduleNameToSourceMapFile(options.outputFile, moduleName))
|
||||
.writeAsString(sourceMap);
|
||||
}
|
||||
|
||||
Future<void> writeJsRuntime(String jsRuntime) {
|
||||
return File(path.setExtension(options.outputFile, '.mjs'))
|
||||
.writeAsString(jsRuntime);
|
||||
}
|
||||
|
||||
Future<void> writeSupportJs(String supportJs) {
|
||||
return File(path.setExtension(options.outputFile, '.support.js'))
|
||||
.writeAsString(supportJs);
|
||||
}
|
||||
|
||||
Future<void> runWasmOpt(
|
||||
String mainWasmModule, int moduleId, List<String> flags) async {
|
||||
final inputModuleName = options.moduleNameForId(mainWasmModule, moduleId);
|
||||
|
||||
final outputModuleName =
|
||||
options.moduleNameForId(options.outputFile, moduleId);
|
||||
final wasmOutName =
|
||||
_moduleNameToWasmFile(options.outputFile, outputModuleName);
|
||||
final wasmInName = _moduleNameToWasmFile(mainWasmModule, inputModuleName);
|
||||
final args = [
|
||||
...flags,
|
||||
wasmInName,
|
||||
'-o',
|
||||
wasmOutName,
|
||||
if (options.translatorOptions.generateSourceMaps) ...[
|
||||
'-ism',
|
||||
_moduleNameToSourceMapFile(mainWasmModule, inputModuleName),
|
||||
'-osm',
|
||||
_moduleNameToSourceMapFile(options.outputFile, outputModuleName),
|
||||
],
|
||||
if (!options.stripWasm) '-g',
|
||||
];
|
||||
if (options.saveUnopt) {
|
||||
await File(wasmInName)
|
||||
.copy(path.setExtension(wasmOutName, '.unopt.wasm'));
|
||||
}
|
||||
final wasmOptPath = options.wasmOptPath?.toFilePath() ?? 'wasm-opt';
|
||||
final result = await _runProcess(wasmOptPath, args);
|
||||
if (result.exitCode != 0) {
|
||||
throw Exception('wasm-opt failed with exit code ${result.exitCode}:'
|
||||
'\n${result.stdout}\n${result.stderr}');
|
||||
}
|
||||
}
|
||||
|
||||
Future<ProcessResult> _runProcess(
|
||||
String executable, List<String> args) async {
|
||||
return await Process.run(executable, args);
|
||||
}
|
||||
|
||||
Future<int> getModuleCount(Uri mainWasmFile) async {
|
||||
final mainPath = (await resolveUri(mainWasmFile))!.toFilePath();
|
||||
final files = (await Directory(path.dirname(mainPath)).list().toList());
|
||||
final prefix = path.basenameWithoutExtension(mainPath);
|
||||
bool isMultiModule = false;
|
||||
int maxModuleId = 0;
|
||||
for (final file in files) {
|
||||
if (file is! File) continue;
|
||||
final fileBase = path.basename(file.path);
|
||||
if (!fileBase.startsWith(prefix)) continue;
|
||||
if (path.extension(fileBase) != '.wasm') continue;
|
||||
final fileSuffix =
|
||||
path.setExtension(fileBase, '').substring(prefix.length);
|
||||
if (!fileSuffix.startsWith('_module')) continue;
|
||||
isMultiModule = true;
|
||||
final moduleId = int.tryParse(fileSuffix.substring('_module'.length));
|
||||
if (moduleId == null) continue;
|
||||
maxModuleId = moduleId > maxModuleId ? moduleId : maxModuleId;
|
||||
}
|
||||
return isMultiModule ? maxModuleId + 1 : 1;
|
||||
}
|
||||
|
||||
Future<Uint8List> readMainDynModuleMetadataBytes() async {
|
||||
final filename = options.dynamicModuleMetadataFile ??
|
||||
Uri.parse(path.setExtension(
|
||||
options.dynamicMainModuleUri!.toFilePath(), '.dyndata'));
|
||||
return await File.fromUri(filename).readAsBytes();
|
||||
}
|
||||
|
||||
Future<void> writeMainDynModuleMetadataBytes(Uint8List bytes) async {
|
||||
final filename = options.dynamicModuleMetadataFile ??
|
||||
Uri.parse(path.setExtension(
|
||||
options.dynamicMainModuleUri!.toFilePath(), '.dyndata'));
|
||||
await File.fromUri(filename).writeAsBytes(bytes);
|
||||
}
|
||||
|
||||
Future<Uri?> resolveUri(Uri? uri) async {
|
||||
if (uri == null) return null;
|
||||
var fileSystemEntity = fileSystem.entityForUri(uri);
|
||||
if (fileSystemEntity is MultiRootFileSystemEntity) {
|
||||
fileSystemEntity = await fileSystemEntity.delegate;
|
||||
}
|
||||
return fileSystemEntity.uri;
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,11 @@
|
||||
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/core_types.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'compiler_options.dart';
|
||||
import 'target.dart';
|
||||
import 'util.dart';
|
||||
|
||||
const _mainModuleId = 0;
|
||||
|
||||
Library? _enclosingLibraryForReference(Reference reference) {
|
||||
TreeNode? current = reference.node;
|
||||
// References generated for constants will not have a node attached.
|
||||
@@ -24,7 +21,7 @@ Library? _enclosingLibraryForReference(Reference reference) {
|
||||
}
|
||||
|
||||
class ModuleMetadataBuilder {
|
||||
int _counter = _mainModuleId;
|
||||
int _counter = WasmCompilerOptions.mainModuleId;
|
||||
final WasmCompilerOptions options;
|
||||
|
||||
ModuleMetadataBuilder(this.options);
|
||||
@@ -34,14 +31,9 @@ class ModuleMetadataBuilder {
|
||||
final id = _counter++;
|
||||
final moduleImportName =
|
||||
options.translatorOptions.minify ? intToMinString(id) : 'module$id';
|
||||
return ModuleMetadata._(
|
||||
moduleImportName,
|
||||
emitAsMain || id == _mainModuleId
|
||||
? path.basename(options.outputFile)
|
||||
: path.basename(
|
||||
path.setExtension(options.outputFile, '_module$id.wasm')),
|
||||
skipEmit: skipEmit,
|
||||
isMain: id == _mainModuleId);
|
||||
return ModuleMetadata._(moduleImportName,
|
||||
options.moduleNameForId(options.outputFile, id, emitAsMain: emitAsMain),
|
||||
skipEmit: skipEmit, isMain: id == WasmCompilerOptions.mainModuleId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,17 +44,21 @@ class ValueOption<T> extends Option<T> {
|
||||
void Function(WasmCompilerOptions o, T v) applyToOptions,
|
||||
T Function(dynamic v) converter,
|
||||
{String? defaultsTo,
|
||||
String? abbr,
|
||||
bool hide = false})
|
||||
: super(name, (a) => a.addOption(name, defaultsTo: defaultsTo),
|
||||
applyToOptions, converter);
|
||||
: super(
|
||||
name,
|
||||
(a) => a.addOption(name, defaultsTo: defaultsTo, abbr: abbr),
|
||||
applyToOptions,
|
||||
converter);
|
||||
}
|
||||
|
||||
class IntOption extends ValueOption<int> {
|
||||
IntOption(
|
||||
String name, void Function(WasmCompilerOptions o, int v) applyToOptions,
|
||||
{String? defaultsTo})
|
||||
{String? defaultsTo, String? abbr})
|
||||
: super(name, applyToOptions, (v) => int.parse(v),
|
||||
defaultsTo: defaultsTo);
|
||||
defaultsTo: defaultsTo, abbr: abbr);
|
||||
}
|
||||
|
||||
class StringOption extends ValueOption<String> {
|
||||
|
||||
@@ -44,15 +44,16 @@ import 'util.dart' as util;
|
||||
class TranslatorOptions {
|
||||
bool enableAsserts = false;
|
||||
bool importSharedMemory = false;
|
||||
bool inlining = true;
|
||||
int optimizationLevel = 1;
|
||||
bool? inliningOverride;
|
||||
bool jsCompatibility = false;
|
||||
bool omitImplicitTypeChecks = false;
|
||||
bool? omitImplicitTypeChecksOverride;
|
||||
bool omitExplicitTypeChecks = false;
|
||||
bool omitBoundsChecks = false;
|
||||
bool? omitBoundsChecksOverride;
|
||||
bool polymorphicSpecialization = false;
|
||||
bool printKernel = false;
|
||||
bool printWasm = false;
|
||||
bool minify = false;
|
||||
bool? minifyOverride;
|
||||
bool verifyTypeChecks = false;
|
||||
bool verbose = false;
|
||||
bool enableExperimentalFfi = false;
|
||||
@@ -67,18 +68,26 @@ class TranslatorOptions {
|
||||
bool requireJsStringBuiltin = false;
|
||||
List<int> watchPoints = [];
|
||||
|
||||
bool get inlining => inliningOverride ?? optimizationLevel >= 1;
|
||||
bool get minify => minifyOverride ?? optimizationLevel >= 2;
|
||||
bool get omitImplicitTypeChecks =>
|
||||
omitImplicitTypeChecksOverride ?? optimizationLevel >= 3;
|
||||
bool get omitBoundsChecks =>
|
||||
omitBoundsChecksOverride ?? optimizationLevel >= 4;
|
||||
|
||||
void serialize(DataSerializer sink) {
|
||||
sink.writeBool(enableAsserts);
|
||||
sink.writeBool(importSharedMemory);
|
||||
sink.writeBool(inlining);
|
||||
sink.writeInt(optimizationLevel);
|
||||
sink.writeNullable(inliningOverride, sink.writeBool);
|
||||
sink.writeBool(jsCompatibility);
|
||||
sink.writeBool(omitImplicitTypeChecks);
|
||||
sink.writeNullable(omitImplicitTypeChecksOverride, sink.writeBool);
|
||||
sink.writeBool(omitExplicitTypeChecks);
|
||||
sink.writeBool(omitBoundsChecks);
|
||||
sink.writeNullable(omitBoundsChecksOverride, sink.writeBool);
|
||||
sink.writeBool(polymorphicSpecialization);
|
||||
sink.writeBool(printKernel);
|
||||
sink.writeBool(printWasm);
|
||||
sink.writeBool(minify);
|
||||
sink.writeNullable(minifyOverride, sink.writeBool);
|
||||
sink.writeBool(verifyTypeChecks);
|
||||
sink.writeBool(verbose);
|
||||
sink.writeBool(enableExperimentalFfi);
|
||||
@@ -97,15 +106,17 @@ class TranslatorOptions {
|
||||
final TranslatorOptions options = TranslatorOptions();
|
||||
options.enableAsserts = source.readBool();
|
||||
options.importSharedMemory = source.readBool();
|
||||
options.inlining = source.readBool();
|
||||
options.optimizationLevel = source.readInt();
|
||||
options.inliningOverride = source.readNullable(source.readBool);
|
||||
options.jsCompatibility = source.readBool();
|
||||
options.omitImplicitTypeChecks = source.readBool();
|
||||
options.omitImplicitTypeChecksOverride =
|
||||
source.readNullable(source.readBool);
|
||||
options.omitExplicitTypeChecks = source.readBool();
|
||||
options.omitBoundsChecks = source.readBool();
|
||||
options.omitBoundsChecksOverride = source.readNullable(source.readBool);
|
||||
options.polymorphicSpecialization = source.readBool();
|
||||
options.printKernel = source.readBool();
|
||||
options.printWasm = source.readBool();
|
||||
options.minify = source.readBool();
|
||||
options.minifyOverride = source.readNullable(source.readBool);
|
||||
options.verifyTypeChecks = source.readBool();
|
||||
options.verbose = source.readBool();
|
||||
options.enableExperimentalFfi = source.readBool();
|
||||
|
||||
@@ -13,6 +13,7 @@ final String mainDart = '${path.dirname(Platform.script.path)}/data/main.dart';
|
||||
final String cfeDillName = 'main.cfe.dill';
|
||||
final String tfaDillName = 'main.tfa.dill';
|
||||
final String wasmOutName = 'main.wasm';
|
||||
final String wasmOptOutName = 'main.opt.wasm';
|
||||
|
||||
Future<void> main() async {
|
||||
await testSuccessCases();
|
||||
@@ -21,11 +22,10 @@ Future<void> main() async {
|
||||
|
||||
Future<void> testSuccessCases() async {
|
||||
await withTempDir((tmpDirPath) async {
|
||||
final tmpDir = File(tmpDirPath);
|
||||
|
||||
final cfeDill = File.fromUri(tmpDir.uri.resolve(cfeDillName));
|
||||
final tfaDill = File.fromUri(tmpDir.uri.resolve(tfaDillName));
|
||||
final wasmOut = File.fromUri(tmpDir.uri.resolve(wasmOutName));
|
||||
final cfeDill = File(path.join(tmpDirPath, cfeDillName));
|
||||
final tfaDill = File(path.join(tmpDirPath, tfaDillName));
|
||||
final wasmOut = File(path.join(tmpDirPath, wasmOutName));
|
||||
final wasmOptOut = File(path.join(tmpDirPath, wasmOptOutName));
|
||||
|
||||
// Run CFE and expect output
|
||||
await run([
|
||||
@@ -62,13 +62,24 @@ Future<void> testSuccessCases() async {
|
||||
]);
|
||||
Expect.isTrue(await wasmOut.exists());
|
||||
Expect.isTrue((await wasmOut.stat()).size > 0);
|
||||
|
||||
// Run opt and expect output
|
||||
await run([
|
||||
dartAotExecutable,
|
||||
dart2wasmSnapshot,
|
||||
'--platform=$platformDill',
|
||||
'--phases=opt',
|
||||
'--wasm-opt=$wasmOptExecutable',
|
||||
wasmOut.path,
|
||||
wasmOptOut.path,
|
||||
]);
|
||||
Expect.isTrue(await wasmOptOut.exists());
|
||||
Expect.isTrue((await wasmOptOut.stat()).size > 0);
|
||||
});
|
||||
|
||||
await withTempDir((tmpDirPath) async {
|
||||
final tmpDir = File(tmpDirPath);
|
||||
|
||||
final tfaDill = File.fromUri(tmpDir.uri.resolve(tfaDillName));
|
||||
final wasmOut = File.fromUri(tmpDir.uri.resolve(wasmOutName));
|
||||
final tfaDill = File(path.join(tmpDirPath, tfaDillName));
|
||||
final wasmOut = File(path.join(tmpDirPath, wasmOutName));
|
||||
|
||||
// Run CFE & TFA and expect output
|
||||
await run([
|
||||
@@ -96,10 +107,8 @@ Future<void> testSuccessCases() async {
|
||||
});
|
||||
|
||||
await withTempDir((tmpDirPath) async {
|
||||
final tmpDir = File(tmpDirPath);
|
||||
|
||||
final cfeDill = File.fromUri(tmpDir.uri.resolve(tfaDillName));
|
||||
final wasmOut = File.fromUri(tmpDir.uri.resolve(wasmOutName));
|
||||
final cfeDill = File(path.join(tmpDirPath, cfeDillName));
|
||||
final wasmOut = File(path.join(tmpDirPath, wasmOutName));
|
||||
|
||||
// Run CFE and expect output
|
||||
await run([
|
||||
@@ -127,9 +136,7 @@ Future<void> testSuccessCases() async {
|
||||
});
|
||||
|
||||
await withTempDir((tmpDirPath) async {
|
||||
final tmpDir = File(tmpDirPath);
|
||||
|
||||
final wasmOut = File.fromUri(tmpDir.uri.resolve(wasmOutName));
|
||||
final wasmOut = File(path.join(tmpDirPath, wasmOutName));
|
||||
|
||||
// Run CFE & TFA & codegen and expect output
|
||||
await run([
|
||||
@@ -143,15 +150,31 @@ Future<void> testSuccessCases() async {
|
||||
Expect.isTrue(await wasmOut.exists());
|
||||
Expect.isTrue((await wasmOut.stat()).size > 0);
|
||||
});
|
||||
|
||||
await withTempDir((tmpDirPath) async {
|
||||
final wasmOptOut = File(path.join(tmpDirPath, wasmOptOutName));
|
||||
|
||||
// Run CFE & TFA & codegen & opt and expect output
|
||||
await run([
|
||||
dartAotExecutable,
|
||||
dart2wasmSnapshot,
|
||||
'--platform=$platformDill',
|
||||
'--phases=cfe,tfa,codegen,opt',
|
||||
'--wasm-opt=$wasmOptExecutable',
|
||||
mainDart,
|
||||
wasmOptOut.path,
|
||||
]);
|
||||
Expect.isTrue(await wasmOptOut.exists());
|
||||
Expect.isTrue((await wasmOptOut.stat()).size > 0);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> testFailureCases() async {
|
||||
await withTempDir((tmpDirPath) async {
|
||||
final tmpDir = File(tmpDirPath);
|
||||
|
||||
final cfeDill = File.fromUri(tmpDir.uri.resolve(cfeDillName));
|
||||
final tfaDill = File.fromUri(tmpDir.uri.resolve(tfaDillName));
|
||||
final wasmOut = File.fromUri(tmpDir.uri.resolve(wasmOutName));
|
||||
final cfeDill = File(path.join(tmpDirPath, cfeDillName));
|
||||
final tfaDill = File(path.join(tmpDirPath, tfaDillName));
|
||||
final wasmOut = File(path.join(tmpDirPath, wasmOutName));
|
||||
final wasmOptOut = File(path.join(tmpDirPath, wasmOptOutName));
|
||||
|
||||
// CFE checks
|
||||
await expectFailedRun([
|
||||
@@ -177,7 +200,6 @@ Future<void> testFailureCases() async {
|
||||
dartAotExecutable,
|
||||
dart2wasmSnapshot,
|
||||
'--platform=$platformDill',
|
||||
'--platform=$platformDill',
|
||||
'--phases=tfa',
|
||||
mainDart,
|
||||
tfaDill.path
|
||||
@@ -197,7 +219,6 @@ Future<void> testFailureCases() async {
|
||||
dartAotExecutable,
|
||||
dart2wasmSnapshot,
|
||||
'--platform=$platformDill',
|
||||
'--platform=$platformDill',
|
||||
'--phases=codegen',
|
||||
mainDart,
|
||||
wasmOut.path
|
||||
@@ -212,6 +233,25 @@ Future<void> testFailureCases() async {
|
||||
cfeDill.path
|
||||
], 'Output from codegen phase must be a .wasm file');
|
||||
|
||||
// Opt checks
|
||||
await expectFailedRun([
|
||||
dartAotExecutable,
|
||||
dart2wasmSnapshot,
|
||||
'--platform=$platformDill',
|
||||
'--phases=opt',
|
||||
mainDart,
|
||||
wasmOptOut.path
|
||||
], 'Input to opt phase must be a .wasm file');
|
||||
|
||||
await expectFailedRun([
|
||||
dartAotExecutable,
|
||||
dart2wasmSnapshot,
|
||||
'--platform=$platformDill',
|
||||
'--phases=opt',
|
||||
wasmOut.path,
|
||||
cfeDill.path
|
||||
], 'Output from opt phase must be a .wasm file');
|
||||
|
||||
// Other checks
|
||||
await expectFailedRun([
|
||||
dartAotExecutable,
|
||||
@@ -230,6 +270,16 @@ Future<void> testFailureCases() async {
|
||||
mainDart,
|
||||
cfeDill.path
|
||||
], 'Invalid compiler phase name');
|
||||
|
||||
await expectFailedRun([
|
||||
dartAotExecutable,
|
||||
dart2wasmSnapshot,
|
||||
'--platform=$platformDill',
|
||||
'--phases=opt',
|
||||
'-O0',
|
||||
wasmOut.path,
|
||||
wasmOptOut.path
|
||||
], 'Cannot specify "opt" phase with optimization level 0');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@ final dartAotExecutable = Uri.parse(Platform.resolvedExecutable)
|
||||
final dart2wasmSnapshot = Uri.parse(Platform.resolvedExecutable)
|
||||
.resolve('snapshots/dart2wasm_product.snapshot')
|
||||
.toFilePath();
|
||||
final wasmOptExecutable = Uri.parse(Platform.resolvedExecutable)
|
||||
.resolve('utils/wasm-opt')
|
||||
.toFilePath();
|
||||
final platformDill = Uri.parse(Platform.resolvedExecutable)
|
||||
.resolve('../lib/_internal/dart2wasm_platform.dill')
|
||||
.toFilePath();
|
||||
|
||||
@@ -58,17 +58,8 @@ LIBRARIES_JSON_ARG="--libraries-spec=$SDK_DIR/sdk/lib/libraries.json"
|
||||
function find_flags {
|
||||
echo -en "$(sed -n "/$1 =/,/end of $1/ p" $SDK_DIR/pkg/dartdev/lib/src/commands/compile.dart | sed '1d' | sed '$d' | tr '\n' ' ' | sed 's#\s\+# #g' | sed 's#^\s\+##' | sed 's#\s\+$##')"
|
||||
}
|
||||
# Use same flags as `dart compile exe`
|
||||
BINARYEN_FLAGS=($(find_flags 'binaryenFlags'))
|
||||
BINARYEN_FLAGS_DEFERRED_LOADING=($(find_flags 'binaryenFlagsDeferredLoading'))
|
||||
OPT_FLAGS_L0=($(find_flags 'optimizationLevel0Flags'))
|
||||
OPT_FLAGS_L1=($(find_flags 'optimizationLevel1Flags'))
|
||||
OPT_FLAGS_L2=($(find_flags 'optimizationLevel2Flags'))
|
||||
OPT_FLAGS_L3=($(find_flags 'optimizationLevel3Flags'))
|
||||
OPT_FLAGS_L4=($(find_flags 'optimizationLevel4Flags'))
|
||||
|
||||
RUN_BINARYEN=1
|
||||
MULTI_MODULE=0
|
||||
RUN_SRC=0
|
||||
GENERATE_SOURCE_MAP=1
|
||||
COMPILE_BENCHMARK_BASE_NAME=""
|
||||
@@ -79,7 +70,6 @@ SNAPSHOT_NAME="dart2wasm"
|
||||
# flags.
|
||||
VM_ARGS=()
|
||||
DART2WASM_ARGS=("--require-js-string-builtin")
|
||||
ADDITIONAL_BINARYEN_FLAGS=()
|
||||
DART_FILE=""
|
||||
OUTPUT_FILE=""
|
||||
while [ $# -gt 0 ]; do
|
||||
@@ -100,11 +90,6 @@ while [ $# -gt 0 ]; do
|
||||
shift
|
||||
;;
|
||||
|
||||
-g | --no-strip-wasm)
|
||||
ADDITIONAL_BINARYEN_FLAGS+=("-g")
|
||||
shift
|
||||
;;
|
||||
|
||||
--compiler-asserts)
|
||||
SNAPSHOT_NAME="dart2wasm_asserts"
|
||||
VM_ARGS+=("--enable-asserts")
|
||||
@@ -112,52 +97,16 @@ while [ $# -gt 0 ]; do
|
||||
;;
|
||||
|
||||
-O0 | --optimization-level=0)
|
||||
DART2WASM_ARGS+=(${OPT_FLAGS_L0[@]})
|
||||
DART2WASM_ARGS+=("-O0")
|
||||
RUN_BINARYEN=0
|
||||
shift
|
||||
;;
|
||||
|
||||
-O1 | --optimization-level=1)
|
||||
DART2WASM_ARGS+=(${OPT_FLAGS_L1[@]})
|
||||
RUN_BINARYEN=1
|
||||
shift
|
||||
;;
|
||||
|
||||
-O2 | --optimization-level=2)
|
||||
DART2WASM_ARGS+=(${OPT_FLAGS_L2[@]})
|
||||
RUN_BINARYEN=1
|
||||
shift
|
||||
;;
|
||||
|
||||
-O3 | --optimization-level=3)
|
||||
DART2WASM_ARGS+=(${OPT_FLAGS_L3[@]})
|
||||
RUN_BINARYEN=1
|
||||
shift
|
||||
;;
|
||||
|
||||
-O4 | --optimization-level=4)
|
||||
DART2WASM_ARGS+=(${OPT_FLAGS_L4[@]})
|
||||
RUN_BINARYEN=1
|
||||
shift
|
||||
;;
|
||||
|
||||
--extra-compiler-option=--platform=*)
|
||||
PLATFORM_FILENAME="${1#--extra-compiler-option=--platform=}"
|
||||
shift
|
||||
;;
|
||||
|
||||
--enable-deferred-loading | --extra-compiler-option=--enable-deferred-loading)
|
||||
MULTI_MODULE=1
|
||||
DART2WASM_ARGS+=("--enable-deferred-loading")
|
||||
shift
|
||||
;;
|
||||
|
||||
--extra-compiler-option=--enable-multi-module-stress-test-mode)
|
||||
MULTI_MODULE=1
|
||||
DART2WASM_ARGS+=("--enable-multi-module-stress-test-mode")
|
||||
shift
|
||||
;;
|
||||
|
||||
--extra-compiler-option=--no-js-compatibility)
|
||||
PLATFORM_FILENAME="$BIN_DIR/dart2wasm_platform.dill"
|
||||
DART2WASM_ARGS+=("--no-js-compatibility")
|
||||
@@ -243,9 +192,9 @@ COMPILER_GZIP_SIZE=0
|
||||
|
||||
function run_compiler() {
|
||||
if [ $RUN_SRC -eq 1 ]; then
|
||||
dart2wasm_command=("$DART" "${VM_ARGS[@]}" "$DART2WASM_SRC" "$LIBRARIES_JSON_ARG" "${DART2WASM_ARGS[@]}" "$DART_FILE" "$OUTPUT_FILE")
|
||||
dart2wasm_command=("$DART" "${VM_ARGS[@]}" "$DART2WASM_SRC" "$LIBRARIES_JSON_ARG" "${DART2WASM_ARGS[@]}" "--phases=cfe,tfa,codegen" "$DART_FILE" "$OUTPUT_FILE")
|
||||
else
|
||||
dart2wasm_command=("$DART_AOT_RUNTIME" "${VM_ARGS[@]}" "$DART2WASM_AOT_SNAPSHOT" "$PLATFORM_ARG" "${DART2WASM_ARGS[@]}" "$DART_FILE" "$OUTPUT_FILE")
|
||||
dart2wasm_command=("$DART_AOT_RUNTIME" "${VM_ARGS[@]}" "$DART2WASM_AOT_SNAPSHOT" "$PLATFORM_ARG" "${DART2WASM_ARGS[@]}" "--phases=cfe,tfa,codegen" "$DART_FILE" "$OUTPUT_FILE")
|
||||
fi
|
||||
|
||||
if [ -n "$COMPILE_BENCHMARK_BASE_NAME" ]; then
|
||||
@@ -257,17 +206,11 @@ function run_compiler() {
|
||||
MJS_SIZE=$SIZE
|
||||
MJS_GZIP_SIZE=$GZIP_SIZE
|
||||
|
||||
if [ $MULTI_MODULE -eq 1 ]; then
|
||||
for OUTPUT_FILE in "${OUTPUT_FILE%.wasm}"*.wasm; do
|
||||
measure_size $OUTPUT_FILE
|
||||
(( COMPILER_SIZE+=$SIZE ))
|
||||
(( COMPILER_GZIP_SIZE+=$GZIP_SIZE ))
|
||||
done
|
||||
else
|
||||
for OUTPUT_FILE in "${OUTPUT_FILE%.wasm}"*.wasm; do
|
||||
measure_size $OUTPUT_FILE
|
||||
COMPILER_SIZE=$SIZE
|
||||
COMPILER_GZIP_SIZE=$GZIP_SIZE
|
||||
fi
|
||||
(( COMPILER_SIZE+=$SIZE ))
|
||||
(( COMPILER_GZIP_SIZE+=$GZIP_SIZE ))
|
||||
done
|
||||
else
|
||||
"${dart2wasm_command[@]}"
|
||||
fi
|
||||
@@ -278,43 +221,24 @@ BINARYEN_MEMORY=0
|
||||
BINARYEN_SIZE=0
|
||||
BINARYEN_GZIP_SIZE=0
|
||||
|
||||
function run_binaryen_single() {
|
||||
OUTPUT_FILE="$1"
|
||||
shift
|
||||
|
||||
if [ $GENERATE_SOURCE_MAP -eq 1 ]; then
|
||||
SOURCE_MAP_FLAGS=("-ism" "${OUTPUT_FILE}.map" "-osm" "${OUTPUT_FILE}.map")
|
||||
function run_binaryen() {
|
||||
if [ $RUN_SRC -eq 1 ]; then
|
||||
opt_command=("$DART" "${VM_ARGS[@]}" "$DART2WASM_SRC" "$LIBRARIES_JSON_ARG" "${DART2WASM_ARGS[@]}" "--phases=opt" "--wasm-opt=$BINARYEN" "$OUTPUT_FILE" "$OUTPUT_FILE")
|
||||
else
|
||||
opt_command=("$DART_AOT_RUNTIME" "${VM_ARGS[@]}" "$DART2WASM_AOT_SNAPSHOT" "$PLATFORM_ARG" "${DART2WASM_ARGS[@]}" "--phases=opt" "--wasm-opt=$BINARYEN" "$OUTPUT_FILE" "$OUTPUT_FILE")
|
||||
fi
|
||||
|
||||
binaryen_command=("$BINARYEN" "${ADDITIONAL_BINARYEN_FLAGS[@]}" "$@" "${SOURCE_MAP_FLAGS[@]}" "$OUTPUT_FILE" -o "$OUTPUT_FILE")
|
||||
if [ -n "$COMPILE_BENCHMARK_BASE_NAME" ]; then
|
||||
# If we're measuring run each binaryen command sequentially.
|
||||
measure ${binaryen_command[@]}
|
||||
measure "${opt_command[@]}"
|
||||
BINARYEN_TIME=$(echo "$BINARYEN_TIME + $TIME" | bc)
|
||||
BINARYEN_MEMORY=$(($BINARYEN_MEMORY > $MEMORY ? $BINARYEN_MEMORY : $MEMORY ))
|
||||
measure_size $OUTPUT_FILE
|
||||
BINARYEN_SIZE=$(echo "$BINARYEN_SIZE + $SIZE" | bc)
|
||||
BINARYEN_GZIP_SIZE=$(echo "$BINARYEN_GZIP_SIZE + $GZIP_SIZE" | bc)
|
||||
else
|
||||
${binaryen_command[@]} &
|
||||
fi
|
||||
}
|
||||
|
||||
function run_binaryen() {
|
||||
if [ $MULTI_MODULE -eq 1 ]; then
|
||||
# Iterate over all matching wasm files and optimize them concurrently in
|
||||
# different processes.
|
||||
for OUTPUT_FILE in "${OUTPUT_FILE%.wasm}"*.wasm; do
|
||||
run_binaryen_single "$OUTPUT_FILE" "${BINARYEN_FLAGS_DEFERRED_LOADING[@]}"
|
||||
done
|
||||
else
|
||||
run_binaryen_single "$OUTPUT_FILE" "${BINARYEN_FLAGS[@]}"
|
||||
fi
|
||||
wait
|
||||
|
||||
if [ -n "$COMPILE_BENCHMARK_BASE_NAME" ]; then
|
||||
MAX_MEMORY=$(($COMPILER_MEMORY > $BINARYEN_MEMORY ? $COMPILER_MEMORY : $BINARYEN_MEMORY ))
|
||||
TOTAL_TIME=$(echo "$COMPILER_TIME + $BINARYEN_TIME" | bc)
|
||||
else
|
||||
"${opt_command[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -723,86 +723,6 @@ class CompileWasmCommand extends CompileSubcommandCommand {
|
||||
static const String commandName = 'wasm';
|
||||
static const String help = 'Compile Dart to a WebAssembly/WasmGC module.';
|
||||
|
||||
// The unique place where we store various flags for dart2wasm & binaryen.
|
||||
//
|
||||
// Other uses (e.g. pkg/dart2wasm/tool/compile_benchmark) will grep in this
|
||||
// file for the flags. So please keep the formatting.
|
||||
|
||||
final List<String> binaryenFlags = _flagList('''
|
||||
--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=*<noInline>*
|
||||
|
||||
--closed-world
|
||||
--traps-never-happen
|
||||
--type-unfinalizing
|
||||
-Os
|
||||
--type-ssa
|
||||
--gufa
|
||||
-Os
|
||||
--type-merging
|
||||
-Os
|
||||
--type-finalizing
|
||||
--minimize-rec-groups
|
||||
'''); // end of binaryenFlags
|
||||
|
||||
final List<String> binaryenFlagsDeferredLoading = _flagList('''
|
||||
--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=*<noInline>*
|
||||
|
||||
--traps-never-happen
|
||||
-Os
|
||||
'''); // end of binaryenFlagsDeferredLoading
|
||||
|
||||
final List<String> optimizationLevel0Flags = _flagList('''
|
||||
--no-inlining
|
||||
--no-minify
|
||||
'''); // end of optimizationLevel0Flags
|
||||
|
||||
final List<String> optimizationLevel1Flags = _flagList('''
|
||||
--inlining
|
||||
--no-minify
|
||||
'''); // end of optimizationLevel1Flags
|
||||
|
||||
final List<String> optimizationLevel2Flags = _flagList('''
|
||||
--inlining
|
||||
--minify
|
||||
'''); // end of optimizationLevel2Flags
|
||||
|
||||
final List<String> optimizationLevel3Flags = _flagList('''
|
||||
--inlining
|
||||
--minify
|
||||
--omit-implicit-checks
|
||||
'''); // end of optimizationLevel3Flags
|
||||
|
||||
final List<String> optimizationLevel4Flags = _flagList('''
|
||||
--inlining
|
||||
--minify
|
||||
--omit-implicit-checks
|
||||
--omit-bounds-checks
|
||||
'''); // end of optimizationLevel4Flags
|
||||
|
||||
static List<String> _flagList(String lines) => lines
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.where((line) => line.isNotEmpty)
|
||||
.toList();
|
||||
|
||||
CompileWasmCommand({bool verbose = false})
|
||||
: super(commandName, help, verbose) {
|
||||
argParser
|
||||
@@ -857,6 +777,14 @@ class CompileWasmCommand extends CompileSubcommandCommand {
|
||||
valueHelp: 'page count',
|
||||
hide: !verbose,
|
||||
)
|
||||
..addMultiOption('phases',
|
||||
help: 'Specifies which phases of the dart2wasm compiler to run. Each '
|
||||
'phase will emit a partial result that is then the input to the '
|
||||
'next phase.',
|
||||
allowed: ['cfe', 'tfa', 'codegen', 'opt'],
|
||||
defaultsTo: ['cfe', 'tfa', 'codegen', 'opt'],
|
||||
hide: !verbose,
|
||||
splitCommas: true)
|
||||
..addMultiOption(
|
||||
'extra-compiler-option',
|
||||
abbr: 'E',
|
||||
@@ -872,13 +800,6 @@ class CompileWasmCommand extends CompileSubcommandCommand {
|
||||
allowed: ['0', '1', '2', '3', '4'],
|
||||
defaultsTo: '1',
|
||||
valueHelp: 'level',
|
||||
allowedHelp: {
|
||||
'0': optimizationLevel0Flags.join(' '),
|
||||
'1': optimizationLevel1Flags.join(' '),
|
||||
'2': optimizationLevel2Flags.join(' '),
|
||||
'3': optimizationLevel3Flags.join(' '),
|
||||
'4': optimizationLevel4Flags.join(' '),
|
||||
},
|
||||
hide: !verbose,
|
||||
)
|
||||
..addFlag(
|
||||
@@ -964,43 +885,23 @@ class CompileWasmCommand extends CompileSubcommandCommand {
|
||||
}
|
||||
}
|
||||
|
||||
final isDeferredLoading = args.flag('enable-deferred-loading');
|
||||
// Used in testing to force multiple modules.
|
||||
final isMultiStressTestMode = extraCompilerOptions
|
||||
.any((e) => e.contains('enable-multi-module-stress-test'));
|
||||
final isMultiModule = isDeferredLoading || isMultiStressTestMode;
|
||||
final optimizationLevel = int.parse(args.option('optimization-level')!);
|
||||
|
||||
final runWasmOpt =
|
||||
optimizationLevel >= 1 && path.extension(outputFile) == '.wasm';
|
||||
|
||||
if (runWasmOpt && !checkArtifactExists(sdk.wasmOpt)) {
|
||||
return 255;
|
||||
int? optimizationLevel;
|
||||
List<String> phases = args.multiOption('phases');
|
||||
if (args.wasParsed('phases')) {}
|
||||
if (args.option('optimization-level') != null) {
|
||||
optimizationLevel = int.tryParse(args.option('optimization-level')!);
|
||||
if (optimizationLevel == null) {
|
||||
usageException(
|
||||
'Error: The --optimization-level flag must specify a number!');
|
||||
}
|
||||
if (optimizationLevel == 0) {
|
||||
if (!args.wasParsed('phases')) {
|
||||
// Don't add the opt phase.
|
||||
phases.removeLast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleOverride(List<String> flags, String name, bool? value) {
|
||||
// If no override provided, default to what -O implies.
|
||||
if (value == null) return;
|
||||
|
||||
flags.removeWhere((option) => option == '--no-$name');
|
||||
flags.removeWhere((option) => option == '--$name');
|
||||
|
||||
// Explicitly use the flag value, irrespective of -O settings.
|
||||
value ? flags.add('--$name') : flags.add('--no-$name');
|
||||
}
|
||||
|
||||
final optimizationFlags = (switch (optimizationLevel) {
|
||||
0 => optimizationLevel0Flags,
|
||||
1 => optimizationLevel1Flags,
|
||||
2 => optimizationLevel2Flags,
|
||||
3 => optimizationLevel3Flags,
|
||||
4 => optimizationLevel4Flags,
|
||||
_ => throw 'unreachable',
|
||||
})
|
||||
.toList();
|
||||
handleOverride(optimizationFlags, 'minify',
|
||||
args.wasParsed('minify') ? args.flag('minify') : null);
|
||||
|
||||
final generateSourceMap = args.flag('source-maps');
|
||||
final enabledExperiments = args.enabledExperiments;
|
||||
final dart2wasmCommand = [
|
||||
@@ -1013,20 +914,19 @@ class CompileWasmCommand extends CompileSubcommandCommand {
|
||||
if (args.flag('print-kernel')) '--print-kernel',
|
||||
if (args.flag(enableAssertsOption.flag)) '--${enableAssertsOption.flag}',
|
||||
if (!generateSourceMap) '--no-source-maps',
|
||||
if (isDeferredLoading) '--enable-deferred-loading',
|
||||
if (optimizationLevel != null) '--optimization-level=$optimizationLevel',
|
||||
if (args.flag('minify')) '--minify',
|
||||
if (args.flag('strip-wasm')) '--strip-wasm',
|
||||
if (args.flag('enable-deferred-loading')) '--enable-deferred-loading',
|
||||
for (final define in defines) '-D$define',
|
||||
if (maxPages != null) ...[
|
||||
'--import-shared-memory',
|
||||
'--shared-memory-max-pages=$maxPages',
|
||||
],
|
||||
'--phases=${phases.join(",")}',
|
||||
'--wasm-opt=${sdk.wasmOpt}',
|
||||
...enabledExperiments.map((e) => '--enable-experiment=$e'),
|
||||
|
||||
// First we pass flags based on the optimization level.
|
||||
...optimizationFlags,
|
||||
|
||||
// Then we pass any extra compiler flags through.
|
||||
...extraCompilerOptions,
|
||||
|
||||
sourcePath,
|
||||
outputFile,
|
||||
];
|
||||
@@ -1042,102 +942,16 @@ class CompileWasmCommand extends CompileSubcommandCommand {
|
||||
return compileErrorExitCode;
|
||||
}
|
||||
|
||||
final bool strip = args.flag('strip-wasm');
|
||||
|
||||
// When running in dry run mode there will not be any file emitted.
|
||||
final isDryRun = extraCompilerOptions.any((e) => e.contains('dry-run'));
|
||||
|
||||
if (isDryRun) return 0;
|
||||
|
||||
if (runWasmOpt) {
|
||||
if (isMultiModule) {
|
||||
// Iterate over all matching wasm files and optimize them concurrently
|
||||
// in different processes.
|
||||
final outputFiles = await _listMultiWasmModules(
|
||||
path.dirname(outputFile), outputFileBasename);
|
||||
final futures = <Future<int>>[];
|
||||
for (final f in outputFiles) {
|
||||
final baseFileName = path.setExtension(f.path, '');
|
||||
futures.add(optimize(baseFileName, f.path,
|
||||
deferredLoadingEnabled: true,
|
||||
generateSourceMap: generateSourceMap,
|
||||
strip: strip));
|
||||
}
|
||||
final exitCode = (await Future.wait(futures))
|
||||
.firstWhere((r) => r != 0, orElse: () => 0);
|
||||
if (exitCode != 0) return exitCode;
|
||||
} else {
|
||||
final exitCode = await optimize(outputFileBasename, outputFile,
|
||||
deferredLoadingEnabled: false,
|
||||
generateSourceMap: generateSourceMap,
|
||||
strip: strip);
|
||||
if (exitCode != 0) return exitCode;
|
||||
}
|
||||
}
|
||||
|
||||
final mjsFile = '$outputFileBasename.mjs';
|
||||
log.stdout(
|
||||
"Generated wasm module '$outputFile', and JS init file '$mjsFile'.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Future<List<File>> _listMultiWasmModules(
|
||||
String outputDir, String outputFileBasename) async {
|
||||
final files = <File>[];
|
||||
final outputFiles = await Directory(outputDir).list().toList();
|
||||
// When multiple modules are produced from wasm (e.g. with deferred
|
||||
// loading), the compiler emits files:
|
||||
// - basename.wasm (main module)
|
||||
// - basename_module{1...N}.wasm (extra modules)
|
||||
for (final f in outputFiles) {
|
||||
if (f is! File) continue;
|
||||
if (!path.split(f.path).last.startsWith(outputFileBasename)) continue;
|
||||
if (path.extension(f.path) != '.wasm') continue;
|
||||
|
||||
files.add(f);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
Future<int> optimize(String outputFileBasename, String outputFile,
|
||||
{required bool deferredLoadingEnabled,
|
||||
required bool generateSourceMap,
|
||||
required bool strip}) async {
|
||||
final unoptFile = '$outputFileBasename.unopt.wasm';
|
||||
File(outputFile).renameSync(unoptFile);
|
||||
|
||||
final unoptSourceMapFile = '$outputFileBasename.unopt.wasm.map';
|
||||
if (generateSourceMap) {
|
||||
File('$outputFile.map').renameSync(unoptSourceMapFile);
|
||||
}
|
||||
|
||||
final flags = [
|
||||
...(deferredLoadingEnabled
|
||||
? binaryenFlagsDeferredLoading
|
||||
: binaryenFlags),
|
||||
if (!strip) '-g',
|
||||
if (generateSourceMap) ...[
|
||||
'-ism',
|
||||
unoptSourceMapFile,
|
||||
'-osm',
|
||||
'$outputFile.map'
|
||||
]
|
||||
];
|
||||
|
||||
if (verbose) {
|
||||
log.stdout('Optimizing output with: ${sdk.wasmOpt} $flags');
|
||||
}
|
||||
final processResult = Process.runSync(
|
||||
sdk.wasmOpt,
|
||||
[...flags, '-o', outputFile, unoptFile],
|
||||
);
|
||||
if (processResult.exitCode != 0) {
|
||||
log.stderr('Error: Wasm compilation failed while optimizing output');
|
||||
log.stderr(processResult.stderr);
|
||||
return compileErrorExitCode;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class CompileSubcommandCommand extends DartdevCommand {
|
||||
|
||||
Reference in New Issue
Block a user