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