[vm/kernel/bytecode] Implement overlapping of type arguments in bytecode

Instantiator type arguments are sometimes reused instead of doing
instantiation at run time. VM has a bunch of assertions to verify
that this optimization happened. In case of bytecode pipeline, decision
to reuse instantiator type arguments is done while generating bytecode.

For this optimization to correctly happen, bytecode generator
should be able to construct instantiator type arguments the same way
as VM does at run time. Besides flattening of type arguments of
superclasses, VM shrinks them by overlapping type parameters with
type arguments of a superclass (if they match). This CL implements
such overlapping in bytecode generator.

Also, --overlap_type_arguments VM option is removed, as disabling
of type arguments overlapping would break bytecode pipeline.

Change-Id: Ib276c0e688e0e86b44278d88a5c0af09dd342798
Reviewed-on: https://dart-review.googlesource.com/70060
Reviewed-by: RĂ©gis Crelier <regis@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index 9d39980..7ff51d4 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -4,6 +4,8 @@
 
 library vm.bytecode.gen_bytecode;
 
+import 'dart:math' show min;
+
 import 'package:kernel/ast.dart' hide MapEntry;
 import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
 import 'package:kernel/core_types.dart' show CoreTypes;
@@ -419,21 +421,49 @@
     }
   }
 
+  bool _canReuseSuperclassTypeArguments(List<DartType> superTypeArgs,
+      List<TypeParameter> typeParameters, int overlap) {
+    for (int i = 0; i < overlap; ++i) {
+      final superTypeArg = superTypeArgs[superTypeArgs.length - overlap + i];
+      if (!(superTypeArg is TypeParameterType &&
+          superTypeArg.parameter == typeParameters[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+
   List<DartType> _flattenInstantiatorTypeArguments(
       Class instantiatedClass, List<DartType> typeArgs) {
-    assert(typeArgs.length == instantiatedClass.typeParameters.length);
+    final typeParameters = instantiatedClass.typeParameters;
+    assert(typeArgs.length == 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);
+      return typeArgs;
     }
-    flatTypeArgs.addAll(typeArgs);
+
+    final superTypeArgs = _flattenInstantiatorTypeArguments(
+        supertype.classNode, supertype.typeArguments);
+
+    // Shrink type arguments by reusing portion of superclass type arguments
+    // if there is an overlapping. This optimization should be consistent with
+    // VM in order to correctly reuse instantiator type arguments.
+    int overlap = min(superTypeArgs.length, typeArgs.length);
+    for (; overlap > 0; --overlap) {
+      if (_canReuseSuperclassTypeArguments(
+          superTypeArgs, typeParameters, overlap)) {
+        break;
+      }
+    }
+
+    final substitution = Substitution.fromPairs(typeParameters, typeArgs);
+
+    List<DartType> flatTypeArgs = <DartType>[];
+    flatTypeArgs
+        .addAll(superTypeArgs.map((t) => substitution.substituteType(t)));
+    flatTypeArgs.addAll(typeArgs.getRange(overlap, typeArgs.length));
+
     return flatTypeArgs;
   }
 
diff --git a/pkg/vm/testcases/bytecode/instance_creation.dart b/pkg/vm/testcases/bytecode/instance_creation.dart
index 85ea5d71..05a745b 100644
--- a/pkg/vm/testcases/bytecode/instance_creation.dart
+++ b/pkg/vm/testcases/bytecode/instance_creation.dart
@@ -73,6 +73,12 @@
   factory J() native "agent_J";
 }
 
+abstract class K<A, B> {
+  factory K() => new TestTypeArgReuse<A, B>();
+}
+
+class TestTypeArgReuse<P, Q> extends Base<P, Q> implements K<P, Q> {}
+
 main() {
   foo1();
   foo2();
diff --git a/pkg/vm/testcases/bytecode/instance_creation.dart.expect b/pkg/vm/testcases/bytecode/instance_creation.dart.expect
index b741c7e..be1eedc 100644
--- a/pkg/vm/testcases/bytecode/instance_creation.dart.expect
+++ b/pkg/vm/testcases/bytecode/instance_creation.dart.expect
@@ -400,6 +400,53 @@
 ]  @_in::ExternalName::•("agent_J")
   external static factory •() → self::J;
 }
+abstract class K<A extends core::Object = dynamic, B extends core::Object = dynamic> extends core::Object {
+[@vm.bytecode=
+Bytecode {
+  Entry                1
+  CheckStack
+  Push                 FP[-5]
+  PushConstant         CP#0
+  AllocateT
+  StoreLocal           r0
+  Push                 r0
+  PushConstant         CP#2
+  IndirectStaticCall   1, CP#1
+  Drop1
+  ReturnTOS
+  PushConstant         CP#3
+  ReturnTOS
+}
+ConstantPool {
+  [0] = Class #lib::TestTypeArgReuse
+  [1] = ArgDesc num-args 1, num-type-args 0, names []
+  [2] = StaticICData target '#lib::TestTypeArgReuse::', arg-desc CP#1
+  [3] = Null
+}
+]  static factory •<A extends core::Object = dynamic, B extends core::Object = dynamic>() → self::K<self::K::•::A, self::K::•::B>
+    return new self::TestTypeArgReuse::•<self::K::•::A, self::K::•::B>();
+}
+class TestTypeArgReuse<P extends core::Object = dynamic, Q extends core::Object = dynamic> extends self::Base<self::TestTypeArgReuse::P, self::TestTypeArgReuse::Q> implements self::K<self::TestTypeArgReuse::P, self::TestTypeArgReuse::Q> {
+[@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::Base::', arg-desc CP#0
+  [2] = Null
+}
+]  synthetic constructor •() → void
+    : super self::Base::•()
+    ;
+}
 [@vm.bytecode=
 Bytecode {
   Entry                1
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 14eb55d..0d14fc2 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -65,12 +65,6 @@
             "Huge method cutoff in unoptimized code size (in bytes).");
 DEFINE_FLAG(
     bool,
-    overlap_type_arguments,
-    true,
-    "When possible, partially or fully overlap the type arguments of a type "
-    "with the type arguments of its super type.");
-DEFINE_FLAG(
-    bool,
     show_internal_names,
     false,
     "Show names of internal classes (e.g. \"OneByteString\") in error messages "
@@ -2533,8 +2527,7 @@
   Isolate* isolate = thread->isolate();
   Zone* zone = thread->zone();
   const intptr_t num_type_params = NumTypeParameters();
-  if (!FLAG_overlap_type_arguments || (num_type_params == 0) ||
-      (super_type() == AbstractType::null()) ||
+  if ((num_type_params == 0) || (super_type() == AbstractType::null()) ||
       (super_type() == isolate->object_store()->object_type())) {
     set_num_own_type_arguments(num_type_params);
     return num_type_params;