[dart2wasm] Make compiler use a compilation queue of compilation tasks
This creates a centralized loop that iterates over wasm functions to be
compiled. That centralized loop can then e.g. act on `--print-wasm`,
etc.
Migrate synthetic wasm functions to also use this compilation queue.
(We also remove the --export-all flag which is unused and untested)
Change-Id: I7a9384716c69ea2dd80424ff3dd7f736ac943f14
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/378703
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
diff --git a/pkg/dart2wasm/lib/async.dart b/pkg/dart2wasm/lib/async.dart
index 6d6d000..dab03be 100644
--- a/pkg/dart2wasm/lib/async.dart
+++ b/pkg/dart2wasm/lib/async.dart
@@ -9,6 +9,7 @@
import 'closures.dart';
import 'code_generator.dart';
import 'state_machine.dart';
+import 'translator.dart' show CompilationTask;
mixin AsyncCodeGeneratorMixin on StateMachineEntryAstCodeGenerator {
late final ClassInfo asyncSuspendStateInfo =
@@ -74,9 +75,10 @@
b.return_();
b.end();
- AsyncStateMachineCodeGenerator(translator, resumeFun, enclosingMember,
- functionNode, functionSource, closures)
- .generate(resumeFun.locals.toList(), null);
+ translator.compilationQueue.add(CompilationTask(
+ resumeFun,
+ AsyncStateMachineCodeGenerator(translator, resumeFun, enclosingMember,
+ functionNode, functionSource, closures)));
}
w.FunctionBuilder _defineInnerBodyFunction(FunctionNode functionNode) =>
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index 1488cfe..04a64bb 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -31,10 +31,8 @@
// * doesn't inline (i.e. makes new function with this code) it will provide
// the parameters of the function and no return label.
//
- void generate(List<w.Local> paramLocals, w.Label? returnLabel);
-
- // TODO(kustermann): Remove this again.
- Closures get closures;
+ void generate(
+ w.InstructionsBuilder b, List<w.Local> paramLocals, w.Label? returnLabel);
}
/// Main code generator for member bodies.
@@ -58,10 +56,10 @@
implements InitializerVisitor<void>, StatementVisitor<void>, CodeGenerator {
final Translator translator;
final w.FunctionType functionType;
- final w.InstructionsBuilder b;
final Member enclosingMember;
// To be initialized in `generate()`
+ late final w.InstructionsBuilder b;
late final List<w.Local> paramLocals;
late final w.Label? returnLabel;
@@ -69,7 +67,6 @@
late final StaticTypeContext typeContext =
StaticTypeContext(enclosingMember, translator.typeEnvironment);
- @override
late final Closures closures;
bool exceptionLocationPrinted = false;
@@ -103,8 +100,7 @@
{};
/// Create a code generator for a member or one of its lambdas.
- AstCodeGenerator(
- this.translator, this.functionType, this.b, this.enclosingMember);
+ AstCodeGenerator(this.translator, this.functionType, this.enclosingMember);
w.ModuleBuilder get m => translator.m;
@@ -208,7 +204,9 @@
/// Generate code while preventing recursive inlining.
@override
- void generate(List<w.Local> paramLocals, w.Label? returnLabel) {
+ void generate(w.InstructionsBuilder b, List<w.Local> paramLocals,
+ w.Label? returnLabel) {
+ this.b = b;
this.paramLocals = paramLocals;
this.returnLabel = returnLabel;
@@ -217,6 +215,16 @@
translator.membersBeingGenerated.remove(enclosingMember);
}
+ void addNestedClosuresToCompilationQueue() {
+ for (Lambda lambda in closures.lambdas.values) {
+ translator.compilationQueue.add(CompilationTask(
+ lambda.function,
+ getLambdaCodeGenerator(
+ translator, lambda, enclosingMember, closures)));
+ }
+ }
+
+ // Generate the body.
void generateInternal();
void _setupLocalParameters(Member member, ParameterInfo paramInfo,
@@ -651,7 +659,7 @@
w.FunctionType targetFunctionType =
translator.signatureForDirectCall(target);
final inliningCodeGen =
- translator.getInliningCodeGenerator(target, targetFunctionType, b);
+ translator.getInliningCodeGenerator(target, targetFunctionType);
if (inliningCodeGen != null) {
List<w.Local> inlinedLocals =
targetFunctionType.inputs.map((t) => addLocal(t)).toList();
@@ -660,7 +668,7 @@
}
w.Label block = b.block(const [], targetFunctionType.outputs);
b.comment("Inlined ${target.asMember}");
- inliningCodeGen.generate(inlinedLocals, block);
+ inliningCodeGen.generate(b, inlinedLocals, block);
return targetFunctionType.outputs;
} else {
w.BaseFunction targetFunction = translator.functions.getFunction(target);
@@ -3030,8 +3038,8 @@
w.FunctionBuilder functionBuilder, Reference memberReference) {
final member = memberReference.asMember;
final asyncMarker = member.function?.asyncMarker ?? AsyncMarker.Sync;
- final codeGen = getInlinableMemberCodeGenerator(translator, asyncMarker,
- functionBuilder.type, functionBuilder.body, memberReference);
+ final codeGen = getInlinableMemberCodeGenerator(
+ translator, asyncMarker, functionBuilder.type, memberReference);
if (codeGen != null) return codeGen;
final procedure = member as Procedure;
@@ -3063,44 +3071,39 @@
/// Returns a [CodeGenerator] for the given member iff that member can be
/// inlined.
-CodeGenerator? getInlinableMemberCodeGenerator(
- Translator translator,
- AsyncMarker asyncMarker,
- w.FunctionType functionType,
- w.InstructionsBuilder b,
- Reference reference) {
+CodeGenerator? getInlinableMemberCodeGenerator(Translator translator,
+ AsyncMarker asyncMarker, w.FunctionType functionType, Reference reference) {
final Member member = reference.asMember;
if (reference.isTearOffReference) {
- return TearOffCodeGenerator(translator, functionType, b, member);
+ return TearOffCodeGenerator(translator, functionType, member);
}
if (reference.isTypeCheckerReference) {
- return TypeCheckerCodeGenerator(translator, functionType, b, member);
+ return TypeCheckerCodeGenerator(translator, functionType, member);
}
if (member is Constructor) {
if (reference.isConstructorBodyReference) {
- return ConstructorCodeGenerator(translator, functionType, b, member);
+ return ConstructorCodeGenerator(translator, functionType, member);
} else if (reference.isInitializerReference) {
- return InitializerListCodeGenerator(translator, functionType, b, member);
+ return InitializerListCodeGenerator(translator, functionType, member);
} else {
return ConstructorAllocatorCodeGenerator(
- translator, functionType, b, member);
+ translator, functionType, member);
}
}
if (member is Field) {
if (member.isStatic) {
return StaticFieldInitializerCodeGenerator(
- translator, functionType, b, member);
+ translator, functionType, member);
}
return ImplicitFieldAccessorCodeGenerator(
- translator, functionType, b, member, reference.isImplicitGetter);
+ translator, functionType, member, reference.isImplicitGetter);
}
if (member is Procedure && asyncMarker == AsyncMarker.Sync) {
- return SynchronousProcedureCodeGenerator(
- translator, functionType, b, member);
+ return SynchronousProcedureCodeGenerator(translator, functionType, member);
}
assert(
asyncMarker == AsyncMarker.SyncStar || asyncMarker == AsyncMarker.Async);
@@ -3110,18 +3113,15 @@
class SynchronousProcedureCodeGenerator extends AstCodeGenerator {
final Procedure member;
- SynchronousProcedureCodeGenerator(Translator translator,
- w.FunctionType functionType, w.InstructionsBuilder b, this.member)
- : super(translator, functionType, b, member);
+ SynchronousProcedureCodeGenerator(
+ Translator translator, w.FunctionType functionType, this.member)
+ : super(translator, functionType, member);
@override
void generateInternal() {
final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
setSourceMapSourceAndFileOffset(source, member.fileOffset);
- // Build closure information.
- closures = Closures(translator, member);
-
if (intrinsifier.generateMemberIntrinsic(
member.reference, functionType, paramLocals, returnLabel)) {
b.end();
@@ -3134,6 +3134,8 @@
return;
}
+ closures = Closures(translator, member);
+
setupParametersAndContexts(member);
Statement? body = member.function.body;
@@ -3143,27 +3145,21 @@
_implicitReturn();
b.end();
+ addNestedClosuresToCompilationQueue();
}
}
class TearOffCodeGenerator extends AstCodeGenerator {
final Member member;
- TearOffCodeGenerator(Translator translator, w.FunctionType functionType,
- w.InstructionsBuilder b, this.member)
- : super(translator, functionType, b, member);
+ TearOffCodeGenerator(
+ Translator translator, w.FunctionType functionType, this.member)
+ : super(translator, functionType, member);
@override
void generateInternal() {
- if (member is Constructor) {
- // Closures are built when constructor functions are added to worklist.
- closures = translator.constructorClosures[member.reference]!;
- } else {
- // Build closure information.
- closures = Closures(translator, member);
- }
-
- return generateTearOffGetter(member as Procedure);
+ closures = Closures(translator, member);
+ generateTearOffGetter(member as Procedure);
}
void generateTearOffGetter(Procedure procedure) {
@@ -3188,25 +3184,18 @@
class TypeCheckerCodeGenerator extends AstCodeGenerator {
final Member member;
- TypeCheckerCodeGenerator(Translator translator, w.FunctionType functionType,
- w.InstructionsBuilder b, this.member)
- : super(translator, functionType, b, member);
+ TypeCheckerCodeGenerator(
+ Translator translator, w.FunctionType functionType, this.member)
+ : super(translator, functionType, member);
@override
void generateInternal() {
- if (member is Constructor) {
- // Closures are built when constructor functions are added to worklist.
- closures = translator.constructorClosures[member.reference]!;
- } else {
- // Build closure information.
- closures = Closures(translator, member);
- }
-
+ closures = Closures(translator, member);
if (member is Field ||
(member is Procedure && (member as Procedure).isSetter)) {
- return _generateFieldSetterTypeCheckerMethod();
+ _generateFieldSetterTypeCheckerMethod();
} else {
- return _generateProcedureTypeCheckerMethod();
+ _generateProcedureTypeCheckerMethod();
}
}
@@ -3423,9 +3412,9 @@
class InitializerListCodeGenerator extends AstCodeGenerator {
final Constructor member;
- InitializerListCodeGenerator(Translator translator,
- w.FunctionType functionType, w.InstructionsBuilder b, this.member)
- : super(translator, functionType, b, member);
+ InitializerListCodeGenerator(
+ Translator translator, w.FunctionType functionType, this.member)
+ : super(translator, functionType, member);
@override
void generateInternal() {
@@ -3437,11 +3426,11 @@
if (member.isExternal) {
emitUnimplementedExternalError(member);
- b.end();
- return;
+ } else {
+ generateInitializerList();
}
-
- generateInitializerList();
+ b.end();
+ addNestedClosuresToCompilationQueue();
}
// Generates a constructor's initializer list method, and returns:
@@ -3475,7 +3464,6 @@
// throws an error
if (!containsSuperInitializer) {
b.unreachable();
- b.end();
return;
}
@@ -3484,7 +3472,6 @@
for (Field field in info.cls!.fields) {
if (field.isInstanceMember && !fieldLocals.containsKey(field)) {
b.unreachable();
- b.end();
return;
}
}
@@ -3509,8 +3496,6 @@
for (w.Local field in initializedFields) {
b.local_get(field);
}
-
- b.end();
}
void _setupInitializerListParametersAndContexts() {
@@ -3579,9 +3564,9 @@
class ConstructorAllocatorCodeGenerator extends AstCodeGenerator {
final Constructor member;
- ConstructorAllocatorCodeGenerator(Translator translator,
- w.FunctionType functionType, w.InstructionsBuilder b, this.member)
- : super(translator, functionType, b, member);
+ ConstructorAllocatorCodeGenerator(
+ Translator translator, w.FunctionType functionType, this.member)
+ : super(translator, functionType, member);
@override
void generateInternal() {
@@ -3692,13 +3677,12 @@
class ConstructorCodeGenerator extends AstCodeGenerator {
final Constructor member;
- ConstructorCodeGenerator(Translator translator, w.FunctionType functionType,
- w.InstructionsBuilder b, this.member)
- : super(translator, functionType, b, member);
+ ConstructorCodeGenerator(
+ Translator translator, w.FunctionType functionType, this.member)
+ : super(translator, functionType, member);
@override
void generateInternal() {
- super.generate;
// Closures are built when constructor functions are added to worklist.
closures = translator.constructorClosures[member.reference]!;
@@ -3802,9 +3786,9 @@
class StaticFieldInitializerCodeGenerator extends AstCodeGenerator {
final Field field;
- StaticFieldInitializerCodeGenerator(Translator translator,
- w.FunctionType functionType, w.InstructionsBuilder b, this.field)
- : super(translator, functionType, b, field);
+ StaticFieldInitializerCodeGenerator(
+ Translator translator, w.FunctionType functionType, this.field)
+ : super(translator, functionType, field);
@override
void generateInternal() {
@@ -3828,6 +3812,7 @@
b.global_get(global);
translator.convertType(b, global.type.type, outputs.single);
b.end();
+ addNestedClosuresToCompilationQueue();
}
}
@@ -3838,15 +3823,12 @@
ImplicitFieldAccessorCodeGenerator(
Translator translator,
w.FunctionType functionType,
- w.InstructionsBuilder b,
this.field,
this.isImplicitGetter,
- ) : super(translator, functionType, b, field);
+ ) : super(translator, functionType, field);
@override
void generateInternal() {
- closures = Closures(translator, field);
-
final source = field.enclosingComponent!.uriToSource[field.fileUri]!;
setSourceMapSourceAndFileOffset(source, field.fileOffset);
@@ -3885,8 +3867,7 @@
SynchronousLambdaCodeGenerator(Translator translator, Member enclosingMember,
this.lambda, this.enclosingMemberClosures)
- : super(translator, lambda.function.type, lambda.function.body,
- enclosingMember);
+ : super(translator, lambda.function.type, enclosingMember);
@override
void generateInternal() {
diff --git a/pkg/dart2wasm/lib/dart2wasm.dart b/pkg/dart2wasm/lib/dart2wasm.dart
index 92a2886..0594156 100644
--- a/pkg/dart2wasm/lib/dart2wasm.dart
+++ b/pkg/dart2wasm/lib/dart2wasm.dart
@@ -18,8 +18,6 @@
final List<Option> options = [
Flag("help", (o, _) {}, abbr: "h", negatable: false, defaultsTo: false),
- Flag("export-all", (o, value) => o.translatorOptions.exportAll = value,
- defaultsTo: _d.translatorOptions.exportAll),
Flag("import-shared-memory",
(o, value) => o.translatorOptions.importSharedMemory = value,
defaultsTo: _d.translatorOptions.importSharedMemory),
diff --git a/pkg/dart2wasm/lib/functions.dart b/pkg/dart2wasm/lib/functions.dart
index 47fd222..24e1d59 100644
--- a/pkg/dart2wasm/lib/functions.dart
+++ b/pkg/dart2wasm/lib/functions.dart
@@ -7,13 +7,14 @@
import 'class_info.dart';
import 'closures.dart';
+import 'code_generator.dart';
import 'dispatch_table.dart';
import 'reference_extensions.dart';
import 'translator.dart';
/// This class is responsible for collecting import and export annotations.
-/// It also creates Wasm functions for Dart members and manages the worklist
-/// used to achieve tree shaking.
+/// It also creates Wasm functions for Dart members and manages the compilation
+/// queue used to achieve tree shaking.
class FunctionCollector {
final Translator translator;
@@ -21,14 +22,12 @@
final Map<Reference, w.BaseFunction> _functions = {};
// Names of exported functions
final Map<Reference, String> _exports = {};
- // Functions for which code has not yet been generated
- final List<Reference> _worklist = [];
// Selector IDs that are invoked via GDT.
final Set<int> _calledSelectors = {};
// Class IDs for classes that are allocated somewhere in the program
final Set<int> _allocatedClasses = {};
- // For each class ID, which functions should be added to the worklist if an
- // allocation of that class is encountered
+ // For each class ID, which functions should be added to the compilation queue
+ // if an allocation of that class is encountered
final Map<int, List<Reference>> _pendingAllocation = {};
FunctionCollector(this.translator);
@@ -45,10 +44,6 @@
}
}
- bool isWorkListEmpty() => _worklist.isEmpty;
-
- Reference popWorkList() => _worklist.removeLast();
-
void _importOrExport(Member member) {
String? importName =
translator.getPragma(member, "wasm:import", member.name.text);
@@ -97,19 +92,20 @@
String? getExport(Reference target) => _exports[target];
void initialize() {
- // Add exports to the module and add exported functions to the worklist
+ // Add exports to the module and add exported functions to the compilationQueue
for (var export in _exports.entries) {
Reference target = export.key;
Member node = target.asMember;
if (node is Procedure) {
- _worklist.add(target);
assert(!node.isInstanceMember);
assert(!node.isGetter);
w.FunctionType ftype =
_makeFunctionType(translator, target, null, isImportOrExport: true);
- w.BaseFunction function = m.functions.define(ftype, "$node");
+ w.FunctionBuilder function = m.functions.define(ftype, "$node");
_functions[target] = function;
m.exports.export(export.value, function);
+ translator.compilationQueue.add(AstCompilationTask(function,
+ getMemberCodeGenerator(translator, function, target), target));
} else if (node is Field) {
w.Table? table = translator.getTable(node);
if (table != null) {
@@ -133,9 +129,11 @@
w.BaseFunction getFunction(Reference target) {
return _functions.putIfAbsent(target, () {
- _worklist.add(target);
- return m.functions.define(
+ final function = m.functions.define(
translator.signatureForDirectCall(target), _getFunctionName(target));
+ translator.compilationQueue.add(AstCompilationTask(function,
+ getMemberCodeGenerator(translator, function, target), target));
+ return function;
});
}
diff --git a/pkg/dart2wasm/lib/state_machine.dart b/pkg/dart2wasm/lib/state_machine.dart
index 1a93fa8..ee36203 100644
--- a/pkg/dart2wasm/lib/state_machine.dart
+++ b/pkg/dart2wasm/lib/state_machine.dart
@@ -567,7 +567,7 @@
final w.FunctionBuilder function;
StateMachineEntryAstCodeGenerator(
Translator translator, Member enclosingMember, this.function)
- : super(translator, function.type, function.body, enclosingMember);
+ : super(translator, function.type, enclosingMember);
/// Generate the outer function.
///
@@ -601,6 +601,7 @@
if (context != null && context.isEmpty) context = context.parent;
generateOuter(member.function, context, source);
+ addNestedClosuresToCompilationQueue();
}
}
@@ -645,7 +646,7 @@
this.functionNode,
this.functionSource,
Closures closures)
- : super(translator, function.type, function.body, enclosingMember) {
+ : super(translator, function.type, enclosingMember) {
this.closures = closures;
}
diff --git a/pkg/dart2wasm/lib/sync_star.dart b/pkg/dart2wasm/lib/sync_star.dart
index 9a9457b..193f562 100644
--- a/pkg/dart2wasm/lib/sync_star.dart
+++ b/pkg/dart2wasm/lib/sync_star.dart
@@ -9,6 +9,7 @@
import 'closures.dart';
import 'code_generator.dart';
import 'state_machine.dart';
+import 'translator.dart' show CompilationTask;
mixin SyncStarCodeGeneratorMixin on StateMachineEntryAstCodeGenerator {
late final ClassInfo suspendStateInfo =
@@ -40,9 +41,10 @@
b.return_();
b.end();
- SyncStarStateMachineCodeGenerator(translator, resumeFun, enclosingMember,
- functionNode, functionSource, closures)
- .generate(resumeFun.locals.toList(), null);
+ translator.compilationQueue.add(CompilationTask(
+ resumeFun,
+ SyncStarStateMachineCodeGenerator(translator, resumeFun,
+ enclosingMember, functionNode, functionSource, closures)));
}
w.FunctionBuilder _defineInnerBodyFunction(FunctionNode functionNode) =>
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index b1d797c..37215a1 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -31,7 +31,6 @@
/// Options controlling the translation.
class TranslatorOptions {
bool enableAsserts = false;
- bool exportAll = false;
bool importSharedMemory = false;
bool inlining = true;
bool jsCompatibility = false;
@@ -101,6 +100,7 @@
late final Globals globals;
late final Constants constants;
late final Types types;
+ late final CompilationQueue compilationQueue;
late final FunctionCollector functions;
late final DynamicForwarders dynamicForwarders;
@@ -133,7 +133,6 @@
final Set<Member> membersContainingInnerFunctions = {};
final Set<Member> membersBeingGenerated = {};
final Map<Reference, Closures> constructorClosures = {};
- final List<_FunctionGenerator> _pendingFunctions = [];
late final w.ModuleBuilder m;
late final w.FunctionBuilder initFunction;
late final w.ValueType voidMarker;
@@ -288,6 +287,7 @@
closureLayouter = ClosureLayouter(this);
classInfoCollector = ClassInfoCollector(this);
dispatchTable = DispatchTable(this);
+ compilationQueue = CompilationQueue();
functions = FunctionCollector(this);
types = Types(this);
dynamicForwarders = DynamicForwarders(this);
@@ -314,91 +314,9 @@
dispatchTable.build();
functions.initialize();
- while (!functions.isWorkListEmpty()) {
- Reference reference = functions.popWorkList();
- Member member = reference.asMember;
- var function = functions.getExistingFunction(reference) as w.BaseFunction;
-
- String canonicalName = "$member";
- if (reference.isSetter) {
- canonicalName = "$canonicalName=";
- } else if (reference.isGetter || reference.isTearOffReference) {
- int dot = canonicalName.indexOf('.');
- canonicalName =
- '${canonicalName.substring(0, dot + 1)}=${canonicalName.substring(dot + 1)}';
- }
- canonicalName = member.enclosingLibrary == libraries.first
- ? canonicalName
- : "${member.enclosingLibrary.importUri} $canonicalName";
-
- String? exportName = functions.getExport(reference);
-
- if (options.printKernel || options.printWasm) {
- String header = "#${function.name}: $canonicalName";
- if (exportName != null) {
- header = "$header (exported as $exportName)";
- }
- if (reference.isTypeCheckerReference) {
- header = "$header (type checker)";
- }
- print(header);
- print(member.function
- ?.computeFunctionType(Nullability.nonNullable)
- .toStringInternal());
- }
- if (options.printKernel && !reference.isTypeCheckerReference) {
- if (member is Constructor) {
- Class cls = member.enclosingClass;
- for (Field field in cls.fields) {
- if (field.isInstanceMember && field.initializer != null) {
- print("${field.name}: ${field.initializer}");
- }
- }
- for (Initializer initializer in member.initializers) {
- print(initializer);
- }
- }
- Statement? body = member.function?.body;
- if (body != null) {
- print(body);
- }
- if (!options.printWasm) print("");
- }
-
- if (options.exportAll && exportName == null) {
- m.exports.export(canonicalName, function);
- }
-
- final functionBuilder = function as w.FunctionBuilder;
- final codeGen = getMemberCodeGenerator(this, functionBuilder, reference);
- codeGen.generate(functionBuilder.locals.toList(), null);
- final memberClosures = codeGen.closures;
-
- if (options.printWasm) {
- print(function.type);
- print(function.body.trace);
- }
-
- // The constructor allocator, initializer, and body functions all
- // share the same Closures, which will contain all the lambdas in the
- // constructor initializer and body. But, we only want to generate
- // the lambda functions once, so we only generate lambdas when the
- // constructor initializer methods are generated.
- if (member is! Constructor || reference.isInitializerReference) {
- final enclosingMember = member;
- for (Lambda lambda in memberClosures.lambdas.values) {
- getLambdaCodeGenerator(this, lambda, enclosingMember, memberClosures)
- .generate(lambda.function.locals.toList(), null);
- _printFunction(function, "$canonicalName (closure)");
- }
- }
-
- // Use an indexed loop to handle pending closure trampolines, since new
- // entries might be added during iteration.
- for (int i = 0; i < _pendingFunctions.length; i++) {
- _pendingFunctions[i].generate(this);
- }
- _pendingFunctions.clear();
+ while (!compilationQueue.isEmpty) {
+ final task = compilationQueue.pop();
+ task.run(this, options.printKernel, options.printWasm);
}
constructorClosures.clear();
@@ -759,26 +677,20 @@
w.BaseFunction makeTrampoline(
w.FunctionType signature, int posArgCount, List<String> argNames) {
final trampoline = m.functions.define(signature, "$name trampoline");
-
- // Defer generation of the trampoline body to avoid cyclic dependency
- // when a tear-off constant is used as default value in the torn-off
- // function.
- _pendingFunctions.add(_ClosureTrampolineGenerator(trampoline, target,
- typeCount, posArgCount, argNames, paramInfo, takesContextOrReceiver));
-
+ compilationQueue.add(CompilationTask(
+ trampoline,
+ _ClosureTrampolineGenerator(this, trampoline, target, typeCount,
+ posArgCount, argNames, paramInfo, takesContextOrReceiver)));
return trampoline;
}
w.BaseFunction makeDynamicCallEntry() {
final function = m.functions.define(
dynamicCallVtableEntryFunctionType, "$name dynamic call entry");
-
- // Defer generation of the trampoline body to avoid cyclic dependency
- // when a tear-off constant is used as default value in the torn-off
- // function.
- _pendingFunctions.add(_ClosureDynamicEntryGenerator(
- functionNode, target, paramInfo, name, function));
-
+ compilationQueue.add(CompilationTask(
+ function,
+ _ClosureDynamicEntryGenerator(
+ this, functionNode, target, paramInfo, name, function)));
return function;
}
@@ -1087,10 +999,10 @@
}
CodeGenerator? getInliningCodeGenerator(
- Reference target, w.FunctionType functionType, w.InstructionsBuilder b) {
+ Reference target, w.FunctionType functionType) {
if (!shouldInline(target)) return null;
return getInlinableMemberCodeGenerator(
- this, AsyncMarker.Sync, functionType, b, target);
+ this, AsyncMarker.Sync, functionType, target);
}
T? getPragma<T>(Annotatable node, String name, [T? defaultValue]) {
@@ -1190,11 +1102,108 @@
}
}
-abstract class _FunctionGenerator {
- void generate(Translator translator);
+class CompilationQueue {
+ final List<CompilationTask> _pending = [];
+
+ bool get isEmpty => _pending.isEmpty;
+ void add(CompilationTask entry) => _pending.add(entry);
+ CompilationTask pop() => _pending.removeLast();
}
-class _ClosureTrampolineGenerator implements _FunctionGenerator {
+class CompilationTask {
+ final w.FunctionBuilder function;
+ final CodeGenerator _codeGenerator;
+
+ CompilationTask(this.function, this._codeGenerator);
+
+ void run(Translator translator, bool printKernel, bool printWasm) {
+ _codeGenerator.generate(function.body, function.locals.toList(), null);
+ if (printWasm) {
+ print("#${function.name} (synthetic)");
+ print(function.type);
+ print(function.body.trace);
+ }
+ }
+}
+
+// Compilation task for AST.
+class AstCompilationTask extends CompilationTask {
+ final Reference reference;
+
+ AstCompilationTask(super.function, super._createCodeGenerator, this.reference)
+ : super();
+
+ @override
+ void run(Translator translator, bool printKernel, bool printWasm) {
+ final member = reference.asMember;
+
+ final codeGen = getMemberCodeGenerator(translator, function, reference);
+ codeGen.generate(function.body, function.locals.toList(), null);
+
+ if (printKernel || printWasm) {
+ final (:name, :exportName) = _getNames(translator);
+
+ String header = "#${function.name}: $name";
+ if (exportName != null) {
+ header = "$header (exported as $exportName)";
+ }
+ if (reference.isTypeCheckerReference) {
+ header = "$header (type checker)";
+ }
+ print(header);
+ print(member.function
+ ?.computeFunctionType(Nullability.nonNullable)
+ .toStringInternal());
+ }
+ if (printKernel && !reference.isTypeCheckerReference) {
+ if (member is Constructor) {
+ Class cls = member.enclosingClass;
+ for (Field field in cls.fields) {
+ if (field.isInstanceMember && field.initializer != null) {
+ print("${field.name}: ${field.initializer}");
+ }
+ }
+ for (Initializer initializer in member.initializers) {
+ print(initializer);
+ }
+ }
+ Statement? body = member.function?.body;
+ if (body != null) {
+ print(body);
+ }
+ if (!printWasm) print("");
+ }
+
+ if (printWasm) {
+ print(function.type);
+ print(function.body.trace);
+ }
+ }
+
+ ({String name, String? exportName}) _getNames(Translator translator) {
+ final member = reference.asMember;
+ String canonicalName = "$member";
+ if (reference.isSetter) {
+ canonicalName = "$canonicalName=";
+ } else if (reference.isGetter || reference.isTearOffReference) {
+ int dot = canonicalName.indexOf('.');
+ canonicalName =
+ '${canonicalName.substring(0, dot + 1)}=${canonicalName.substring(dot + 1)}';
+ }
+ canonicalName = member.enclosingLibrary ==
+ translator.component.mainMethod!.enclosingLibrary
+ ? canonicalName
+ : "${member.enclosingLibrary.importUri} $canonicalName";
+
+ return (
+ name: canonicalName,
+ exportName: translator.functions.getExport(reference)
+ );
+ }
+}
+
+class _ClosureTrampolineGenerator implements CodeGenerator {
+ final Translator translator;
final w.FunctionBuilder trampoline;
final w.BaseFunction target;
final int typeCount;
@@ -1204,6 +1213,7 @@
final bool takesContextOrReceiver;
_ClosureTrampolineGenerator(
+ this.translator,
this.trampoline,
this.target,
this.typeCount,
@@ -1213,8 +1223,10 @@
this.takesContextOrReceiver);
@override
- void generate(Translator translator) {
- final b = trampoline.body;
+ void generate(w.InstructionsBuilder b, List<w.Local> paramLocals,
+ w.Label? returnLabel) {
+ assert(returnLabel == null);
+
int targetIndex = 0;
if (takesContextOrReceiver) {
w.Local receiver = trampoline.locals[0];
@@ -1264,18 +1276,22 @@
/// Similar to [_ClosureTrampolineGenerator], but generates dynamic call
/// entries.
-class _ClosureDynamicEntryGenerator implements _FunctionGenerator {
+class _ClosureDynamicEntryGenerator implements CodeGenerator {
+ final Translator translator;
final FunctionNode functionNode;
final w.BaseFunction target;
final ParameterInfo paramInfo;
final String name;
final w.FunctionBuilder function;
- _ClosureDynamicEntryGenerator(
- this.functionNode, this.target, this.paramInfo, this.name, this.function);
+ _ClosureDynamicEntryGenerator(this.translator, this.functionNode, this.target,
+ this.paramInfo, this.name, this.function);
@override
- void generate(Translator translator) {
+ void generate(w.InstructionsBuilder b, List<w.Local> paramLocals,
+ w.Label? returnLabel) {
+ assert(returnLabel == null);
+
final b = function.body;
final bool takesContextOrReceiver =
@@ -1550,46 +1566,62 @@
return cache.putIfAbsent(selector, () {
final name = '${selector.name} (polymorphic dispatcher)';
final signature = selector.signature;
- final inputs = signature.inputs;
- final outputs = signature.outputs;
- final function = translator.m.functions
- .define(translator.m.types.defineFunction(inputs, outputs), name);
+ final function = translator.m.functions.define(
+ translator.m.types
+ .defineFunction(signature.inputs, signature.outputs),
+ name);
- final b = function.body;
-
- final targetRanges = selector.staticDispatchRanges
- .map((entry) => (range: entry.range, value: entry.target))
- .toList();
-
- final bool needFallback =
- selector.targetRanges.length > selector.staticDispatchRanges.length;
- void emitDirectCall(Reference target) {
- for (int i = 0; i < inputs.length; ++i) {
- b.local_get(b.locals[i]);
- }
- b.call(translator.functions.getFunction(target));
- }
-
- void emitDispatchTableCall() {
- for (int i = 0; i < inputs.length; ++i) {
- b.local_get(b.locals[i]);
- }
- b.local_get(b.locals[0]);
- b.struct_get(translator.topInfo.struct, FieldIndex.classId);
- b.i32_const(selector.offset!);
- b.i32_add();
- b.call_indirect(signature, translator.dispatchTable.wasmTable);
- translator.functions.recordSelectorUse(selector);
- }
-
- b.local_get(b.locals[0]);
- b.struct_get(translator.topInfo.struct, FieldIndex.classId);
- b.classIdSearch(targetRanges, outputs, emitDirectCall,
- needFallback ? emitDispatchTableCall : null);
- b.return_();
- b.end();
+ translator.compilationQueue.add(CompilationTask(function,
+ PolymorphicDispatcherCodeGenerator(translator, signature, selector)));
return function;
});
}
}
+
+class PolymorphicDispatcherCodeGenerator implements CodeGenerator {
+ final Translator translator;
+ final w.FunctionType signature;
+ final SelectorInfo selector;
+
+ PolymorphicDispatcherCodeGenerator(
+ this.translator, this.signature, this.selector);
+
+ @override
+ void generate(w.InstructionsBuilder b, List<w.Local> paramLocals,
+ w.Label? returnLabel) {
+ assert(returnLabel == null);
+
+ final targetRanges = selector.staticDispatchRanges
+ .map((entry) => (range: entry.range, value: entry.target))
+ .toList();
+
+ final bool needFallback =
+ selector.targetRanges.length > selector.staticDispatchRanges.length;
+ void emitDirectCall(Reference target) {
+ for (int i = 0; i < signature.inputs.length; ++i) {
+ b.local_get(b.locals[i]);
+ }
+ b.call(translator.functions.getFunction(target));
+ }
+
+ void emitDispatchTableCall() {
+ for (int i = 0; i < signature.inputs.length; ++i) {
+ b.local_get(b.locals[i]);
+ }
+ b.local_get(b.locals[0]);
+ b.struct_get(translator.topInfo.struct, FieldIndex.classId);
+ b.i32_const(selector.offset!);
+ b.i32_add();
+ b.call_indirect(signature, translator.dispatchTable.wasmTable);
+ translator.functions.recordSelectorUse(selector);
+ }
+
+ b.local_get(b.locals[0]);
+ b.struct_get(translator.topInfo.struct, FieldIndex.classId);
+ b.classIdSearch(targetRanges, signature.outputs, emitDirectCall,
+ needFallback ? emitDispatchTableCall : null);
+ b.return_();
+ b.end();
+ }
+}
diff --git a/pkg/dart2wasm/lib/types.dart b/pkg/dart2wasm/lib/types.dart
index 4bdd5b4..005b7b8 100644
--- a/pkg/dart2wasm/lib/types.dart
+++ b/pkg/dart2wasm/lib/types.dart
@@ -604,95 +604,10 @@
),
name);
- final b = function.body;
-
- w.Local operand = b.locals[0];
- w.Local boolTemp = b.addLocal(w.NumType.i32);
-
- final w.Label resultLabel = b.block(const [], const [w.NumType.i32]);
- if (operandIsNullable) {
- w.Label nullLabel = b.block(const [], const []);
- b.local_get(operand);
- b.br_on_null(nullLabel);
- final nonNullableOperand =
- b.addLocal(translator.topInfo.nonNullableType);
- b.local_get(operand);
- b.ref_cast(nonNullableOperand.type as w.RefType);
- b.local_set(nonNullableOperand);
- operand = nonNullableOperand;
- }
-
- if (checkArguments) {
- b.local_get(operand);
- b.call(_generateIsChecker(testedAgainstType, false, false));
- b.local_set(boolTemp);
-
- // If cid ranges fail, we fail
- {
- final w.Label okBlock = b.block(const [], const []);
- b.local_get(boolTemp);
- b.i32_const(1);
- b.i32_eq();
- b.br_if(okBlock);
- b.i32_const(0);
- b.br(resultLabel);
- b.end();
- }
-
- // Otherwise we have to check each argument.
-
- // Call Object._getArguments()
- w.Local typeArguments = b.addLocal(typeArrayExpectedType);
- b.local_get(operand);
- b.call(translator.functions
- .getFunction(translator.objectGetTypeArguments.reference));
- b.local_set(typeArguments);
- for (int i = 0; i < argumentCount; ++i) {
- b.local_get(typeArguments);
- b.i32_const(i);
- b.array_get(typeArrayArrayType);
- b.local_get(b.locals[1 + i]);
- b.call(translator.functions
- .getFunction(translator.isTypeSubtype.reference));
- {
- b.local_set(boolTemp);
- final w.Label okBlock = b.block(const [], const []);
- b.local_get(boolTemp);
- b.i32_const(1);
- b.i32_eq();
- b.br_if(okBlock);
- b.i32_const(0);
- b.br(resultLabel);
- b.end();
- }
- }
- b.i32_const(1);
- b.br(resultLabel);
- } else {
- if (interfaceClass == coreTypes.objectClass) {
- b.drop();
- b.i32_const(1);
- } else if (interfaceClass == coreTypes.functionClass) {
- b.local_get(operand);
- b.ref_test(translator.closureInfo.nonNullableType);
- } else {
- final ranges = translator.classIdNumbering
- .getConcreteClassIdRanges(interfaceClass);
- b.local_get(operand);
- b.struct_get(translator.topInfo.struct, FieldIndex.classId);
- b.emitClassIdRangeCheck(ranges);
- }
- b.br(resultLabel);
- }
-
- if (operandIsNullable) {
- b.end(); // nullLabel
- b.i32_const(encodedNullability(testedAgainstType));
- }
- b.end(); // resultLabel
-
- b.return_();
- b.end();
+ translator.compilationQueue.add(CompilationTask(
+ function,
+ IsCheckerCodeGenerator(translator, testedAgainstType,
+ operandIsNullable, checkArguments, argumentCount)));
return function;
});
@@ -741,61 +656,209 @@
),
name);
- final b = function.body;
- w.Label asCheckBlock = b.block();
- b.local_get(b.locals[0]);
- for (int i = 0; i < argumentCount; ++i) {
- b.local_get(b.locals[1 + i]);
- }
- b.call(_generateIsChecker(
- testedAgainstType, checkArguments, operandIsNullable));
- b.br_if(asCheckBlock);
-
- if (checkArguments) {
- final testedAgainstClassId =
- translator.classInfo[testedAgainstType.classNode]!.classId;
- b.local_get(b.locals[0]);
- b.i32_const(encodedNullability(testedAgainstType));
- b.i32_const(testedAgainstClassId);
- if (argumentCount == 1) {
- b.local_get(b.locals[1]);
- b.call(translator.functions.getFunction(
- translator.throwInterfaceTypeAsCheckError1.reference));
- } else if (argumentCount == 2) {
- b.local_get(b.locals[1]);
- b.local_get(b.locals[2]);
- b.call(translator.functions.getFunction(
- translator.throwInterfaceTypeAsCheckError2.reference));
- } else {
- for (int i = 0; i < argumentCount; ++i) {
- b.local_get(b.locals[1 + i]);
- }
- b.array_new_fixed(typeArrayArrayType, argumentCount);
- b.call(translator.functions.getFunction(
- translator.throwInterfaceTypeAsCheckError.reference));
- }
- } else {
- b.local_get(b.locals[0]);
- translator.constants.instantiateConstant(
- b, TypeLiteralConstant(testedAgainstType), nonNullableTypeType);
- b.call(translator.functions
- .getFunction(translator.throwAsCheckError.reference));
- }
- b.unreachable();
-
- b.end();
-
- b.local_get(b.locals[0]);
- translator.convertType(b, argumentType, returnType);
- b.return_();
- b.end();
+ translator.compilationQueue.add(CompilationTask(
+ function,
+ AsCheckerCodeGenerator(
+ translator,
+ argumentType,
+ returnType,
+ testedAgainstType,
+ operandIsNullable,
+ checkArguments,
+ argumentCount)));
return function;
});
}
+}
- int encodedNullability(DartType type) =>
- type.declaredNullability == Nullability.nullable ? 1 : 0;
+int encodedNullability(DartType type) =>
+ type.declaredNullability == Nullability.nullable ? 1 : 0;
+
+class IsCheckerCodeGenerator implements CodeGenerator {
+ final Translator translator;
+
+ final InterfaceType testedAgainstType;
+ final bool operandIsNullable;
+ final bool checkArguments;
+ final int argumentCount;
+
+ IsCheckerCodeGenerator(this.translator, this.testedAgainstType,
+ this.operandIsNullable, this.checkArguments, this.argumentCount);
+
+ @override
+ void generate(w.InstructionsBuilder b, List<w.Local> paramLocals,
+ w.Label? returnLabel) {
+ assert(returnLabel == null);
+
+ final interfaceClass = testedAgainstType.classNode;
+
+ w.Local operand = b.locals[0];
+ w.Local boolTemp = b.addLocal(w.NumType.i32);
+
+ final w.Label resultLabel = b.block(const [], const [w.NumType.i32]);
+ if (operandIsNullable) {
+ w.Label nullLabel = b.block(const [], const []);
+ b.local_get(operand);
+ b.br_on_null(nullLabel);
+ final nonNullableOperand = b.addLocal(translator.topInfo.nonNullableType);
+ b.local_get(operand);
+ b.ref_cast(nonNullableOperand.type as w.RefType);
+ b.local_set(nonNullableOperand);
+ operand = nonNullableOperand;
+ }
+
+ if (checkArguments) {
+ b.local_get(operand);
+ b.call(
+ translator.types._generateIsChecker(testedAgainstType, false, false));
+ b.local_set(boolTemp);
+
+ // If cid ranges fail, we fail
+ {
+ final w.Label okBlock = b.block(const [], const []);
+ b.local_get(boolTemp);
+ b.i32_const(1);
+ b.i32_eq();
+ b.br_if(okBlock);
+ b.i32_const(0);
+ b.br(resultLabel);
+ b.end();
+ }
+
+ // Otherwise we have to check each argument.
+
+ // Call Object._getArguments()
+ w.Local typeArguments =
+ b.addLocal(translator.types.typeArrayExpectedType);
+ b.local_get(operand);
+ b.call(translator.functions
+ .getFunction(translator.objectGetTypeArguments.reference));
+ b.local_set(typeArguments);
+ for (int i = 0; i < argumentCount; ++i) {
+ b.local_get(typeArguments);
+ b.i32_const(i);
+ b.array_get(translator.types.typeArrayArrayType);
+ b.local_get(b.locals[1 + i]);
+ b.call(translator.functions
+ .getFunction(translator.isTypeSubtype.reference));
+ {
+ b.local_set(boolTemp);
+ final w.Label okBlock = b.block(const [], const []);
+ b.local_get(boolTemp);
+ b.i32_const(1);
+ b.i32_eq();
+ b.br_if(okBlock);
+ b.i32_const(0);
+ b.br(resultLabel);
+ b.end();
+ }
+ }
+ b.i32_const(1);
+ b.br(resultLabel);
+ } else {
+ if (interfaceClass == translator.coreTypes.objectClass) {
+ b.drop();
+ b.i32_const(1);
+ } else if (interfaceClass == translator.coreTypes.functionClass) {
+ b.local_get(operand);
+ b.ref_test(translator.closureInfo.nonNullableType);
+ } else {
+ final ranges = translator.classIdNumbering
+ .getConcreteClassIdRanges(interfaceClass);
+ b.local_get(operand);
+ b.struct_get(translator.topInfo.struct, FieldIndex.classId);
+ b.emitClassIdRangeCheck(ranges);
+ }
+ b.br(resultLabel);
+ }
+
+ if (operandIsNullable) {
+ b.end(); // nullLabel
+ b.i32_const(encodedNullability(testedAgainstType));
+ }
+ b.end(); // resultLabel
+
+ b.return_();
+ b.end();
+ }
+}
+
+class AsCheckerCodeGenerator implements CodeGenerator {
+ final Translator translator;
+
+ final w.ValueType argumentType;
+ final w.ValueType returnType;
+
+ final InterfaceType testedAgainstType;
+ final bool operandIsNullable;
+ final bool checkArguments;
+ final int argumentCount;
+
+ AsCheckerCodeGenerator(
+ this.translator,
+ this.argumentType,
+ this.returnType,
+ this.testedAgainstType,
+ this.operandIsNullable,
+ this.checkArguments,
+ this.argumentCount);
+
+ @override
+ void generate(w.InstructionsBuilder b, List<w.Local> paramLocals,
+ w.Label? returnLabel) {
+ assert(returnLabel == null);
+
+ w.Label asCheckBlock = b.block();
+ b.local_get(b.locals[0]);
+ for (int i = 0; i < argumentCount; ++i) {
+ b.local_get(b.locals[1 + i]);
+ }
+ b.call(translator.types._generateIsChecker(
+ testedAgainstType, checkArguments, operandIsNullable));
+ b.br_if(asCheckBlock);
+
+ if (checkArguments) {
+ final testedAgainstClassId =
+ translator.classInfo[testedAgainstType.classNode]!.classId;
+ b.local_get(b.locals[0]);
+ b.i32_const(encodedNullability(testedAgainstType));
+ b.i32_const(testedAgainstClassId);
+ if (argumentCount == 1) {
+ b.local_get(b.locals[1]);
+ b.call(translator.functions
+ .getFunction(translator.throwInterfaceTypeAsCheckError1.reference));
+ } else if (argumentCount == 2) {
+ b.local_get(b.locals[1]);
+ b.local_get(b.locals[2]);
+ b.call(translator.functions
+ .getFunction(translator.throwInterfaceTypeAsCheckError2.reference));
+ } 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));
+ }
+ } else {
+ b.local_get(b.locals[0]);
+ translator.constants.instantiateConstant(
+ b,
+ TypeLiteralConstant(testedAgainstType),
+ translator.types.nonNullableTypeType);
+ b.call(translator.functions
+ .getFunction(translator.throwAsCheckError.reference));
+ }
+ b.unreachable();
+
+ b.end();
+
+ b.local_get(b.locals[0]);
+ translator.convertType(b, argumentType, returnType);
+ b.return_();
+ b.end();
+ }
}
/// Builds up data structures that the Runtime Type System implementation uses.