[dart2wasm] Remove RTT support and emit isorecursive types
Relatedly, remove the --nominal-types and --runtime-types options and
eliminate the instruction and type wrappers that abstracted over these
options.
Rename instructions to the names in the latest WasmGC working draft.
Change-Id: Icd0e4bbcf444d5a3cea1f88f4e3bf8dd2926f70f
Cq-Include-Trybots: luci.dart.try:dart2wasm-linux-x64-d8-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/254902
Commit-Queue: Aske Simon Christensen <askesc@google.com>
Reviewed-by: Joshua Litt <joshualitt@google.com>
diff --git a/pkg/dart2wasm/bin/dart2wasm.dart b/pkg/dart2wasm/bin/dart2wasm.dart
index 7ca6392..ce2dcc9 100644
--- a/pkg/dart2wasm/bin/dart2wasm.dart
+++ b/pkg/dart2wasm/bin/dart2wasm.dart
@@ -30,8 +30,6 @@
defaultsTo: _d.translatorOptions.lazyConstants),
Flag("name-section", (o, value) => o.translatorOptions.nameSection = value,
defaultsTo: _d.translatorOptions.nameSection),
- Flag("nominal-types", (o, value) => o.translatorOptions.nominalTypes = value,
- defaultsTo: _d.translatorOptions.nominalTypes),
Flag("polymorphic-specialization",
(o, value) => o.translatorOptions.polymorphicSpecialization = value,
defaultsTo: _d.translatorOptions.polymorphicSpecialization),
@@ -39,8 +37,6 @@
defaultsTo: _d.translatorOptions.printKernel),
Flag("print-wasm", (o, value) => o.translatorOptions.printWasm = value,
defaultsTo: _d.translatorOptions.printWasm),
- Flag("runtime-types", (o, value) => o.translatorOptions.runtimeTypes = value,
- defaultsTo: _d.translatorOptions.runtimeTypes),
Flag("string-data-segments",
(o, value) => o.translatorOptions.stringDataSegments = value,
defaultsTo: _d.translatorOptions.stringDataSegments),
diff --git a/pkg/dart2wasm/dart2wasm.md b/pkg/dart2wasm/dart2wasm.md
index 74bfe10..22863c5 100644
--- a/pkg/dart2wasm/dart2wasm.md
+++ b/pkg/dart2wasm/dart2wasm.md
@@ -17,11 +17,9 @@
| `--`[`no-`]`inlining` | no | Inline small functions.
| `--`[`no-`]`lazy-constants` | no | Instantiate constants lazily.
| `--`[`no-`]`name-section` | yes | Emit Name Section with function names.
-| `--`[`no-`]`nominal-types` | yes | Emit nominal types.
| `--`[`no-`]`polymorphic-specialization` | no | Do virtual calls by switching on the class ID instead of using `call_indirect`.
| `--`[`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/class_info.dart b/pkg/dart2wasm/lib/class_info.dart
index b9fc9e9..4d6538e 100644
--- a/pkg/dart2wasm/lib/class_info.dart
+++ b/pkg/dart2wasm/lib/class_info.dart
@@ -79,9 +79,6 @@
/// sometimes use the same struct as its superclass.
final w.StructType struct;
- /// Wasm global containing the RTT for this class.
- late final w.DefinedGlobal rtt;
-
/// The superclass for this class. This will usually be the Dart superclass,
/// but there are a few exceptions, where the Wasm type hierarchy does not
/// follow the Dart class hierarchy.
@@ -109,9 +106,6 @@
ClassInfo(this.cls, this.classId, this.depth, this.struct, this.superInfo,
ClassInfoCollector collector,
{this.typeParameterMatch = const {}}) {
- if (collector.options.useRttGlobals) {
- rtt = collector.makeRtt(struct, superInfo);
- }
implementedBy.add(this);
}
@@ -157,24 +151,8 @@
TranslatorOptions get options => translator.options;
- w.DefinedGlobal makeRtt(w.StructType struct, ClassInfo? superInfo) {
- assert(options.useRttGlobals);
- int depth = superInfo != null ? superInfo.depth + 1 : 0;
- final w.DefinedGlobal rtt =
- m.addGlobal(w.GlobalType(w.Rtt(struct, depth), mutable: false));
- final w.Instructions b = rtt.initializer;
- if (superInfo != null) {
- b.global_get(superInfo.rtt);
- b.rtt_sub(struct);
- } else {
- b.rtt_canon(struct);
- }
- b.end();
- return rtt;
- }
-
void initializeTop() {
- final w.StructType struct = translator.structType("#Top");
+ final w.StructType struct = m.addStructType("#Top");
topInfo = ClassInfo(null, nextClassId++, 0, struct, null, this);
translator.classes.add(topInfo);
translator.classForHeapType[struct] = topInfo;
@@ -187,7 +165,7 @@
if (superclass == null) {
ClassInfo superInfo = topInfo;
final w.StructType struct =
- translator.structType(cls.name, superType: superInfo.struct);
+ m.addStructType(cls.name, superType: superInfo.struct);
info = ClassInfo(
cls, nextClassId++, superInfo.depth + 1, struct, superInfo, this);
// Mark Top type as implementing Object to force the representation
@@ -248,7 +226,7 @@
cls != translator.byteDataViewClass;
w.StructType struct = canReuseSuperStruct
? superInfo.struct
- : translator.structType(cls.name, superType: superInfo.struct);
+ : m.addStructType(cls.name, superType: superInfo.struct);
info = ClassInfo(
cls, nextClassId++, superInfo.depth + 1, struct, superInfo, this,
typeParameterMatch: typeParameterMatch);
diff --git a/pkg/dart2wasm/lib/closures.dart b/pkg/dart2wasm/lib/closures.dart
index 7edda581..882182e 100644
--- a/pkg/dart2wasm/lib/closures.dart
+++ b/pkg/dart2wasm/lib/closures.dart
@@ -102,6 +102,8 @@
Translator get translator => codeGen.translator;
+ w.Module get m => translator.m;
+
late final w.ValueType typeType =
translator.classInfo[translator.typeClass]!.nullableType;
@@ -128,7 +130,7 @@
// Make struct definitions
for (Context context in contexts.values) {
if (!context.isEmpty) {
- context.struct = translator.structType("<context>");
+ context.struct = m.addStructType("<context>");
}
}
@@ -171,6 +173,8 @@
Translator get translator => closures.translator;
+ w.Module get m => translator.m;
+
@override
void visitAssertStatement(AssertStatement node) {}
@@ -267,8 +271,7 @@
}
int parameterCount = node.requiredParameterCount;
w.FunctionType type = translator.closureFunctionType(parameterCount);
- w.DefinedFunction function =
- translator.m.addFunction(type, "$member (closure)");
+ w.DefinedFunction function = m.addFunction(type, "$member (closure)");
closures.lambdas[node] = Lambda(node, function);
depth++;
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index 39baec0..28b61f7 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -172,7 +172,7 @@
b.i32_const(initialIdentityHash);
b.local_get(paramLocals[0]);
b.global_get(global);
- translator.struct_new(b, parameterCount);
+ b.struct_new(translator.closureStructType(parameterCount));
b.end();
}
@@ -261,7 +261,7 @@
translator.isFfiCompound(cls))) {
preciseThisLocal = addLocal(thisType);
b.local_get(paramLocals[0]);
- translator.ref_cast(b, info);
+ b.ref_cast(info.struct);
b.local_set(preciseThisLocal!);
} else {
preciseThisLocal = paramLocals[0];
@@ -325,7 +325,7 @@
Context? context = closures.contexts[lambda.functionNode]?.parent;
if (context != null) {
b.local_get(paramLocals[0]);
- translator.ref_cast(b, context.struct);
+ b.ref_cast(context.struct);
while (true) {
w.Local contextLocal =
addLocal(w.RefType.def(context!.struct, nullable: false));
@@ -382,7 +382,7 @@
w.Local contextLocal =
addLocal(w.RefType.def(context.struct, nullable: false));
context.currentLocal = contextLocal;
- translator.struct_new_default(b, context.struct);
+ b.struct_new_default(context.struct);
b.local_set(contextLocal);
if (context.containsThis) {
b.local_get(contextLocal);
@@ -1114,7 +1114,7 @@
ClassInfo info = translator.classInfo[node.target.enclosingClass]!;
translator.functions.allocateClass(info.classId);
w.Local temp = addLocal(info.nonNullableType);
- translator.struct_new_default(b, info);
+ b.struct_new_default(info.struct);
b.local_tee(temp);
b.local_get(temp);
b.i32_const(info.classId);
@@ -1756,7 +1756,7 @@
b.i32_const(initialIdentityHash);
_pushContext(functionNode);
b.global_get(global);
- translator.struct_new(b, parameterCount);
+ b.struct_new(translator.closureStructType(parameterCount));
return struct;
}
@@ -2001,7 +2001,7 @@
if (options.lazyConstants) {
// Avoid array.init instruction in lazy constants mode
b.i32_const(length);
- translator.array_new_default(b, arrayType);
+ b.array_new_default(arrayType);
if (length > 0) {
w.Local arrayLocal = addLocal(refType.withNullability(false));
b.local_set(arrayLocal);
@@ -2017,9 +2017,9 @@
for (int i = 0; i < length; i++) {
generateItem(elementType, i);
}
- translator.array_init(b, arrayType, length);
+ b.array_new_fixed(arrayType, length);
}
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
return info.nonNullableType;
}
diff --git a/pkg/dart2wasm/lib/constants.dart b/pkg/dart2wasm/lib/constants.dart
index b83d6e7..a29932e 100644
--- a/pkg/dart2wasm/lib/constants.dart
+++ b/pkg/dart2wasm/lib/constants.dart
@@ -83,8 +83,8 @@
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
b.i32_const(0);
- translator.array_new_default(b, arrayType);
- translator.struct_new(b, info);
+ b.array_new_default(arrayType);
+ b.struct_new(info.struct);
b.global_set(emptyString);
} else {
w.RefType emptyStringType = info.nonNullableType;
@@ -92,8 +92,8 @@
w.Instructions ib = emptyString.initializer;
ib.i32_const(info.classId);
ib.i32_const(initialIdentityHash);
- translator.array_init(ib, arrayType, 0);
- translator.struct_new(ib, info);
+ ib.array_new_fixed(arrayType, 0);
+ ib.struct_new(info.struct);
ib.end();
}
@@ -121,8 +121,8 @@
b.ref_null(typeInfo.struct); // Initialized later
b.i64_const(0);
b.i32_const(0);
- translator.array_new_default(b, arrayType);
- translator.struct_new(b, info);
+ b.array_new_default(arrayType);
+ b.struct_new(info.struct);
b.global_set(emptyTypeList);
} else {
w.RefType emptyListType = info.nonNullableType;
@@ -132,8 +132,8 @@
ib.i32_const(initialIdentityHash);
ib.ref_null(typeInfo.struct); // Initialized later
ib.i64_const(0);
- translator.array_init(ib, arrayType, 0);
- translator.struct_new(ib, info);
+ ib.array_new_fixed(arrayType, 0);
+ ib.struct_new(info.struct);
ib.end();
}
@@ -199,7 +199,7 @@
/// in the corresponding string data segment from which to copy this string.
w.DefinedFunction makeStringFunction(Class cls) {
ClassInfo info = translator.classInfo[cls]!;
- w.FunctionType ftype = translator.functionType(
+ w.FunctionType ftype = m.addFunctionType(
const [w.NumType.i32, w.NumType.i32], [info.nonNullableType]);
return m.addFunction(ftype, "makeString (${cls.name})");
}
@@ -218,7 +218,7 @@
w.Instructions b = function.body;
b.local_get(length);
- translator.array_new_default(b, arrayType);
+ b.array_new_default(arrayType);
b.local_set(array);
b.i32_const(0);
@@ -243,7 +243,7 @@
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
b.local_get(array);
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
b.end();
}
@@ -359,7 +359,7 @@
ClassInfo info = translator.classInfo[translator.boxedClasses[wasmType]]!;
b.i32_const(info.classId);
pushValue();
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
return info.nonNullableType;
} else {
pushValue();
@@ -419,7 +419,7 @@
m.addGlobal(w.GlobalType(type.withNullability(true)));
global.initializer.ref_null(type.heapType);
global.initializer.end();
- w.FunctionType ftype = translator.functionType(const [], [type]);
+ w.FunctionType ftype = m.addFunctionType(const [], [type]);
w.DefinedFunction function = m.addFunction(ftype, "$constant");
generator(function, function.body);
w.Local temp = function.addLocal(type);
@@ -493,15 +493,15 @@
segment.append(bytes);
b.i32_const(offset);
b.i32_const(constant.value.length);
- translator.array_init_from_data(b, arrayType, segment);
+ b.array_new_data(arrayType, segment);
} else {
// Initialize string contents from i32 constants on the stack.
for (int charCode in constant.value.codeUnits) {
b.i32_const(charCode);
}
- translator.array_init(b, arrayType, constant.value.length);
+ b.array_new_fixed(arrayType, constant.value.length);
}
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
}
});
}
@@ -551,7 +551,7 @@
constants.instantiateConstant(
function, b, subConstant, info.struct.fields[i].type.unpacked);
}
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
});
}
@@ -580,7 +580,7 @@
// Allocate array and set each entry to the corresponding sub-constant.
w.Local arrayLocal = function!.addLocal(refType.withNullability(false));
b.i32_const(length);
- translator.array_new_default(b, arrayType);
+ b.array_new_default(arrayType);
b.local_set(arrayLocal);
for (int i = 0; i < length; i++) {
b.local_get(arrayLocal);
@@ -596,9 +596,9 @@
constants.instantiateConstant(
function, b, constant.entries[i], elementType);
}
- translator.array_init(b, arrayType, length);
+ b.array_new_fixed(arrayType, length);
}
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
});
}
@@ -636,7 +636,7 @@
function, b, keyTypeConstant, constants.typeInfo.nullableType);
constants.instantiateConstant(
function, b, valueTypeConstant, constants.typeInfo.nullableType);
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
});
}
@@ -665,7 +665,7 @@
b.i64_const(0); // _deletedKeys
constants.instantiateConstant(
function, b, elementTypeConstant, constants.typeInfo.nullableType);
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
});
}
@@ -700,7 +700,7 @@
} else {
b.ref_func(closureFunction);
}
- translator.struct_new(b, parameterCount);
+ b.struct_new(translator.closureStructType(parameterCount));
});
}
@@ -719,7 +719,7 @@
b.i64_const(typeInfo.classId);
constants.instantiateConstant(
function, b, typeArgs, typeListExpectedType);
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
});
}
@@ -733,7 +733,7 @@
types.encodeNullability(b, type);
constants.instantiateConstant(
function, b, typeArgument, types.nonNullableTypeType);
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
});
}
@@ -763,7 +763,7 @@
function, b, requiredParameterCountConstant, w.NumType.i64);
constants.instantiateConstant(function, b, namedParametersConstant,
types.namedParametersExpectedType);
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
});
}
@@ -785,7 +785,7 @@
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
types.encodeNullability(b, type);
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
});
} else {
return _makeFunctionType(constant, type, info);
@@ -806,7 +806,7 @@
// explicitly declared to be nullabe.
b.i32_const(type.declaredNullability == Nullability.nullable ? 1 : 0);
b.i64_const(environmentIndex);
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
});
} else {
assert(type is VoidType ||
@@ -817,7 +817,7 @@
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
types.encodeNullability(b, type);
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
});
}
}
@@ -834,7 +834,7 @@
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
constants.instantiateConstant(function, b, nameConstant, stringType);
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
});
}
}
diff --git a/pkg/dart2wasm/lib/dispatch_table.dart b/pkg/dart2wasm/lib/dispatch_table.dart
index 5682eda..1e00b2d 100644
--- a/pkg/dart2wasm/lib/dispatch_table.dart
+++ b/pkg/dart2wasm/lib/dispatch_table.dart
@@ -35,6 +35,8 @@
Reference? singularTarget;
int? offset;
+ w.Module get m => translator.m;
+
String get name => paramInfo.member.name.text;
bool get alive => callCount > 0 && targetCount > 1 || forced;
@@ -135,7 +137,7 @@
outputSets.length,
(i) => translator.typeForInfo(
upperBound(outputSets[i]), outputNullable[i]) as w.ValueType);
- return translator.functionType(
+ return m.addFunctionType(
[inputs[0], ...typeParameters, ...inputs.sublist(1)], outputs);
}
}
@@ -151,6 +153,8 @@
late final List<Reference?> table;
late final w.DefinedTable wasmTable;
+ w.Module get m => translator.m;
+
DispatchTable(this.translator)
: selectorMetadata =
(translator.component.metadata["vm.table-selector.metadata"]
@@ -294,7 +298,7 @@
}
}
- wasmTable = translator.m.addTable(w.RefType.func(), table.length);
+ wasmTable = m.addTable(w.RefType.func(), table.length);
}
void output() {
diff --git a/pkg/dart2wasm/lib/functions.dart b/pkg/dart2wasm/lib/functions.dart
index 3f938f3..cd71948 100644
--- a/pkg/dart2wasm/lib/functions.dart
+++ b/pkg/dart2wasm/lib/functions.dart
@@ -220,6 +220,6 @@
? const []
: [adjustExternalType(translator.translateType(returnType))];
- return translator.functionType(inputs, outputs);
+ return m.addFunctionType(inputs, outputs);
}
}
diff --git a/pkg/dart2wasm/lib/globals.dart b/pkg/dart2wasm/lib/globals.dart
index c72aeb1..dd0bc36 100644
--- a/pkg/dart2wasm/lib/globals.dart
+++ b/pkg/dart2wasm/lib/globals.dart
@@ -18,17 +18,19 @@
final Map<w.HeapType, w.DefinedGlobal> dummyValues = {};
late final w.DefinedGlobal dummyGlobal;
+ w.Module get m => translator.m;
+
Globals(this.translator) {
_initDummyValues();
}
void _initDummyValues() {
// Create dummy struct for anyref/eqref/dataref/context dummy values
- w.StructType structType = translator.structType("#Dummy");
+ w.StructType structType = m.addStructType("#Dummy");
w.RefType type = w.RefType.def(structType, nullable: false);
- dummyGlobal = translator.m.addGlobal(w.GlobalType(type, mutable: false));
+ dummyGlobal = m.addGlobal(w.GlobalType(type, mutable: false));
w.Instructions ib = dummyGlobal.initializer;
- translator.struct_new(ib, structType);
+ ib.struct_new(structType);
ib.end();
dummyValues[w.HeapType.any] = dummyGlobal;
dummyValues[w.HeapType.eq] = dummyGlobal;
@@ -45,25 +47,25 @@
for (w.FieldType field in heapType.fields) {
prepareDummyValue(field.type.unpacked);
}
- global = translator.m.addGlobal(w.GlobalType(type, mutable: false));
+ global = m.addGlobal(w.GlobalType(type, mutable: false));
w.Instructions ib = global.initializer;
for (w.FieldType field in heapType.fields) {
instantiateDummyValue(ib, field.type.unpacked);
}
- translator.struct_new(ib, heapType);
+ ib.struct_new(heapType);
ib.end();
} else if (heapType is w.ArrayType) {
- global = translator.m.addGlobal(w.GlobalType(type, mutable: false));
+ global = m.addGlobal(w.GlobalType(type, mutable: false));
w.Instructions ib = global.initializer;
- translator.array_init(ib, heapType, 0);
+ ib.array_new_fixed(heapType, 0);
ib.end();
} else if (heapType is w.FunctionType) {
w.DefinedFunction function =
- translator.m.addFunction(heapType, "#dummy function $heapType");
+ m.addFunction(heapType, "#dummy function $heapType");
w.Instructions b = function.body;
b.unreachable();
b.end();
- global = translator.m.addGlobal(w.GlobalType(type, mutable: false));
+ global = m.addGlobal(w.GlobalType(type, mutable: false));
w.Instructions ib = global.initializer;
ib.ref_func(function);
ib.end();
@@ -128,8 +130,8 @@
if (init != null) {
// Initialized to a constant
translator.constants.ensureConstant(init);
- w.DefinedGlobal global = translator.m
- .addGlobal(w.GlobalType(type, mutable: !variable.isFinal));
+ w.DefinedGlobal global =
+ m.addGlobal(w.GlobalType(type, mutable: !variable.isFinal));
translator.constants
.instantiateConstant(null, global.initializer, init, type);
global.initializer.end();
@@ -140,14 +142,13 @@
type = type.withNullability(true);
} else {
// Explicit initialization flag
- w.DefinedGlobal flag =
- translator.m.addGlobal(w.GlobalType(w.NumType.i32));
+ w.DefinedGlobal flag = m.addGlobal(w.GlobalType(w.NumType.i32));
flag.initializer.i32_const(0);
flag.initializer.end();
globalInitializedFlag[variable] = flag;
}
- w.DefinedGlobal global = translator.m.addGlobal(w.GlobalType(type));
+ w.DefinedGlobal global = m.addGlobal(w.GlobalType(type));
instantiateDummyValue(global.initializer, type);
global.initializer.end();
diff --git a/pkg/dart2wasm/lib/intrinsics.dart b/pkg/dart2wasm/lib/intrinsics.dart
index 65dd8b7..6a9ec59 100644
--- a/pkg/dart2wasm/lib/intrinsics.dart
+++ b/pkg/dart2wasm/lib/intrinsics.dart
@@ -131,7 +131,7 @@
w.Label fail = b.block(const [], const [w.RefType.any(nullable: false)]);
codeGen.wrap(receiver, w.RefType.any(nullable: false));
b.br_on_non_data(fail);
- translator.ref_test(b, translator.topInfo);
+ b.ref_test(translator.topInfo.struct);
b.br(succeed);
b.end(); // fail
b.drop();
@@ -353,7 +353,7 @@
w.Label fail = b.block(const [], const [w.RefType.any(nullable: false)]);
codeGen.wrap(receiver, w.RefType.any(nullable: false));
b.br_on_non_data(fail);
- translator.br_on_cast(b, succeed, translator.topInfo);
+ b.br_on_cast(succeed, translator.topInfo.struct);
b.end(); // fail
codeGen.throwWasmRefError("a Dart object");
b.end(); // succeed
@@ -704,7 +704,7 @@
b.i32_const(0);
getID(object);
codeGen.wrap(typeArguments, translator.types.typeListExpectedType);
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
return info.nonNullableType;
}
}
@@ -731,8 +731,8 @@
b.i32_const(initialIdentityHash);
codeGen.wrap(length, w.NumType.i64);
b.i32_wrap_i64();
- translator.array_new_default(b, arrayType);
- translator.struct_new(b, info);
+ b.array_new_default(arrayType);
+ b.struct_new(info.struct);
return info.nonNullableType;
case "writeIntoOneByteString":
ClassInfo info = translator.classInfo[translator.oneByteStringClass]!;
@@ -762,8 +762,8 @@
b.i32_const(initialIdentityHash);
codeGen.wrap(length, w.NumType.i64);
b.i32_wrap_i64();
- translator.array_new_default(b, arrayType);
- translator.struct_new(b, info);
+ b.array_new_default(arrayType);
+ b.struct_new(info.struct);
return info.nonNullableType;
case "writeIntoTwoByteString":
ClassInfo info = translator.classInfo[translator.twoByteStringClass]!;
@@ -952,7 +952,7 @@
translator.arrayTypeForDartType(node.arguments.types.single);
codeGen.wrap(length, w.NumType.i64);
b.i32_wrap_i64();
- translator.array_new_default(b, arrayType);
+ b.array_new_default(arrayType);
return w.RefType.def(arrayType, nullable: false);
}
@@ -968,8 +968,7 @@
codeGen.wrap(ref, w.RefType.any(nullable: false));
b.br_on_non_func(fail);
if (cls == translator.wasmFunctionClass) {
- assert(resultType.heapType is w.FunctionType);
- translator.br_on_cast_fail(b, fail, resultType.heapType);
+ b.br_on_cast_fail(fail, resultType.heapType as w.FunctionType);
}
b.br(succeed);
b.end(); // fail
@@ -1137,11 +1136,11 @@
b.i32_eq();
b.if_();
b.local_get(first);
- translator.ref_cast(b, boolInfo);
+ b.ref_cast(boolInfo.struct);
b.struct_get(boolInfo.struct, FieldIndex.boxValue);
w.Label bothBool = b.block(const [], [boolInfo.nullableType]);
b.local_get(second);
- translator.br_on_cast(b, bothBool, boolInfo);
+ b.br_on_cast(bothBool, boolInfo.struct);
b.i32_const(0);
b.return_();
b.end();
@@ -1156,11 +1155,11 @@
b.i32_eq();
b.if_();
b.local_get(first);
- translator.ref_cast(b, intInfo);
+ b.ref_cast(intInfo.struct);
b.struct_get(intInfo.struct, FieldIndex.boxValue);
w.Label bothInt = b.block(const [], [intInfo.nullableType]);
b.local_get(second);
- translator.br_on_cast(b, bothInt, intInfo);
+ b.br_on_cast(bothInt, intInfo.struct);
b.i32_const(0);
b.return_();
b.end();
@@ -1175,12 +1174,12 @@
b.i32_eq();
b.if_();
b.local_get(first);
- translator.ref_cast(b, doubleInfo);
+ b.ref_cast(doubleInfo.struct);
b.struct_get(doubleInfo.struct, FieldIndex.boxValue);
b.i64_reinterpret_f64();
w.Label bothDouble = b.block(const [], [doubleInfo.nullableType]);
b.local_get(second);
- translator.br_on_cast(b, bothDouble, doubleInfo);
+ b.br_on_cast(bothDouble, doubleInfo.struct);
b.i32_const(0);
b.return_();
b.end();
@@ -1209,7 +1208,7 @@
TypeParameter typeParameter = cls.typeParameters[i];
int typeParameterIndex = translator.typeParameterIndex[typeParameter]!;
b.local_get(object);
- translator.ref_cast(b, classInfo);
+ b.ref_cast(classInfo.struct);
b.struct_get(classInfo.struct, typeParameterIndex);
});
return true;
@@ -1243,8 +1242,8 @@
b.i64_shl();
}
b.i32_wrap_i64();
- translator.array_new_default(b, arrayType);
- translator.struct_new(b, info);
+ b.array_new_default(arrayType);
+ b.struct_new(info.struct);
return true;
}
@@ -1265,7 +1264,7 @@
b.local_get(buffer);
b.local_get(offsetInBytes);
b.i32_wrap_i64();
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
return true;
}
}
@@ -1280,7 +1279,7 @@
Class cls = member.enclosingClass!;
ClassInfo info = translator.classInfo[cls]!;
b.local_get(paramLocals[0]);
- translator.ref_cast(b, info);
+ b.ref_cast(info.struct);
// TODO(joshualitt): Because we currently merge getters to support
// dynamic calls, the return types of `.length` and `.offsetInBytes` can
// change. Should we decide to stop merging getters, we should remove
@@ -1360,7 +1359,7 @@
ClassInfo intInfo = translator.classInfo[translator.boxedIntClass]!;
w.Label intArg = b.block(const [], [intInfo.nonNullableType]);
b.local_get(function.locals[1]);
- translator.br_on_cast(b, intArg, intInfo);
+ b.br_on_cast(intArg, intInfo.struct);
// double argument
b.drop();
b.local_get(function.locals[0]);
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index f54810a..9226106 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -35,16 +35,12 @@
int inliningLimit = 3;
bool lazyConstants = false;
bool nameSection = true;
- bool nominalTypes = true;
bool polymorphicSpecialization = false;
bool printKernel = false;
bool printWasm = false;
- bool runtimeTypes = false;
int? sharedMemoryMaxPages;
bool stringDataSegments = false;
List<int>? watchPoints = null;
-
- bool get useRttGlobals => runtimeTypes && !nominalTypes;
}
typedef CodeGenCallback = void Function(w.Instructions);
@@ -160,8 +156,6 @@
// Caches for when identical source constructs need a common representation.
final Map<w.StorageType, w.ArrayType> arrayTypeCache = {};
final Map<int, w.StructType> functionTypeCache = {};
- final Map<w.StructType, int> functionTypeParameterCount = {};
- final Map<int, w.DefinedGlobal> functionTypeRtt = {};
final Map<w.BaseFunction, w.DefinedGlobal> functionRefCache = {};
final Map<Procedure, w.DefinedFunction> tearOffFunctionCache = {};
@@ -338,7 +332,8 @@
mainFunction = _findMainMethod(libraries.first);
functions.addExport(mainFunction.reference, "main");
- initFunction = m.addFunction(functionType(const [], const []), "#init");
+ initFunction =
+ m.addFunction(m.addFunctionType(const [], const []), "#init");
m.startFunction = initFunction;
globals = Globals(this);
@@ -459,7 +454,7 @@
/// [stackTraceInfo.nonNullableType] to hold a stack trace. This single
/// exception tag is used to throw and catch all Dart exceptions.
w.Tag createExceptionTag() {
- w.FunctionType tagType = functionType(
+ w.FunctionType tagType = m.addFunctionType(
[topInfo.nonNullableType, stackTraceInfo.nonNullableType], const []);
w.Tag tag = m.addTag(tagType);
return tag;
@@ -535,7 +530,7 @@
if (functionType.returnType != const VoidType())
translateType(functionType.returnType)
];
- w.FunctionType wasmType = this.functionType(inputs, outputs);
+ w.FunctionType wasmType = m.addFunctionType(inputs, outputs);
return w.RefType.def(wasmType, nullable: type.isPotentiallyNullable);
}
return typeForInfo(
@@ -577,30 +572,25 @@
}
w.ArrayType wasmArrayType(w.StorageType type, String name) {
- return arrayTypeCache.putIfAbsent(
- type, () => arrayType("Array<$name>", elementType: w.FieldType(type)));
+ return arrayTypeCache.putIfAbsent(type,
+ () => m.addArrayType("Array<$name>", elementType: w.FieldType(type)));
}
w.StructType closureStructType(int parameterCount) {
return functionTypeCache.putIfAbsent(parameterCount, () {
ClassInfo info = classInfo[functionClass]!;
- w.StructType struct = structType("Function$parameterCount",
+ w.StructType struct = m.addStructType("Function$parameterCount",
fields: info.struct.fields, superType: info.struct);
assert(struct.fields.length == FieldIndex.closureFunction);
struct.fields.add(w.FieldType(
w.RefType.def(closureFunctionType(parameterCount), nullable: false),
mutable: false));
- if (options.useRttGlobals) {
- functionTypeRtt[parameterCount] =
- classInfoCollector.makeRtt(struct, info);
- }
- functionTypeParameterCount[struct] = parameterCount;
return struct;
});
}
w.FunctionType closureFunctionType(int parameterCount) {
- return functionType([
+ return m.addFunctionType([
w.RefType.data(),
...List<w.ValueType>.filled(parameterCount, topInfo.nullableType)
], [
@@ -608,10 +598,6 @@
]);
}
- int parameterCountForFunctionStruct(w.HeapType heapType) {
- return functionTypeParameterCount[heapType]!;
- }
-
w.DefinedGlobal makeFunctionRef(w.BaseFunction f) {
return functionRefCache.putIfAbsent(f, () {
w.DefinedGlobal global = m.addGlobal(
@@ -698,7 +684,7 @@
b.local_set(temp);
b.i32_const(info.classId);
b.local_get(temp);
- struct_new(b, info);
+ b.struct_new(info.struct);
} else if (from is w.RefType && to is! w.RefType) {
// Unboxing
ClassInfo info = classInfo[boxedClasses[to]!]!;
@@ -707,7 +693,7 @@
if (!from.heapType.isSubtypeOf(w.HeapType.data)) {
b.ref_as_data();
}
- ref_cast(b, info);
+ b.ref_cast(info.struct);
}
b.struct_get(info.struct, FieldIndex.boxValue);
} else if (from.withNullability(false).isSubtypeOf(to)) {
@@ -721,19 +707,15 @@
var heapType = (to as w.RefType).heapType;
if (heapType is w.FunctionType) {
b.ref_as_func();
- ref_cast(b, heapType);
+ b.ref_cast(heapType);
return;
}
- ClassInfo? info = classForHeapType[heapType];
if (!(from as w.RefType).heapType.isSubtypeOf(w.HeapType.data)) {
b.ref_as_data();
}
- ref_cast(
- b,
- info ??
- (heapType.isSubtypeOf(classInfo[functionClass]!.struct)
- ? parameterCountForFunctionStruct(heapType)
- : heapType));
+ if (heapType is w.DefType) {
+ b.ref_cast(heapType);
+ }
}
}
}
@@ -823,159 +805,6 @@
}
return null;
}
-
- // Wrappers for type creation to abstract over equi-recursive versus nominal
- // typing. The given supertype is ignored when nominal types are disabled,
- // and a suitable default is inserted when nominal types are enabled.
-
- w.FunctionType functionType(
- Iterable<w.ValueType> inputs, Iterable<w.ValueType> outputs,
- {w.HeapType? superType}) {
- return m.addFunctionType(inputs, outputs,
- superType: options.nominalTypes ? superType ?? w.HeapType.func : null);
- }
-
- w.StructType structType(String name,
- {Iterable<w.FieldType>? fields, w.HeapType? superType}) {
- return m.addStructType(name,
- fields: fields,
- superType: options.nominalTypes ? superType ?? w.HeapType.data : null);
- }
-
- w.ArrayType arrayType(String name,
- {w.FieldType? elementType, w.HeapType? superType}) {
- return m.addArrayType(name,
- elementType: elementType,
- superType: options.nominalTypes ? superType ?? w.HeapType.data : null);
- }
-
- // Wrappers for object allocation and cast instructions to abstract over
- // RTT-based and static versions of the instructions.
- // The [type] parameter taken by the methods is either a [ClassInfo] (to use
- // the RTT for the class), an [int] (to use the RTT for the closure struct
- // corresponding to functions with that number of parameters) or a
- // [w.DefType] (to use the canonical RTT for the type).
-
- void struct_new(w.Instructions b, Object type) {
- if (options.runtimeTypes) {
- final struct = _emitRtt(b, type) as w.StructType;
- b.struct_new_with_rtt(struct);
- } else {
- b.struct_new(_targetType(type) as w.StructType);
- }
- }
-
- void struct_new_default(w.Instructions b, Object type) {
- if (options.runtimeTypes) {
- final struct = _emitRtt(b, type) as w.StructType;
- b.struct_new_default_with_rtt(struct);
- } else {
- b.struct_new_default(_targetType(type) as w.StructType);
- }
- }
-
- void array_new(w.Instructions b, w.ArrayType type) {
- if (options.runtimeTypes) {
- b.rtt_canon(type);
- b.array_new_with_rtt(type);
- } else {
- b.array_new(type);
- }
- }
-
- void array_new_default(w.Instructions b, w.ArrayType type) {
- if (options.runtimeTypes) {
- b.rtt_canon(type);
- b.array_new_default_with_rtt(type);
- } else {
- b.array_new_default(type);
- }
- }
-
- void array_init(w.Instructions b, w.ArrayType type, int length) {
- if (options.runtimeTypes) {
- b.rtt_canon(type);
- b.array_init(type, length);
- } else {
- b.array_init_static(type, length);
- }
- }
-
- void array_init_from_data(
- w.Instructions b, w.ArrayType type, w.DataSegment data) {
- if (options.runtimeTypes) {
- b.rtt_canon(type);
- b.array_init_from_data(type, data);
- } else {
- b.array_init_from_data_static(type, data);
- }
- }
-
- void ref_test(w.Instructions b, Object type) {
- if (options.runtimeTypes) {
- _emitRtt(b, type);
- b.ref_test();
- } else {
- b.ref_test_static(_targetType(type));
- }
- }
-
- void ref_cast(w.Instructions b, Object type) {
- if (options.runtimeTypes) {
- _emitRtt(b, type);
- b.ref_cast();
- } else {
- b.ref_cast_static(_targetType(type));
- }
- }
-
- void br_on_cast(w.Instructions b, w.Label label, Object type) {
- if (options.runtimeTypes) {
- _emitRtt(b, type);
- b.br_on_cast(label);
- } else {
- b.br_on_cast_static(label, _targetType(type));
- }
- }
-
- void br_on_cast_fail(w.Instructions b, w.Label label, Object type) {
- if (options.runtimeTypes) {
- _emitRtt(b, type);
- b.br_on_cast_fail(label);
- } else {
- b.br_on_cast_static_fail(label, _targetType(type));
- }
- }
-
- w.DefType _emitRtt(w.Instructions b, Object type) {
- if (type is ClassInfo) {
- if (options.nominalTypes) {
- b.rtt_canon(type.struct);
- } else {
- b.global_get(type.rtt);
- }
- return type.struct;
- } else if (type is int) {
- int parameterCount = type;
- w.StructType struct = closureStructType(parameterCount);
- if (options.nominalTypes) {
- b.rtt_canon(struct);
- } else {
- w.DefinedGlobal rtt = functionTypeRtt[parameterCount]!;
- b.global_get(rtt);
- }
- return struct;
- } else {
- b.rtt_canon(type as w.DefType);
- return type;
- }
- }
-
- w.DefType _targetType(Object type) => type is ClassInfo
- ? type.struct
- : type is int
- ? closureStructType(type)
- : type as w.DefType;
}
class NodeCounter extends Visitor<void> with VisitorVoidMixin {
diff --git a/pkg/dart2wasm/lib/types.dart b/pkg/dart2wasm/lib/types.dart
index b60f7a5..ab61a93 100644
--- a/pkg/dart2wasm/lib/types.dart
+++ b/pkg/dart2wasm/lib/types.dart
@@ -421,7 +421,7 @@
} else {
throw '`$type` should have already been handled.';
}
- translator.struct_new(b, info);
+ b.struct_new(info.struct);
return info.nonNullableType;
}
@@ -484,7 +484,7 @@
List<Class> concrete = _getConcreteSubtypes(type.classNode).toList();
if (type.classNode == coreTypes.functionClass) {
ClassInfo functionInfo = translator.classInfo[translator.functionClass]!;
- translator.ref_test(b, functionInfo);
+ b.ref_test(functionInfo.struct);
} else if (concrete.isEmpty) {
b.drop();
b.i32_const(0);
diff --git a/pkg/wasm_builder/lib/src/instructions.dart b/pkg/wasm_builder/lib/src/instructions.dart
index 7711a09..0e034f5 100644
--- a/pkg/wasm_builder/lib/src/instructions.dart
+++ b/pkg/wasm_builder/lib/src/instructions.dart
@@ -943,25 +943,6 @@
_writeLabel(label);
}
- /// Emit a `struct.new_with_rtt` instruction.
- void struct_new_with_rtt(StructType structType) {
- assert(_verifyTypes(
- [...structType.fields.map((f) => f.type.unpacked), Rtt(structType)],
- [RefType.def(structType, nullable: false)],
- trace: ['struct.new_with_rtt', structType]));
- writeBytes(const [0xFB, 0x01]);
- writeUnsigned(structType.index);
- }
-
- /// Emit a `struct.new_default_with_rtt` instruction.
- void struct_new_default_with_rtt(StructType structType) {
- assert(_verifyTypes(
- [Rtt(structType)], [RefType.def(structType, nullable: false)],
- trace: ['struct.new_default_with_rtt', structType]));
- writeBytes(const [0xFB, 0x02]);
- writeUnsigned(structType.index);
- }
-
/// Emit a `struct.get` instruction.
void struct_get(StructType structType, int fieldIndex) {
assert(structType.fields[fieldIndex].type is ValueType);
@@ -1027,25 +1008,6 @@
writeUnsigned(structType.index);
}
- /// Emit an `array.new_with_rtt` instruction.
- void array_new_with_rtt(ArrayType arrayType) {
- assert(_verifyTypes(
- [arrayType.elementType.type.unpacked, NumType.i32, Rtt(arrayType)],
- [RefType.def(arrayType, nullable: false)],
- trace: ['array.new_with_rtt', arrayType]));
- writeBytes(const [0xFB, 0x11]);
- writeUnsigned(arrayType.index);
- }
-
- /// Emit an `array.new_default_with_rtt` instruction.
- void array_new_default_with_rtt(ArrayType arrayType) {
- assert(_verifyTypes([NumType.i32, Rtt(arrayType)],
- [RefType.def(arrayType, nullable: false)],
- trace: ['array.new_default_with_rtt', arrayType]));
- writeBytes(const [0xFB, 0x12]);
- writeUnsigned(arrayType.index);
- }
-
/// Emit an `array.get` instruction.
void array_get(ArrayType arrayType) {
assert(arrayType.elementType.type is ValueType);
@@ -1099,23 +1061,12 @@
writeUnsigned(arrayType.index);
}
- /// Emit an `array.init` instruction.
- void array_init(ArrayType arrayType, int length) {
- ValueType elementType = arrayType.elementType.type.unpacked;
- assert(_verifyTypes([...List.filled(length, elementType), Rtt(arrayType)],
- [RefType.def(arrayType, nullable: false)],
- trace: ['array.init', arrayType, length]));
- writeBytes(const [0xFB, 0x19]);
- writeUnsigned(arrayType.index);
- writeUnsigned(length);
- }
-
- /// Emit an `array.init_static` instruction.
- void array_init_static(ArrayType arrayType, int length) {
+ /// Emit an `array.new_fixed` instruction.
+ void array_new_fixed(ArrayType arrayType, int length) {
ValueType elementType = arrayType.elementType.type.unpacked;
assert(_verifyTypes([...List.filled(length, elementType)],
[RefType.def(arrayType, nullable: false)],
- trace: ['array.init_static', arrayType, length]));
+ trace: ['array.new_fixed', arrayType, length]));
writeBytes(const [0xFB, 0x1a]);
writeUnsigned(arrayType.index);
writeUnsigned(length);
@@ -1139,30 +1090,18 @@
writeUnsigned(arrayType.index);
}
- /// Emit an `array.init_from_data_static` instruction.
- void array_init_from_data_static(ArrayType arrayType, DataSegment data) {
+ /// Emit an `array.new_data` instruction.
+ void array_new_data(ArrayType arrayType, DataSegment data) {
assert(arrayType.elementType.type.isPrimitive);
assert(_verifyTypes(
[NumType.i32, NumType.i32], [RefType.def(arrayType, nullable: false)],
- trace: ['array.init_from_data_static', arrayType, data.index]));
+ trace: ['array.new_data', arrayType, data.index]));
writeBytes(const [0xFB, 0x1d]);
writeUnsigned(arrayType.index);
writeUnsigned(data.index);
if (isGlobalInitializer) module.dataReferencedFromGlobalInitializer = true;
}
- /// Emit an `array.init_from_data` instruction.
- void array_init_from_data(ArrayType arrayType, DataSegment data) {
- assert(arrayType.elementType.type.isPrimitive);
- assert(_verifyTypes([NumType.i32, NumType.i32, Rtt(arrayType)],
- [RefType.def(arrayType, nullable: false)],
- trace: ['array.init_from_data', arrayType, data.index]));
- writeBytes(const [0xFB, 0x1e]);
- writeUnsigned(arrayType.index);
- writeUnsigned(data.index);
- if (isGlobalInitializer) module.dataReferencedFromGlobalInitializer = true;
- }
-
/// Emit an `i31.new` instruction.
void i31_new() {
assert(_verifyTypes(const [NumType.i32], const [RefType.i31()],
@@ -1184,98 +1123,11 @@
writeBytes(const [0xFB, 0x22]);
}
- /// Emit an `rtt.canon` instruction.
- void rtt_canon(DefType defType) {
- assert(_verifyTypes(const [], [Rtt(defType, defType.depth)],
- trace: ['rtt.canon', defType]));
- writeBytes(const [0xFB, 0x30]);
- writeSigned(defType.index);
- }
-
- bool _verifyRttSub(DefType subType) {
- if (!reachable) {
- return _debugTrace(['rtt.sub', subType], reachableAfter: false);
- }
- final ValueType input = _topOfStack;
- if (input is! Rtt) _reportError("Expected rtt, but stack contained $input");
- final int? depth = input.depth;
- if (depth == null) _reportError("Expected rtt with known depth");
- final DefType superType = input.defType;
- if (!subType.isSubtypeOf(superType)) {
- _reportError("Expected supertype of $subType, but got $superType");
- }
- return _verifyTypes([input], [Rtt(subType, depth + 1)],
- trace: ['rtt.sub', subType]);
- }
-
- /// Emit an `rtt.sub` instruction.
- void rtt_sub(DefType defType) {
- assert(_verifyRttSub(defType));
- writeBytes(const [0xFB, 0x31]);
- writeSigned(defType.index);
- }
-
bool _verifyCast(List<ValueType> Function(List<ValueType>) outputsFun,
{List<Object>? trace}) {
if (!reachable) {
return _debugTrace(trace, reachableAfter: false);
}
- final stack = _stack(2);
- final ValueType value = stack[0];
- final ValueType rtt = stack[1];
- if (rtt is! Rtt ||
- !value.isSubtypeOf(const RefType.data(nullable: true)) &&
- !value.isSubtypeOf(const RefType.func(nullable: true))) {
- _reportError("Expected [data or func, rtt], but stack contained $stack");
- }
- return _verifyTypesFun(stack, outputsFun, trace: trace);
- }
-
- /// Emit a `ref.test` instruction.
- void ref_test() {
- assert(_verifyCast((_) => const [NumType.i32], trace: const ['ref.test']));
- writeBytes(const [0xFB, 0x40]);
- }
-
- /// Emit a `ref.cast` instruction.
- void ref_cast() {
- assert(_verifyCast(
- (inputs) => [
- RefType.def((inputs[1] as Rtt).defType,
- nullable: inputs[0].nullable)
- ],
- trace: const ['ref.cast']));
- writeBytes(const [0xFB, 0x41]);
- }
-
- /// Emit a `br_on_cast` instruction.
- void br_on_cast(Label label) {
- late final DefType targetType;
- assert(_verifyCast((inputs) {
- targetType = (inputs[1] as Rtt).defType;
- return [inputs[0]];
- }, trace: ['br_on_cast', label]));
- assert(_verifyBranchTypes(
- label, 1, [RefType.def(targetType, nullable: false)]));
- writeBytes(const [0xFB, 0x42]);
- _writeLabel(label);
- }
-
- /// Emit a `br_on_cast_fail` instruction.
- void br_on_cast_fail(Label label) {
- assert(_verifyBranchTypes(label, 1, [_topOfStack]));
- assert(_verifyCast(
- (inputs) => [RefType.def((inputs[1] as Rtt).defType, nullable: false)],
- trace: ['br_on_cast_fail', label]));
- writeBytes(const [0xFB, 0x43]);
- _writeLabel(label);
- }
-
- bool _verifyCastStatic(List<ValueType> Function(List<ValueType>) outputsFun,
- {List<Object>? trace}) {
- if (!reachable) {
- return _debugTrace(trace, reachableAfter: false);
- }
final ValueType value = _topOfStack;
if (!value.isSubtypeOf(const RefType.data(nullable: true)) &&
!value.isSubtypeOf(const RefType.func(nullable: true))) {
@@ -1284,28 +1136,28 @@
return _verifyTypesFun([value], outputsFun, trace: trace);
}
- /// Emit a `ref.test_static` instruction.
- void ref_test_static(DefType targetType) {
- assert(_verifyCastStatic((_) => const [NumType.i32],
- trace: ['ref.test_static', targetType]));
+ /// Emit a `ref.test` instruction.
+ void ref_test(DefType targetType) {
+ assert(_verifyCast((_) => const [NumType.i32],
+ trace: ['ref.test', targetType]));
writeBytes(const [0xFB, 0x44]);
writeSigned(targetType.index);
}
- /// Emit a `ref.cast_static` instruction.
- void ref_cast_static(DefType targetType) {
- assert(_verifyCastStatic(
+ /// Emit a `ref.cast` instruction.
+ void ref_cast(DefType targetType) {
+ assert(_verifyCast(
(inputs) => [RefType.def(targetType, nullable: inputs[0].nullable)],
- trace: ['ref.cast_static', targetType]));
+ trace: ['ref.cast', targetType]));
writeBytes(const [0xFB, 0x45]);
writeSigned(targetType.index);
}
- /// Emit a `br_on_cast_static` instruction.
- void br_on_cast_static(Label label, DefType targetType) {
- assert(_verifyCastStatic((inputs) {
+ /// Emit a `br_on_cast` instruction.
+ void br_on_cast(Label label, DefType targetType) {
+ assert(_verifyCast((inputs) {
return [inputs[0]];
- }, trace: ['br_on_cast_static', label, targetType]));
+ }, trace: ['br_on_cast', label, targetType]));
assert(_verifyBranchTypes(
label, 1, [RefType.def(targetType, nullable: false)]));
writeBytes(const [0xFB, 0x46]);
@@ -1313,12 +1165,11 @@
writeSigned(targetType.index);
}
- /// Emit a `br_on_cast_static_fail` instruction.
- void br_on_cast_static_fail(Label label, DefType targetType) {
+ /// Emit a `br_on_cast_fail` instruction.
+ void br_on_cast_fail(Label label, DefType targetType) {
assert(_verifyBranchTypes(label, 1, [_topOfStack]));
- assert(_verifyCastStatic(
- (inputs) => [RefType.def(targetType, nullable: false)],
- trace: ['br_on_cast_static_fail', label, targetType]));
+ assert(_verifyCast((inputs) => [RefType.def(targetType, nullable: false)],
+ trace: ['br_on_cast_fail', label, targetType]));
writeBytes(const [0xFB, 0x47]);
_writeLabel(label);
writeSigned(targetType.index);
diff --git a/pkg/wasm_builder/lib/src/module.dart b/pkg/wasm_builder/lib/src/module.dart
index 0562900..7187a39 100644
--- a/pkg/wasm_builder/lib/src/module.dart
+++ b/pkg/wasm_builder/lib/src/module.dart
@@ -17,6 +17,7 @@
final Map<_FunctionTypeKey, FunctionType> functionTypeMap = {};
final List<DefType> defTypes = [];
+ final List<int> recursionGroupSplits = [];
final List<BaseFunction> functions = [];
final List<Table> tables = [];
final List<Memory> memories = [];
@@ -79,7 +80,7 @@
/// on the recursion path) are not supported.
FunctionType addFunctionType(
Iterable<ValueType> inputs, Iterable<ValueType> outputs,
- {HeapType? superType}) {
+ {DefType? superType}) {
final List<ValueType> inputList = List.unmodifiable(inputs);
final List<ValueType> outputList = List.unmodifiable(outputs);
final _FunctionTypeKey key = _FunctionTypeKey(inputList, outputList);
@@ -96,7 +97,7 @@
/// Fields can be added later, by adding to the [fields] list. This enables
/// struct types to be recursive.
StructType addStructType(String name,
- {Iterable<FieldType>? fields, HeapType? superType}) {
+ {Iterable<FieldType>? fields, DefType? superType}) {
final type = StructType(name, fields: fields, superType: superType)
..index = defTypes.length;
defTypes.add(type);
@@ -108,13 +109,19 @@
/// The element type can be specified later. This enables array types to be
/// recursive.
ArrayType addArrayType(String name,
- {FieldType? elementType, HeapType? superType}) {
+ {FieldType? elementType, DefType? superType}) {
final type = ArrayType(name, elementType: elementType, superType: superType)
..index = defTypes.length;
defTypes.add(type);
return type;
}
+ /// Insert a recursion group split in the list of type definitions. Types can
+ /// only reference other types in the same or earlier recursion groups.
+ void splitRecursionGroup() {
+ recursionGroupSplits.add(defTypes.length);
+ }
+
/// Add a new function to the module with the given function type.
///
/// The [DefinedFunction.body] must be completed (including the terminating
@@ -740,9 +747,25 @@
@override
void serializeContents() {
- writeUnsigned(module.defTypes.length);
- for (DefType defType in module.defTypes) {
- defType.serializeDefinition(this);
+ writeUnsigned(module.recursionGroupSplits.length + 1);
+ int typeIndex = 0;
+ for (int split
+ in module.recursionGroupSplits.followedBy([module.defTypes.length])) {
+ writeByte(0x4F);
+ writeUnsigned(split - typeIndex);
+ for (; typeIndex < split; typeIndex++) {
+ DefType defType = module.defTypes[typeIndex];
+ assert(defType.superType == null || defType.superType!.index < split,
+ "Type '$defType' has a supertype in a later recursion group");
+ assert(
+ defType.constituentTypes
+ .whereType<RefType>()
+ .map((t) => t.heapType)
+ .whereType<DefType>()
+ .every((d) => d.index < split),
+ "Type '$defType' depends on a type in a later recursion group");
+ defType.serializeDefinition(this);
+ }
}
}
}
diff --git a/pkg/wasm_builder/lib/src/types.dart b/pkg/wasm_builder/lib/src/types.dart
index d867dc6..d25ab81 100644
--- a/pkg/wasm_builder/lib/src/types.dart
+++ b/pkg/wasm_builder/lib/src/types.dart
@@ -128,44 +128,6 @@
}
}
-/// An RTT (runtime type) type.
-class Rtt extends ValueType {
- final DefType defType;
- final int? depth;
-
- const Rtt(this.defType, [this.depth]);
-
- @override
- bool get defaultable => false;
-
- @override
- bool isSubtypeOf(StorageType other) =>
- other is Rtt &&
- defType == other.defType &&
- (other.depth == null || depth == other.depth);
-
- @override
- void serialize(Serializer s) {
- if (depth != null) {
- s.writeByte(0x69);
- s.writeUnsigned(depth!);
- } else {
- s.writeByte(0x68);
- }
- s.writeSigned(defType.index);
- }
-
- @override
- String toString() => depth == null ? "rtt $defType" : "rtt $depth $defType";
-
- @override
- bool operator ==(Object other) =>
- other is Rtt && other.defType == defType && other.depth == depth;
-
- @override
- int get hashCode => defType.hashCode * (3 + (depth ?? -3) * 2);
-}
-
/// A *reference type*.
class RefType extends ValueType {
/// The *heap type* of this reference type.
@@ -378,14 +340,14 @@
abstract class DefType extends HeapType {
int? _index;
- /// For nominal types: the declared supertype of this heap type.
- final HeapType? superType;
+ /// The declared supertype of this heap type.
+ final DefType? superType;
/// The length of the supertype chain of this heap type.
final int depth;
DefType({this.superType})
- : depth = superType is DefType ? superType.depth + 1 : 0;
+ : depth = superType != null ? superType.depth + 1 : 0;
int get index => _index ?? (throw "$runtimeType $this not added to module");
set index(int i) => _index = i;
@@ -398,16 +360,30 @@
@override
bool isSubtypeOf(HeapType other) {
if (this == other) return true;
- if (hasSuperType) {
- return superType!.isSubtypeOf(other);
- }
- return isStructuralSubtypeOf(other);
+ return (superType ?? abstractSuperType).isSubtypeOf(other);
}
+ HeapType get abstractSuperType;
+
+ Iterable<StorageType> get constituentTypes;
+
@override
void serialize(Serializer s) => s.writeSigned(index);
- void serializeDefinition(Serializer s);
+ // Serialize the type for the type section, including the supertype reference,
+ // if any.
+ void serializeDefinition(Serializer s) {
+ if (hasSuperType) {
+ s.writeByte(0x50);
+ s.writeUnsigned(1);
+ assert(isStructuralSubtypeOf(superType!));
+ s.write(superType!);
+ }
+ serializeDefinitionInner(s);
+ }
+
+ // Serialize the type for the type section, excluding supertype references.
+ void serializeDefinitionInner(Serializer s);
}
/// A custom function type.
@@ -418,6 +394,12 @@
FunctionType(this.inputs, this.outputs, {super.superType});
@override
+ HeapType get abstractSuperType => HeapType.func;
+
+ @override
+ Iterable<StorageType> get constituentTypes => [...inputs, ...outputs];
+
+ @override
bool isStructuralSubtypeOf(HeapType other) {
if (other == HeapType.any || other == HeapType.func) return true;
if (other is! FunctionType) return false;
@@ -435,14 +417,10 @@
}
@override
- void serializeDefinition(Serializer s) {
- s.writeByte(hasSuperType ? 0x5D : 0x60);
+ void serializeDefinitionInner(Serializer s) {
+ s.writeByte(0x60);
s.writeList(inputs);
s.writeList(outputs);
- if (hasSuperType) {
- assert(isStructuralSubtypeOf(superType!));
- s.write(superType!);
- }
}
@override
@@ -468,6 +446,14 @@
}
@override
+ // TODO(askesc): Change this to HeapType.struct if that is added.
+ HeapType get abstractSuperType => HeapType.data;
+
+ @override
+ Iterable<StorageType> get constituentTypes =>
+ [for (FieldType f in fields) f.type];
+
+ @override
bool isStructuralSubtypeOf(HeapType other) {
if (other == HeapType.any ||
other == HeapType.eq ||
@@ -483,13 +469,9 @@
}
@override
- void serializeDefinition(Serializer s) {
- s.writeByte(hasSuperType ? 0x5C : 0x5F);
+ void serializeDefinitionInner(Serializer s) {
+ s.writeByte(0x5F);
s.writeList(fields);
- if (hasSuperType) {
- assert(isStructuralSubtypeOf(superType!));
- s.write(superType!);
- }
}
}
@@ -502,6 +484,13 @@
}
@override
+ // TODO(askesc): Change this to HeapType.array when that is added.
+ HeapType get abstractSuperType => HeapType.data;
+
+ @override
+ Iterable<StorageType> get constituentTypes => [elementType.type];
+
+ @override
bool isStructuralSubtypeOf(HeapType other) {
if (other == HeapType.any ||
other == HeapType.eq ||
@@ -513,13 +502,9 @@
}
@override
- void serializeDefinition(Serializer s) {
- s.writeByte(hasSuperType ? 0x5B : 0x5E);
+ void serializeDefinitionInner(Serializer s) {
+ s.writeByte(0x5E);
s.write(elementType);
- if (hasSuperType) {
- assert(isStructuralSubtypeOf(superType!));
- s.write(superType!);
- }
}
}
diff --git a/pkg/wasm_builder/lib/wasm_builder.dart b/pkg/wasm_builder/lib/wasm_builder.dart
index 7ad807b..b598a35 100644
--- a/pkg/wasm_builder/lib/wasm_builder.dart
+++ b/pkg/wasm_builder/lib/wasm_builder.dart
@@ -33,7 +33,6 @@
NumType,
PackedType,
RefType,
- Rtt,
StorageType,
StructType,
ValueType;