[vm,modular_aot] Lowering of type parameters

Issue: https://github.com/dart-lang/sdk/issues/61635
Change-Id: I06098a379ba3f04b5e13bad11acc0dcc391fb30c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/482364
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
Alexander Markov
2026-02-24 06:16:51 -08:00
committed by Commit Queue
parent 8852aa5c19
commit dc2d8a06b9
21 changed files with 506 additions and 393 deletions
+99 -121
View File
@@ -19,6 +19,14 @@ import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/type_environment.dart' show StaticTypeContext;
/// Strategy for adding [TypeParameters] instructions when building CFG IR.
enum TypeParametersStyle {
/// Maintain separate [TypeParameters] for function type parameters and
/// class type parameters.
/// Represent incoming function type parameters as the first parameter.
separateFunctionAndClassTypeParameters,
}
/// Translates kernel AST to the flow graph.
///
/// Not implemented yet:
@@ -41,6 +49,7 @@ class AstToIr extends ast.RecursiveVisitor {
final RecognizedMethods recognizedMethods;
final FlowGraphBuilder builder;
final bool enableAsserts;
final TypeParametersStyle typeParametersStyle;
late final AstToIrTypes _typeTranslator;
late final LocalVariableIndexer localVarIndexer;
late final StaticTypeContext _staticTypeContext = StaticTypeContext(
@@ -51,13 +60,17 @@ class AstToIr extends ast.RecursiveVisitor {
Map<ast.LabeledStatement, JoinBlock>? labeledStatements;
Map<ast.SwitchCase, JoinBlock>? switchCases;
Map<ast.TryFinally, List<FinallyBlock>>? finallyBlocks;
TypeParameters? typeParameters;
bool _hasTypeParametersInScope = false;
TypeParameters? functionTypeParameters;
TypeParameters? classTypeParameters;
AstToIr(
this.function,
this.functionRegistry,
this.recognizedMethods, {
required this.enableAsserts,
required this.typeParametersStyle,
}) : coreTypes = GlobalContext.instance.coreTypes,
hierarchy = GlobalContext.instance.classHierarchy,
builder = FlowGraphBuilder(function) {
@@ -76,11 +89,23 @@ class AstToIr extends ast.RecursiveVisitor {
for (final param in localVarIndexer.parameters) {
builder.addParameter(param);
}
if (function.hasClassTypeParameters) {
builder.addLoadLocal(localVarIndexer.receiver);
typeParameters = builder.addTypeParameters(receiver: builder.pop());
} else if (function.hasFunctionTypeParameters) {
typeParameters = builder.addTypeParameters();
if (function.hasFunctionTypeParameters || function.hasClassTypeParameters) {
_hasTypeParametersInScope = true;
switch (typeParametersStyle) {
case .separateFunctionAndClassTypeParameters:
if (function.hasFunctionTypeParameters) {
builder.addLoadLocal(localVarIndexer.functionTypeParameters);
functionTypeParameters = builder.addTypeParameters(
.functionTypeParameters,
);
}
if (function.hasClassTypeParameters) {
builder.addLoadLocal(localVarIndexer.receiver);
classTypeParameters = builder.addTypeParameters(
.classTypeParameters,
);
}
}
}
final member = function.member;
switch (function) {
@@ -334,25 +359,41 @@ class AstToIr extends ast.RecursiveVisitor {
}
}
bool _hasTypeParameterReferences(ast.DartType type) =>
type.accept(const _FindTypeParameters());
TypeParameters? _typeParametersForType(ast.DartType type) {
if (typeParameters != null && _hasTypeParameterReferences(type)) {
return typeParameters;
List<Definition> _referencedTypeParameters(_FindTypeParameters visitor) {
if (!visitor.containsClassTypeParams &&
!visitor.containsFunctionTypeParams) {
return const [];
}
return null;
return switch (typeParametersStyle) {
.separateFunctionAndClassTypeParameters => [
visitor.containsFunctionTypeParams
? functionTypeParameters!
: builder.graph.getConstant(ConstantValue.fromNull()),
visitor.containsClassTypeParams
? classTypeParameters!
: builder.graph.getConstant(ConstantValue.fromNull()),
],
};
}
TypeParameters? _typeParametersForTypes(List<ast.DartType> types) {
if (typeParameters != null) {
for (final type in types) {
if (_hasTypeParameterReferences(type)) {
return typeParameters;
}
}
List<Definition> _typeParametersForType(ast.DartType type) {
if (!_hasTypeParametersInScope) {
return const [];
}
return null;
final visitor = _FindTypeParameters();
type.accept(visitor);
return _referencedTypeParameters(visitor);
}
List<Definition> _typeParametersForTypes(List<ast.DartType> types) {
if (!_hasTypeParametersInScope) {
return const [];
}
final visitor = _FindTypeParameters();
for (final type in types) {
type.accept(visitor);
}
return _referencedTypeParameters(visitor);
}
@override
@@ -386,12 +427,10 @@ class AstToIr extends ast.RecursiveVisitor {
@override
void visitTypeLiteral(ast.TypeLiteral node) {
final typeParameters = _typeParametersForType(node.type);
if (typeParameters != null) {
builder.addTypeLiteral(node.type, typeParameters: typeParameters);
} else {
builder.addConstant(ConstantValue(ast.TypeLiteralConstant(node.type)));
}
builder.addTypeLiteral(
node.type,
typeParameters: _typeParametersForType(node.type),
);
}
@override
@@ -1667,7 +1706,8 @@ class LocalVariableIndexer {
final Map<ast.TreeNode, LocalVariable> _stackTraceVariables = {};
final List<LocalVariable> parameters = [];
LocalVariable get receiver => parameters[0];
late final LocalVariable functionTypeParameters;
late final LocalVariable receiver;
LocalVariableIndexer(
this.builder,
@@ -1675,17 +1715,24 @@ class LocalVariableIndexer {
this.typeTranslator,
CFunction function,
) {
if (function.hasFunctionTypeParameters) {
functionTypeParameters = builder.declareLocalVariable(
'#functionTypeParameters',
null,
const TypeParametersType(),
);
parameters.add(functionTypeParameters);
}
if (function.hasReceiverParameter) {
final cls = function.member.enclosingClass!;
parameters.add(
builder.declareLocalVariable(
'this',
null,
typeTranslator.translate(
cls.getThisType(coreTypes, ast.Nullability.nonNullable),
),
receiver = builder.declareLocalVariable(
'this',
null,
typeTranslator.translate(
cls.getThisType(coreTypes, ast.Nullability.nonNullable),
),
);
parameters.add(receiver);
}
if (function.hasClosureParameter) {
parameters.add(
@@ -1760,94 +1807,25 @@ class FinallyBlock {
FinallyBlock(FlowGraphBuilder builder, this.generateContinuation);
}
/// Look up references to free type parameters.
class _FindTypeParameters
with ast.DartTypeVisitorExperimentExclusionMixin<bool>
implements ast.DartTypeVisitor<bool> {
const _FindTypeParameters();
/// Look up references to type parameters.
class _FindTypeParameters extends ast.RecursiveVisitor {
_FindTypeParameters();
bool containsClassTypeParams = false;
bool containsFunctionTypeParams = false;
@override
bool visitFunctionType(ast.FunctionType node) {
if (node.returnType.accept(this)) return true;
for (final param in node.positionalParameters) {
if (param.accept(this)) return true;
void visitTypeParameterType(ast.TypeParameterType node) {
final declaration = node.parameter.declaration;
switch (declaration) {
case ast.Class():
containsClassTypeParams = true;
break;
case ast.GenericFunction():
containsFunctionTypeParams = true;
break;
default:
throw 'Unexpected type parameter $node declaration ${declaration.runtimeType} $declaration';
}
for (final namedParam in node.namedParameters) {
if (namedParam.type.accept(this)) return true;
}
for (final typeParam in node.typeParameters) {
if (typeParam.bound.accept(this)) return true;
if (typeParam.defaultType.accept(this)) return true;
}
return false;
}
@override
bool visitInterfaceType(ast.InterfaceType node) {
for (final type in node.typeArguments) {
if (type.accept(this)) return true;
}
return false;
}
@override
bool visitTypedefType(ast.TypedefType node) {
for (final type in node.typeArguments) {
if (type.accept(this)) return true;
}
return false;
}
@override
bool visitTypeParameterType(ast.TypeParameterType node) => true;
@override
bool visitStructuralParameterType(ast.StructuralParameterType node) => false;
@override
bool visitIntersectionType(ast.IntersectionType node) {
return node.left.accept(this) || node.right.accept(this);
}
@override
bool visitExtensionType(ast.ExtensionType node) {
for (final type in node.typeArguments) {
if (type.accept(this)) return true;
}
return false;
}
@override
bool visitRecordType(ast.RecordType node) {
for (final type in node.positional) {
if (type.accept(this)) return true;
}
for (final namedType in node.named) {
if (namedType.type.accept(this)) return true;
}
return false;
}
@override
bool visitFutureOrType(ast.FutureOrType node) =>
node.typeArgument.accept(this);
@override
bool visitInvalidType(ast.InvalidType node) => false;
@override
bool visitNeverType(ast.NeverType node) => false;
@override
bool visitNullType(ast.NullType node) => false;
@override
bool visitVoidType(ast.VoidType node) => false;
@override
bool visitDynamicType(ast.DynamicType node) => false;
@override
bool visitAuxiliaryType(ast.AuxiliaryType node) =>
throw 'Unsupported type ${node.runtimeType} $node';
}
+37 -21
View File
@@ -12,7 +12,7 @@ import 'package:cfg/ir/source_position.dart';
import 'package:cfg/ir/types.dart';
import 'package:kernel/ast.dart'
as ast
show DartType, Name, VariableDeclaration;
show DartType, Name, TypeLiteralConstant, VariableDeclaration;
/// Helper class to create IR instructions and populate [FlowGraph].
///
@@ -418,12 +418,10 @@ class FlowGraphBuilder {
return instr;
}
/// Append [TypeParameters] to the graph.
///
/// Optional [receiver] input should be passed if there are class type
/// parameters in scope.
TypeParameters addTypeParameters({Definition? receiver}) {
final instr = TypeParameters(graph, currentSourcePosition, receiver);
/// Append [TypeParameters] taking a parameter as input to the graph.
TypeParameters addTypeParameters(TypeParametersKind kind) {
final parameter = pop();
final instr = TypeParameters(graph, currentSourcePosition, kind, parameter);
appendInstruction(instr);
return instr;
}
@@ -437,7 +435,7 @@ class FlowGraphBuilder {
/// check at runtime.
TypeCast addTypeCast(
CType testedType, {
Definition? typeParameters,
List<Definition> typeParameters = const [],
bool isChecked = true,
}) {
final object = pop();
@@ -446,9 +444,12 @@ class FlowGraphBuilder {
currentSourcePosition,
object,
testedType,
typeParameters,
inputCount: 1 + typeParameters.length,
isChecked: isChecked,
);
for (var i = 0, n = typeParameters.length; i < n; ++i) {
instr.setInputAt(1 + i, typeParameters[i]);
}
push(instr);
appendInstruction(instr);
return instr;
@@ -458,15 +459,21 @@ class FlowGraphBuilder {
///
/// Optional [typeParameters] input should be passed if tested type
/// depends on type parameters (not fully instantiated).
TypeTest addTypeTest(CType testedType, {Definition? typeParameters}) {
TypeTest addTypeTest(
CType testedType, {
List<Definition> typeParameters = const [],
}) {
final object = pop();
final instr = TypeTest(
graph,
currentSourcePosition,
object,
testedType,
typeParameters,
inputCount: 1 + typeParameters.length,
);
for (var i = 0, n = typeParameters.length; i < n; ++i) {
instr.setInputAt(1 + i, typeParameters[i]);
}
push(instr);
appendInstruction(instr);
return instr;
@@ -479,9 +486,9 @@ class FlowGraphBuilder {
/// depend on type parameters (not fully instantiated).
void addTypeArguments(
List<ast.DartType> types, {
Definition? typeParameters,
List<Definition> typeParameters = const [],
}) {
if (typeParameters == null) {
if (typeParameters.isEmpty) {
addConstant(ConstantValue(TypeArgumentsConstant(types)));
return;
}
@@ -489,26 +496,35 @@ class FlowGraphBuilder {
graph,
currentSourcePosition,
types,
typeParameters,
inputCount: typeParameters.length,
);
for (var i = 0, n = typeParameters.length; i < n; ++i) {
instr.setInputAt(i, typeParameters[i]);
}
push(instr);
appendInstruction(instr);
}
/// Append [TypeLiteral] to the graph.
TypeLiteral addTypeLiteral(
ast.DartType uninstantiatedType, {
required Definition typeParameters,
/// Append [TypeLiteral] or [Constant] representing type to the graph.
void addTypeLiteral(
ast.DartType type, {
required List<Definition> typeParameters,
}) {
if (typeParameters.isEmpty) {
addConstant(ConstantValue(ast.TypeLiteralConstant(type)));
return;
}
final instr = TypeLiteral(
graph,
currentSourcePosition,
uninstantiatedType,
typeParameters,
type,
inputCount: typeParameters.length,
);
for (var i = 0, n = typeParameters.length; i < n; ++i) {
instr.setInputAt(i, typeParameters[i]);
}
push(instr);
appendInstruction(instr);
return instr;
}
/// Append [AllocateObject] to the graph.
+3 -8
View File
@@ -316,19 +316,14 @@ final class FlowGraphChecker extends Pass implements InstructionVisitor<void> {
@override
void visitTypeParameters(TypeParameters instr) {
assert(instr.block is EntryBlock);
// TypeParameters can only be used in TypeCast, TypeTest,
// TypeArguments and TypeLiteral.
for (final use in instr.inputUses) {
final user = use.getInstruction(graph);
switch (user) {
case TypeCast():
assert(instr == user.typeParameters);
case TypeTest():
assert(instr == user.typeParameters);
case TypeArguments():
assert(instr == user.typeParameters);
case TypeLiteral():
assert(instr == user.typeParameters);
case TypeCast() || TypeTest() || TypeArguments() || TypeLiteral():
break;
default:
throw 'Unexpected user ${IrToText.instruction(user)} of TypeParameters';
}
+6 -1
View File
@@ -38,7 +38,9 @@ sealed class CFunction {
bool get hasFunctionTypeParameters =>
member is ast.Procedure && member.function!.typeParameters.isNotEmpty;
/// Total number of parameters including receiver, closure and optional parameters.
/// Total number of parameters including function type parameters
/// (represented with a single parameter), receiver, closure and
/// optional parameters.
int get numberOfParameters;
/// Return type of this function.
@@ -115,6 +117,7 @@ final class RegularFunction extends CFunction {
@override
int get numberOfParameters =>
(hasFunctionTypeParameters ? 1 : 0) +
(hasReceiverParameter ? 1 : 0) +
member.function!.positionalParameters.length +
member.function!.namedParameters.length;
@@ -170,6 +173,7 @@ final class LocalFunction extends ClosureFunction {
@override
int get numberOfParameters =>
(hasFunctionTypeParameters ? 1 : 0) +
1 /* closure */ +
localFunction.function.positionalParameters.length +
localFunction.function.namedParameters.length;
@@ -200,6 +204,7 @@ final class TearOffFunction extends ClosureFunction {
@override
int get numberOfParameters =>
(hasFunctionTypeParameters ? 1 : 0) +
1 /* closure */ +
member.function!.positionalParameters.length +
member.function!.namedParameters.length;
+38 -36
View File
@@ -93,6 +93,8 @@ abstract base class Instruction {
_inputs.truncateTo(graph, newInputCount);
}
int getInputIndex(Use use) => _inputs.indexOf(graph, use);
/// Link this instruction to the [next] instruction in basic block.
void linkTo(Instruction next) {
assert(!identical(this, next));
@@ -1099,15 +1101,30 @@ final class NullCheck extends Definition with CanThrow, Pure, Idempotent {
R accept<R>(InstructionVisitor<R> v) => v.visitNullCheck(this);
}
/// Represents collection of class and function type parameters.
enum TypeParametersKind {
functionTypeParameters,
classTypeParameters,
// Add kinds for a single function/class type parameter.
}
/// Represents collection of type parameters corresponding to the
/// given parameter.
/// Can be used as inputs in [TypeCast], [TypeTest], [TypeArguments] and
/// [TypeLiteral] instructions.
final class TypeParameters extends Definition with NoThrow, Pure {
TypeParameters(super.graph, super.sourcePosition, Definition? receiver)
: super(inputCount: receiver != null ? 1 : 0) {
if (receiver != null) {
setInputAt(0, receiver);
}
final TypeParametersKind kind;
TypeParameters(
super.graph,
super.sourcePosition,
this.kind,
Definition parameter,
) : super(inputCount: 1) {
setInputAt(0, parameter);
}
Definition get parameter => inputDefAt(0);
@override
CType get type => const TypeParametersType();
@@ -1117,8 +1134,7 @@ final class TypeParameters extends Definition with NoThrow, Pure {
/// Casts input object to the given type.
///
/// Checked casts throw TypeError if
/// object is not assignable to the given type.
/// Checked casts throw TypeError if object is not assignable to the given type.
final class TypeCast extends Definition with CanThrow, Pure, Idempotent {
/// Target type for the type cast.
final CType testedType;
@@ -1130,18 +1146,15 @@ final class TypeCast extends Definition with CanThrow, Pure, Idempotent {
super.graph,
super.sourcePosition,
Definition object,
this.testedType,
Definition? typeParameters, {
this.testedType, {
required super.inputCount,
this.isChecked = true,
}) : super(inputCount: typeParameters != null ? 2 : 1) {
}) {
assert(inputCount > 0);
setInputAt(0, object);
if (typeParameters != null) {
setInputAt(1, typeParameters);
}
}
Definition get operand => inputDefAt(0);
Definition? get typeParameters => (inputCount > 1) ? inputDefAt(1) : null;
@override
CType get type => testedType;
@@ -1167,17 +1180,14 @@ final class TypeTest extends Definition with NoThrow, Pure, Idempotent {
super.graph,
super.sourcePosition,
Definition object,
this.testedType,
Definition? typeParameters,
) : super(inputCount: typeParameters != null ? 2 : 1) {
this.testedType, {
required super.inputCount,
}) {
assert(inputCount > 0);
setInputAt(0, object);
if (typeParameters != null) {
setInputAt(1, typeParameters);
}
}
Definition get operand => inputDefAt(0);
Definition? get typeParameters => (inputCount > 1) ? inputDefAt(1) : null;
@override
CType get type => const BoolType();
@@ -1200,13 +1210,9 @@ final class TypeArguments extends Definition with NoThrow, Pure, Idempotent {
TypeArguments(
super.graph,
super.sourcePosition,
this.types,
Definition typeParameters,
) : super(inputCount: 1) {
setInputAt(0, typeParameters);
}
Definition get typeParameters => inputDefAt(0);
this.types, {
required super.inputCount,
});
@override
CType get type => const TypeArgumentsType();
@@ -1225,13 +1231,9 @@ final class TypeLiteral extends Definition with NoThrow, Pure, Idempotent {
TypeLiteral(
super.graph,
super.sourcePosition,
this.uninstantiatedType,
Definition typeParameters,
) : super(inputCount: 1) {
setInputAt(0, typeParameters);
}
Definition get typeParameters => inputDefAt(0);
this.uninstantiatedType, {
required super.inputCount,
});
@override
CType get type =>
+9
View File
@@ -81,6 +81,15 @@ extension type const UsesArray(ArenaPointer _ptr) {
return Use(_ptr + elementsOffset + index * Use.useSize);
}
int indexOf(FlowGraph graph, Use use) {
assert(_ptr != ArenaPointer.Null);
final offset = use._ptr - (_ptr + elementsOffset);
assert((offset % Use.useSize) == 0);
final index = offset ~/ Use.useSize;
assert(0 <= index && index < getLength(graph));
return index;
}
void truncateTo(FlowGraph graph, int newLength) {
assert(_ptr != ArenaPointer.Null);
assert((0 <= newLength) && (newLength <= getLength(graph)));
+7
View File
@@ -18,6 +18,13 @@ extension type const ArenaPointer(int _index) {
return ArenaPointer(_index + size);
}
/// Number of 32-bit elements between [base] and [this] pointers.
int operator -(ArenaPointer base) {
assert(this != Null);
assert(base != Null);
return this._index - base._index;
}
/// 32-bit unsigned integer value of this pointer.
int toInt() => _index;
}
+1
View File
@@ -144,6 +144,7 @@ class CompileAndDumpIr extends RecursiveVisitor {
functionRegistry,
recognizedMethods,
enableAsserts: true,
typeParametersStyle: .separateFunctionAndClassTypeParameters,
).buildFlowGraph();
final pipeline = Pipeline([
SSAComputation(),
+52 -50
View File
@@ -1,58 +1,60 @@
--- listLiterals
B0 = EntryBlock()
v3 = Constant(<dynamic>)
v4 = Constant(0)
v10 = Constant(<int>)
v11 = Constant(1)
v12 = Constant(2)
v13 = Constant(3)
v16 = Constant(4)
v17 = Constant(5)
v18 = Constant(6)
v19 = Constant(7)
v20 = Constant(8)
v21 = Constant(9)
v36 = Constant(null)
v1 = Parameter(x)
v2 = TypeParameters()
v5 = DirectCall _GrowableList.(v3, v4)
DirectCall print(v5)
v7 = TypeArguments(v2, <listLiterals.T%>)
v8 = DirectCall _GrowableList.(v7, v4)
DirectCall print(v8)
v14 = DirectCall _GrowableList._literal3(v10, v11, v12, v13)
DirectCall print(v14)
v22 = AllocateListLiteral(v10, v11, v12, v13, v16, v17, v18, v19, v20, v21)
DirectCall print(v22)
v34 = AllocateListLiteral(v7, v1, v1, v1, v1, v1, v1, v1, v1, v1)
DirectCall print(v34)
Return(v36)
v5 = Constant(<dynamic>)
v6 = Constant(0)
v9 = Constant(null)
v13 = Constant(<int>)
v14 = Constant(1)
v15 = Constant(2)
v16 = Constant(3)
v19 = Constant(4)
v20 = Constant(5)
v21 = Constant(6)
v22 = Constant(7)
v23 = Constant(8)
v24 = Constant(9)
v1 = Parameter(#functionTypeParameters)
v2 = Parameter(x)
v4 = TypeParameters(v1)
v7 = DirectCall _GrowableList.(v5, v6)
DirectCall print(v7)
v10 = TypeArguments(v4, v9, <listLiterals.T%>)
v11 = DirectCall _GrowableList.(v10, v6)
DirectCall print(v11)
v17 = DirectCall _GrowableList._literal3(v13, v14, v15, v16)
DirectCall print(v17)
v25 = AllocateListLiteral(v13, v14, v15, v16, v19, v20, v21, v22, v23, v24)
DirectCall print(v25)
v37 = AllocateListLiteral(v10, v2, v2, v2, v2, v2, v2, v2, v2, v2)
DirectCall print(v37)
Return(v9)
--- mapLiterals
B0 = EntryBlock()
v6 = Constant(<dynamic, dynamic>)
v12 = Constant(<String, String>)
v13 = Constant("a")
v14 = Constant("aa")
v15 = Constant("b")
v16 = Constant("bb")
v27 = Constant(null)
v1 = Parameter(key)
v2 = Parameter(value)
v3 = Parameter(key2)
v4 = Parameter(value2)
v5 = TypeParameters()
v7 = AllocateMapLiteral(v6)
DirectCall print(v7)
v9 = TypeArguments(v5, <mapLiterals.S%, mapLiterals.T%>)
v10 = AllocateMapLiteral(v9)
DirectCall print(v10)
v17 = AllocateMapLiteral(v12, v13, v14, v15, v16)
DirectCall print(v17)
v22 = ClosureCall(v2)
v25 = AllocateMapLiteral(v9, v1, v22, v3, v4)
DirectCall print(v25)
Return(v27)
v8 = Constant(<dynamic, dynamic>)
v11 = Constant(null)
v15 = Constant(<String, String>)
v16 = Constant("a")
v17 = Constant("aa")
v18 = Constant("b")
v19 = Constant("bb")
v1 = Parameter(#functionTypeParameters)
v2 = Parameter(key)
v3 = Parameter(value)
v4 = Parameter(key2)
v5 = Parameter(value2)
v7 = TypeParameters(v1)
v9 = AllocateMapLiteral(v8)
DirectCall print(v9)
v12 = TypeArguments(v7, v11, <mapLiterals.S%, mapLiterals.T%>)
v13 = AllocateMapLiteral(v12)
DirectCall print(v13)
v20 = AllocateMapLiteral(v15, v16, v17, v18, v19)
DirectCall print(v20)
v25 = ClosureCall(v3)
v28 = AllocateMapLiteral(v12, v2, v25, v4, v5)
DirectCall print(v28)
Return(v11)
--- nullChecks
B0 = EntryBlock()
+41 -35
View File
@@ -10,52 +10,58 @@ B0 = EntryBlock()
B0 = EntryBlock()
v4 = Constant(<int>)
v6 = Constant(1)
v10 = Constant(2)
v12 = Constant(null)
v8 = Constant(null)
v11 = Constant(2)
v1 = Parameter(this)
v3 = TypeParameters(v1)
InterfaceCall A.foo(v4, v1, v6)
v8 = TypeArguments(v3, <Map<String, A.T%>>)
InterfaceCall A.foo(v8, v1, v10)
Return(v12)
v9 = TypeArguments(v8, v3, <Map<String, A.T%>>)
InterfaceCall A.foo(v9, v1, v11)
Return(v8)
--- A.foo
B0 = EntryBlock() dominates:(B8, B10, B7)
v16 = Constant(null)
v1 = Parameter(this)
v2 = Parameter(o)
v4 = TypeParameters(v1)
v6 = TypeTest(v2, v4, List<A.T%>)
Branch(v6, true: B7, false: B8)
B7 = TargetBlock() idom:B0
v13 = TypeCast(v2, v4, List<A.T%>, unchecked)
TypeCast(v13, Map<dynamic, dynamic>)
Goto(B10)
B8 = TargetBlock() idom:B0
Goto(B10)
B10 = JoinBlock(B8, B7) idom:B0
Return(v16)
B0 = EntryBlock() dominates:(B12, B14, B11)
v9 = Constant(null)
v1 = Parameter(#functionTypeParameters)
v2 = Parameter(this)
v3 = Parameter(o)
TypeParameters(v1)
v7 = TypeParameters(v2)
v10 = TypeTest(v3, v9, v7, List<A.T%>)
Branch(v10, true: B11, false: B12)
B11 = TargetBlock() idom:B0
v17 = TypeCast(v3, v9, v7, List<A.T%>, unchecked)
TypeCast(v17, Map<dynamic, dynamic>)
Goto(B14)
B12 = TargetBlock() idom:B0
Goto(B14)
B14 = JoinBlock(B12, B11) idom:B0
Return(v9)
--- A.baz
B0 = EntryBlock()
v4 = Constant(TypeLiteralConstant(List<dynamic>))
v10 = Constant(null)
v1 = Parameter(this)
v3 = TypeParameters(v1)
DirectCall print(v4)
v6 = TypeLiteral(A.T%, v3)
DirectCall print(v6)
v8 = TypeLiteral(Map<A.baz.S%, A.T%>, v3)
DirectCall print(v8)
Return(v10)
v7 = Constant(TypeLiteralConstant(List<dynamic>))
v9 = Constant(null)
v1 = Parameter(#functionTypeParameters)
v2 = Parameter(this)
v4 = TypeParameters(v1)
v6 = TypeParameters(v2)
DirectCall print(v7)
v10 = TypeLiteral(A.T%, v9, v6)
DirectCall print(v10)
v12 = TypeLiteral(Map<A.baz.S%, A.T%>, v4, v6)
DirectCall print(v12)
Return(v9)
--- A.
B0 = EntryBlock()
v1 = TypeParameters()
v2 = TypeArguments(v1, <A..T%>)
v3 = AllocateObject A<A..T%>(v2)
DirectCall A._(v3)
Return(v3)
v4 = Constant(null)
v1 = Parameter(#functionTypeParameters)
v3 = TypeParameters(v1)
v5 = TypeArguments(v3, v4, <A..T%>)
v6 = AllocateObject A<A..T%>(v5)
DirectCall A._(v6)
Return(v6)
--- main
B0 = EntryBlock()
@@ -443,11 +443,6 @@ final class Arm64CodeGenerator extends CodeGenerator {
_asm.unimplemented('Unimplemented: code generation for NullCheck');
}
@override
void visitTypeParameters(TypeParameters instr) {
_asm.unimplemented('Unimplemented: code generation for TypeParameters');
}
@override
void visitTypeCast(TypeCast instr) {
_asm.unimplemented('Unimplemented: code generation for TypeCast');
@@ -175,33 +175,39 @@ final class Arm64Constraints extends Constraints {
InstructionConstraints? visitNullCheck(NullCheck instr) =>
const InstructionConstraints(anyCpuRegister, [anyCpuRegister]);
@override
InstructionConstraints? visitTypeParameters(TypeParameters instr) =>
InstructionConstraints(anyCpuRegister, [
if (instr.inputCount == 1) anyCpuRegister,
]);
@override
InstructionConstraints? visitTypeCast(TypeCast instr) =>
InstructionConstraints(anyCpuRegister, [
anyCpuRegister,
if (instr.inputCount == 2) anyCpuRegister,
if (instr.inputCount > 1) ...[
anyRegisterOrImmediate(instr.inputDefAt(1)),
anyRegisterOrImmediate(instr.inputDefAt(2)),
],
]);
@override
InstructionConstraints? visitTypeTest(TypeTest instr) =>
InstructionConstraints(anyCpuRegister, [
anyCpuRegister,
if (instr.inputCount == 2) anyCpuRegister,
if (instr.inputCount > 1) ...[
anyRegisterOrImmediate(instr.inputDefAt(1)),
anyRegisterOrImmediate(instr.inputDefAt(2)),
],
]);
@override
InstructionConstraints? visitTypeArguments(TypeArguments instr) =>
const InstructionConstraints(anyCpuRegister, [anyCpuRegister]);
InstructionConstraints(anyCpuRegister, [
anyRegisterOrImmediate(instr.inputDefAt(0)),
anyRegisterOrImmediate(instr.inputDefAt(1)),
]);
@override
InstructionConstraints? visitTypeLiteral(TypeLiteral instr) =>
const InstructionConstraints(anyCpuRegister, [anyCpuRegister]);
InstructionConstraints(anyCpuRegister, [
anyRegisterOrImmediate(instr.inputDefAt(0)),
anyRegisterOrImmediate(instr.inputDefAt(1)),
]);
@override
InstructionConstraints? visitAllocateObject(AllocateObject instr) =>
@@ -286,6 +286,10 @@ abstract base class CodeGenerator extends Pass
void generatePush(Location loc);
void generatePop(Location loc);
@override
void visitTypeParameters(TypeParameters instr) =>
throw 'Unexpected TypeParameters (should be lowered)';
@override
void visitAllocateListLiteral(AllocateListLiteral instr) =>
throw 'Unexpected AllocateListLiteral (should be lowered)';
@@ -127,6 +127,10 @@ abstract base class Constraints
@override
InstructionConstraints? visitParallelMove(ParallelMove instr) => null;
@override
InstructionConstraints? visitTypeParameters(TypeParameters instr) =>
throw 'Unexpected TypeParameters (should be lowered)';
@override
InstructionConstraints? visitAllocateListLiteral(AllocateListLiteral instr) =>
throw 'Unexpected AllocateListLiteral (should be lowered)';
@@ -87,8 +87,10 @@ final class LinearScanRegisterAllocator extends RegisterAllocator {
Instruction instructionByPos(int pos) =>
graph.instructions[_instructionByPos[pos ~/ step]];
bool hasLiveRange(Definition instr) => instr is! Constant;
LiveRange liveRangeFor(Definition instr) {
assert(instr is! Constant);
assert(hasLiveRange(instr));
return _liveRanges[instr.id] ??= LiveRange(registerClass(instr));
}
@@ -172,7 +174,7 @@ final class LinearScanRegisterAllocator extends RegisterAllocator {
// Add intervals for values which are live-out.
for (final instrId in liveness.liveOut(block).elements) {
final instr = graph.instructions[instrId] as Definition;
if (instr is! Constant) {
if (hasLiveRange(instr)) {
final liveRange = liveRangeFor(instr);
liveRange.addInterval(blockStart, blockEnd);
}
@@ -241,7 +243,7 @@ final class LinearScanRegisterAllocator extends RegisterAllocator {
_processTemp(pos, constr.temps[i], operandId);
}
// Process output.
if (instr is Definition && instr is! Constant) {
if (instr is Definition && hasLiveRange(instr)) {
final operandId = OperandId.result(instr.id);
final liveRange = liveRangeFor(instr);
final resultConstr = constr.result;
@@ -262,7 +264,7 @@ final class LinearScanRegisterAllocator extends RegisterAllocator {
errorContext.annotator = (Instruction instr) {
if (instr is ParallelMove) return null;
return '[${instructionPos(instr)}]' +
((instr is Definition && instr is! Constant)
((instr is Definition && hasLiveRange(instr))
? ' ${liveRangeFor(instr)}'
: '');
};
@@ -119,6 +119,7 @@ class CompilationSet {
functionRegistry,
recognizedMethods,
enableAsserts: config.enableAsserts,
typeParametersStyle: .separateFunctionAndClassTypeParameters,
).buildFlowGraph();
} catch (_) {
print('Compiler crashed while compiling $function');
+1 -1
View File
@@ -132,7 +132,7 @@ final class DevelopmentCompilerConfiguration extends Configuration {
ValueNumbering(simplification: Simplification()),
ConstantPropagation(),
ControlFlowOptimizations(),
Lowering(functionRegistry),
Lowering(functionRegistry, objectLayout),
ReorderBlocks(backEndState),
LinearScanRegisterAllocator(backEndState, constraints),
RegisterAllocationChecker(backEndState, constraints),
+32 -1
View File
@@ -11,6 +11,7 @@ import 'package:cfg/ir/visitor.dart';
import 'package:cfg/passes/pass.dart';
import 'package:cfg/utils/misc.dart';
import 'package:kernel/ast.dart' as ast;
import 'package:native_compiler/runtime/object_layout.dart';
/// IR lowering for native back-end.
///
@@ -21,8 +22,9 @@ import 'package:kernel/ast.dart' as ast;
/// TODO: insert boxing/unboxing
final class Lowering extends Pass with DefaultInstructionVisitor<void> {
final FunctionRegistry functionRegistry;
final ObjectLayout objectLayout;
Lowering(this.functionRegistry) : super('Lowering');
Lowering(this.functionRegistry, this.objectLayout) : super('Lowering');
late final CFunction _growableListLiteral = functionRegistry.getFunction(
GlobalContext.instance.coreTypes.index.getProcedure(
@@ -139,6 +141,35 @@ final class Lowering extends Pass with DefaultInstructionVisitor<void> {
}
}
@override
void visitTypeParameters(TypeParameters instr) {
switch (instr.kind) {
case .classTypeParameters:
final receiver = instr.inputDefAt(0);
final receiverClass =
(receiver.type.dartType as ast.InterfaceType).classNode;
final typeArgsField = objectLayout.getTypeArgumentsField(
receiverClass,
)!;
for (final use in instr.inputUses) {
final user = use.getInstruction(graph);
final load = LoadInstanceField(
graph,
user.sourcePosition,
typeArgsField,
receiver,
);
load.insertBefore(user);
user.replaceInputAt(user.getInputIndex(use), load);
}
case .functionTypeParameters:
final replacement = instr.inputDefAt(0);
instr.replaceUsesWith(replacement);
break;
}
instr.removeFromGraph();
}
@override
void visitAllocateListLiteral(AllocateListLiteral instr) {
// List literals up to 8 elements are lowered in the front-end
+10 -1
View File
@@ -34,6 +34,8 @@ import 'package:native_compiler/back_end/regalloc_checker.dart';
import 'package:native_compiler/back_end/register_allocator.dart';
import 'package:native_compiler/passes/lowering.dart';
import 'package:native_compiler/passes/reorder_blocks.dart';
import 'package:native_compiler/runtime/object_layout.dart';
import 'package:native_compiler/runtime/vm_defs.dart';
import 'package:test/test.dart';
import 'package:vm/modular/target/vm.dart';
@@ -151,16 +153,23 @@ class CompileAndDumpIr extends RecursiveVisitor {
functionRegistry,
recognizedMethods,
enableAsserts: true,
typeParametersStyle: .separateFunctionAndClassTypeParameters,
).buildFlowGraph();
final backEndState = BackEndState();
final constraints = Arm64Constraints();
final vmOffsets = Arm64VMOffsets();
final objectLayout = ObjectLayout(
vmOffsets,
wordSize: 8,
compressedWordSize: 8,
);
backEndState.stackFrame = Arm64StackFrame(function);
final pipeline = Pipeline([
SSAComputation(),
ValueNumbering(simplification: Simplification()),
ConstantPropagation(),
ControlFlowOptimizations(),
Lowering(functionRegistry),
Lowering(functionRegistry, objectLayout),
ReorderBlocks(backEndState),
LinearScanRegisterAllocator(backEndState, constraints),
RegisterAllocationChecker(backEndState, constraints),
@@ -70,4 +70,12 @@ void stringInterpolation(int x, String s, Object o) {
print('Hey, x=$x, s=$s, o=$o');
}
class C<T> {
void typeParameters<U>(Object x) {
print(x is List<T>);
print(x is List<U>);
print(x as Map<T, U>);
}
}
void main() {}
@@ -1,3 +1,35 @@
--- C.
B0 = EntryBlock()
v6 = Constant(null)
v1 = Parameter(this) # RA: param[0] <- ()
ParallelMove output(param[0] -> vloc:R0)
DirectCall Object.(v1) # RA: R0 <- (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(v6) # RA: (R0)
--- C.typeParameters
B0 = EntryBlock()
v9 = Constant(null)
v1 = Parameter(#functionTypeParameters) # RA: param[0] <- ()
ParallelMove output(param[0] -> vloc:R0)
v2 = Parameter(this) # RA: param[1] <- ()
ParallelMove output(param[1] -> vloc:R1)
v3 = Parameter(x) # RA: param[2] <- ()
ParallelMove output(param[2] -> vloc:R2)
v20 = LoadInstanceField(C.#typeArguments, v2) # RA: R3 <- (R1)
v10 = TypeTest(v3, v9, v20, List<C.T%>) # RA: R3 <- (R2, -, R3)
DirectCall print(v10) # RA: R0 <- (R3) 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 split(param[0] -> R1, param[2] -> R2)
v13 = TypeTest(v3, v1, v9, List<C.typeParameters.U%>) # RA: R0 <- (R2, R1, -)
DirectCall print(v13) # RA: R0 <- (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 split(param[1] -> R1)
v19 = LoadInstanceField(C.#typeArguments, v2) # RA: R0 <- (R1)
ParallelMove split(param[0] -> R1, param[2] -> R2)
v16 = TypeCast(v3, v1, v19, Map<C.T%, C.typeParameters.U%>) # RA: R0 <- (R2, R1, R0)
DirectCall print(v16) # RA: R0 <- (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)
--- test1
B0 = EntryBlock()
Constant(0)
@@ -145,155 +177,155 @@ B43 = TargetBlock() idom:B33
--- listLiterals
B0 = EntryBlock()
v3 = Constant(<dynamic>)
v4 = Constant(0)
v10 = Constant(<int>)
v11 = Constant(1)
v12 = Constant(2)
v13 = Constant(3)
v16 = Constant(4)
v17 = Constant(5)
v18 = Constant(6)
v19 = Constant(7)
v20 = Constant(8)
v21 = Constant(9)
v36 = Constant(null)
v1 = Parameter(x) # RA: param[0] <- ()
v5 = Constant(<dynamic>)
v6 = Constant(0)
v9 = Constant(null)
v13 = Constant(<int>)
v14 = Constant(1)
v15 = Constant(2)
v16 = Constant(3)
v19 = Constant(4)
v20 = Constant(5)
v21 = Constant(6)
v22 = Constant(7)
v23 = Constant(8)
v24 = Constant(9)
v1 = Parameter(#functionTypeParameters) # RA: param[0] <- ()
ParallelMove output(param[0] -> vloc:R0)
v2 = TypeParameters() # RA: R1 <- ()
ParallelMove spill(R1 -> stack[0])
v5 = DirectCall _GrowableList.(v3, v4) # RA: 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]
v2 = Parameter(x) # RA: param[1] <- ()
ParallelMove output(param[1] -> vloc:R1)
v7 = DirectCall _GrowableList.(v5, v6) # RA: 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)
DirectCall print(v5) # RA: R0 <- (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 split(stack[0] -> R1)
v7 = TypeArguments(v2, <listLiterals.T%>) # RA: R0 <- (R1)
ParallelMove spill(R0 -> stack[1])
v8 = DirectCall _GrowableList.(v7, v4) # RA: R0 <- (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]
DirectCall print(v7) # RA: R0 <- (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 split(param[0] -> R1)
v10 = TypeArguments(v1, v9, <listLiterals.T%>) # RA: R0 <- (R1, -)
ParallelMove spill(R0 -> stack[0])
v11 = DirectCall _GrowableList.(v10, v6) # RA: R0 <- (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)
DirectCall print(v8) # RA: R0 <- (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]
v14 = DirectCall _GrowableList._literal3(v10, v11, v12, v13) # RA: 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]
DirectCall print(v11) # RA: R0 <- (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]
v17 = DirectCall _GrowableList._literal3(v13, v14, v15, v16) # RA: 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)
DirectCall print(v14) # RA: R0 <- (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]
DirectCall print(v17) # RA: R0 <- (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(IntConstant(9) -> vloc:R0)
v38 = AllocateList(v21) # RA: R0 <- (R0)
v40 = AllocateList(v24) # RA: R0 <- (R0)
ParallelMove input(IntConstant(0) -> vloc:R2, IntConstant(1) -> vloc:R1)
SetListElement(v38, v4, v11) # RA: (R0, R2, R1)
SetListElement(v40, v6, v14) # RA: (R0, R2, R1)
ParallelMove input(IntConstant(1) -> vloc:R2, IntConstant(2) -> vloc:R1)
SetListElement(v38, v11, v12) # RA: (R0, R2, R1)
SetListElement(v40, v14, v15) # RA: (R0, R2, R1)
ParallelMove input(IntConstant(2) -> vloc:R2, IntConstant(3) -> vloc:R1)
SetListElement(v38, v12, v13) # RA: (R0, R2, R1)
SetListElement(v40, v15, v16) # RA: (R0, R2, R1)
ParallelMove input(IntConstant(3) -> vloc:R2, IntConstant(4) -> vloc:R1)
SetListElement(v38, v13, v16) # RA: (R0, R2, R1)
SetListElement(v40, v16, v19) # RA: (R0, R2, R1)
ParallelMove input(IntConstant(4) -> vloc:R1, IntConstant(5) -> vloc:R2)
SetListElement(v38, v16, v17) # RA: (R0, R1, R2)
SetListElement(v40, v19, v20) # RA: (R0, R1, R2)
ParallelMove input(IntConstant(5) -> vloc:R1, IntConstant(6) -> vloc:R2)
SetListElement(v38, v17, v18) # RA: (R0, R1, R2)
SetListElement(v40, v20, v21) # RA: (R0, R1, R2)
ParallelMove input(IntConstant(6) -> vloc:R1, IntConstant(7) -> vloc:R2)
SetListElement(v38, v18, v19) # RA: (R0, R1, R2)
SetListElement(v40, v21, v22) # RA: (R0, R1, R2)
ParallelMove input(IntConstant(7) -> vloc:R2, IntConstant(8) -> vloc:R1)
SetListElement(v38, v19, v20) # RA: (R0, R2, R1)
SetListElement(v40, v22, v23) # RA: (R0, R2, R1)
ParallelMove input(IntConstant(8) -> vloc:R1, IntConstant(9) -> vloc:R2)
SetListElement(v38, v20, v21) # RA: (R0, R1, R2)
v48 = DirectCall _GrowableList._literal(v10, v38) # RA: R0 <- (-, 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]
SetListElement(v40, v23, v24) # RA: (R0, R1, R2)
v50 = DirectCall _GrowableList._literal(v13, v40) # RA: R0 <- (-, 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)
DirectCall print(v48) # RA: R0 <- (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]
DirectCall print(v50) # RA: R0 <- (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(IntConstant(9) -> vloc:R0)
v49 = AllocateList(v21) # RA: R0 <- (R0)
ParallelMove split(param[0] -> R2)
v51 = AllocateList(v24) # RA: R0 <- (R0)
ParallelMove split(param[1] -> R2)
ParallelMove input(IntConstant(0) -> vloc:R1)
SetListElement(v49, v4, v1) # RA: (R0, R1, R2)
SetListElement(v51, v6, v2) # RA: (R0, R1, R2)
ParallelMove input(IntConstant(1) -> vloc:R1)
SetListElement(v49, v11, v1) # RA: (R0, R1, R2)
SetListElement(v51, v14, v2) # RA: (R0, R1, R2)
ParallelMove input(IntConstant(2) -> vloc:R1)
SetListElement(v49, v12, v1) # RA: (R0, R1, R2)
SetListElement(v51, v15, v2) # RA: (R0, R1, R2)
ParallelMove input(IntConstant(3) -> vloc:R1)
SetListElement(v49, v13, v1) # RA: (R0, R1, R2)
SetListElement(v51, v16, v2) # RA: (R0, R1, R2)
ParallelMove input(IntConstant(4) -> vloc:R1)
SetListElement(v49, v16, v1) # RA: (R0, R1, R2)
SetListElement(v51, v19, v2) # RA: (R0, R1, R2)
ParallelMove input(IntConstant(5) -> vloc:R1)
SetListElement(v49, v17, v1) # RA: (R0, R1, R2)
SetListElement(v51, v20, v2) # RA: (R0, R1, R2)
ParallelMove input(IntConstant(6) -> vloc:R1)
SetListElement(v49, v18, v1) # RA: (R0, R1, R2)
SetListElement(v51, v21, v2) # RA: (R0, R1, R2)
ParallelMove input(IntConstant(7) -> vloc:R1)
SetListElement(v49, v19, v1) # RA: (R0, R1, R2)
SetListElement(v51, v22, v2) # RA: (R0, R1, R2)
ParallelMove input(IntConstant(8) -> vloc:R1)
SetListElement(v49, v20, v1) # RA: (R0, R1, R2)
ParallelMove split(stack[1] -> R1)
v59 = DirectCall _GrowableList._literal(v7, v49) # 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]
SetListElement(v51, v23, v2) # RA: (R0, R1, R2)
ParallelMove split(stack[0] -> R1)
v61 = DirectCall _GrowableList._literal(v10, v51) # 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)
DirectCall print(v59) # RA: R0 <- (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]
DirectCall print(v61) # RA: R0 <- (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(v36) # RA: (R0)
Return(v9) # RA: (R0)
--- mapLiterals
B0 = EntryBlock()
v6 = Constant(<dynamic, dynamic>)
v12 = Constant(<String, String>)
v13 = Constant("a")
v14 = Constant("aa")
v15 = Constant("b")
v16 = Constant("bb")
v27 = Constant(null)
v29 = Constant(InstanceConstant(const List<dynamic>{}))
v32 = Constant(4)
v34 = Constant(0)
v36 = Constant(1)
v38 = Constant(2)
v40 = Constant(3)
v1 = Parameter(key) # RA: param[0] <- ()
v8 = Constant(<dynamic, dynamic>)
v11 = Constant(null)
v15 = Constant(<String, String>)
v16 = Constant("a")
v17 = Constant("aa")
v18 = Constant("b")
v19 = Constant("bb")
v31 = Constant(InstanceConstant(const List<dynamic>{}))
v34 = Constant(4)
v36 = Constant(0)
v38 = Constant(1)
v40 = Constant(2)
v42 = Constant(3)
v1 = Parameter(#functionTypeParameters) # RA: param[0] <- ()
ParallelMove output(param[0] -> vloc:R0)
v2 = Parameter(value) # RA: param[1] <- ()
v2 = Parameter(key) # RA: param[1] <- ()
ParallelMove output(param[1] -> vloc:R1)
v3 = Parameter(key2) # RA: param[2] <- ()
v3 = Parameter(value) # RA: param[2] <- ()
ParallelMove output(param[2] -> vloc:R2)
v4 = Parameter(value2) # RA: param[3] <- ()
v4 = Parameter(key2) # RA: param[3] <- ()
ParallelMove output(param[3] -> vloc:R3)
v5 = TypeParameters() # RA: R4 <- ()
ParallelMove spill(R4 -> stack[0])
v30 = DirectCall Map._fromLiteral(v6, v29) # RA: 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]
v5 = Parameter(value2) # RA: param[4] <- ()
ParallelMove output(param[4] -> vloc:R4)
v32 = DirectCall Map._fromLiteral(v8, v31) # RA: 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)
DirectCall print(v30) # RA: R0 <- (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 split(stack[0] -> R1)
v9 = TypeArguments(v5, <mapLiterals.S%, mapLiterals.T%>) # RA: R0 <- (R1)
ParallelMove spill(R0 -> stack[1])
v31 = DirectCall Map._fromLiteral(v9, v29) # RA: R0 <- (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]
DirectCall print(v32) # RA: R0 <- (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 split(param[0] -> R1)
v12 = TypeArguments(v1, v11, <mapLiterals.S%, mapLiterals.T%>) # RA: R0 <- (R1, -)
ParallelMove spill(R0 -> stack[0])
v33 = DirectCall Map._fromLiteral(v12, v31) # RA: R0 <- (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)
DirectCall print(v31) # RA: R0 <- (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]
DirectCall print(v33) # RA: R0 <- (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(IntConstant(4) -> vloc:R1)
v33 = AllocateList(v32) # RA: R0 <- (R1)
v35 = AllocateList(v34) # RA: R0 <- (R1)
ParallelMove input(IntConstant(0) -> vloc:R1, StringConstant("a") -> vloc:R2)
SetListElement(v33, v34, v13) # RA: (R0, R1, R2)
SetListElement(v35, v36, v16) # RA: (R0, R1, R2)
ParallelMove input(IntConstant(1) -> vloc:R1, StringConstant("aa") -> vloc:R2)
SetListElement(v33, v36, v14) # RA: (R0, R1, R2)
SetListElement(v35, v38, v17) # RA: (R0, R1, R2)
ParallelMove input(IntConstant(2) -> vloc:R1, StringConstant("b") -> vloc:R2)
SetListElement(v33, v38, v15) # RA: (R0, R1, R2)
SetListElement(v35, v40, v18) # RA: (R0, R1, R2)
ParallelMove input(IntConstant(3) -> vloc:R1, StringConstant("bb") -> vloc:R2)
SetListElement(v33, v40, v16) # RA: (R0, R1, R2)
v42 = DirectCall Map._fromLiteral(v12, v33) # RA: R0 <- (-, 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]
SetListElement(v35, v42, v19) # RA: (R0, R1, R2)
v44 = DirectCall Map._fromLiteral(v15, v35) # RA: R0 <- (-, 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)
DirectCall print(v42) # RA: R0 <- (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 split(param[1] -> R1)
v22 = ClosureCall(v2) # RA: R0 <- (R1) 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]
DirectCall print(v44) # RA: R0 <- (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 split(param[2] -> R1)
v25 = ClosureCall(v3) # RA: R0 <- (R1) 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)
ParallelMove input(IntConstant(4) -> vloc:R1)
v43 = AllocateList(v32) # RA: R1 <- (R1)
ParallelMove split(param[0] -> R3)
v45 = AllocateList(v34) # RA: R1 <- (R1)
ParallelMove split(param[1] -> R3)
ParallelMove input(IntConstant(0) -> vloc:R2)
SetListElement(v43, v34, v1) # RA: (R1, R2, R3)
SetListElement(v45, v36, v2) # RA: (R1, R2, R3)
ParallelMove input(IntConstant(1) -> vloc:R2)
SetListElement(v43, v36, v22) # RA: (R1, R2, R0)
ParallelMove split(param[2] -> R2)
ParallelMove input(IntConstant(2) -> vloc:R0)
SetListElement(v43, v38, v3) # RA: (R1, R0, R2)
SetListElement(v45, v38, v25) # RA: (R1, R2, R0)
ParallelMove split(param[3] -> R2)
ParallelMove input(IntConstant(2) -> vloc:R0)
SetListElement(v45, v40, v4) # RA: (R1, R0, R2)
ParallelMove split(param[4] -> R2)
ParallelMove input(IntConstant(3) -> vloc:R0)
SetListElement(v43, v40, v4) # RA: (R1, R0, R2)
ParallelMove split(stack[1] -> R0)
v48 = DirectCall Map._fromLiteral(v9, v43) # RA: R0 <- (R0, R1) 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]
SetListElement(v45, v42, v5) # RA: (R1, R0, R2)
ParallelMove split(stack[0] -> R0)
v50 = DirectCall Map._fromLiteral(v12, v45) # RA: R0 <- (R0, R1) 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)
DirectCall print(v48) # RA: R0 <- (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]
DirectCall print(v50) # RA: R0 <- (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(v27) # RA: (R0)
Return(v11) # RA: (R0)
--- stringInterpolation
B0 = EntryBlock()