[vm] Specialize 'new List()' on kernel AST

In VM, 'new List' is equivalent to either 'new _GrowableList' or
'new _List' depending on the number of arguments.
This change does the transformation early (on kernel AST), in order
to have specialized representation in TFA and in bytecode.

Change-Id: I46f0db8cc19efb3a53fdbe971ac26bdd2736fbda
Reviewed-on: https://dart-review.googlesource.com/76283
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/front_end/testcases/expressions.dart.direct.transformed.expect b/pkg/front_end/testcases/expressions.dart.direct.transformed.expect
index 7fece02..04022a3 100644
--- a/pkg/front_end/testcases/expressions.dart.direct.transformed.expect
+++ b/pkg/front_end/testcases/expressions.dart.direct.transformed.expect
@@ -42,7 +42,7 @@
   core::print(let final dynamic #t3 = i in let final dynamic #t4 = i = #t3.+(1) in #t3);
   core::print(new core::Object::•());
   core::print(const core::Object::•());
-  core::print(core::List::•<core::String>(2).runtimeType);
+  core::print(core::_List::•<core::String>(2).runtimeType);
   self::foo(fisk: "Blorp gulp");
   function f() → dynamic {
     core::print("f was called");
diff --git a/pkg/front_end/testcases/expressions.dart.strong.transformed.expect b/pkg/front_end/testcases/expressions.dart.strong.transformed.expect
index c38365b..aa3ea16 100644
--- a/pkg/front_end/testcases/expressions.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/expressions.dart.strong.transformed.expect
@@ -48,7 +48,7 @@
   core::print(let final core::int #t3 = i in let final core::int #t4 = i = #t3.{core::num::+}(1) in #t3);
   core::print(new core::Object::•());
   core::print(const core::Object::•());
-  core::print(core::List::•<core::String>(2).{core::Object::runtimeType});
+  core::print(core::_List::•<core::String>(2).{core::Object::runtimeType});
   self::foo(fisk: "Blorp gulp");
   function f() → core::Null {
     core::print("f was called");
diff --git a/pkg/front_end/testcases/inference/downwards_inference_yield_yield_star.dart.direct.transformed.expect b/pkg/front_end/testcases/inference/downwards_inference_yield_yield_star.dart.direct.transformed.expect
index 5b85710..3c303a7 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_yield_yield_star.dart.direct.transformed.expect
+++ b/pkg/front_end/testcases/inference/downwards_inference_yield_yield_star.dart.direct.transformed.expect
@@ -64,7 +64,7 @@
         [yield] true;
       }
       {
-        :iterator.{core::_SyncIterator::_current} = core::List::•<dynamic>();
+        :iterator.{core::_SyncIterator::_current} = core::_GrowableList::•<dynamic>(0);
         [yield] true;
       }
       {
@@ -72,7 +72,7 @@
         [yield] true;
       }
       {
-        :iterator.{core::_SyncIterator::_yieldEachIterable} = core::List::•<dynamic>();
+        :iterator.{core::_SyncIterator::_yieldEachIterable} = core::_GrowableList::•<dynamic>(0);
         [yield] true;
       }
     }
diff --git a/pkg/front_end/testcases/rasta/issue_000070.dart.direct.transformed.expect b/pkg/front_end/testcases/rasta/issue_000070.dart.direct.transformed.expect
index 3ea2923b..935af28 100644
--- a/pkg/front_end/testcases/rasta/issue_000070.dart.direct.transformed.expect
+++ b/pkg/front_end/testcases/rasta/issue_000070.dart.direct.transformed.expect
@@ -6,7 +6,7 @@
 class A<N extends core::Object = dynamic, S extends core::Object = dynamic, U extends core::Object = dynamic> extends core::Object {
   final field core::List<self::A::U> field;
   constructor •(self::A::N n, self::A::S s) → void
-    : self::A::field = core::List::•<self::A::U>(), super core::Object::•() {
+    : self::A::field = core::_GrowableList::•<self::A::U>(0), super core::Object::•() {
     exp::Expect::isTrue(n is self::A::N);
     exp::Expect::isTrue(s is self::A::S);
   }
diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart
index 251be65..6dd942a 100644
--- a/pkg/vm/lib/target/vm.dart
+++ b/pkg/vm/lib/target/vm.dart
@@ -15,6 +15,8 @@
     show transformLibraries, transformProcedure;
 
 import '../transformations/call_site_annotator.dart' as callSiteAnnotator;
+import '../transformations/list_factory_specializer.dart'
+    as listFactorySpecializer;
 
 /// Specializes the kernel IR to the Dart VM.
 class VmTarget extends Target {
@@ -77,6 +79,8 @@
     transformAsync.transformLibraries(coreTypes, libraries, flags.syncAsync);
     logger?.call("Transformed async methods");
 
+    listFactorySpecializer.transformLibraries(libraries, coreTypes);
+
     callSiteAnnotator.transformLibraries(
         component, libraries, coreTypes, hierarchy);
     logger?.call("Annotated call sites");
diff --git a/pkg/vm/lib/transformations/list_factory_specializer.dart b/pkg/vm/lib/transformations/list_factory_specializer.dart
new file mode 100644
index 0000000..ca9df25
--- /dev/null
+++ b/pkg/vm/lib/transformations/list_factory_specializer.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library vm.transformations.list_factory_specializer;
+
+import 'package:kernel/ast.dart';
+import 'package:kernel/core_types.dart' show CoreTypes;
+
+/// Replaces new List() and new List(n) with VM-specific
+/// new _GrowableList(0) and new _List(n).
+void transformLibraries(List<Library> libraries, CoreTypes coreTypes) {
+  final transformer = new _ListFactorySpecializer(coreTypes);
+  libraries.forEach(transformer.visitLibrary);
+}
+
+class _ListFactorySpecializer extends Transformer {
+  final Procedure _listFactory;
+  final Procedure _growableListFactory;
+  final Procedure _fixedListFactory;
+
+  _ListFactorySpecializer(CoreTypes coreTypes)
+      : _listFactory = coreTypes.index.getMember('dart:core', 'List', ''),
+        _growableListFactory =
+            coreTypes.index.getMember('dart:core', '_GrowableList', ''),
+        _fixedListFactory =
+            coreTypes.index.getMember('dart:core', '_List', '') {
+    assert(_listFactory.isFactory);
+    assert(_growableListFactory.isFactory);
+    assert(_fixedListFactory.isFactory);
+  }
+
+  @override
+  visitStaticInvocation(StaticInvocation node) {
+    super.visitStaticInvocation(node);
+
+    if (node.target == _listFactory) {
+      if (node.arguments.positional.isEmpty) {
+        return new StaticInvocation(_growableListFactory,
+            new Arguments([new IntLiteral(0)], types: node.arguments.types))
+          ..parent = node.parent
+          ..fileOffset = node.fileOffset;
+      } else {
+        return new StaticInvocation(_fixedListFactory, node.arguments)
+          ..parent = node.parent
+          ..fileOffset = node.fileOffset;
+      }
+    }
+
+    return node;
+  }
+}
diff --git a/pkg/vm/testcases/bytecode/instance_creation.dart b/pkg/vm/testcases/bytecode/instance_creation.dart
index 05a745b..8bfa5d7 100644
--- a/pkg/vm/testcases/bytecode/instance_creation.dart
+++ b/pkg/vm/testcases/bytecode/instance_creation.dart
@@ -79,6 +79,10 @@
 
 class TestTypeArgReuse<P, Q> extends Base<P, Q> implements K<P, Q> {}
 
+foo6() => new List<String>();
+
+foo7(int n) => new List<int>(n);
+
 main() {
   foo1();
   foo2();
diff --git a/pkg/vm/testcases/bytecode/instance_creation.dart.expect b/pkg/vm/testcases/bytecode/instance_creation.dart.expect
index e0b0287..e0d510a 100644
--- a/pkg/vm/testcases/bytecode/instance_creation.dart.expect
+++ b/pkg/vm/testcases/bytecode/instance_creation.dart.expect
@@ -564,6 +564,44 @@
 Bytecode {
   Entry                0
   CheckStack
+  PushConstant         CP#0
+  PushInt              0
+  PushConstant         CP#2
+  IndirectStaticCall   2, CP#1
+  ReturnTOS
+  PushNull
+  ReturnTOS
+}
+ConstantPool {
+  [0] = TypeArgumentsForInstanceAllocation dart.core::_GrowableList [dart.core::String]
+  [1] = ArgDesc num-args 2, num-type-args 0, names []
+  [2] = StaticICData target 'dart.core::_GrowableList::', arg-desc CP#1
+}
+]static method foo6() → dynamic
+  return core::_GrowableList::•<core::String>(0);
+[@vm.bytecode=
+Bytecode {
+  Entry                0
+  CheckStack
+  PushConstant         CP#0
+  Push                 FP[-5]
+  PushConstant         CP#2
+  IndirectStaticCall   2, CP#1
+  ReturnTOS
+  PushNull
+  ReturnTOS
+}
+ConstantPool {
+  [0] = TypeArgumentsForInstanceAllocation dart.core::_List [dart.core::int]
+  [1] = ArgDesc num-args 2, num-type-args 0, names []
+  [2] = StaticICData target 'dart.core::_List::', arg-desc CP#1
+}
+]static method foo7(core::int n) → dynamic
+  return core::_List::•<core::int>(n);
+[@vm.bytecode=
+Bytecode {
+  Entry                0
+  CheckStack
   PushConstant         CP#1
   IndirectStaticCall   0, CP#0
   Drop1
diff --git a/runtime/vm/profiler_test.cc b/runtime/vm/profiler_test.cc
index f69e319..881cc0d 100644
--- a/runtime/vm/profiler_test.cc
+++ b/runtime/vm/profiler_test.cc
@@ -1006,8 +1006,6 @@
     EXPECT(walker.Down());
     EXPECT_STREQ("new _List", walker.CurrentName());
     EXPECT(walker.Down());
-    EXPECT_STREQ("new List", walker.CurrentName());
-    EXPECT(walker.Down());
     EXPECT_STREQ("foo", walker.CurrentName());
     EXPECT(!walker.Down());
   }