[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;