diff --git a/pkg/dart2wasm/bin/dart2wasm.dart b/pkg/dart2wasm/bin/dart2wasm.dart index 19b96042d29..6a77001cef6 100644 --- a/pkg/dart2wasm/bin/dart2wasm.dart +++ b/pkg/dart2wasm/bin/dart2wasm.dart @@ -13,6 +13,7 @@ import 'package:dart2wasm/translator.dart'; final Map boolOptionMap = { "export-all": (o, value) => o.exportAll = value, + "import-shared-memory": (o, value) => o.importSharedMemory = value, "inlining": (o, value) => o.inlining = value, "lazy-constants": (o, value) => o.lazyConstants = value, "local-nullability": (o, value) => o.localNullability = value, @@ -27,6 +28,7 @@ final Map boolOptionMap = { "string-data-segments": (o, value) => o.stringDataSegments = value, }; final Map intOptionMap = { + "shared-memory-max-pages": (o, value) => o.sharedMemoryMaxPages = value, "watch": (o, value) => (o.watchPoints ??= []).add(value), }; @@ -83,6 +85,11 @@ Future main(List args) async { usage("Missing argument to ${args.last}"); } + if (options.importSharedMemory && options.sharedMemoryMaxPages == null) { + usage("--shared-memory-max-pages must be " + "specified if --import-shared-memory is used."); + } + if (nonOptions.length != 2) usage("Requires two file arguments"); String input = nonOptions[0]; String output = nonOptions[1]; diff --git a/pkg/dart2wasm/dart2wasm.md b/pkg/dart2wasm/dart2wasm.md index bfc2d473517..8cc6b91c785 100644 --- a/pkg/dart2wasm/dart2wasm.md +++ b/pkg/dart2wasm/dart2wasm.md @@ -12,6 +12,7 @@ where *options* include: | --------------------------------------- | ------- | ----------- | | `--dart-sdk=`*path* | relative to script | The location of the `sdk` directory inside the Dart SDK, containing the core library sources. | `--`[`no-`]`export-all` | no | Export all functions; otherwise, just export `main`. +| `--`[`no-`]`import-shared-memory` | no | Import a shared memory buffer. If this is on, `--shared-memory-max-pages` must also be specified. | `--`[`no-`]`inlining` | no | Inline small functions. | `--`[`no-`]`lazy-constants` | no | Instantiate constants lazily. | `--`[`no-`]`local-nullability` | no | Use non-nullable types for non-nullable locals and temporaries. @@ -22,6 +23,7 @@ where *options* include: | `--`[`no-`]`print-kernel` | no | Print IR for each function before compiling it. | `--`[`no-`]`print-wasm` | no | Print Wasm instructions of each compiled function. | `--`[`no-`]`runtime-types` | no | Use RTTs for allocations and casts. +| `--shared-memory-max-pages` *pagecount* | | Max size of the imported memory buffer. If `--shared-import-memory` is specified, this must also be specified. | `--`[`no-`]`string-data-segments` | no | Use experimental array init from data segment for string constants. | `--watch` *offset* | | Print stack trace leading to the byte at offset *offset* in the `.wasm` output file. Can be specified multiple times. diff --git a/pkg/dart2wasm/lib/constants.dart b/pkg/dart2wasm/lib/constants.dart index 4440b4545a0..2744ea7d9f9 100644 --- a/pkg/dart2wasm/lib/constants.dart +++ b/pkg/dart2wasm/lib/constants.dart @@ -175,7 +175,7 @@ class Constants { .toBytes(); w.Memory stringMemory = - m.addMemory(stringsAsBytes.length, stringsAsBytes.length); + m.addMemory(false, stringsAsBytes.length, stringsAsBytes.length); m.addDataSegment(stringsAsBytes, stringMemory, 0); makeStringFunctionBody(translator.oneByteStringClass, oneByteStringFunction, (b) { diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart index 2a61d3c1831..ecb5a41e970 100644 --- a/pkg/dart2wasm/lib/translator.dart +++ b/pkg/dart2wasm/lib/translator.dart @@ -27,6 +27,7 @@ import 'package:wasm_builder/wasm_builder.dart' as w; /// Options controlling the translation. class TranslatorOptions { bool exportAll = false; + bool importSharedMemory = false; bool inlining = false; int inliningLimit = 3; bool lazyConstants = false; @@ -38,6 +39,7 @@ class TranslatorOptions { bool printKernel = false; bool printWasm = false; bool runtimeTypes = false; + int? sharedMemoryMaxPages; bool stringDataSegments = false; List? watchPoints = null; @@ -127,7 +129,8 @@ class Translator { // Lazily create exception tag if used. late final w.Tag exceptionTag = createExceptionTag(); // Lazily import FFI memory if used. - late final w.Memory ffiMemory = m.importMemory("ffi", "memory", 0); + late final w.Memory ffiMemory = m.importMemory("ffi", "memory", + options.importSharedMemory, 0, options.sharedMemoryMaxPages); // Caches for when identical source constructs need a common representation. final Map arrayTypeCache = {}; diff --git a/pkg/wasm_builder/lib/src/module.dart b/pkg/wasm_builder/lib/src/module.dart index 2e039191803..f75618e5163 100644 --- a/pkg/wasm_builder/lib/src/module.dart +++ b/pkg/wasm_builder/lib/src/module.dart @@ -130,9 +130,9 @@ class Module with SerializerMixin { } /// Add a new memory to the module. - DefinedMemory addMemory(int minSize, [int? maxSize]) { + DefinedMemory addMemory(bool shared, int minSize, [int? maxSize]) { anyMemoriesDefined = true; - final memory = DefinedMemory(memories.length, minSize, maxSize); + final memory = DefinedMemory(memories.length, shared, minSize, maxSize); memories.add(memory); return memory; } @@ -195,13 +195,14 @@ class Module with SerializerMixin { /// /// All imported memories must be specified before any memories are declared /// using [Module.addMemory]. - ImportedMemory importMemory(String module, String name, int minSize, + ImportedMemory importMemory( + String module, String name, bool shared, int minSize, [int? maxSize]) { if (anyMemoriesDefined) { throw "All memory imports must be specified before any definitions."; } final memory = - ImportedMemory(module, name, memories.length, minSize, maxSize); + ImportedMemory(module, name, memories.length, shared, minSize, maxSize); memories.add(memory); return memory; } @@ -410,13 +411,23 @@ class Table implements Serializable { /// A memory in a module. class Memory { final int index; + final bool shared; final int minSize; final int? maxSize; - Memory(this.index, this.minSize, [this.maxSize]); + Memory(this.index, this.shared, this.minSize, [this.maxSize]) { + if (shared && maxSize == null) { + throw "Shared memory must specify a maximum size."; + } + } void _serializeLimits(Serializer s) { - if (maxSize == null) { + if (shared) { + assert(maxSize != null); + s.writeByte(0x03); + s.writeUnsigned(minSize); + s.writeUnsigned(maxSize!); + } else if (maxSize == null) { s.writeByte(0x00); s.writeUnsigned(minSize); } else { @@ -428,8 +439,8 @@ class Memory { } class DefinedMemory extends Memory implements Serializable { - DefinedMemory(int index, int minSize, int? maxSize) - : super(index, minSize, maxSize); + DefinedMemory(int index, bool shared, int minSize, int? maxSize) + : super(index, shared, minSize, maxSize); @override void serialize(Serializer s) => _serializeLimits(s); @@ -556,8 +567,9 @@ class ImportedMemory extends Memory implements Import { final String module; final String name; - ImportedMemory(this.module, this.name, int index, int minSize, int? maxSize) - : super(index, minSize, maxSize); + ImportedMemory( + this.module, this.name, int index, bool shared, int minSize, int? maxSize) + : super(index, shared, minSize, maxSize); @override void serialize(Serializer s) { diff --git a/tests/lib/wasm/fn_import_error_test.dart b/tests/lib/wasm/fn_import_error_test.dart index aeca278487d..8e4aa00d797 100644 --- a/tests/lib/wasm/fn_import_error_test.dart +++ b/tests/lib/wasm/fn_import_error_test.dart @@ -38,7 +38,7 @@ void main() { // Wrong kind of import. Expect.throws( - () => mod.instantiate().addMemory("env", "someFn", mod.createMemory(10)), + () => mod.instantiate().addMemory(false, "env", "someFn", mod.createMemory(10)), (Exception e) => "$e".contains("Import is not a memory")); // Wrong namespace. diff --git a/tests/lib_2/wasm/fn_import_error_test.dart b/tests/lib_2/wasm/fn_import_error_test.dart index b15a1be4c43..c99054cbf80 100644 --- a/tests/lib_2/wasm/fn_import_error_test.dart +++ b/tests/lib_2/wasm/fn_import_error_test.dart @@ -40,7 +40,7 @@ void main() { // Wrong kind of import. Expect.throws( - () => mod.instantiate().addMemory("env", "someFn", mod.createMemory(10)), + () => mod.instantiate().addMemory(false, "env", "someFn", mod.createMemory(10)), (Exception e) => "$e".contains("Import is not a memory")); // Wrong namespace.