[vm/kernel/bytecode] Optimize instantiation of types and type arguments

This CL contains 2 optimizations:

* instantiator type arguments are reused if possible;

* instantiator and function type arguments are not loaded if they are
  not needed for a particular type being instantiated.

Change-Id: Id1f6e5385051044b3b9f78cef884a917e65e801d
Reviewed-on: https://dart-review.googlesource.com/58680
Reviewed-by: Zach Anderson <zra@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov
2018-06-06 22:03:54 +00:00
committed by commit-bot@chromium.org
parent f059df7cc5
commit e285bc52d7
6 changed files with 384 additions and 114 deletions
+107 -15
View File
@@ -11,6 +11,8 @@ import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/library_index.dart' show LibraryIndex;
import 'package:kernel/transformations/constants.dart'
show ConstantEvaluator, ConstantsBackend, EvaluationEnvironment;
import 'package:kernel/type_algebra.dart'
show Substitution, containsTypeVariable;
import 'package:kernel/type_environment.dart' show TypeEnvironment;
import 'package:kernel/vm/constants_native_effects.dart'
show VmConstantsBackend;
@@ -57,6 +59,9 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
Class enclosingClass;
Member enclosingMember;
Set<TypeParameter> classTypeParameters;
Set<TypeParameter> functionTypeParameters;
List<DartType> instantiatorTypeArguments;
LocalVariables locals;
ConstantEvaluator constantEvaluator;
Map<LabeledStatement, Label> labeledStatements;
@@ -285,31 +290,45 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
}
void _genTypeArguments(List<DartType> typeArgs, {Class instantiatingClass}) {
int typeArgsCPIndex = cp.add(new ConstantTypeArguments(typeArgs));
if (instantiatingClass != null) {
typeArgsCPIndex = cp.add(new ConstantTypeArgumentsForInstanceAllocation(
instantiatingClass, typeArgsCPIndex));
int typeArgsCPIndex() {
int cpIndex = cp.add(new ConstantTypeArguments(typeArgs));
if (instantiatingClass != null) {
cpIndex = cp.add(new ConstantTypeArgumentsForInstanceAllocation(
instantiatingClass, cpIndex));
}
return cpIndex;
}
if (typeArgs.isEmpty || !hasTypeParameters(typeArgs)) {
asm.emitPushConstant(typeArgsCPIndex);
asm.emitPushConstant(typeArgsCPIndex());
} else {
// TODO(alexmarkov): try to reuse instantiator type arguments
_genPushInstantiatorAndFunctionTypeArguments(typeArgs);
asm.emitInstantiateTypeArgumentsTOS(1, typeArgsCPIndex);
if (_canReuseInstantiatorTypeArguments(typeArgs, instantiatingClass)) {
_genPushInstantiatorTypeArguments();
} else {
_genPushInstantiatorAndFunctionTypeArguments(typeArgs);
asm.emitInstantiateTypeArgumentsTOS(1, typeArgsCPIndex());
}
}
}
void _genPushInstantiatorAndFunctionTypeArguments(List<DartType> types) {
// TODO(alexmarkov): do not load instantiator type arguments / function type
// arguments if they are not needed for these particular [types].
_genPushInstantiatorTypeArguments();
_genPushFunctionTypeArguments();
if (classTypeParameters != null &&
types.any((t) => containsTypeVariable(t, classTypeParameters))) {
assert(instantiatorTypeArguments != null);
_genPushInstantiatorTypeArguments();
} else {
_genPushNull();
}
if (functionTypeParameters != null &&
types.any((t) => containsTypeVariable(t, functionTypeParameters))) {
_genPushFunctionTypeArguments();
} else {
_genPushNull();
}
}
void _genPushInstantiatorTypeArguments() {
// TODO(alexmarkov): access to type arguments in factory constructors.
if ((enclosingMember.isInstanceMember || enclosingMember is Constructor) &&
hasInstantiatorTypeArguments(enclosingClass)) {
if (instantiatorTypeArguments != null) {
_genPushReceiver();
final int cpIndex =
cp.add(new ConstantTypeArgumentsFieldOffset(enclosingClass));
@@ -319,6 +338,48 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
}
}
List<DartType> _flattenInstantiatorTypeArguments(
Class instantiatedClass, List<DartType> typeArgs) {
assert(typeArgs.length == instantiatedClass.typeParameters.length);
List<DartType> flatTypeArgs;
final supertype = instantiatedClass.supertype;
if (supertype == null) {
flatTypeArgs = <DartType>[];
} else {
final substitution =
Substitution.fromPairs(instantiatedClass.typeParameters, typeArgs);
flatTypeArgs = _flattenInstantiatorTypeArguments(supertype.classNode,
substitution.substituteSupertype(supertype).typeArguments);
}
flatTypeArgs.addAll(typeArgs);
return flatTypeArgs;
}
bool _canReuseInstantiatorTypeArguments(
List<DartType> typeArgs, Class instantiatingClass) {
if (instantiatorTypeArguments == null) {
return false;
}
if (instantiatingClass != null) {
typeArgs =
_flattenInstantiatorTypeArguments(instantiatingClass, typeArgs);
}
if (typeArgs.length > instantiatorTypeArguments.length) {
return false;
}
for (int i = 0; i < typeArgs.length; ++i) {
if (typeArgs[i] != instantiatorTypeArguments[i]) {
return false;
}
}
return true;
}
void _genPushFunctionTypeArguments() {
if (locals.hasTypeArgsVar) {
asm.emitPush(locals.typeArgsVarIndexInFrame);
@@ -440,6 +501,24 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
void start(Member node) {
enclosingClass = node.enclosingClass;
enclosingMember = node;
if (enclosingMember.isInstanceMember || enclosingMember is Constructor) {
if (enclosingClass.typeParameters.isNotEmpty) {
classTypeParameters =
new Set<TypeParameter>.from(enclosingClass.typeParameters);
}
if (hasInstantiatorTypeArguments(enclosingClass)) {
final typeParameters = enclosingClass.typeParameters
.map((p) => new TypeParameterType(p))
.toList();
instantiatorTypeArguments =
_flattenInstantiatorTypeArguments(enclosingClass, typeParameters);
}
}
if (enclosingMember.function != null &&
enclosingMember.function.typeParameters.isNotEmpty) {
functionTypeParameters =
new Set<TypeParameter>.from(enclosingMember.function.typeParameters);
}
locals = new LocalVariables(node);
// TODO(alexmarkov): improve caching in ConstantEvaluator and reuse it
constantEvaluator = new ConstantEvaluator(constantsBackend, typeEnvironment,
@@ -496,6 +575,9 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
enclosingClass = null;
enclosingMember = null;
classTypeParameters = null;
functionTypeParameters = null;
instantiatorTypeArguments = null;
locals = null;
constantEvaluator = null;
labeledStatements = null;
@@ -610,6 +692,12 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
_pushAssemblerState();
locals.enterScope(node);
if (function.typeParameters.isNotEmpty) {
functionTypeParameters ??= new Set<TypeParameter>();
functionTypeParameters.addAll(function.typeParameters);
}
List<Label> savedYieldPoints = yieldPoints;
yieldPoints = locals.isSyncYieldingFrame ? <Label>[] : null;
@@ -644,6 +732,10 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
cp.add(new ConstantEndClosureFunctionScope());
if (function.typeParameters.isNotEmpty) {
functionTypeParameters.removeAll(function.typeParameters);
}
locals.leaveScope();
closures.add(new ClosureBytecode(
+55 -67
View File
@@ -228,7 +228,7 @@ Bytecode {
Push r2
InstanceCall1 1, CP#52
Drop1
PushConstant CP#36
PushConstant CP#18
ReturnTOS
}
ConstantPool {
@@ -250,30 +250,30 @@ ConstantPool {
[15] = Int 0
[16] = Type #lib::A::T1
[17] = TypeArgumentsFieldOffset #lib::A
[18] = Int 1
[19] = Type #lib::A::T2
[20] = Type #lib::A::foo::T3
[21] = Int 3
[22] = Type #lib::A::foo::T4
[23] = Type T5
[24] = Int 5
[25] = Type T6
[26] = Type T7
[27] = Int 7
[28] = Type T8
[29] = ArgDesc num-args 1, num-type-args 1, names []
[30] = StaticICData target 'dart.core::List::_fromLiteral', arg-desc CP#29
[31] = ArgDesc num-args 1, num-type-args 0, names []
[32] = StaticICData target 'dart.core::print', arg-desc CP#31
[33] = TypeArgs [#lib::A::T1, #lib::A::T2, #lib::A::foo::T3, #lib::A::foo::T4, T5, T6, T7, T8]
[34] = ArgDesc num-args 0, num-type-args 8, names []
[35] = StaticICData target '#lib::callWithArgs', arg-desc CP#34
[36] = Null
[18] = Null
[19] = Int 1
[20] = Type #lib::A::T2
[21] = Type #lib::A::foo::T3
[22] = Int 3
[23] = Type #lib::A::foo::T4
[24] = Type T5
[25] = Int 5
[26] = Type T6
[27] = Type T7
[28] = Int 7
[29] = Type T8
[30] = ArgDesc num-args 1, num-type-args 1, names []
[31] = StaticICData target 'dart.core::List::_fromLiteral', arg-desc CP#30
[32] = ArgDesc num-args 1, num-type-args 0, names []
[33] = StaticICData target 'dart.core::print', arg-desc CP#32
[34] = TypeArgs [#lib::A::T1, #lib::A::T2, #lib::A::foo::T3, #lib::A::foo::T4, T5, T6, T7, T8]
[35] = ArgDesc num-args 0, num-type-args 8, names []
[36] = StaticICData target '#lib::callWithArgs', arg-desc CP#35
[37] = EndClosureFunctionScope
[38] = Class dart.core::_Closure
[39] = FieldOffset dart.core::_Closure::_instantiator_type_arguments
[40] = FieldOffset dart.core::_Closure::_function
[41] = ICData target-name 'call', arg-desc CP#31
[41] = ICData target-name 'call', arg-desc CP#32
[42] = EndClosureFunctionScope
[43] = TypeArgs [#lib::C7, #lib::C8]
[44] = ArgDesc num-args 1, num-type-args 2, names []
@@ -306,79 +306,67 @@ Closure CP#12 {
Push r1
LoadFieldTOS CP#1
LoadFieldTOS CP#17
Push r0
PushConstant CP#18
InstantiateType CP#16
StoreIndexedTOS
Push r3
PushConstant CP#19
Push r1
LoadFieldTOS CP#1
LoadFieldTOS CP#17
PushConstant CP#18
Push r1
LoadFieldTOS CP#1
LoadFieldTOS CP#17
Push r0
InstantiateType CP#19
StoreIndexedTOS
Push r3
PushConstant CP#5
Push r1
LoadFieldTOS CP#1
LoadFieldTOS CP#17
Push r0
InstantiateType CP#20
StoreIndexedTOS
Push r3
PushConstant CP#21
Push r1
LoadFieldTOS CP#1
LoadFieldTOS CP#17
PushConstant CP#5
PushConstant CP#18
Push r0
InstantiateType CP#22
InstantiateType CP#21
StoreIndexedTOS
Push r3
PushConstant CP#6
Push r1
LoadFieldTOS CP#1
LoadFieldTOS CP#17
PushConstant CP#22
PushConstant CP#18
Push r0
InstantiateType CP#23
StoreIndexedTOS
Push r3
PushConstant CP#24
Push r1
LoadFieldTOS CP#1
LoadFieldTOS CP#17
PushConstant CP#6
PushConstant CP#18
Push r0
InstantiateType CP#25
InstantiateType CP#24
StoreIndexedTOS
Push r3
PushConstant CP#10
Push r1
LoadFieldTOS CP#1
LoadFieldTOS CP#17
PushConstant CP#25
PushConstant CP#18
Push r0
InstantiateType CP#26
StoreIndexedTOS
Push r3
PushConstant CP#27
Push r1
LoadFieldTOS CP#1
LoadFieldTOS CP#17
PushConstant CP#10
PushConstant CP#18
Push r0
InstantiateType CP#28
InstantiateType CP#27
StoreIndexedTOS
PushConstant CP#30
IndirectStaticCall 2, CP#29
PushConstant CP#32
IndirectStaticCall 1, CP#31
Push r3
PushConstant CP#28
PushConstant CP#18
Push r0
InstantiateType CP#29
StoreIndexedTOS
PushConstant CP#31
IndirectStaticCall 2, CP#30
PushConstant CP#33
IndirectStaticCall 1, CP#32
Drop1
Push r1
LoadFieldTOS CP#1
LoadFieldTOS CP#17
Push r0
InstantiateTypeArgumentsTOS 1, CP#33
PushConstant CP#35
IndirectStaticCall 1, CP#34
Drop1
InstantiateTypeArgumentsTOS 1, CP#34
PushConstant CP#36
IndirectStaticCall 1, CP#35
Drop1
PushConstant CP#18
ReturnTOS
}
@@ -417,7 +405,7 @@ Closure CP#9 {
Push r2
InstanceCall1 1, CP#41
Drop1
PushConstant CP#36
PushConstant CP#18
ReturnTOS
}
@@ -461,7 +449,7 @@ Closure CP#2 {
Push r2
InstanceCall1 1, CP#47
Drop1
PushConstant CP#36
PushConstant CP#18
ReturnTOS
}
@@ -38,6 +38,25 @@ void foo3<T>() {
new B<List<T>>();
}
class E<K, V> {
test_reuse1() => new Map<K, V>();
}
class F<K, V> extends E<String, List<V>> {
test_reuse2() => new Map<String, List<V>>();
}
class G<K, V> {
G();
factory G.test_factory() => new H<String, K, V>();
}
class H<P1, P2, P3> extends G<P2, P3> {}
void foo4() {
new G<int, List<String>>.test_factory();
}
main() {
foo1();
foo2();
@@ -185,6 +185,158 @@ ConstantPool {
core::print("C: ${s}");
}
}
class E<K extends core::Object = dynamic, V extends core::Object = dynamic> extends core::Object {
[@vm.bytecode=
Bytecode {
Entry 0
CheckStack
Push FP[-5]
PushConstant CP#1
IndirectStaticCall 1, CP#0
Drop1
PushConstant CP#2
ReturnTOS
}
ConstantPool {
[0] = ArgDesc num-args 1, num-type-args 0, names []
[1] = StaticICData target 'dart.core::Object::', arg-desc CP#0
[2] = Null
}
] synthetic constructor •() → void
: super core::Object::•()
;
[@vm.bytecode=
Bytecode {
Entry 0
CheckStack
Push FP[-5]
LoadFieldTOS CP#0
PushConstant CP#2
IndirectStaticCall 1, CP#1
ReturnTOS
PushConstant CP#3
ReturnTOS
}
ConstantPool {
[0] = TypeArgumentsFieldOffset #lib::E
[1] = ArgDesc num-args 0, num-type-args 2, names []
[2] = StaticICData target 'dart.core::Map::', arg-desc CP#1
[3] = Null
}
] method test_reuse1() → dynamic
return core::Map::•<self::E::K, self::E::V>();
}
class F<K extends core::Object = dynamic, V extends core::Object = dynamic> extends self::E<core::String, core::List<self::F::V>> {
[@vm.bytecode=
Bytecode {
Entry 0
CheckStack
Push FP[-5]
PushConstant CP#1
IndirectStaticCall 1, CP#0
Drop1
PushConstant CP#2
ReturnTOS
}
ConstantPool {
[0] = ArgDesc num-args 1, num-type-args 0, names []
[1] = StaticICData target '#lib::E::', arg-desc CP#0
[2] = Null
}
] synthetic constructor •() → void
: super self::E::•()
;
[@vm.bytecode=
Bytecode {
Entry 0
CheckStack
Push FP[-5]
LoadFieldTOS CP#0
PushConstant CP#2
IndirectStaticCall 1, CP#1
ReturnTOS
PushConstant CP#3
ReturnTOS
}
ConstantPool {
[0] = TypeArgumentsFieldOffset #lib::F
[1] = ArgDesc num-args 0, num-type-args 2, names []
[2] = StaticICData target 'dart.core::Map::', arg-desc CP#1
[3] = Null
}
] method test_reuse2() → dynamic
return core::Map::•<core::String, core::List<self::F::V>>();
}
class G<K extends core::Object = dynamic, V extends core::Object = dynamic> extends core::Object {
[@vm.bytecode=
Bytecode {
Entry 0
CheckStack
Push FP[-5]
PushConstant CP#1
IndirectStaticCall 1, CP#0
Drop1
PushConstant CP#2
ReturnTOS
}
ConstantPool {
[0] = ArgDesc num-args 1, num-type-args 0, names []
[1] = StaticICData target 'dart.core::Object::', arg-desc CP#0
[2] = Null
}
] constructor •() → void
: super core::Object::•()
;
[@vm.bytecode=
Bytecode {
Entry 1
CheckStack
PushConstant CP#1
Push FP[-5]
InstantiateTypeArgumentsTOS 1, CP#3
PushConstant CP#0
AllocateT
StoreLocal r0
Push r0
PushConstant CP#5
IndirectStaticCall 1, CP#4
Drop1
ReturnTOS
PushConstant CP#1
ReturnTOS
}
ConstantPool {
[0] = Class #lib::H
[1] = Null
[2] = TypeArgs [dart.core::String, #lib::G::test_factory::K, #lib::G::test_factory::V]
[3] = TypeArgumentsForInstanceAllocation #lib::H type-args CP#2
[4] = ArgDesc num-args 1, num-type-args 0, names []
[5] = StaticICData target '#lib::H::', arg-desc CP#4
}
] static factory test_factory<K extends core::Object = dynamic, V extends core::Object = dynamic>() → self::G<self::G::test_factory::K, self::G::test_factory::V>
return new self::H::•<core::String, self::G::test_factory::K, self::G::test_factory::V>();
}
class H<P1 extends core::Object = dynamic, P2 extends core::Object = dynamic, P3 extends core::Object = dynamic> extends self::G<self::H::P2, self::H::P3> {
[@vm.bytecode=
Bytecode {
Entry 0
CheckStack
Push FP[-5]
PushConstant CP#1
IndirectStaticCall 1, CP#0
Drop1
PushConstant CP#2
ReturnTOS
}
ConstantPool {
[0] = ArgDesc num-args 1, num-type-args 0, names []
[1] = StaticICData target '#lib::G::', arg-desc CP#0
[2] = Null
}
] synthetic constructor •() → void
: super self::G::•()
;
}
[@vm.bytecode=
Bytecode {
Entry 1
@@ -257,9 +409,9 @@ ConstantPool {
Bytecode {
Entry 1
CheckStack
PushConstant CP#3
PushConstant CP#1
Push FP[-5]
InstantiateTypeArgumentsTOS 1, CP#2
InstantiateTypeArgumentsTOS 1, CP#3
PushConstant CP#0
AllocateT
StoreLocal r0
@@ -268,14 +420,14 @@ Bytecode {
IndirectStaticCall 1, CP#4
Drop1
Drop1
PushConstant CP#3
PushConstant CP#1
ReturnTOS
}
ConstantPool {
[0] = Class #lib::B
[1] = TypeArgs [dart.core::List<#lib::foo3::T>]
[2] = TypeArgumentsForInstanceAllocation #lib::B type-args CP#1
[3] = Null
[1] = Null
[2] = TypeArgs [dart.core::List<#lib::foo3::T>]
[3] = TypeArgumentsForInstanceAllocation #lib::B type-args CP#2
[4] = ArgDesc num-args 1, num-type-args 0, names []
[5] = StaticICData target '#lib::B::', arg-desc CP#4
}
@@ -283,6 +435,26 @@ ConstantPool {
new self::B::•<core::List<self::foo3::T>>();
}
[@vm.bytecode=
Bytecode {
Entry 0
CheckStack
PushConstant CP#0
PushConstant CP#2
IndirectStaticCall 1, CP#1
Drop1
PushConstant CP#3
ReturnTOS
}
ConstantPool {
[0] = TypeArgs [dart.core::int, dart.core::List<dart.core::String>]
[1] = ArgDesc num-args 0, num-type-args 2, names []
[2] = StaticICData target '#lib::G::test_factory', arg-desc CP#1
[3] = Null
}
]static method foo4() → void {
self::G::test_factory<core::int, core::List<core::String>>();
}
[@vm.bytecode=
Bytecode {
Entry 0
CheckStack
@@ -551,16 +551,16 @@ Bytecode {
PushConstant CP#15
IndirectStaticCall 1, CP#9
Drop1
PushConstant CP#17
PushConstant CP#16
Push FP[-8]
InstantiateTypeArgumentsTOS 1, CP#16
InstantiateTypeArgumentsTOS 1, CP#17
PushConstant CP#18
PushConstant CP#19
IndirectStaticCall 2, CP#7
PushConstant CP#20
IndirectStaticCall 1, CP#9
Drop1
PushConstant CP#17
PushConstant CP#16
Push FP[-8]
InstantiateTypeArgumentsTOS 1, CP#21
PushConstant CP#1
@@ -580,7 +580,7 @@ Bytecode {
PushConstant CP#23
IndirectStaticCall 1, CP#9
Drop1
PushConstant CP#17
PushConstant CP#16
ReturnTOS
}
ConstantPool {
@@ -600,8 +600,8 @@ ConstantPool {
[13] = ICData target-name 'toString', arg-desc CP#9
[14] = StaticICData target 'dart.core::Map::_fromLiteral', arg-desc CP#7
[15] = StaticICData target 'dart.core::print', arg-desc CP#9
[16] = TypeArgs [dart.core::String, #lib::test_map_literal::T]
[17] = Null
[16] = Null
[17] = TypeArgs [dart.core::String, #lib::test_map_literal::T]
[18] = List type-arg dynamic, entries CP# []
[19] = StaticICData target 'dart.core::Map::_fromLiteral', arg-desc CP#7
[20] = StaticICData target 'dart.core::print', arg-desc CP#9
+19 -20
View File
@@ -159,8 +159,7 @@ Bytecode {
Entry 0
CheckStack
Push FP[-5]
Push FP[-6]
LoadFieldTOS CP#0
PushConstant CP#0
Push FP[-7]
PushConstant CP#1
InstanceCall1 4, CP#3
@@ -174,31 +173,31 @@ Bytecode {
L1:
Push FP[-5]
Push FP[-6]
LoadFieldTOS CP#0
LoadFieldTOS CP#8
Push FP[-7]
PushConstant CP#8
InstanceCall1 4, CP#9
PushConstant CP#9
InstanceCall1 4, CP#10
PushConstant CP#4
IfNeStrictTOS
Jump L2
PushConstant CP#10
PushConstant CP#11
PushConstant CP#12
IndirectStaticCall 1, CP#6
Drop1
L2:
Push FP[-5]
Push FP[-6]
LoadFieldTOS CP#0
LoadFieldTOS CP#8
Push FP[-7]
PushConstant CP#12
InstanceCall1 4, CP#13
InstanceCall1 1, CP#14
PushConstant CP#13
InstanceCall1 4, CP#14
InstanceCall1 1, CP#15
ReturnTOS
PushConstant CP#15
PushConstant CP#0
ReturnTOS
}
ConstantPool {
[0] = TypeArgumentsFieldOffset #lib::D
[0] = Null
[1] = Type #lib::A<#lib::D::foo3::T1>
[2] = ArgDesc num-args 4, num-type-args 0, names []
[3] = ICData target-name 'dart.core::_instanceOf', arg-desc CP#2
@@ -206,14 +205,14 @@ ConstantPool {
[5] = String '31'
[6] = ArgDesc num-args 1, num-type-args 0, names []
[7] = StaticICData target 'dart.core::print', arg-desc CP#6
[8] = Type #lib::C<dart.core::Map<#lib::D::foo3::T1, #lib::D::P>, dart.core::List<#lib::D::foo3::T2>, #lib::D::Q>
[9] = ICData target-name 'dart.core::_instanceOf', arg-desc CP#2
[10] = String '32'
[11] = StaticICData target 'dart.core::print', arg-desc CP#6
[12] = Type dart.core::Map<#lib::D::foo3::T2, #lib::D::Q>
[13] = ICData target-name 'dart.core::_as', arg-desc CP#2
[14] = ICData get target-name 'values', arg-desc CP#6
[15] = Null
[8] = TypeArgumentsFieldOffset #lib::D
[9] = Type #lib::C<dart.core::Map<#lib::D::foo3::T1, #lib::D::P>, dart.core::List<#lib::D::foo3::T2>, #lib::D::Q>
[10] = ICData target-name 'dart.core::_instanceOf', arg-desc CP#2
[11] = String '32'
[12] = StaticICData target 'dart.core::print', arg-desc CP#6
[13] = Type dart.core::Map<#lib::D::foo3::T2, #lib::D::Q>
[14] = ICData target-name 'dart.core::_as', arg-desc CP#2
[15] = ICData get target-name 'values', arg-desc CP#6
}
] method foo3<T1 extends core::Object = dynamic, T2 extends core::Object = dynamic>(dynamic z) → dynamic {
if(z is self::A<self::D::foo3::T1>) {