[dart2wasm] Add deferred loading support to dart2wasm (4/X).

Adds `Translator.callReference` which will be the main indirection point for calls between modules.

Any call that might need to be made across modules should go through `callReference` and this will handle checking if the call is local to the same module. If it is then it will use a normal "call" instruction, otherwise it will re-route the call through a table and "call_indirect".
`Reference` is the primary module assignment mechanism, we will generate synthetic References for anything that doesn't have one from the Kernel.

For now just maintains the current behavior of generating a `call` instruction.

Change-Id: I9a300d100bc7c27ec2aba42af367e91201dcadc3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/381322
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/pkg/dart2wasm/lib/closures.dart b/pkg/dart2wasm/lib/closures.dart
index 2bb9afa..b29405b 100644
--- a/pkg/dart2wasm/lib/closures.dart
+++ b/pkg/dart2wasm/lib/closures.dart
@@ -691,8 +691,8 @@
     b.array_new_fixed(translator.typeArrayType, typeCount);
 
     // Call [_TypeUniverse.substituteFunctionTypeArgument].
-    b.call(translator.functions
-        .getFunction(translator.substituteFunctionTypeArgument.reference));
+    translator.callReference(
+        translator.substituteFunctionTypeArgument.reference, b);
 
     // Finally, allocate closure struct.
     b.struct_new(instantiatedRepresentation.closureStruct);
@@ -735,8 +735,7 @@
       b.struct_get(contextStructType, typeFieldIdx);
       b.local_get(otherContextLocal);
       b.struct_get(contextStructType, typeFieldIdx);
-      b.call(translator.functions
-          .getFunction(translator.runtimeTypeEquals.reference));
+      translator.callReference(translator.runtimeTypeEquals.reference, b);
       b.if_();
     }
 
@@ -777,10 +776,8 @@
     for (int typeFieldIdx = 1; typeFieldIdx <= numTypes; typeFieldIdx += 1) {
       b.local_get(thisContextLocal);
       b.struct_get(contextStructType, typeFieldIdx);
-      b.call(translator.functions
-          .getFunction(translator.runtimeTypeHashCode.reference));
-      b.call(translator.functions
-          .getFunction(translator.systemHashCombine.reference));
+      translator.callReference(translator.runtimeTypeHashCode.reference, b);
+      translator.callReference(translator.systemHashCombine.reference, b);
     }
 
     b.end();
@@ -1280,6 +1277,7 @@
   }
 
   void _visitLambda(FunctionNode node, [VariableDeclaration? variable]) {
+    final module = translator.moduleForReference(member.reference);
     List<w.ValueType> inputs = [
       w.RefType.struct(nullable: false),
       ...List.filled(node.typeParameters.length, closures.typeType),
@@ -1298,7 +1296,7 @@
     } else {
       functionName = "$member closure $functionNodeName at ${node.location}";
     }
-    final function = m.functions.define(type, functionName);
+    final function = module.functions.define(type, functionName);
     closures.lambdas[node] = Lambda(node, function, _currentSource);
 
     functionIsSyncStarOrAsync.add(node.asyncMarker == AsyncMarker.SyncStar ||
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index c0329e2..1c5472b 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -2705,11 +2705,11 @@
         ? translator.growableListFromWasmArray.reference
         : translator.growableListEmpty.reference;
 
-    final w.BaseFunction target = useSharedCreator
-        ? translator.partialInstantiator.getOneTypeArgumentForwarder(
-            targetReference,
-            node.typeArgument,
-            'create${passArray ? '' : 'Empty'}List<${node.typeArgument}>')
+    final target = useSharedCreator
+        ? translator
+            .getPartialInstantiatorForModule(b.module)
+            .getOneTypeArgumentForwarder(targetReference, node.typeArgument,
+                'create${passArray ? '' : 'Empty'}List<${node.typeArgument}>')
         : translator.functions.getFunction(targetReference);
 
     if (passType) {
@@ -2720,7 +2720,8 @@
           translator.coreTypes.objectRawType(Nullability.nullable));
     }
 
-    b.call(target);
+    translator.callFunction(target, b);
+
     return target.type.outputs.single;
   }
 
@@ -2750,12 +2751,15 @@
         ? translator.mapFromWasmArray.reference
         : translator.mapFactory.reference;
 
-    final w.BaseFunction target = useSharedCreator
-        ? translator.partialInstantiator.getTwoTypeArgumentForwarder(
-            targetReference,
-            node.keyType,
-            node.valueType,
-            'create${passArray ? '' : 'Empty'}Map<${node.keyType}, ${node.valueType}>')
+    final target = useSharedCreator
+        ? translator
+            .getPartialInstantiatorForModule(b.module)
+            .getTwoTypeArgumentForwarder(
+                targetReference,
+                node.keyType,
+                node.valueType,
+                'create${passArray ? '' : 'Empty'}'
+                'Map<${node.keyType}, ${node.valueType}>')
         : translator.functions.getFunction(targetReference);
 
     if (passTypes) {
@@ -2774,7 +2778,8 @@
         }
       });
     }
-    b.call(target);
+    translator.callFunction(target, b);
+
     return target.type.outputs.single;
   }
 
@@ -2789,11 +2794,11 @@
         ? translator.setFromWasmArray.reference
         : translator.setFactory.reference;
 
-    final w.BaseFunction target = useSharedCreator
-        ? translator.partialInstantiator.getOneTypeArgumentForwarder(
-            targetReference,
-            node.typeArgument,
-            'create${passArray ? '' : 'Empty'}Set<${node.typeArgument}>')
+    final target = useSharedCreator
+        ? translator
+            .getPartialInstantiatorForModule(b.module)
+            .getOneTypeArgumentForwarder(targetReference, node.typeArgument,
+                'create${passArray ? '' : 'Empty'}Set<${node.typeArgument}>')
         : translator.functions.getFunction(targetReference);
 
     if (passType) {
@@ -2803,7 +2808,8 @@
       makeArrayFromExpressions(node.expressions,
           translator.coreTypes.objectRawType(Nullability.nullable));
     }
-    b.call(target);
+    translator.callFunction(target, b);
+
     return target.type.outputs.single;
   }
 
@@ -3025,7 +3031,7 @@
         translator.functions.getFunction(translator.printToConsole.reference);
     translator.constants.instantiateConstant(
         b, StringConstant(s), printFunction.type.inputs[0]);
-    b.call(printFunction);
+    translator.callFunction(printFunction, b);
   }
 
   @override
diff --git a/pkg/dart2wasm/lib/dynamic_forwarders.dart b/pkg/dart2wasm/lib/dynamic_forwarders.dart
index 811c849..6828820 100644
--- a/pkg/dart2wasm/lib/dynamic_forwarders.dart
+++ b/pkg/dart2wasm/lib/dynamic_forwarders.dart
@@ -124,7 +124,7 @@
       b.local_get(receiverLocal);
       translator.convertType(
           b, receiverLocal.type, targetFunction.type.inputs.first);
-      b.call(targetFunction);
+      translator.callFunction(targetFunction, b);
       // Box return value if needed
       translator.convertType(b, targetFunction.type.outputs.single,
           _kind.functionType(translator).outputs.single);
@@ -156,8 +156,7 @@
       final Member targetMember = target.asMember;
       b.local_get(receiverLocal);
       b.local_get(positionalArgLocal);
-      b.call(
-          translator.functions.getFunction(targetMember.typeCheckerReference));
+      translator.callReference(targetMember.typeCheckerReference, b);
     }, () {
       generateNoSuchMethodCall(
           translator,
@@ -379,8 +378,8 @@
                 SymbolConstant(name, null),
                 translator.classInfo[translator.symbolClass]!.nonNullableType);
 
-            b.call(translator.functions
-                .getFunction(translator.getNamedParameterIndex.reference));
+            translator.callReference(
+                translator.getNamedParameterIndex.reference, b);
             b.local_tee(namedParameterIdxLocal);
 
             b.ref_is_null();
@@ -464,9 +463,7 @@
         b.local_get(typeArgsLocal);
         b.local_get(adjustedPositionalArgsLocal ?? positionalArgsLocal);
         b.local_get(adjustedNamedArgsLocal ?? namedArgsLocal);
-        final wasmFunction =
-            translator.functions.getFunction(targetMember.typeCheckerReference);
-        b.call(wasmFunction);
+        translator.callReference(targetMember.typeCheckerReference, b);
         b.return_();
         b.end(); // classIdNoMatch
       }
@@ -508,7 +505,7 @@
           b.local_get(receiverLocal);
           translator.convertType(
               b, receiverLocal.type, targetFunction.type.inputs.first);
-          b.call(targetFunction);
+          translator.callFunction(targetFunction, b);
           translator.convertType(b, targetFunction.type.outputs.single,
               translator.topInfo.nullableType);
           b.local_tee(getterValueLocal);
@@ -647,9 +644,7 @@
   b.local_get(typeArgsLocal);
   b.local_get(posArgsLocal);
   b.local_get(namedArgsLocal);
-  b.call(
-      translator.functions.getFunction(translator.checkClosureShape.reference));
-
+  translator.callReference(translator.checkClosureShape.reference, b);
   b.i32_eqz();
   b.br_if(noSuchMethodBlock);
 
@@ -659,8 +654,7 @@
     b.local_get(typeArgsLocal);
     b.local_get(posArgsLocal);
     b.local_get(namedArgsLocal);
-    b.call(translator.functions
-        .getFunction(translator.checkClosureType.reference));
+    translator.callReference(translator.checkClosureType.reference, b);
     b.drop();
   }
 
@@ -692,16 +686,13 @@
       translator.classInfo[translator.symbolClass]!.nonNullableType);
 
   b.local_get(typeArgsLocal);
-  b.call(translator.functions
-      .getFunction(translator.typeArgumentsToList.reference));
+  translator.callReference(translator.typeArgumentsToList.reference, b);
   b.local_get(positionalArgsLocal);
-  b.call(translator.functions
-      .getFunction(translator.positionalParametersToList.reference));
+  translator.callReference(translator.positionalParametersToList.reference, b);
   b.local_get(namedArgsLocal);
-  b.call(translator.functions
-      .getFunction(translator.namedParametersToMap.reference));
-  b.call(translator.functions
-      .getFunction(translator.invocationGenericMethodFactory.reference));
+  translator.callReference(translator.namedParametersToMap.reference, b);
+  translator.callReference(
+      translator.invocationGenericMethodFactory.reference, b);
 }
 
 void createGetterInvocationObject(
@@ -712,8 +703,7 @@
   translator.constants.instantiateConstant(b, SymbolConstant(memberName, null),
       translator.classInfo[translator.symbolClass]!.nonNullableType);
 
-  b.call(translator.functions
-      .getFunction(translator.invocationGetterFactory.reference));
+  translator.callReference(translator.invocationGetterFactory.reference, b);
 }
 
 void createSetterInvocationObject(
@@ -728,8 +718,7 @@
       translator.classInfo[translator.symbolClass]!.nonNullableType);
 
   b.local_get(positionalArgLocal);
-  b.call(translator.functions
-      .getFunction(translator.invocationSetterFactory.reference));
+  translator.callReference(translator.invocationSetterFactory.reference, b);
 }
 
 void generateNoSuchMethodCall(
diff --git a/pkg/dart2wasm/lib/functions.dart b/pkg/dart2wasm/lib/functions.dart
index 7f1bb4d..a8d6d43 100644
--- a/pkg/dart2wasm/lib/functions.dart
+++ b/pkg/dart2wasm/lib/functions.dart
@@ -118,7 +118,8 @@
 
   w.BaseFunction getFunction(Reference target) {
     return _functions.putIfAbsent(target, () {
-      final function = m.functions.define(
+      final module = translator.moduleForReference(target);
+      final function = module.functions.define(
           translator.signatureForDirectCall(target), getFunctionName(target));
       translator.compilationQueue.add(AstCompilationTask(function,
           getMemberCodeGenerator(translator, function, target), target));
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index 3583fbc..79b8234 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -174,7 +174,11 @@
   late final w.RefType nullableObjectArrayTypeRef =
       w.RefType.def(nullableObjectArrayType, nullable: false);
 
-  late final partialInstantiator = PartialInstantiator(this);
+  final Map<w.ModuleBuilder, PartialInstantiator> _partialInstantiators = {};
+  PartialInstantiator getPartialInstantiatorForModule(w.ModuleBuilder module) {
+    return _partialInstantiators[module] ??= PartialInstantiator(this, module);
+  }
+
   late final polymorphicDispatchers = PolymorphicDispatchers(this);
 
   /// Dart types that have specialized Wasm representations.
@@ -364,6 +368,24 @@
     }
   }
 
+  /// Gets the function associated with [reference] and calls its using
+  /// [callFunction].
+  List<w.ValueType> callReference(
+      Reference reference, w.InstructionsBuilder b) {
+    return callFunction(functions.getFunction(reference), b);
+  }
+
+  /// Generates a set of instructions to call [function] adding indirection
+  /// if the call crosses a module boundary. Calls the function directly if it
+  /// is local. Imports the function and calls it directly if is in the main
+  /// module. Otherwise does an indirect call through the static dispatch table.
+  List<w.ValueType> callFunction(
+      w.BaseFunction function, w.InstructionsBuilder b) {
+    // TODO(natebiggs): Add indirect call.
+    b.call(function);
+    return function.type.outputs;
+  }
+
   Class classForType(DartType type) {
     return type is InterfaceType
         ? type.classNode
@@ -1310,7 +1332,7 @@
     assert(targetIndex == target.type.inputs.length);
     assert(argNameIndex == argNames.length);
 
-    b.call(target);
+    translator.callFunction(target, b);
 
     translator.convertType(b, translator.outputOrVoid(target.type.outputs),
         translator.outputOrVoid(trampoline.type.outputs));
@@ -1437,8 +1459,7 @@
           b,
           SymbolConstant(paramName, null),
           translator.classInfo[translator.symbolClass]!.nonNullableType);
-      b.call(translator.functions
-          .getFunction(translator.getNamedParameterIndex.reference));
+      translator.callReference(translator.getNamedParameterIndex.reference, b);
       b.local_set(namedArgValueIndexLocal);
 
       if (functionNodeDefaultValue == null && paramInfoDefaultValue == null) {
@@ -1484,7 +1505,7 @@
       inputIdx += 1;
     }
 
-    b.call(target);
+    translator.callFunction(target, b);
 
     translator.convertType(b, translator.outputOrVoid(target.type.outputs),
         translator.outputOrVoid(function.type.outputs));
@@ -1609,12 +1630,13 @@
 /// This saves code size on the call site.
 class PartialInstantiator {
   final Translator translator;
+  final w.ModuleBuilder callingModule;
 
   final Map<(Reference, DartType), w.BaseFunction> _oneTypeArgument = {};
   final Map<(Reference, DartType, DartType), w.BaseFunction> _twoTypeArguments =
       {};
 
-  PartialInstantiator(this.translator);
+  PartialInstantiator(this.translator, this.callingModule);
 
   w.BaseFunction getOneTypeArgumentForwarder(
       Reference target, DartType type, String name) {
@@ -1623,7 +1645,7 @@
     return _oneTypeArgument.putIfAbsent((target, type), () {
       final wasmTarget = translator.functions.getFunction(target);
 
-      final function = translator.m.functions.define(
+      final function = callingModule.functions.define(
           translator.typesBuilder.defineFunction(
             [...wasmTarget.type.inputs.skip(1)],
             wasmTarget.type.outputs,
@@ -1635,7 +1657,7 @@
       for (int i = 1; i < wasmTarget.type.inputs.length; ++i) {
         b.local_get(b.locals[i - 1]);
       }
-      b.call(wasmTarget);
+      translator.callFunction(wasmTarget, b);
       b.return_();
       b.end();
 
@@ -1651,7 +1673,7 @@
     return _twoTypeArguments.putIfAbsent((target, type1, type2), () {
       final wasmTarget = translator.functions.getFunction(target);
 
-      final function = translator.m.functions.define(
+      final function = callingModule.functions.define(
           translator.typesBuilder.defineFunction(
             [...wasmTarget.type.inputs.skip(2)],
             wasmTarget.type.outputs,
@@ -1665,7 +1687,7 @@
       for (int i = 2; i < wasmTarget.type.inputs.length; ++i) {
         b.local_get(b.locals[i - 2]);
       }
-      b.call(wasmTarget);
+      translator.callFunction(wasmTarget, b);
       b.return_();
       b.end();
 
@@ -1741,7 +1763,7 @@
       for (int i = 0; i < signature.inputs.length; ++i) {
         b.local_get(paramLocals[i]);
       }
-      b.call(translator.functions.getFunction(target));
+      translator.callReference(target, b);
     }
 
     void emitDispatchTableCall() {
diff --git a/pkg/dart2wasm/lib/types.dart b/pkg/dart2wasm/lib/types.dart
index a968422..42276a3 100644
--- a/pkg/dart2wasm/lib/types.dart
+++ b/pkg/dart2wasm/lib/types.dart
@@ -962,20 +962,20 @@
       b.i32_const(testedAgainstClassId);
       if (argumentCount == 1) {
         b.local_get(b.locals[1]);
-        b.call(translator.functions
-            .getFunction(translator.throwInterfaceTypeAsCheckError1.reference));
+        translator.callReference(
+            translator.throwInterfaceTypeAsCheckError1.reference, b);
       } else if (argumentCount == 2) {
         b.local_get(b.locals[1]);
         b.local_get(b.locals[2]);
-        b.call(translator.functions
-            .getFunction(translator.throwInterfaceTypeAsCheckError2.reference));
+        translator.callReference(
+            translator.throwInterfaceTypeAsCheckError2.reference, b);
       } else {
         for (int i = 0; i < argumentCount; ++i) {
           b.local_get(b.locals[1 + i]);
         }
         b.array_new_fixed(translator.types.typeArrayArrayType, argumentCount);
-        b.call(translator.functions
-            .getFunction(translator.throwInterfaceTypeAsCheckError.reference));
+        translator.callReference(
+            translator.throwInterfaceTypeAsCheckError.reference, b);
       }
     } else {
       b.local_get(b.locals[0]);
@@ -983,8 +983,7 @@
           b,
           TypeLiteralConstant(testedAgainstType),
           translator.types.nonNullableTypeType);
-      b.call(translator.functions
-          .getFunction(translator.throwAsCheckError.reference));
+      translator.callReference(translator.throwAsCheckError.reference, b);
     }
     b.unreachable();