[dart2wasm] Produce wrapper functions for closures

Instead of using top types for the parameters and return of the
implementation function for a function expression or local function,
use precise types and generate a wrapper function with top types that
casts the parameters and calls the implementation function.

This unifies the wrapper function generation with tear-offs, setting
the stage for the upcoming generalization to support type parameters
and optional parameters.

The change also brings some benefits in its own right:
- Arguments to closures are converted once instead of at every use
- Direct calls to local functions avoid the conversion of arguments

Also add source location to closure function names and generally avoid
parentheses in function names, as they are confusing in stack traces.

Change-Id: If83073bb2e2dd17554ffa03fa2596e79d730fb67
Cq-Include-Trybots: luci.dart.try:dart2wasm-linux-x64-d8-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/256665
Reviewed-by: Joshua Litt <joshualitt@google.com>
This commit is contained in:
Aske Simon Christensen 2022-09-12 10:04:46 +00:00 committed by Commit Bot
parent 25b1bfe7a6
commit 0cb91b9824
5 changed files with 48 additions and 32 deletions

View file

@ -269,9 +269,18 @@ class CaptureFinder extends RecursiveVisitor {
throw "Not supported: Type parameters for "
"function expression or local function at ${node.location}";
}
int parameterCount = node.requiredParameterCount;
w.FunctionType type = translator.closureFunctionType(parameterCount);
w.DefinedFunction function = m.addFunction(type, "$member (closure)");
List<w.ValueType> inputs = [
w.RefType.data(nullable: false),
for (VariableDeclaration param in node.positionalParameters)
translator.translateType(param.type)
];
List<w.ValueType> outputs = [
if (node.returnType != const VoidType())
translator.translateType(node.returnType)
];
w.FunctionType type = m.addFunctionType(inputs, outputs);
w.DefinedFunction function =
m.addFunction(type, "$member closure at ${node.location}");
closures.lambdas[node] = Lambda(node, function);
depth++;

View file

@ -1891,7 +1891,9 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
w.StructType _instantiateClosure(FunctionNode functionNode) {
int parameterCount = functionNode.requiredParameterCount;
Lambda lambda = closures.lambdas[functionNode]!;
w.DefinedGlobal global = translator.makeFunctionRef(lambda.function);
w.DefinedFunction wrapper = translator.getClosureWrapper(functionNode,
lambda.function, "closure wrapper at ${functionNode.location}");
w.DefinedGlobal global = translator.makeFunctionRef(wrapper);
ClassInfo info = translator.classInfo[translator.functionClass]!;
translator.functions.allocateClass(info.classId);
@ -1901,7 +1903,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
b.i32_const(initialIdentityHash);
_pushContext(functionNode);
b.global_get(global);
b.struct_new(translator.closureStructType(parameterCount));
b.struct_new(struct);
return struct;
}
@ -1948,14 +1950,15 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
w.ValueType visitLocalFunctionInvocation(
LocalFunctionInvocation node, w.ValueType expectedType) {
var decl = node.variable.parent as FunctionDeclaration;
_pushContext(decl.function);
for (Expression arg in node.arguments.positional) {
wrap(arg, translator.topInfo.nullableType);
}
Lambda lambda = closures.lambdas[decl.function]!;
List<w.ValueType> inputs = lambda.function.type.inputs;
_pushContext(decl.function);
for (int i = 0; i < node.arguments.positional.length; i++) {
wrap(node.arguments.positional[i], inputs[1 + i]);
}
b.comment("Local call of ${decl.variable.name}");
b.call(lambda.function);
return translator.topInfo.nullableType;
return translator.outputOrVoid(lambda.function.type.outputs);
}
@override

View file

@ -201,7 +201,7 @@ class Constants {
ClassInfo info = translator.classInfo[cls]!;
w.FunctionType ftype = m.addFunctionType(
const [w.NumType.i32, w.NumType.i32], [info.nonNullableType]);
return m.addFunction(ftype, "makeString (${cls.name})");
return m.addFunction(ftype, "makeString ${cls.name}");
}
void makeStringFunctionBody(Class cls, w.DefinedFunction function,

View file

@ -132,7 +132,7 @@ class FunctionCollector extends MemberVisitor1<w.FunctionType, Reference> {
w.FunctionType ftype = m.addFunctionType(
[...outer.type.inputs, asyncStackType],
[translator.topInfo.nullableType]);
return m.addFunction(ftype, "${outer.functionName} (inner)");
return m.addFunction(ftype, "${outer.functionName} inner");
}
void activateSelector(SelectorInfo selector) {

View file

@ -689,8 +689,8 @@ class Translator {
return tearOffFunctionCache.putIfAbsent(member, () {
assert(member.kind == ProcedureKind.Method);
FunctionNode functionNode = member.function;
int parameterCount = functionNode.requiredParameterCount;
if (functionNode.positionalParameters.length != parameterCount ||
if (functionNode.positionalParameters.length !=
functionNode.requiredParameterCount ||
functionNode.namedParameters.isNotEmpty) {
throw "Not supported: Tear-off with optional parameters"
" at ${member.location}";
@ -699,28 +699,32 @@ class Translator {
throw "Not supported: Tear-off with type parameters"
" at ${member.location}";
}
w.FunctionType memberSignature = signatureFor(member.reference);
w.FunctionType closureSignature = closureFunctionType(parameterCount);
int signatureOffset = member.isInstanceMember ? 1 : 0;
assert(memberSignature.inputs.length == signatureOffset + parameterCount);
assert(closureSignature.inputs.length == 1 + parameterCount);
w.DefinedFunction function =
m.addFunction(closureSignature, "$member (tear-off)");
w.BaseFunction target = functions.getFunction(member.reference);
w.Instructions b = function.body;
for (int i = 0; i < memberSignature.inputs.length; i++) {
w.Local paramLocal = function.locals[(1 - signatureOffset) + i];
b.local_get(paramLocal);
convertType(function, paramLocal.type, memberSignature.inputs[i]);
}
b.call(target);
convertType(function, outputOrVoid(target.type.outputs),
outputOrVoid(closureSignature.outputs));
b.end();
return function;
return getClosureWrapper(functionNode, target, "$member tear-off");
});
}
w.DefinedFunction getClosureWrapper(
FunctionNode functionNode, w.BaseFunction target, String name) {
int parameterCount = functionNode.requiredParameterCount;
w.FunctionType targetSignature = target.type;
w.FunctionType closureSignature = closureFunctionType(parameterCount);
assert(closureSignature.inputs.length == 1 + parameterCount);
int signatureOffset = targetSignature.inputs.length - parameterCount;
w.DefinedFunction function = m.addFunction(closureSignature, name);
w.Instructions b = function.body;
for (int i = 0; i < targetSignature.inputs.length; i++) {
w.Local paramLocal = function.locals[(1 - signatureOffset) + i];
b.local_get(paramLocal);
convertType(function, paramLocal.type, targetSignature.inputs[i]);
}
b.call(target);
convertType(function, outputOrVoid(target.type.outputs),
outputOrVoid(closureSignature.outputs));
b.end();
return function;
}
w.ValueType outputOrVoid(List<w.ValueType> outputs) {
return outputs.isEmpty ? voidMarker : outputs.single;
}