diff --git a/pkg/cfg/lib/front_end/ast_to_ir.dart b/pkg/cfg/lib/front_end/ast_to_ir.dart index 1e05a9cab48..959d3dee19f 100644 --- a/pkg/cfg/lib/front_end/ast_to_ir.dart +++ b/pkg/cfg/lib/front_end/ast_to_ir.dart @@ -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? labeledStatements; Map? switchCases; Map>? 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 _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 types) { - if (typeParameters != null) { - for (final type in types) { - if (_hasTypeParameterReferences(type)) { - return typeParameters; - } - } + List _typeParametersForType(ast.DartType type) { + if (!_hasTypeParametersInScope) { + return const []; } - return null; + final visitor = _FindTypeParameters(); + type.accept(visitor); + return _referencedTypeParameters(visitor); + } + + List _typeParametersForTypes(List 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 _stackTraceVariables = {}; final List 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 - implements ast.DartTypeVisitor { - 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'; } diff --git a/pkg/cfg/lib/ir/flow_graph_builder.dart b/pkg/cfg/lib/ir/flow_graph_builder.dart index 6a4c35a974a..0673237ec00 100644 --- a/pkg/cfg/lib/ir/flow_graph_builder.dart +++ b/pkg/cfg/lib/ir/flow_graph_builder.dart @@ -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 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 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 types, { - Definition? typeParameters, + List 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 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. diff --git a/pkg/cfg/lib/ir/flow_graph_checker.dart b/pkg/cfg/lib/ir/flow_graph_checker.dart index 193735bbbb5..efcf70158ec 100644 --- a/pkg/cfg/lib/ir/flow_graph_checker.dart +++ b/pkg/cfg/lib/ir/flow_graph_checker.dart @@ -316,19 +316,14 @@ final class FlowGraphChecker extends Pass implements InstructionVisitor { @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'; } diff --git a/pkg/cfg/lib/ir/functions.dart b/pkg/cfg/lib/ir/functions.dart index ba02a03bb6d..2d782b88144 100644 --- a/pkg/cfg/lib/ir/functions.dart +++ b/pkg/cfg/lib/ir/functions.dart @@ -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; diff --git a/pkg/cfg/lib/ir/instructions.dart b/pkg/cfg/lib/ir/instructions.dart index 43cccdeee12..01f255d07d3 100644 --- a/pkg/cfg/lib/ir/instructions.dart +++ b/pkg/cfg/lib/ir/instructions.dart @@ -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(InstructionVisitor 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 => diff --git a/pkg/cfg/lib/ir/use_lists.dart b/pkg/cfg/lib/ir/use_lists.dart index 407528085fd..2ffedc885f4 100644 --- a/pkg/cfg/lib/ir/use_lists.dart +++ b/pkg/cfg/lib/ir/use_lists.dart @@ -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))); diff --git a/pkg/cfg/lib/utils/arena.dart b/pkg/cfg/lib/utils/arena.dart index 8b117de57ff..a424f5efb31 100644 --- a/pkg/cfg/lib/utils/arena.dart +++ b/pkg/cfg/lib/utils/arena.dart @@ -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; } diff --git a/pkg/cfg/test/ir_test.dart b/pkg/cfg/test/ir_test.dart index 998935c34b3..9ae015b8dcf 100644 --- a/pkg/cfg/test/ir_test.dart +++ b/pkg/cfg/test/ir_test.dart @@ -144,6 +144,7 @@ class CompileAndDumpIr extends RecursiveVisitor { functionRegistry, recognizedMethods, enableAsserts: true, + typeParametersStyle: .separateFunctionAndClassTypeParameters, ).buildFlowGraph(); final pipeline = Pipeline([ SSAComputation(), diff --git a/pkg/cfg/testcases/expressions.dart.expect b/pkg/cfg/testcases/expressions.dart.expect index c824a72c31d..b8fb2310693 100644 --- a/pkg/cfg/testcases/expressions.dart.expect +++ b/pkg/cfg/testcases/expressions.dart.expect @@ -1,58 +1,60 @@ --- listLiterals B0 = EntryBlock() - v3 = Constant() - v4 = Constant(0) - v10 = Constant() - 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, ) - 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() + v6 = Constant(0) + v9 = Constant(null) + v13 = Constant() + 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, ) + 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() - v12 = Constant() - 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, ) - 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() + v11 = Constant(null) + v15 = Constant() + 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, ) + 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() diff --git a/pkg/cfg/testcases/types.dart.expect b/pkg/cfg/testcases/types.dart.expect index 53b2f216ba2..28e071db09f 100644 --- a/pkg/cfg/testcases/types.dart.expect +++ b/pkg/cfg/testcases/types.dart.expect @@ -10,52 +10,58 @@ B0 = EntryBlock() B0 = EntryBlock() v4 = Constant() 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, >) - InterfaceCall A.foo(v8, v1, v10) - Return(v12) + v9 = TypeArguments(v8, v3, >) + 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) - Branch(v6, true: B7, false: B8) -B7 = TargetBlock() idom:B0 - v13 = TypeCast(v2, v4, List, unchecked) - TypeCast(v13, Map) - 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) + Branch(v10, true: B11, false: B12) +B11 = TargetBlock() idom:B0 + v17 = TypeCast(v3, v9, v7, List, unchecked) + TypeCast(v17, Map) + Goto(B14) +B12 = TargetBlock() idom:B0 + Goto(B14) +B14 = JoinBlock(B12, B11) idom:B0 + Return(v9) --- A.baz B0 = EntryBlock() - v4 = Constant(TypeLiteralConstant(List)) - v10 = Constant(null) - v1 = Parameter(this) - v3 = TypeParameters(v1) - DirectCall print(v4) - v6 = TypeLiteral(A.T%, v3) - DirectCall print(v6) - v8 = TypeLiteral(Map, v3) - DirectCall print(v8) - Return(v10) + v7 = Constant(TypeLiteralConstant(List)) + 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, v4, v6) + DirectCall print(v12) + Return(v9) --- A. B0 = EntryBlock() - v1 = TypeParameters() - v2 = TypeArguments(v1, ) - v3 = AllocateObject A(v2) - DirectCall A._(v3) - Return(v3) + v4 = Constant(null) + v1 = Parameter(#functionTypeParameters) + v3 = TypeParameters(v1) + v5 = TypeArguments(v3, v4, ) + v6 = AllocateObject A(v5) + DirectCall A._(v6) + Return(v6) --- main B0 = EntryBlock() diff --git a/pkg/native_compiler/lib/back_end/arm64/code_generator.dart b/pkg/native_compiler/lib/back_end/arm64/code_generator.dart index e969cc0bdb3..034e6c62a1e 100644 --- a/pkg/native_compiler/lib/back_end/arm64/code_generator.dart +++ b/pkg/native_compiler/lib/back_end/arm64/code_generator.dart @@ -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'); diff --git a/pkg/native_compiler/lib/back_end/arm64/constraints.dart b/pkg/native_compiler/lib/back_end/arm64/constraints.dart index d1f339ba188..28eb72af2d7 100644 --- a/pkg/native_compiler/lib/back_end/arm64/constraints.dart +++ b/pkg/native_compiler/lib/back_end/arm64/constraints.dart @@ -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) => diff --git a/pkg/native_compiler/lib/back_end/code_generator.dart b/pkg/native_compiler/lib/back_end/code_generator.dart index 16c5768dc57..40e9172bb0b 100644 --- a/pkg/native_compiler/lib/back_end/code_generator.dart +++ b/pkg/native_compiler/lib/back_end/code_generator.dart @@ -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)'; diff --git a/pkg/native_compiler/lib/back_end/constraints.dart b/pkg/native_compiler/lib/back_end/constraints.dart index 30e837ea25a..17a43029d91 100644 --- a/pkg/native_compiler/lib/back_end/constraints.dart +++ b/pkg/native_compiler/lib/back_end/constraints.dart @@ -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)'; diff --git a/pkg/native_compiler/lib/back_end/register_allocator.dart b/pkg/native_compiler/lib/back_end/register_allocator.dart index afba59e9516..31a39d14ee7 100644 --- a/pkg/native_compiler/lib/back_end/register_allocator.dart +++ b/pkg/native_compiler/lib/back_end/register_allocator.dart @@ -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)}' : ''); }; diff --git a/pkg/native_compiler/lib/compilation_set.dart b/pkg/native_compiler/lib/compilation_set.dart index ef8b1387f49..e861ba858d0 100644 --- a/pkg/native_compiler/lib/compilation_set.dart +++ b/pkg/native_compiler/lib/compilation_set.dart @@ -119,6 +119,7 @@ class CompilationSet { functionRegistry, recognizedMethods, enableAsserts: config.enableAsserts, + typeParametersStyle: .separateFunctionAndClassTypeParameters, ).buildFlowGraph(); } catch (_) { print('Compiler crashed while compiling $function'); diff --git a/pkg/native_compiler/lib/configuration.dart b/pkg/native_compiler/lib/configuration.dart index 2660bc5a108..e27f2c26d1d 100644 --- a/pkg/native_compiler/lib/configuration.dart +++ b/pkg/native_compiler/lib/configuration.dart @@ -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), diff --git a/pkg/native_compiler/lib/passes/lowering.dart b/pkg/native_compiler/lib/passes/lowering.dart index 6a4e7b77edf..0dc9bfd92f4 100644 --- a/pkg/native_compiler/lib/passes/lowering.dart +++ b/pkg/native_compiler/lib/passes/lowering.dart @@ -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 { 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 { } } + @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 diff --git a/pkg/native_compiler/test/ir_test.dart b/pkg/native_compiler/test/ir_test.dart index 7de08c22db9..9a83d085ec0 100644 --- a/pkg/native_compiler/test/ir_test.dart +++ b/pkg/native_compiler/test/ir_test.dart @@ -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), diff --git a/pkg/native_compiler/testcases/lowering_test.dart b/pkg/native_compiler/testcases/lowering_test.dart index 7e7bc317904..f7e7e2fed39 100644 --- a/pkg/native_compiler/testcases/lowering_test.dart +++ b/pkg/native_compiler/testcases/lowering_test.dart @@ -70,4 +70,12 @@ void stringInterpolation(int x, String s, Object o) { print('Hey, x=$x, s=$s, o=$o'); } +class C { + void typeParameters(Object x) { + print(x is List); + print(x is List); + print(x as Map); + } +} + void main() {} diff --git a/pkg/native_compiler/testcases/lowering_test.dart.expect b/pkg/native_compiler/testcases/lowering_test.dart.expect index 6b714ed1edc..c2498d67ca2 100644 --- a/pkg/native_compiler/testcases/lowering_test.dart.expect +++ b/pkg/native_compiler/testcases/lowering_test.dart.expect @@ -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) # 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) # 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) # 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() - v4 = Constant(0) - v10 = Constant() - 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() + v6 = Constant(0) + v9 = Constant(null) + v13 = Constant() + 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, ) # 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, ) # 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() - v12 = Constant() - v13 = Constant("a") - v14 = Constant("aa") - v15 = Constant("b") - v16 = Constant("bb") - v27 = Constant(null) - v29 = Constant(InstanceConstant(const List{})) - v32 = Constant(4) - v34 = Constant(0) - v36 = Constant(1) - v38 = Constant(2) - v40 = Constant(3) - v1 = Parameter(key) # RA: param[0] <- () + v8 = Constant() + v11 = Constant(null) + v15 = Constant() + v16 = Constant("a") + v17 = Constant("aa") + v18 = Constant("b") + v19 = Constant("bb") + v31 = Constant(InstanceConstant(const List{})) + 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, ) # 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, ) # 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()