[modular_aot] Closures, part 2

Issue: https://github.com/dart-lang/sdk/issues/61635
Change-Id: Id8029dddd75b5f771ddf8197190d18aea9efd192
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/503080
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
Alexander Markov
2026-05-13 05:58:22 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 433ee9bb92
commit e09282c8f2
13 changed files with 481 additions and 312 deletions
+119 -19
View File
@@ -56,6 +56,7 @@ class AstToIr extends ast.RecursiveVisitor {
function.member,
GlobalContext.instance.typeEnvironment,
);
late final ClosureLayout _currentClosureLayout;
Map<ast.LabeledStatement, JoinBlock>? labeledStatements;
Map<ast.SwitchCase, JoinBlock>? switchCases;
@@ -85,6 +86,10 @@ class AstToIr extends ast.RecursiveVisitor {
scopes,
function,
);
final f = function;
if (f is ClosureFunction) {
_currentClosureLayout = _computeClosureLayout(f);
}
}
/// Create [FlowGraph] for the body of the [function].
@@ -151,13 +156,34 @@ class AstToIr extends ast.RecursiveVisitor {
if (function.hasClassTypeParameters) {
if (function.hasReceiverParameter) {
builder.addLoadLocal(localVarIndexer.receiver);
} else {
assert(_isCaptured(localVarIndexer.receiverDeclaration!));
throw 'Unimplemented _buildPrologue for class type parameters with captured receiver';
classTypeParameters = builder.addTypeParameters(
.classTypeParameters,
);
} else if (_isCaptured(localVarIndexer.receiverDeclaration!)) {
// Read captured receiver. Load context from closure
// as contexts are not defined yet.
final variable = localVarIndexer.receiverDeclaration!;
builder.addLoadLocal(localVarIndexer.closure);
builder.addLoadInstanceField(
CField(
ClosureField(
_currentClosureLayout.firstContextIndex +
scopes
.getCapturedContexts(
function.functionNode!,
enableAsserts: enableAsserts,
)
.indexOf(scopes.getVariableContext(variable)),
),
),
);
builder.addLoadInstanceField(
localVarIndexer.contextField(variable),
);
classTypeParameters = builder.addTypeParameters(
.classTypeParameters,
);
}
classTypeParameters = builder.addTypeParameters(
.classTypeParameters,
);
}
}
}
@@ -819,16 +845,26 @@ class AstToIr extends ast.RecursiveVisitor {
return;
}
if (node is ast.FunctionNode) {
var index = 0;
for (final context in scopes.getCapturedContexts(
node,
enableAsserts: enableAsserts,
)) {
assert(_isCapturedContext(context));
builder.addLoadLocal(localVarIndexer.closure);
builder.addLoadInstanceField(CField(ClosureField(index)));
localVarIndexer.defineContext(context, builder.pop());
++index;
assert(function.functionNode == node);
if (function is ClosureFunction) {
var index = _currentClosureLayout.firstContextIndex;
for (final context in scopes.getCapturedContexts(
node,
enableAsserts: enableAsserts,
)) {
assert(_isCapturedContext(context));
assert(index < _currentClosureLayout.length);
builder.addLoadLocal(localVarIndexer.closure);
builder.addLoadInstanceField(CField(ClosureField(index)));
localVarIndexer.defineContext(context, builder.pop());
++index;
}
} else {
assert(
scopes
.getCapturedContexts(node, enableAsserts: enableAsserts)
.isEmpty,
);
}
}
final scope = scopes.getScope(node);
@@ -1763,18 +1799,82 @@ class AstToIr extends ast.RecursiveVisitor {
as ClosureFunction;
onLocalFunction(closureFunction);
// TODO: capture (parent) function type arguments and instantiator
// type arguments if needed.
final closureLayout = _computeClosureLayout(closureFunction);
final closure = builder.addAllocateClosure(
closureFunction,
closureLayout,
type,
);
if (closureLayout.hasClassTypeArgs) {
assert(typeParametersStyle == .separateFunctionAndClassTypeParameters);
builder.push(closure);
builder.push(classTypeParameters!);
builder.addStoreInstanceField(
CField(ClosureField(closureLayout.classTypeArgsIndex)),
);
}
if (closureLayout.hasFunctionTypeArgs) {
assert(typeParametersStyle == .separateFunctionAndClassTypeParameters);
builder.push(closure);
builder.push(functionTypeParameters!);
builder.addStoreInstanceField(
CField(ClosureField(closureLayout.functionTypeArgsIndex)),
);
}
final contexts = scopes.getCapturedContexts(
node.function,
enableAsserts: enableAsserts,
);
var index = closureLayout.firstContextIndex;
for (final context in contexts) {
assert(_isCapturedContext(context));
assert(index < closureLayout.length);
builder.push(closure);
builder.push(localVarIndexer.contextDef(context));
// TODO: cache and reuse ClosureField objects.
builder.addStoreInstanceField(CField(ClosureField(index++)));
}
builder.addAllocateClosure(closureFunction, type, contexts.length);
}
ClosureLayout _computeClosureLayout(ClosureFunction closureFunction) {
var hasDelayedTypeArgs = false;
var hasClassTypeArgs = false;
var hasFunctionTypeArgs = false;
switch (typeParametersStyle) {
case .separateFunctionAndClassTypeParameters:
hasDelayedTypeArgs = closureFunction.hasFunctionTypeParameters;
final visitor = _FindTypeParameters();
closureFunction.functionNode!
.computeFunctionType(ast.Nullability.nonNullable)
.accept(visitor);
hasClassTypeArgs = visitor.containsClassTypeParams;
hasFunctionTypeArgs = switch (closureFunction) {
LocalFunction() => closureFunction.hasGenericEnclosingFunction(),
TearOffFunction() => false,
};
}
final numContexts = switch (closureFunction) {
LocalFunction() =>
scopes
.getCapturedContexts(
closureFunction.functionNode!,
enableAsserts: enableAsserts,
)
.length,
TearOffFunction() =>
closureFunction.member.isInstanceMember ? /* receiver */ 1 : 0,
};
return ClosureLayout(
numContexts,
hasDelayedTypeArgs: hasDelayedTypeArgs,
hasClassTypeArgs: hasClassTypeArgs,
hasFunctionTypeArgs: hasFunctionTypeArgs,
);
}
@override
+7 -1
View File
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:cfg/front_end/scopes.dart';
import 'package:cfg/ir/global_context.dart';
import 'package:kernel/ast.dart' as ast;
/// Implementation of [Scopes] by computing scopes and contexts.
@@ -16,8 +17,13 @@ final class ComputedScopes implements Scopes {
ComputedScopes(this.member, {required this.enableAsserts}) {
if (member.isInstanceMember || member is ast.Constructor) {
final type = member.enclosingClass!.getThisType(
GlobalContext.instance.coreTypes,
.nonNullable,
);
thisVariable =
member.function?.thisVariable ?? ast.VariableDeclaration('this');
member.function?.thisVariable ??
ast.VariableDeclaration('this', type: type);
}
final builder = _ScopeBuilder(this);
member.accept(builder);
+62
View File
@@ -56,3 +56,65 @@ final class ClosureField extends SyntheticField {
isFinal: true,
);
}
/// Defines assignment of the closure elements.
class ClosureLayout {
static const int hasDelayedTypeArgsFlag = 1 << 0;
static const int hasClassTypeArgsFlag = 1 << 1;
static const int hasFunctionTypeArgsFlag = 1 << 2;
final int _flags;
/// Total number of closure elements.
final int length;
ClosureLayout(
int numContexts, {
required bool hasDelayedTypeArgs,
required bool hasClassTypeArgs,
required bool hasFunctionTypeArgs,
}) : _flags =
(hasDelayedTypeArgs ? hasDelayedTypeArgsFlag : 0) |
(hasClassTypeArgs ? hasClassTypeArgsFlag : 0) |
(hasFunctionTypeArgs ? hasFunctionTypeArgsFlag : 0),
length =
(hasDelayedTypeArgs ? 1 : 0) +
(hasClassTypeArgs ? 1 : 0) +
(hasFunctionTypeArgs ? 1 : 0) +
numContexts {
assert(length == firstContextIndex + numContexts);
}
/// Whether closure has an element for delayed type arguments.
bool get hasDelayedTypeArgs => (_flags & hasDelayedTypeArgsFlag) != 0;
/// Whether closure has an element for enclosing class type arguments.
bool get hasClassTypeArgs => (_flags & hasClassTypeArgsFlag) != 0;
/// Whether closure has an element for enclosing function type arguments.
bool get hasFunctionTypeArgs => (_flags & hasFunctionTypeArgsFlag) != 0;
/// Index of the delayed type arguments element.
int get delayedTypeArgsIndex {
assert(hasDelayedTypeArgs);
return 0;
}
/// Index of the enclosing class type arguments element.
int get classTypeArgsIndex {
assert(hasClassTypeArgs);
return hasDelayedTypeArgs ? 1 : 0;
}
/// Index of the enclosing function type arguments element.
int get functionTypeArgsIndex {
assert(hasFunctionTypeArgs);
return (hasDelayedTypeArgs ? 1 : 0) + (hasClassTypeArgs ? 1 : 0);
}
int get firstContextIndex {
return (hasDelayedTypeArgs ? 1 : 0) +
(hasClassTypeArgs ? 1 : 0) +
(hasFunctionTypeArgs ? 1 : 0);
}
}
+2 -3
View File
@@ -546,17 +546,16 @@ class FlowGraphBuilder {
/// Append [AllocateClosure] to the graph.
AllocateClosure addAllocateClosure(
ClosureFunction function,
ClosureLayout closureLayout,
CType type,
int inputCount,
) {
final instr = AllocateClosure(
graph,
currentSourcePosition,
function,
closureLayout,
type,
inputCount: inputCount,
);
popInputs(instr, 0, inputCount);
push(instr);
appendInstruction(instr);
return instr;
+6 -1
View File
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:cfg/ir/constant_value.dart';
import 'package:cfg/ir/field.dart';
import 'package:cfg/ir/instructions.dart';
import 'package:cfg/ir/ir_to_text.dart';
import 'package:cfg/ir/types.dart';
@@ -352,11 +353,15 @@ final class FlowGraphChecker extends Pass implements InstructionVisitor<void> {
void visitTypeParameters(TypeParameters instr) {
assert(instr.block is EntryBlock);
// TypeParameters can only be used in TypeCast, TypeTest,
// TypeArguments and TypeLiteral.
// TypeArguments, TypeLiteral and StoreInstanceField for capturing.
for (final use in instr.inputUses) {
final user = use.getInstruction(graph);
switch (user) {
case TypeCast() || TypeTest() || TypeArguments() || TypeLiteral():
case StoreInstanceField(:var field)
when field.isSynthetic &&
(field.asSynthetic is ClosureField ||
field.asSynthetic is ContextField):
break;
default:
throw 'Unexpected user ${IrToText.instruction(user)} of TypeParameters';
+13
View File
@@ -232,6 +232,19 @@ final class LocalFunction extends ClosureFunction {
@override
SourcePosition get sourcePosition => SourcePosition(localFunction.fileOffset);
bool hasGenericEnclosingFunction() {
ast.TreeNode node = localFunction;
for (;;) {
node = node.parent!;
if (node is ast.Member) {
return false;
}
if (node is ast.FunctionNode && node.typeParameters.isNotEmpty) {
return true;
}
}
}
}
/// Tear-off (result of function closurization).
+4 -5
View File
@@ -1319,10 +1319,9 @@ final class AllocateObject extends Definition with CanThrow, Pure {
}
/// Allocate a closure instance.
///
/// Takes captured values as inputs.
final class AllocateClosure extends Definition with CanThrow, Pure {
final ClosureFunction function;
final ClosureLayout closureLayout;
@override
final CType type;
@@ -1331,9 +1330,9 @@ final class AllocateClosure extends Definition with CanThrow, Pure {
super.graph,
super.sourcePosition,
this.function,
this.type, {
required super.inputCount,
});
this.closureLayout,
this.type,
) : super(inputCount: 0);
@override
R accept<R>(InstructionVisitor<R> v) => v.visitAllocateClosure(this);
+2 -3
View File
@@ -156,12 +156,11 @@ void foo46() {
});
}
// TODO: support class type parameters via captured receiver
class A /*<T>*/ {
class A<T> {
List<int> aField = [
for (int i = 0; i < 10; ++i)
() {
// print(T);
print(T);
print(i);
i += 2;
return i;
+236 -196
View File
@@ -1,66 +1,83 @@
--- A.foo47
B0 = EntryBlock() dominates:(B9)
v3 = Constant(<int>)
v4 = Constant(0)
v12 = Constant(10)
v22 = Constant(1)
v30 = Constant(null)
B0 = EntryBlock() dominates:(B14)
v8 = Constant(<int>)
v9 = Constant(0)
v17 = Constant(10)
v29 = Constant(1)
v37 = Constant(null)
v1 = Parameter(this)
v5 = DirectCall _GrowableList.(v3, v4)
v7 = AllocateContext()
StoreInstanceField(#context-field:i, v7, v4)
Goto(B9)
B9 = JoinBlock(B0, B14) idom:B0 dominates:(B15, B14) loop-header (depth:1 body:(B9, B14) back-edges:(B14))
v11 = LoadInstanceField(#context-field:i, v7)
v13 = Comparison int <(v11, v12)
Branch(v13, true: B14, false: B15)
B14 = TargetBlock() idom:B9 in-loop:B9
v18 = AllocateClosure(v7)
v32 = DirectCall closure FunctionExpression(int () {
TypeParameters(v1)
v4 = AllocateContext()
StoreInstanceField(#context-field:this, v4, v1)
v10 = DirectCall _GrowableList.(v8, v9)
v12 = AllocateContext()
StoreInstanceField(#context-field:i, v12, v9)
Goto(B14)
B14 = JoinBlock(B0, B19) idom:B0 dominates:(B20, B19) loop-header (depth:1 body:(B14, B19) back-edges:(B19))
v16 = LoadInstanceField(#context-field:i, v12)
v18 = Comparison int <(v16, v17)
Branch(v18, true: B19, false: B20)
B19 = TargetBlock() idom:B14 in-loop:B14
v23 = AllocateClosure()
StoreInstanceField(#closure-field[0], v23, v4)
StoreInstanceField(#closure-field[1], v23, v12)
v39 = DirectCall closure FunctionExpression(int () {
print(A.T%);
print(i);
i = i.{num.+}(2);
return i;
}) at A.foo47(v18)
InterfaceCall List.add(v5, v32)
v21 = LoadInstanceField(#context-field:i, v7)
v23 = BinaryIntOp +(v21, v22)
StoreInstanceField(#context-field:i, v7, v23)
Goto(B9)
B15 = TargetBlock() idom:B9
StoreInstanceField(A.aField, v1, v5)
DirectCall Object.(v1)
Return(v30)
}) at A.foo47(v23)
InterfaceCall List.add(v10, v39)
v28 = LoadInstanceField(#context-field:i, v12)
v30 = BinaryIntOp +(v28, v29)
StoreInstanceField(#context-field:i, v12, v30)
Goto(B14)
B20 = TargetBlock() idom:B14
StoreInstanceField(A.aField, v1, v10)
v35 = LoadInstanceField(#context-field:this, v4)
DirectCall Object.(v35)
Return(v37)
--- closure FunctionExpression(int () {
print(A.T%);
print(i);
i = i.{num.+}(2);
return i;
}) at A.foo47
B0 = EntryBlock()
v7 = Constant(2)
v10 = Constant(null)
v16 = Constant(2)
v1 = Parameter(#closure)
v3 = LoadInstanceField(#closure-field[0], v1)
v4 = LoadInstanceField(#context-field:i, v3)
DirectCall print(v4)
v6 = LoadInstanceField(#context-field:i, v3)
v8 = BinaryIntOp +(v6, v7)
StoreInstanceField(#context-field:i, v3, v8)
v10 = LoadInstanceField(#context-field:i, v3)
Return(v10)
v4 = LoadInstanceField(#context-field:this, v3)
v5 = TypeParameters(v4)
LoadInstanceField(#closure-field[0], v1)
v9 = LoadInstanceField(#closure-field[1], v1)
v11 = TypeLiteral(A.T%, v5, v10)
DirectCall print(v11)
v13 = LoadInstanceField(#context-field:i, v9)
DirectCall print(v13)
v15 = LoadInstanceField(#context-field:i, v9)
v17 = BinaryIntOp +(v15, v16)
StoreInstanceField(#context-field:i, v9, v17)
v19 = LoadInstanceField(#context-field:i, v9)
Return(v19)
--- getter A.aField
B0 = EntryBlock()
v1 = Parameter(this)
v3 = LoadInstanceField(A.aField, v1)
Return(v3)
TypeParameters(v1)
v5 = LoadInstanceField(A.aField, v1)
Return(v5)
--- setter A.aField
B0 = EntryBlock()
v6 = Constant(null)
v8 = Constant(null)
v1 = Parameter(this)
v2 = Parameter(#value)
TypeParameters(v1)
StoreInstanceField(A.aField, v1, v2)
Return(v6)
Return(v8)
--- opaqueVal
B0 = EntryBlock()
@@ -85,16 +102,16 @@ B8 = JoinBlock(B6, B5) idom:B0
Return(v13)
--- foo42
B0 = EntryBlock() dominates:(B14)
B0 = EntryBlock() dominates:(B16)
Constant(true)
v21 = Constant(50)
v23 = Constant(60)
v25 = Constant(70)
v42 = Constant(1)
v54 = Constant(0)
v59 = Constant(10)
v77 = Constant(90)
v91 = Constant(null)
v23 = Constant(50)
v25 = Constant(60)
v27 = Constant(70)
v47 = Constant(1)
v59 = Constant(0)
v64 = Constant(10)
v83 = Constant(90)
v98 = Constant(null)
v1 = AllocateContext()
v2 = DirectCall opaqueVal()
StoreInstanceField(#context-field:a, v1, v2)
@@ -104,72 +121,79 @@ B0 = EntryBlock() dominates:(B14)
StoreInstanceField(#context-field:c, v1, v6)
v8 = DirectCall opaqueVal()
StoreInstanceField(#context-field:d, v1, v8)
v10 = AllocateClosure(v1)
v10 = AllocateClosure()
StoreInstanceField(#closure-field[0], v10, v1)
DirectCall callClosure(v10)
v12 = AllocateClosure(v1)
DirectCall callClosure(v12)
Goto(B14)
B14 = JoinBlock(B0, B45) idom:B0 dominates:(B45, B44) loop-header (depth:1 body:(B14, B45) back-edges:(B45))
v20 = AllocateContext()
StoreInstanceField(#context-field:e, v20, v21)
StoreInstanceField(#context-field:f, v20, v23)
StoreInstanceField(#context-field:g, v20, v25)
v27 = AllocateClosure(v1)
DirectCall callClosure(v27)
v29 = AllocateClosure(v20)
v13 = AllocateClosure()
StoreInstanceField(#closure-field[0], v13, v1)
DirectCall callClosure(v13)
Goto(B16)
B16 = JoinBlock(B0, B50) idom:B0 dominates:(B50, B49) loop-header (depth:1 body:(B16, B50) back-edges:(B50))
v22 = AllocateContext()
StoreInstanceField(#context-field:e, v22, v23)
StoreInstanceField(#context-field:f, v22, v25)
StoreInstanceField(#context-field:g, v22, v27)
v29 = AllocateClosure()
StoreInstanceField(#closure-field[0], v29, v1)
DirectCall callClosure(v29)
v31 = AllocateClosure(v20)
DirectCall callClosure(v31)
v33 = LoadInstanceField(#context-field:d, v1)
DirectCall print(v33)
v35 = LoadInstanceField(#context-field:e, v20)
DirectCall print(v35)
v37 = LoadInstanceField(#context-field:f, v20)
DirectCall print(v37)
v39 = LoadInstanceField(#context-field:g, v20)
DirectCall print(v39)
v41 = DirectCall opaqueVal()
v43 = Comparison int ==(v41, v42)
Branch(v43, true: B44, false: B45)
B44 = TargetBlock() idom:B14 dominates:(B56)
v53 = AllocateContext()
StoreInstanceField(#context-field:i, v53, v54)
Goto(B56)
B56 = JoinBlock(B44, B75) idom:B44 dominates:(B62, B61) loop-header (depth:1 body:(B56, B61, B70, B75, B74) back-edges:(B75))
v58 = LoadInstanceField(#context-field:i, v53)
v60 = Comparison int <(v58, v59)
Branch(v60, true: B61, false: B62)
B61 = TargetBlock() idom:B56 dominates:(B70) in-loop:B56
v64 = DirectCall opaqueVal()
StoreInstanceField(#context-field:h, v53, v64)
v66 = AllocateClosure(v53)
DirectCall callClosure(v66)
v68 = AllocateContext()
StoreInstanceField(#context-field:j, v68, v54)
Goto(B70)
B70 = JoinBlock(B61, B74) idom:B61 dominates:(B75, B74) loop-header (depth:2 body:(B70, B74) back-edges:(B74))
v72 = LoadInstanceField(#context-field:j, v68)
v73 = Comparison int <(v72, v59)
Branch(v73, true: B74, false: B75)
B74 = TargetBlock() idom:B70 in-loop:B70
StoreInstanceField(#context-field:k, v68, v77)
v79 = AllocateClosure(v68)
DirectCall callClosure(v79)
v81 = LoadInstanceField(#context-field:k, v68)
DirectCall print(v81)
v83 = LoadInstanceField(#context-field:j, v68)
v84 = BinaryIntOp +(v83, v42)
StoreInstanceField(#context-field:j, v68, v84)
Goto(B70)
B75 = TargetBlock() idom:B70 in-loop:B56
v87 = LoadInstanceField(#context-field:i, v53)
v88 = BinaryIntOp +(v87, v42)
StoreInstanceField(#context-field:i, v53, v88)
Goto(B56)
B62 = TargetBlock() idom:B56
Return(v91)
B45 = TargetBlock() idom:B14 in-loop:B14
Goto(B14)
v32 = AllocateClosure()
StoreInstanceField(#closure-field[0], v32, v22)
DirectCall callClosure(v32)
v35 = AllocateClosure()
StoreInstanceField(#closure-field[0], v35, v22)
DirectCall callClosure(v35)
v38 = LoadInstanceField(#context-field:d, v1)
DirectCall print(v38)
v40 = LoadInstanceField(#context-field:e, v22)
DirectCall print(v40)
v42 = LoadInstanceField(#context-field:f, v22)
DirectCall print(v42)
v44 = LoadInstanceField(#context-field:g, v22)
DirectCall print(v44)
v46 = DirectCall opaqueVal()
v48 = Comparison int ==(v46, v47)
Branch(v48, true: B49, false: B50)
B49 = TargetBlock() idom:B16 dominates:(B61)
v58 = AllocateContext()
StoreInstanceField(#context-field:i, v58, v59)
Goto(B61)
B61 = JoinBlock(B49, B81) idom:B49 dominates:(B67, B66) loop-header (depth:1 body:(B61, B66, B76, B81, B80) back-edges:(B81))
v63 = LoadInstanceField(#context-field:i, v58)
v65 = Comparison int <(v63, v64)
Branch(v65, true: B66, false: B67)
B66 = TargetBlock() idom:B61 dominates:(B76) in-loop:B61
v69 = DirectCall opaqueVal()
StoreInstanceField(#context-field:h, v58, v69)
v71 = AllocateClosure()
StoreInstanceField(#closure-field[0], v71, v58)
DirectCall callClosure(v71)
v74 = AllocateContext()
StoreInstanceField(#context-field:j, v74, v59)
Goto(B76)
B76 = JoinBlock(B66, B80) idom:B66 dominates:(B81, B80) loop-header (depth:2 body:(B76, B80) back-edges:(B80))
v78 = LoadInstanceField(#context-field:j, v74)
v79 = Comparison int <(v78, v64)
Branch(v79, true: B80, false: B81)
B80 = TargetBlock() idom:B76 in-loop:B76
StoreInstanceField(#context-field:k, v74, v83)
v85 = AllocateClosure()
StoreInstanceField(#closure-field[0], v85, v74)
DirectCall callClosure(v85)
v88 = LoadInstanceField(#context-field:k, v74)
DirectCall print(v88)
v90 = LoadInstanceField(#context-field:j, v74)
v91 = BinaryIntOp +(v90, v47)
StoreInstanceField(#context-field:j, v74, v91)
Goto(B76)
B81 = TargetBlock() idom:B76 in-loop:B61
v94 = LoadInstanceField(#context-field:i, v58)
v95 = BinaryIntOp +(v94, v47)
StoreInstanceField(#context-field:i, v58, v95)
Goto(B61)
B67 = TargetBlock() idom:B61
Return(v98)
B50 = TargetBlock() idom:B16 in-loop:B16
Goto(B16)
--- closure FunctionExpression(void () {
print(a);
@@ -289,21 +313,25 @@ B0 = EntryBlock()
v4 = Constant(20)
v6 = Constant(30)
v8 = Constant(40)
v18 = Constant(null)
v22 = Constant(null)
v1 = AllocateContext()
StoreInstanceField(#context-field:a, v1, v2)
StoreInstanceField(#context-field:b, v1, v4)
StoreInstanceField(#context-field:c, v1, v6)
StoreInstanceField(#context-field:d, v1, v8)
v10 = AllocateClosure(v1)
v10 = AllocateClosure()
StoreInstanceField(#closure-field[0], v10, v1)
DirectCall callClosure(v10)
v12 = AllocateClosure(v1)
DirectCall callClosure(v12)
v14 = AllocateClosure(v1)
DirectCall callClosure(v14)
v16 = AllocateClosure(v1)
v13 = AllocateClosure()
StoreInstanceField(#closure-field[0], v13, v1)
DirectCall callClosure(v13)
v16 = AllocateClosure()
StoreInstanceField(#closure-field[0], v16, v1)
DirectCall callClosure(v16)
Return(v18)
v19 = AllocateClosure()
StoreInstanceField(#closure-field[0], v19, v1)
DirectCall callClosure(v19)
Return(v22)
--- closure FunctionExpression(void () {
a = a.{num.+}(11);
@@ -373,39 +401,42 @@ B0 = EntryBlock()
Return(v8)
--- foo45
B0 = EntryBlock() dominates:(B10, B12, B9)
B0 = EntryBlock() dominates:(B11, B13, B10)
v2 = Constant(10)
v7 = Constant(1)
v14 = Constant(20)
v26 = Constant(30)
v31 = Constant(null)
v8 = Constant(1)
v15 = Constant(20)
v28 = Constant(30)
v34 = Constant(null)
v1 = AllocateContext()
StoreInstanceField(#context-field:a, v1, v2)
v4 = AllocateClosure(v1)
v4 = AllocateClosure()
StoreInstanceField(#closure-field[0], v4, v1)
DirectCall callClosure(v4)
v6 = DirectCall opaqueVal()
v8 = Comparison int ==(v6, v7)
Branch(v8, true: B9, false: B10)
B9 = TargetBlock() idom:B0
StoreInstanceField(#context-field:b, v1, v14)
v16 = AllocateClosure(v1)
DirectCall callClosure(v16)
Goto(B12)
v7 = DirectCall opaqueVal()
v9 = Comparison int ==(v7, v8)
Branch(v9, true: B10, false: B11)
B10 = TargetBlock() idom:B0
Goto(B12)
B12 = JoinBlock(B10, B9) idom:B0 dominates:(B22, B24, B21)
v19 = DirectCall opaqueVal()
v20 = Comparison int ==(v19, v7)
Branch(v20, true: B21, false: B22)
B21 = TargetBlock() idom:B12
StoreInstanceField(#context-field:c, v1, v26)
v28 = AllocateClosure(v1)
DirectCall callClosure(v28)
Goto(B24)
B22 = TargetBlock() idom:B12
Goto(B24)
B24 = JoinBlock(B22, B21) idom:B12
Return(v31)
StoreInstanceField(#context-field:b, v1, v15)
v17 = AllocateClosure()
StoreInstanceField(#closure-field[0], v17, v1)
DirectCall callClosure(v17)
Goto(B13)
B11 = TargetBlock() idom:B0
Goto(B13)
B13 = JoinBlock(B11, B10) idom:B0 dominates:(B24, B26, B23)
v21 = DirectCall opaqueVal()
v22 = Comparison int ==(v21, v8)
Branch(v22, true: B23, false: B24)
B23 = TargetBlock() idom:B13
StoreInstanceField(#context-field:c, v1, v28)
v30 = AllocateClosure()
StoreInstanceField(#closure-field[0], v30, v1)
DirectCall callClosure(v30)
Goto(B26)
B24 = TargetBlock() idom:B13
Goto(B26)
B26 = JoinBlock(B24, B23) idom:B13
Return(v34)
--- closure FunctionExpression(void () {
a = a.{num.+}(11);
@@ -458,17 +489,19 @@ B0 = EntryBlock()
--- foo46
B0 = EntryBlock()
v10 = Constant(null)
v12 = Constant(null)
v1 = AllocateContext()
v2 = DirectCall opaqueVal()
StoreInstanceField(#context-field:a, v1, v2)
v4 = DirectCall opaqueVal()
StoreInstanceField(#context-field:b, v1, v4)
v6 = AllocateClosure(v1)
v6 = AllocateClosure()
StoreInstanceField(#closure-field[0], v6, v1)
DirectCall callClosure(v6)
v8 = AllocateClosure(v1)
DirectCall callClosure(v8)
Return(v10)
v9 = AllocateClosure()
StoreInstanceField(#closure-field[0], v9, v1)
DirectCall callClosure(v9)
Return(v12)
--- closure FunctionExpression(void () {
callClosure(void () {
@@ -476,12 +509,13 @@ B0 = EntryBlock()
});
}) at foo46
B0 = EntryBlock()
v6 = Constant(null)
v7 = Constant(null)
v1 = Parameter(#closure)
v3 = LoadInstanceField(#closure-field[0], v1)
v4 = AllocateClosure(v3)
v4 = AllocateClosure()
StoreInstanceField(#closure-field[0], v4, v3)
DirectCall callClosure(v4)
Return(v6)
Return(v7)
--- closure FunctionExpression(void () {
print(a);
@@ -500,12 +534,13 @@ B0 = EntryBlock()
});
}) at foo46
B0 = EntryBlock()
v6 = Constant(null)
v7 = Constant(null)
v1 = Parameter(#closure)
v3 = LoadInstanceField(#closure-field[0], v1)
v4 = AllocateClosure(v3)
v4 = AllocateClosure()
StoreInstanceField(#closure-field[0], v4, v3)
DirectCall callClosure(v4)
Return(v6)
Return(v7)
--- closure FunctionExpression(void () {
b = b.{num.+}(11);
@@ -524,33 +559,35 @@ B0 = EntryBlock()
B0 = EntryBlock() dominates:(B5, B7, B4)
v2 = Constant(1)
v10 = Constant(10)
v23 = Constant(20)
v28 = Constant(null)
v24 = Constant(20)
v30 = Constant(null)
v1 = DirectCall opaqueVal()
v3 = Comparison int ==(v1, v2)
Branch(v3, true: B4, false: B5)
B4 = TargetBlock() idom:B0
v9 = AllocateContext()
StoreInstanceField(#context-field:a, v9, v10)
v12 = AllocateClosure(v9)
v12 = AllocateClosure()
StoreInstanceField(#closure-field[0], v12, v9)
DirectCall callClosure(v12)
Goto(B7)
B5 = TargetBlock() idom:B0
Goto(B7)
B7 = JoinBlock(B5, B4) idom:B0 dominates:(B18, B20, B17)
v15 = DirectCall opaqueVal()
v16 = Comparison int ==(v15, v2)
Branch(v16, true: B17, false: B18)
B17 = TargetBlock() idom:B7
v22 = AllocateContext()
StoreInstanceField(#context-field:b, v22, v23)
v25 = AllocateClosure(v22)
DirectCall callClosure(v25)
Goto(B20)
B7 = JoinBlock(B5, B4) idom:B0 dominates:(B19, B21, B18)
v16 = DirectCall opaqueVal()
v17 = Comparison int ==(v16, v2)
Branch(v17, true: B18, false: B19)
B18 = TargetBlock() idom:B7
Goto(B20)
B20 = JoinBlock(B18, B17) idom:B7
Return(v28)
v23 = AllocateContext()
StoreInstanceField(#context-field:b, v23, v24)
v26 = AllocateClosure()
StoreInstanceField(#closure-field[0], v26, v23)
DirectCall callClosure(v26)
Goto(B21)
B19 = TargetBlock() idom:B7
Goto(B21)
B21 = JoinBlock(B19, B18) idom:B7
Return(v30)
--- closure FunctionExpression(void () {
a = a.{num.+}(11);
@@ -579,28 +616,30 @@ B0 = EntryBlock()
Return(v8)
--- foo49
B0 = EntryBlock() dominates:(B8)
v6 = Constant(0)
v11 = Constant(4)
v19 = Constant(1)
v23 = Constant(null)
B0 = EntryBlock() dominates:(B9)
v7 = Constant(0)
v12 = Constant(4)
v21 = Constant(1)
v25 = Constant(null)
v1 = AllocateContext()
v2 = DirectCall opaqueVal()
StoreInstanceField(#context-field:a, v1, v2)
v4 = AllocateClosure(v1)
v4 = AllocateClosure()
StoreInstanceField(#closure-field[0], v4, v1)
DirectCall callClosure(v4)
Goto(B8)
B8 = JoinBlock(B0, B13) idom:B0 dominates:(B14, B13) loop-header (depth:1 body:(B8, B13) back-edges:(B13))
v25 = Phi(v6, v20)
v12 = Comparison int <(v25, v11)
Branch(v12, true: B13, false: B14)
B13 = TargetBlock() idom:B8 in-loop:B8
v16 = AllocateClosure(v1)
DirectCall callClosure(v16)
v20 = BinaryIntOp +(v25, v19)
Goto(B8)
B14 = TargetBlock() idom:B8
Return(v23)
Goto(B9)
B9 = JoinBlock(B0, B14) idom:B0 dominates:(B15, B14) loop-header (depth:1 body:(B9, B14) back-edges:(B14))
v27 = Phi(v7, v22)
v13 = Comparison int <(v27, v12)
Branch(v13, true: B14, false: B15)
B14 = TargetBlock() idom:B9 in-loop:B9
v17 = AllocateClosure()
StoreInstanceField(#closure-field[0], v17, v1)
DirectCall callClosure(v17)
v22 = BinaryIntOp +(v27, v21)
Goto(B9)
B15 = TargetBlock() idom:B9
Return(v25)
--- closure FunctionExpression(void () {
a = a.{num.+}(10);
@@ -623,15 +662,16 @@ B0 = EntryBlock()
}) at foo49
B0 = EntryBlock()
v5 = Constant(20)
v10 = Constant(null)
v11 = Constant(null)
v1 = Parameter(#closure)
v3 = LoadInstanceField(#closure-field[0], v1)
v4 = LoadInstanceField(#context-field:a, v3)
v6 = BinaryIntOp +(v4, v5)
StoreInstanceField(#context-field:a, v3, v6)
v8 = AllocateClosure(v3)
v8 = AllocateClosure()
StoreInstanceField(#closure-field[0], v8, v3)
DirectCall callClosure(v8)
Return(v10)
Return(v11)
--- closure FunctionExpression(void () {
a = a.{num.+}(30);
@@ -719,7 +719,9 @@ final class Arm64CodeGenerator extends CodeGenerator {
bool _canBeSmi(Definition def) => switch (def) {
Constant(:var value) => value.isInt && objectLayout.isSmi(value.intValue),
_ => def.type is IntType || const IntType().isSubtypeOf(def.type),
Definition(type: IntType()) => true,
Definition(type: ExtendedType()) => false,
Definition(:var type) => const IntType().isSubtypeOf(type),
};
void _writeBarrier(
@@ -1283,37 +1285,16 @@ final class Arm64CodeGenerator extends CodeGenerator {
@override
void visitAllocateClosure(AllocateClosure instr) {
final function = instr.function;
final hasDelayedTypeArgs = function.hasFunctionTypeParameters;
final hasInstantiatorTypeArgs = switch (function) {
LocalFunction() => containsClassTypeParameters(
function.functionNode!.computeFunctionType(ast.Nullability.nonNullable),
),
TearOffFunction() =>
function.member.isInstanceMember &&
containsClassTypeParameters(
function.member.function!.computeFunctionType(
ast.Nullability.nonNullable,
),
),
};
final hasFunctionTypeArgs = switch (function) {
LocalFunction() => hasGenericEnclosingFunction(function.localFunction),
TearOffFunction() => false,
};
final numElements =
(hasDelayedTypeArgs ? 1 : 0) +
(hasInstantiatorTypeArgs ? 1 : 0) +
(hasFunctionTypeArgs ? 1 : 0) +
1 /* context */;
final closureLayout = instr.closureLayout;
final lengthAndFlags = vmOffsets.encodeClosureLengthAndFlags(
numElements,
hasDelayedTypeArgs: hasDelayedTypeArgs,
hasInstantiatorTypeArgs: hasInstantiatorTypeArgs,
hasFunctionTypeArgs: hasFunctionTypeArgs,
closureLayout.length,
hasDelayedTypeArgs: closureLayout.hasDelayedTypeArgs,
hasInstantiatorTypeArgs: closureLayout.hasClassTypeArgs,
hasFunctionTypeArgs: closureLayout.hasFunctionTypeArgs,
);
final instanceSize = roundUp(
vmOffsets.Closure_elementsStartOffset +
numElements * objectLayout.compressedWordSize,
closureLayout.length * objectLayout.compressedWordSize,
objectAlignment(wordSize),
);
@@ -1357,8 +1338,7 @@ final class Arm64CodeGenerator extends CodeGenerator {
_asm.fieldAddress(resultReg, vmOffsets.Closure_length_and_flags_offset),
);
_asm.str(ZR, _asm.fieldAddress(resultReg, vmOffsets.Closure_hash_offset));
// TODO: initialize the rest of the fields.
assert(instr.inputCount == 0);
// TODO: initialize delayed type arguments.
_asm.bind(done);
}
@@ -330,18 +330,11 @@ final class Arm64Constraints extends Constraints {
@override
InstructionConstraints? visitAllocateClosure(AllocateClosure instr) =>
InstructionConstraints(
AllocationStub.resultReg,
List<Constraint?>.generate(
instr.inputCount,
(int i) => anyLocationOrImmediate(instr.inputDefAt(i)),
),
const [
AllocationStub.tagsReg,
AllocationStub.scratch1Reg,
AllocationStub.scratch2Reg,
],
);
const InstructionConstraints(AllocationStub.resultReg, [], [
AllocationStub.tagsReg,
AllocationStub.scratch1Reg,
AllocationStub.scratch2Reg,
]);
@override
InstructionConstraints? visitAllocateContext(AllocateContext instr) =>
@@ -150,35 +150,3 @@ bool hasNonTrivialInitializer(ast.Field field) {
};
}
}
/// Returns true if [type] references class type parameters.
bool containsClassTypeParameters(ast.DartType type) {
final visitor = _FindClassTypeParameters();
type.accept(visitor);
return visitor.containsClassTypeParams;
}
class _FindClassTypeParameters extends ast.RecursiveVisitor {
bool containsClassTypeParams = false;
_FindClassTypeParameters();
@override
void visitTypeParameterType(ast.TypeParameterType node) {
if (node.parameter.declaration is ast.Class) {
containsClassTypeParams = true;
}
}
}
bool hasGenericEnclosingFunction(ast.TreeNode node) {
for (;;) {
node = node.parent!;
if (node is ast.Member) {
return false;
}
if (node is ast.FunctionNode && node.typeParameters.isNotEmpty) {
return true;
}
}
}
@@ -506,7 +506,7 @@ B0 = EntryBlock() dominates:(B11, B10)
v3 = Parameter(compare) # RA: R2 <- ()
v4 = Parameter(isValidKey) # RA: R3 <- ()
ParallelMove output(R3 -> vloc:R10, R2 -> vloc:R9, R1 -> vloc:R6, R0 -> vloc:R5)
ParallelMove spill(R6 -> stack[0])
ParallelMove spill(R5 -> stack[0], R6 -> stack[1])
ParallelMove input(vloc:R6 -> R0, NullConstant(null) -> R2, vloc:R5 -> R1)
v9 = TypeTest(v2, v8, v1, Map<SplayTreeMap.from.K%, SplayTreeMap.from.V%>) # RA: R7 <- (R0, R2, R1) temps: [R8, R3, R4]
ParallelMove output(R7 -> vloc:R7)
@@ -527,12 +527,14 @@ B11 = TargetBlock() idom:B0
ParallelMove input(vloc:R1 -> R1)
v23 = AllocateObject SplayTreeMap<SplayTreeMap.from.K%, SplayTreeMap.from.V%>(v22) # RA: R0 <- (R1) temps: [R2, R3, R4]
ParallelMove output(R0 -> vloc:R0)
ParallelMove spill(R0 -> stack[1])
ParallelMove spill(R0 -> stack[2])
DirectCall SplayTreeMap.(v23, v3, v4) # RA: R0 <- (R0, R9, R10) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
v29 = AllocateClosure() # RA: R0 <- () temps: [R2, R3, R4]
ParallelMove output(R0 -> vloc:R0, stack[0] -> R1)
InterfaceCall Map.forEach(v2, v29) # RA: R0 <- (R1, R0) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
ParallelMove output(R0 -> vloc:R0, stack[0] -> R3)
StoreInstanceField(#closure-field[0], v29, v1) # RA: (R0, R3) temps: [R1, R2]
ParallelMove output(stack[1] -> R1)
InterfaceCall Map.forEach(v2, v29) # RA: R0 <- (R1, R0) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
ParallelMove output(stack[2] -> R1)
ParallelMove input(vloc:R1 -> R0)
Return(v23) # RA: (R0)
@@ -1784,7 +1786,7 @@ B0 = EntryBlock()
--- closures
B0 = EntryBlock()
v9 = Constant(null)
v11 = Constant(null)
Parameter(a1) # RA: R0 <- ()
v2 = Parameter(a2) # RA: R1 <- ()
ParallelMove output(R1 -> vloc:R1)
@@ -1792,13 +1794,16 @@ B0 = EntryBlock()
ParallelMove output(R0 -> vloc:R0)
StoreInstanceField(#context-field:a2, v3, v2) # RA: (R0, R1) temps: [R2, R3]
ParallelMove output(R0 -> R1)
v6 = AllocateClosure(v3) # RA: R0 <- (R1) temps: [R2, R3, R4]
ParallelMove output(R0 -> vloc:R5)
v7 = AllocateClosure(v3) # RA: R0 <- (R1) temps: [R2, R3, R4]
v6 = AllocateClosure() # RA: R0 <- () temps: [R2, R3, R4]
ParallelMove output(R0 -> vloc:R0)
DirectCall foo2(v6, v7) # RA: R0 <- (R5, R0) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
StoreInstanceField(#closure-field[0], v6, v3) # RA: (R0, R1) temps: [R2, R3]
ParallelMove output(R0 -> R5)
v8 = AllocateClosure() # RA: R0 <- () temps: [R2, R3, R4]
ParallelMove output(R0 -> vloc:R0)
StoreInstanceField(#closure-field[0], v8, v3) # RA: (R0, R1) temps: [R2, R3]
DirectCall foo2(v6, v8) # RA: R0 <- (R5, R0) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
ParallelMove input(NullConstant(null) -> R0)
Return(v9) # RA: (R0)
Return(v11) # RA: (R0)
--- closure FunctionExpression(Null (dynamic _#wc0#formal) {
print(a2);