[vm] Re-land use of multiple entrypoints for closure calls.

Original patchset is in revision 1.

Change-Id: I29ab0dbc3711f99895fd4f04a49d4185463a1602
Cq-Include-Trybots: luci.dart.try:vm-kernel-win-release-x64-try,vm-kernel-optcounter-threshold-linux-release-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-release-simarm-try,vm-kernel-precomp-linux-release-simarm64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-linux-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/71241
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
diff --git a/pkg/vm/lib/transformations/call_site_annotator.dart b/pkg/vm/lib/transformations/call_site_annotator.dart
index dd87e57..3d80936 100644
--- a/pkg/vm/lib/transformations/call_site_annotator.dart
+++ b/pkg/vm/lib/transformations/call_site_annotator.dart
@@ -65,9 +65,12 @@
   }
 
   // TODO(vegorov) handle setters as well.
+  // TODO(34162): We don't need to save the type here, just whether or not it's
+  // a statically-checked call.
   static bool shouldAnnotate(MethodInvocation node) =>
-      node.interfaceTarget != null &&
-      hasGenericCovariantParameters(node.interfaceTarget);
+      (node.interfaceTarget != null &&
+          hasGenericCovariantParameters(node.interfaceTarget)) ||
+      node.name.name == "call";
 
   /// Return [true] if the given list of [VariableDeclaration] contains
   /// any annotated with generic-covariant-impl.
diff --git a/pkg/vm/testcases/bytecode/asserts.dart.expect b/pkg/vm/testcases/bytecode/asserts.dart.expect
index 3f5cf69..176424f 100644
--- a/pkg/vm/testcases/bytecode/asserts.dart.expect
+++ b/pkg/vm/testcases/bytecode/asserts.dart.expect
@@ -65,7 +65,7 @@
   [7] = Null
 }
 ]static method test2(() → core::bool condition, () → core::String message) → void {
-  assert(condition.call(), message.call());
+  assert([@vm.call-site-attributes.metadata=receiverType:() → dart.core::bool] condition.call(), [@vm.call-site-attributes.metadata=receiverType:() → dart.core::String] message.call());
 }
 [@vm.bytecode=
 Bytecode {
diff --git a/pkg/vm/testcases/bytecode/bootstrapping.dart.expect b/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
index 16c0efe..a20221a 100644
--- a/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
+++ b/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
@@ -365,7 +365,7 @@
 }
 ]  static get platformScript() → dynamic {
     if(self::VMLibraryHooks::_cachedScript.{core::Object::==}(null) && !self::VMLibraryHooks::_computeScriptUri.{core::Object::==}(null)) {
-      self::VMLibraryHooks::_cachedScript = self::VMLibraryHooks::_computeScriptUri.call();
+      self::VMLibraryHooks::_cachedScript = [@vm.call-site-attributes.metadata=receiverType:dynamic] self::VMLibraryHooks::_computeScriptUri.call();
     }
     return self::VMLibraryHooks::_cachedScript;
   }
diff --git a/pkg/vm/testcases/bytecode/closures.dart.expect b/pkg/vm/testcases/bytecode/closures.dart.expect
index 6912332..3347fd6 100644
--- a/pkg/vm/testcases/bytecode/closures.dart.expect
+++ b/pkg/vm/testcases/bytecode/closures.dart.expect
@@ -474,13 +474,13 @@
           core::print(<core::Type>[self::A::T1, self::A::T2, self::A::foo::T3, self::A::foo::T4, T5, T6, T7, T8]);
           self::callWithArgs<self::A::T1, self::A::T2, self::A::foo::T3, self::A::foo::T4, T5, T6, T7, T8>();
         };
-        nested3.call();
+        [@vm.call-site-attributes.metadata=receiverType:() → dart.core::Null] nested3.call();
       }
-      nested2.call<self::C7, self::C8>();
-      nested2.call<core::List<self::C7>, core::List<self::C8>>();
+      [@vm.call-site-attributes.metadata=receiverType:<T7 extends dart.core::Object = dynamic, T8 extends dart.core::Object = dynamic>() → void] nested2.call<self::C7, self::C8>();
+      [@vm.call-site-attributes.metadata=receiverType:<T7 extends dart.core::Object = dynamic, T8 extends dart.core::Object = dynamic>() → void] nested2.call<core::List<self::C7>, core::List<self::C8>>();
     }
-    nested1.call<self::C5, self::C6>();
-    nested1.call<core::List<self::C5>, core::List<self::C6>>();
+    [@vm.call-site-attributes.metadata=receiverType:<T5 extends dart.core::Object = dynamic, T6 extends dart.core::Object = dynamic>() → void] nested1.call<self::C5, self::C6>();
+    [@vm.call-site-attributes.metadata=receiverType:<T5 extends dart.core::Object = dynamic, T6 extends dart.core::Object = dynamic>() → void] nested1.call<core::List<self::C5>, core::List<self::C6>>();
   }
 }
 class B extends core::Object {
@@ -768,12 +768,12 @@
               z = x.{core::num::+}(2);
               w = this.{self::B::foo}.{core::num::+}(y);
             }
-            closure2.call();
+            [@vm.call-site-attributes.metadata=receiverType:() → void] closure2.call();
             core::print(w);
           }
         };
-        closure1.call(10);
-        closure1.call(11);
+        [@vm.call-site-attributes.metadata=receiverType:(dart.core::int) → dart.core::Null] closure1.call(10);
+        [@vm.call-site-attributes.metadata=receiverType:(dart.core::int) → dart.core::Null] closure1.call(11);
         core::print(y);
         core::print(z);
       }
@@ -784,7 +784,7 @@
       () → core::Null closure3 = () → core::Null {
         this.{self::B::foo} = x;
       };
-      closure3.call();
+      [@vm.call-site-attributes.metadata=receiverType:() → dart.core::Null] closure3.call();
     }
   }
 }
@@ -1118,7 +1118,7 @@
       () → core::Null inc = () → core::Null {
         i = i.{core::num::+}(1);
       };
-      inc.call();
+      [@vm.call-site-attributes.metadata=receiverType:() → dart.core::Null] inc.call();
       core::print(i);
     }
   }
@@ -1313,7 +1313,7 @@
   (core::int) → core::Null inc = (core::int y) → core::Null {
     x = x.{core::num::+}(y);
   };
-  inc.call(3);
+  [@vm.call-site-attributes.metadata=receiverType:(dart.core::int) → dart.core::Null] inc.call(3);
   return x;
 }
 [@vm.bytecode=
diff --git a/pkg/vm/testcases/bytecode/super_calls.dart.expect b/pkg/vm/testcases/bytecode/super_calls.dart.expect
index d0e8cf4..64c96cf 100644
--- a/pkg/vm/testcases/bytecode/super_calls.dart.expect
+++ b/pkg/vm/testcases/bytecode/super_calls.dart.expect
@@ -205,7 +205,7 @@
   [6] = Null
 }
 ]  method testSuperCallViaGetter() → dynamic
-    return super.{self::Base1::bar}.call<core::int>("param");
+    return [@vm.call-site-attributes.metadata=receiverType:dynamic] super.{self::Base1::bar}.call<core::int>("param");
 [@vm.bytecode=
 Bytecode {
   Entry                1
@@ -467,7 +467,7 @@
   [14] = Null
 }
 ]  method testSuperCallViaGetter() → dynamic
-    return super.{self::Base2::bar}.call<core::int>("param");
+    return [@vm.call-site-attributes.metadata=receiverType:dynamic] super.{self::Base2::bar}.call<core::int>("param");
 [@vm.bytecode=
 Bytecode {
   Entry                1
diff --git a/pkg/vm/testcases/bytecode/try_blocks.dart.expect b/pkg/vm/testcases/bytecode/try_blocks.dart.expect
index 696f42d..bd7e762 100644
--- a/pkg/vm/testcases/bytecode/try_blocks.dart.expect
+++ b/pkg/vm/testcases/bytecode/try_blocks.dart.expect
@@ -522,7 +522,7 @@
         y = 3;
       }
     }
-    foo.call();
+    [@vm.call-site-attributes.metadata=receiverType:() → void] foo.call();
     core::print(y);
   }
   on dynamic catch(final dynamic e, final core::StackTrace st) {
@@ -968,7 +968,7 @@
               core::print(x);
               core::print(y);
             }
-            foo.call();
+            [@vm.call-site-attributes.metadata=receiverType:() → void] foo.call();
             continue #L4;
           }
           finally {
@@ -1293,7 +1293,7 @@
   }
   finally {
     core::print(x);
-    y.call();
+    [@vm.call-site-attributes.metadata=receiverType:dynamic] y.call();
   }
 }
 [@vm.bytecode=
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/future_or.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/future_or.dart.expect
index f58857b..b75afef 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/future_or.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/future_or.dart.expect
@@ -35,7 +35,7 @@
   self::foo2_a4(a4);
 }
 static method getDynamic() → dynamic
-  return self::unknown.call();
+  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function] self::unknown.call();
 static method main(core::List<core::String> args) → dynamic {
   self::foo1([@vm.inferred-type.metadata=dart.async::_Future] asy::Future::value<self::B>(new self::B::•()), new self::B::•(), [@vm.inferred-type.metadata=dart.async::_Future] asy::Future::value<self::B>(new self::B::•()), new self::B::•());
   self::foo2(self::getDynamic() as{TypeError} asy::Future<self::A>, self::getDynamic() as{TypeError} self::A, self::getDynamic() as{TypeError} asy::FutureOr<self::A>, self::getDynamic() as{TypeError} asy::FutureOr<self::A>);
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_field_initializer.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_field_initializer.dart.expect
index 740d76d..a8d6be6 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_field_initializer.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_field_initializer.dart.expect
@@ -64,7 +64,7 @@
 [@vm.inferred-type.metadata=dart.core::Null?]static field core::Function unknown;
 static field core::Object field1 = [@vm.inferred-type.metadata=!] self::getValue();
 static method getDynamic() → dynamic
-  return self::unknown.call();
+  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function] self::unknown.call();
 static method getValue() → core::Object {
   self::A aa = self::getDynamic() as{TypeError} self::A;
   return [@vm.inferred-type.metadata=!] aa.{self::A::foo}();
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class1.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class1.dart.expect
index bee0c8e..9388a7d 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class1.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class1.dart.expect
@@ -33,7 +33,7 @@
 static method use2([@vm.inferred-type.metadata=#lib::Intermediate] self::Intermediate i, [@vm.inferred-type.metadata=#lib::B?] self::A aa) → dynamic
   return [@vm.direct-call.metadata=#lib::Intermediate::bar] [@vm.inferred-type.metadata=#lib::T1] i.{self::Intermediate::bar}(aa);
 static method getDynamic() → dynamic
-  return self::unknown.call();
+  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function] self::unknown.call();
 static method allocateB() → dynamic {
   new self::B::•();
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class2.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class2.dart.expect
index 2a40057..b6e35fd 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class2.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class2.dart.expect
@@ -59,7 +59,7 @@
 static method use3([@vm.inferred-type.metadata=#lib::Intermediate] self::Intermediate i, self::A aa) → dynamic
   return [@vm.direct-call.metadata=#lib::Intermediate::bar] [@vm.inferred-type.metadata=!] i.{self::Intermediate::bar}(aa);
 static method getDynamic() → dynamic
-  return self::unknown.call();
+  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function] self::unknown.call();
 static method allocateB() → dynamic {
   new self::B::•();
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_dynamic_target.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_dynamic_target.dart.expect
index 55d3d4e..3fd5f9f 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_dynamic_target.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_dynamic_target.dart.expect
@@ -49,7 +49,7 @@
 static method use_bazz(dynamic x) → dynamic
   return [@vm.inferred-type.metadata=#lib::T3] x.bazz();
 static method getDynamic() → dynamic
-  return self::unknown.call();
+  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function] self::unknown.call();
 static method allocateA() → dynamic {
   new self::A::•();
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_set_field.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_set_field.dart.expect
index 78b259d..10fdcc6 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_set_field.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_set_field.dart.expect
@@ -51,7 +51,7 @@
 static method use2([@vm.inferred-type.metadata=#lib::DeepCaller2] self::DeepCaller2 x, [@vm.inferred-type.metadata=#lib::A?] self::A aa) → dynamic
   return [@vm.direct-call.metadata=#lib::DeepCaller2::barL1] [@vm.inferred-type.metadata=!] x.{self::DeepCaller2::barL1}(aa);
 static method getDynamic() → dynamic
-  return self::unknown.call();
+  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function] self::unknown.call();
 static method setField2([@vm.inferred-type.metadata=#lib::A] self::A aa, [@vm.inferred-type.metadata=#lib::T2] dynamic value) → void {
   [@vm.direct-call.metadata=#lib::A::field2] aa.{self::A::field2} = value;
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
index 86a3d0f..64585aa 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
@@ -113,7 +113,7 @@
 [@vm.inferred-type.metadata=#lib::D?]static field self::A dd = new self::D::•();
 [@vm.inferred-type.metadata=dart.core::Null?]static field core::Function unknown;
 static method getDynamic() → dynamic
-  return self::unknown.call();
+  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function] self::unknown.call();
 static method main(core::List<core::String> args) → dynamic {
   core::print([@vm.direct-call.metadata=#lib::B::foo??] [@vm.inferred-type.metadata=#lib::T1] [@vm.inferred-type.metadata=#lib::B?] self::bb.{self::A::foo}());
   core::print([@vm.direct-call.metadata=#lib::B::bar??] [@vm.inferred-type.metadata=#lib::T1] [@vm.inferred-type.metadata=#lib::B?] self::bb.{self::A::bar});
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/param_types_before_strong_mode_checks.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/param_types_before_strong_mode_checks.dart.expect
index 3ea010c..eb9c442 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/param_types_before_strong_mode_checks.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/param_types_before_strong_mode_checks.dart.expect
@@ -49,9 +49,9 @@
   [@vm.direct-call.metadata=#lib::T2::foo??] t0.{self::T0::foo}();
 }
 static method getDynamic() → dynamic
-  return self::unknown.call();
+  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function] self::unknown.call();
 static method use(dynamic x) → dynamic
-  return self::unknown.call(x);
+  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function] self::unknown.call(x);
 static method main(core::List<core::String> args) → dynamic {
   self::func1(self::getDynamic() as{TypeError} self::T0);
   self::use(self::func2);
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
index 1ad7392..8ef426f 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
@@ -56,7 +56,7 @@
     : super self::B2Base::•()
     ;
   method doSuperCall() → void {
-    [@vm.direct-call.metadata=#lib::A2::call] [@vm.inferred-type.metadata=#lib::A2] super.{self::B2Base::aa2}.call(1, 2, 3, 4, 5, new self::T2::•());
+    [@vm.call-site-attributes.metadata=receiverType:dynamic] [@vm.direct-call.metadata=#lib::A2::call] [@vm.inferred-type.metadata=#lib::A2] super.{self::B2Base::aa2}.call(1, 2, 3, 4, 5, new self::T2::•());
   }
 }
 class T3 extends core::Object {
@@ -127,7 +127,7 @@
   exp::Expect::isTrue([@vm.inferred-type.metadata=dart.core::bool?] self::ok);
 }
 static method getDynamic3() → dynamic
-  return self::unknown3.call();
+  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function] self::unknown3.call();
 static method test3() → void {
   self::getDynamic3().aa3(1, 2, 3, 4, 5, 6, new self::T3::•());
   self::ok = false;
@@ -135,7 +135,7 @@
   exp::Expect::isTrue([@vm.inferred-type.metadata=dart.core::bool?] self::ok);
 }
 static method getDynamic4() → dynamic
-  return self::unknown4.call();
+  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function] self::unknown4.call();
 static method test4() → void {
   self::getDynamic4().aa4(1, 2, 3, 4, 5, 6, 7, new self::T4::•());
   self::ok = false;
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect
index 8492698..871430a 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect
@@ -25,5 +25,5 @@
   return new self::B::•();
 static method main(core::List<core::String> args) → dynamic {
   core::Function closure = () → self::B => new self::B::•();
-  new self::TearOffDynamicMethod::•(closure.call());
+  new self::TearOffDynamicMethod::•([@vm.call-site-attributes.metadata=receiverType:dart.core::Function] closure.call());
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
index 689ae8c..eed4b00 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
@@ -22,7 +22,7 @@
   method foo() → core::int
     return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] [@vm.inferred-type.metadata=int?] 3.{core::num::+}([@vm.direct-call.metadata=#lib::B::foo] [@vm.inferred-type.metadata=int?] [@vm.inferred-type.metadata=#lib::B] self::knownResult().foo() as{TypeError} core::num) as{TypeError} core::int;
   method doCall(dynamic x) → core::int
-    return x.call() as{TypeError} core::int;
+    return [@vm.call-site-attributes.metadata=receiverType:dynamic] x.call() as{TypeError} core::int;
 }
 class TearOffSuperMethod extends self::Base {
   synthetic constructor •() → void
diff --git a/runtime/tests/vm/dart/entrypoints/tearoff.dart b/runtime/tests/vm/dart/entrypoints/tearoff.dart
new file mode 100644
index 0000000..cdebd3a
--- /dev/null
+++ b/runtime/tests/vm/dart/entrypoints/tearoff.dart
@@ -0,0 +1,37 @@
+// 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.
+
+// Test that typed calls against tearoffs go into the unchecked entrypoint.
+
+import "package:expect/expect.dart";
+import "common.dart";
+
+class C<T> {
+  @NeverInline
+  @pragma("vm:testing.unsafe.trace-entrypoints-fn", validateTearoff)
+  void target1(T x, String y) {
+    Expect.notEquals(x, -1);
+    Expect.equals(y, "foo");
+  }
+}
+
+test(List<String> args) {
+  var f = (new C<int>()).target1;
+
+  // Warmup.
+  expectedEntryPoint = -1;
+  expectedTearoffEntryPoint = -1;
+  for (int i = 0; i < 100; ++i) {
+    f(i, "foo");
+  }
+
+  expectedEntryPoint = 0;
+  expectedTearoffEntryPoint = 1;
+  const int iterations = benchmarkMode ? 100000000 : 100;
+  for (int i = 0; i < iterations; ++i) {
+    f(i, "foo");
+  }
+
+  Expect.isTrue(validateRan);
+}
diff --git a/runtime/tests/vm/dart/entrypoints/tearoff_inline_test.dart b/runtime/tests/vm/dart/entrypoints/tearoff_inline_test.dart
new file mode 100644
index 0000000..843324a
--- /dev/null
+++ b/runtime/tests/vm/dart/entrypoints/tearoff_inline_test.dart
@@ -0,0 +1,9 @@
+// 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.
+
+// VMOptions=--enable-testing-pragmas --no-background-compilation --enable-inlining-annotations --optimization-counter-threshold=10 -Denable_inlining=true
+
+import "tearoff.dart";
+
+main(args) => test(args);
diff --git a/runtime/tests/vm/dart/entrypoints/tearoff_noinline_test.dart b/runtime/tests/vm/dart/entrypoints/tearoff_noinline_test.dart
new file mode 100644
index 0000000..aac3f75
--- /dev/null
+++ b/runtime/tests/vm/dart/entrypoints/tearoff_noinline_test.dart
@@ -0,0 +1,9 @@
+// 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.
+
+// VMOptions=--enable-testing-pragmas --no-background-compilation --enable-inlining-annotations --optimization-counter-threshold=10
+
+import "tearoff.dart";
+
+main(args) => test(args);
diff --git a/runtime/tests/vm/dart/entrypoints/tearoff_prologue.dart b/runtime/tests/vm/dart/entrypoints/tearoff_prologue.dart
new file mode 100644
index 0000000..8ce9916
--- /dev/null
+++ b/runtime/tests/vm/dart/entrypoints/tearoff_prologue.dart
@@ -0,0 +1,35 @@
+// No type checks are removed here, but we can skip the argument count check.
+
+import "package:expect/expect.dart";
+import "common.dart";
+
+class C<T> {
+  @NeverInline
+  @pragma("vm:testing.unsafe.trace-entrypoints-fn", validateTearoff)
+  void samir1(T x) {
+    if (x == -1) {
+      throw "oh no";
+    }
+  }
+}
+
+test(List<String> args) {
+  var c = new C<int>();
+  var f = c.samir1;
+
+  // Warmup.
+  expectedEntryPoint = -1;
+  expectedTearoffEntryPoint = -1;
+  for (int i = 0; i < 100; ++i) {
+    f(i);
+  }
+
+  expectedEntryPoint = 0;
+  expectedTearoffEntryPoint = 1;
+  int iterations = benchmarkMode ? 100000000 : 100;
+  for (int i = 0; i < iterations; ++i) {
+    f(i);
+  }
+
+  Expect.isTrue(validateRan);
+}
diff --git a/runtime/tests/vm/dart/entrypoints/tearoff_prologue_inline_test.dart b/runtime/tests/vm/dart/entrypoints/tearoff_prologue_inline_test.dart
new file mode 100644
index 0000000..3ccf462
--- /dev/null
+++ b/runtime/tests/vm/dart/entrypoints/tearoff_prologue_inline_test.dart
@@ -0,0 +1,5 @@
+// VMOptions=--enable-testing-pragmas --no-background-compilation --enable-inlining-annotations --optimization-counter-threshold=10 -Denable_inlining=true
+
+import "tearoff_prologue.dart";
+
+main(args) => test(args);
diff --git a/runtime/tests/vm/dart/entrypoints/tearoff_prologue_noinline_test.dart b/runtime/tests/vm/dart/entrypoints/tearoff_prologue_noinline_test.dart
new file mode 100644
index 0000000..4f1bbcd
--- /dev/null
+++ b/runtime/tests/vm/dart/entrypoints/tearoff_prologue_noinline_test.dart
@@ -0,0 +1,5 @@
+// VMOptions=--enable-testing-pragmas --no-background-compilation --enable-inlining-annotations --optimization-counter-threshold=10
+
+import "tearoff_prologue.dart";
+
+main(args) => test(args);
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index e4fc014..d767ec2 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -3193,12 +3193,15 @@
   ClosureCallInstr(Value* function,
                    ClosureCallNode* node,
                    PushArgumentsArray* arguments,
-                   intptr_t deopt_id)
+                   intptr_t deopt_id,
+                   Code::EntryKind entry_kind = Code::EntryKind::kNormal)
       : TemplateDartCall(deopt_id,
                          node->arguments()->type_args_len(),
                          node->arguments()->names(),
                          arguments,
-                         node->token_pos()) {
+                         node->token_pos()),
+        entry_kind_(entry_kind) {
+    ASSERT(entry_kind != Code::EntryKind::kMonomorphic);
     ASSERT(!arguments->is_empty());
     SetInputAt(0, function);
   }
@@ -3208,12 +3211,14 @@
                    intptr_t type_args_len,
                    const Array& argument_names,
                    TokenPosition token_pos,
-                   intptr_t deopt_id)
+                   intptr_t deopt_id,
+                   Code::EntryKind entry_kind = Code::EntryKind::kNormal)
       : TemplateDartCall(deopt_id,
                          type_args_len,
                          argument_names,
                          arguments,
-                         token_pos) {
+                         token_pos),
+        entry_kind_(entry_kind) {
     ASSERT(!arguments->is_empty());
     SetInputAt(0, function);
   }
@@ -3227,9 +3232,13 @@
 
   virtual bool HasUnknownSideEffects() const { return true; }
 
+  Code::EntryKind entry_kind() const { return entry_kind_; }
+
   PRINT_OPERANDS_TO_SUPPORT
 
  private:
+  const Code::EntryKind entry_kind_;
+
   DISALLOW_COPY_AND_ASSIGN(ClosureCallInstr);
 };
 
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index 446bde8..290267c 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -265,25 +265,14 @@
   // R0: Function.
   ASSERT(locs()->in(0).reg() == R0);
   __ ldr(CODE_REG, FieldAddress(R0, Function::code_offset()));
-  __ ldr(R2, FieldAddress(R0, Function::entry_point_offset()));
+  __ ldr(R2, FieldAddress(R0, Code::function_entry_point_offset(entry_kind())));
 
   // R2: instructions entry point.
   // R9: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
   __ LoadImmediate(R9, 0);
   __ blx(R2);
-  compiler->RecordSafepoint(locs());
-  compiler->EmitCatchEntryState();
-  // Marks either the continuation point in unoptimized code or the
-  // deoptimization point in optimized code, after call.
-  const intptr_t deopt_id_after = Thread::ToDeoptAfter(deopt_id());
-  if (compiler->is_optimizing()) {
-    compiler->AddDeoptIndexAtCall(deopt_id_after);
-  }
-  // Add deoptimization continuation point after the call and before the
-  // arguments are removed.
-  // In optimized code this descriptor is needed for exception handling.
-  compiler->AddCurrentDescriptor(RawPcDescriptors::kDeopt, deopt_id_after,
-                                 token_pos());
+  compiler->EmitCallsiteMetadata(token_pos(), deopt_id(),
+                                 RawPcDescriptors::kOther, locs());
   __ Drop(argument_count);
 }
 
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 18780af..dc73b6f 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -268,19 +268,8 @@
   __ LoadImmediate(R5, 0);
   //??
   __ blr(R2);
-  compiler->RecordSafepoint(locs());
-  compiler->EmitCatchEntryState();
-  // Marks either the continuation point in unoptimized code or the
-  // deoptimization point in optimized code, after call.
-  const intptr_t deopt_id_after = Thread::ToDeoptAfter(deopt_id());
-  if (compiler->is_optimizing()) {
-    compiler->AddDeoptIndexAtCall(deopt_id_after);
-  }
-  // Add deoptimization continuation point after the call and before the
-  // arguments are removed.
-  // In optimized code this descriptor is needed for exception handling.
-  compiler->AddCurrentDescriptor(RawPcDescriptors::kDeopt, deopt_id_after,
-                                 token_pos());
+  compiler->EmitCallsiteMetadata(token_pos(), deopt_id(),
+                                 RawPcDescriptors::kOther, locs());
   __ Drop(argument_count);
 }
 
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index b6d4586..bcc4b04 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -6147,18 +6147,8 @@
   // ECX: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
   __ xorl(ECX, ECX);
   __ call(EBX);
-  compiler->RecordSafepoint(locs());
-  // Marks either the continuation point in unoptimized code or the
-  // deoptimization point in optimized code, after call.
-  const intptr_t deopt_id_after = Thread::ToDeoptAfter(deopt_id());
-  if (compiler->is_optimizing()) {
-    compiler->AddDeoptIndexAtCall(deopt_id_after);
-  }
-  // Add deoptimization continuation point after the call and before the
-  // arguments are removed.
-  // In optimized code this descriptor is needed for exception handling.
-  compiler->AddCurrentDescriptor(RawPcDescriptors::kDeopt, deopt_id_after,
-                                 token_pos());
+  compiler->EmitCallsiteMetadata(token_pos(), deopt_id(),
+                                 RawPcDescriptors::kOther, locs());
   __ Drop(argument_count);
 }
 
diff --git a/runtime/vm/compiler/backend/il_printer.cc b/runtime/vm/compiler/backend/il_printer.cc
index b1570d7..322e888 100644
--- a/runtime/vm/compiler/backend/il_printer.cc
+++ b/runtime/vm/compiler/backend/il_printer.cc
@@ -489,6 +489,9 @@
     f->Print(", ");
     PushArgumentAt(i)->value()->PrintTo(f);
   }
+  if (entry_kind() == Code::EntryKind::kUnchecked) {
+    f->Print(" using unchecked entrypoint");
+  }
 }
 
 void InstanceCallInstr::PrintOperandsTo(BufferFormatter* f) const {
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 1cf3331..7379b83 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -6238,26 +6238,16 @@
   // Function in RAX.
   ASSERT(locs()->in(0).reg() == RAX);
   __ movq(CODE_REG, FieldAddress(RAX, Function::code_offset()));
-  __ movq(RCX, FieldAddress(RAX, Function::entry_point_offset()));
+  __ movq(RCX,
+          FieldAddress(RAX, Code::function_entry_point_offset(entry_kind())));
 
   // RAX: Function.
   // R10: Arguments descriptor array.
   // RBX: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
   __ xorq(RBX, RBX);
   __ call(RCX);
-  compiler->RecordSafepoint(locs());
-  compiler->EmitCatchEntryState();
-  // Marks either the continuation point in unoptimized code or the
-  // deoptimization point in optimized code, after call.
-  const intptr_t deopt_id_after = Thread::ToDeoptAfter(deopt_id());
-  if (compiler->is_optimizing()) {
-    compiler->AddDeoptIndexAtCall(deopt_id_after);
-  }
-  // Add deoptimization continuation point after the call and before the
-  // arguments are removed.
-  // In optimized code this descriptor is needed for exception handling.
-  compiler->AddCurrentDescriptor(RawPcDescriptors::kDeopt, deopt_id_after,
-                                 token_pos());
+  compiler->EmitCallsiteMetadata(token_pos(), deopt_id(),
+                                 RawPcDescriptors::kOther, locs());
   __ Drop(argument_count);
 }
 
diff --git a/runtime/vm/compiler/backend/inliner.cc b/runtime/vm/compiler/backend/inliner.cc
index dace5a9..6988601 100644
--- a/runtime/vm/compiler/backend/inliner.cc
+++ b/runtime/vm/compiler/backend/inliner.cc
@@ -971,6 +971,9 @@
           } else if (PolymorphicInstanceCallInstr* instr =
                          call_data->call->AsPolymorphicInstanceCall()) {
             entry_kind = instr->instance_call()->entry_kind();
+          } else if (ClosureCallInstr* instr =
+                         call_data->call->AsClosureCall()) {
+            entry_kind = instr->entry_kind();
           }
           kernel::FlowGraphBuilder builder(
               parsed_function, *ic_data_array, /* not building var desc */ NULL,
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index b7124b2..0cac1db 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -1624,7 +1624,8 @@
   call_hook += LoadLocal(entry_point_num);
   call_hook += PushArgument();
   call_hook += Constant(Function::ZoneHandle(Z, closure.function()));
-  call_hook += B->ClosureCall(/*type_args_len=*/0, /*argument_count=*/3,
+  call_hook += B->ClosureCall(TokenPosition::kNoSource,
+                              /*type_args_len=*/0, /*argument_count=*/3,
                               /*argument_names=*/Array::Handle());
   call_hook += Drop();  // result of closure call
   call_hook += Drop();  // entrypoint number
@@ -2388,10 +2389,13 @@
   return flow_graph_builder_->LoadStaticField();
 }
 
-Fragment StreamingFlowGraphBuilder::CheckNull(TokenPosition position,
-                                              LocalVariable* receiver,
-                                              const String& function_name) {
-  return flow_graph_builder_->CheckNull(position, receiver, function_name);
+Fragment StreamingFlowGraphBuilder::CheckNull(
+    TokenPosition position,
+    LocalVariable* receiver,
+    const String& function_name,
+    bool clear_the_temp /* = true */) {
+  return flow_graph_builder_->CheckNull(position, receiver, function_name,
+                                        clear_the_temp);
 }
 
 Fragment StreamingFlowGraphBuilder::StaticCall(TokenPosition position,
@@ -2840,8 +2844,8 @@
   }
   for (intptr_t i = 0; i < list_length; ++i) {
     String& name =
-        H.DartSymbolObfuscate(ReadStringReference());    // read ith name index.
-    instructions += BuildExpression();                   // read ith expression.
+        H.DartSymbolObfuscate(ReadStringReference());  // read ith name index.
+    instructions += BuildExpression();                 // read ith expression.
     if (!skip_push_arguments) instructions += PushArgument();
     if (do_drop) instructions += Drop();
     if (argument_names != NULL) {
@@ -3452,6 +3456,11 @@
   const InferredTypeMetadata result_type =
       inferred_type_metadata_helper_.GetInferredType(offset);
 
+#ifndef TARGET_ARCH_DBC
+  const CallSiteAttributesMetadata call_site_attributes =
+      call_site_attributes_metadata_helper_.GetCallSiteAttributes(offset);
+#endif
+
   const Tag receiver_tag = PeekTag();  // peek tag for receiver.
   if (IsNumberLiteral(receiver_tag) &&
       (!optimizing() || constant_evaluator_.IsCached(offset))) {
@@ -3487,6 +3496,16 @@
     SetOffset(before_branch_offset);
   }
 
+  bool is_unchecked_closure_call = false;
+#ifndef TARGET_ARCH_DBC
+  if (call_site_attributes.receiver_type != nullptr &&
+      call_site_attributes.receiver_type->IsFunctionType()) {
+    AlternativeReadingScope alt(&reader_);
+    SkipExpression();  // skip receiver
+    is_unchecked_closure_call = ReadNameAsMethodName().Equals(Symbols::Call());
+  }
+#endif
+
   Fragment instructions;
 
   intptr_t type_args_len = 0;
@@ -3501,7 +3520,7 @@
       const TypeArguments& type_arguments =
           T.BuildTypeArguments(list_length);  // read types.
       instructions += TranslateInstantiatedTypeArguments(type_arguments);
-      if (direct_call.check_receiver_for_null_) {
+      if (direct_call.check_receiver_for_null_ || is_unchecked_closure_call) {
         // Don't yet push type arguments if we need to check receiver for null.
         // In this case receiver will be duplicated so instead of pushing
         // type arguments here we need to push it between receiver_temp
@@ -3538,7 +3557,7 @@
   }
 
   LocalVariable* receiver_temp = NULL;
-  if (direct_call.check_receiver_for_null_) {
+  if (direct_call.check_receiver_for_null_ || is_unchecked_closure_call) {
     // Duplicate receiver for CheckNull before it is consumed by PushArgument.
     receiver_temp = MakeTemporary();
     if (type_arguments_temp != NULL) {
@@ -3583,11 +3602,26 @@
             Field::GetterSymbol(name) == interface_target->name()));
   }
 
-  if (direct_call.check_receiver_for_null_) {
-    instructions += CheckNull(position, receiver_temp, name);
+  // TODO(sjindel): Avoid the check for null on unchecked closure calls if TFA
+  // allows.
+  if (direct_call.check_receiver_for_null_ || is_unchecked_closure_call) {
+    // Receiver temp is needed to load the function to call from the closure.
+    instructions += CheckNull(position, receiver_temp, name,
+                              /*clear_temp=*/!is_unchecked_closure_call);
   }
 
-  if (!direct_call.target_.IsNull()) {
+  if (is_unchecked_closure_call) {
+    // Lookup the function in the closure.
+    instructions += LoadLocal(receiver_temp);
+    instructions += LoadField(Closure::function_offset());
+    if (parsed_function()->function().is_debuggable()) {
+      ASSERT(!parsed_function()->function().is_native());
+      instructions += DebugStepCheck(position);
+    }
+    instructions +=
+        B->ClosureCall(position, type_args_len, argument_count, argument_names,
+                       /*use_unchecked_entry=*/true);
+  } else if (!direct_call.target_.IsNull()) {
     ASSERT(FLAG_precompiled_mode);
     instructions += StaticCall(position, direct_call.target_, argument_count,
                                argument_names, ICData::kNoRebind, &result_type,
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
index ebdf3a0..c4aecce 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
@@ -209,7 +209,8 @@
   Fragment LoadStaticField();
   Fragment CheckNull(TokenPosition position,
                      LocalVariable* receiver,
-                     const String& function_name);
+                     const String& function_name,
+                     bool clear_the_temp = true);
   Fragment StaticCall(TokenPosition position,
                       const Function& target,
                       intptr_t argument_count,
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 769f2a9..53d17b4 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -356,10 +356,10 @@
     const InferredTypeMetadata* result_type) {
   const intptr_t total_count = argument_count + (type_args_len > 0 ? 1 : 0);
   ArgumentArray arguments = GetArguments(total_count);
-  InstanceCallInstr* call = new (Z) InstanceCallInstr(
-      position, name, kind, arguments, type_args_len, argument_names,
-      checked_argument_count, ic_data_array_, GetNextDeoptId(),
-      interface_target);
+  InstanceCallInstr* call = new (Z)
+      InstanceCallInstr(position, name, kind, arguments, type_args_len,
+                        argument_names, checked_argument_count, ic_data_array_,
+                        GetNextDeoptId(), interface_target);
   if ((result_type != NULL) && !result_type->IsTrivial()) {
     call->SetResultType(Z, result_type->ToCompileType(Z));
   }
@@ -367,15 +367,19 @@
   return Fragment(call);
 }
 
-Fragment FlowGraphBuilder::ClosureCall(intptr_t type_args_len,
+Fragment FlowGraphBuilder::ClosureCall(TokenPosition position,
+                                       intptr_t type_args_len,
                                        intptr_t argument_count,
-                                       const Array& argument_names) {
+                                       const Array& argument_names,
+                                       bool is_statically_checked) {
   Value* function = Pop();
   const intptr_t total_count = argument_count + (type_args_len > 0 ? 1 : 0);
   ArgumentArray arguments = GetArguments(total_count);
   ClosureCallInstr* call = new (Z)
       ClosureCallInstr(function, arguments, type_args_len, argument_names,
-                       TokenPosition::kNoSource, GetNextDeoptId());
+                       position, GetNextDeoptId(),
+                       is_statically_checked ? Code::EntryKind::kUnchecked
+                                             : Code::EntryKind::kNormal);
   Push(call);
   return Fragment(call);
 }
@@ -486,7 +490,8 @@
 
 Fragment FlowGraphBuilder::CheckNull(TokenPosition position,
                                      LocalVariable* receiver,
-                                     const String& function_name) {
+                                     const String& function_name,
+                                     bool clear_the_temp /* = true */) {
   Fragment instructions = LoadLocal(receiver);
 
   CheckNullInstr* check_null =
@@ -494,11 +499,13 @@
 
   instructions <<= check_null;
 
-  // Null out receiver to make sure it is not saved into the frame before
-  // doing the call.
-  instructions += NullConstant();
-  instructions += StoreLocal(TokenPosition::kNoSource, receiver);
-  instructions += Drop();
+  if (clear_the_temp) {
+    // Null out receiver to make sure it is not saved into the frame before
+    // doing the call.
+    instructions += NullConstant();
+    instructions += StoreLocal(TokenPosition::kNoSource, receiver);
+    instructions += Drop();
+  }
 
   return instructions;
 }
@@ -1352,8 +1359,8 @@
     body += LoadLocal(closure);
     body += LoadField(Closure::function_offset());
 
-    body += ClosureCall(descriptor.TypeArgsLen(), descriptor.Count(),
-                        argument_names);
+    body += ClosureCall(TokenPosition::kNoSource, descriptor.TypeArgsLen(),
+                        descriptor.Count(), argument_names);
   } else {
     const intptr_t kNumArgsChecked = 1;
     body += InstanceCall(TokenPosition::kMinSource, Symbols::Call(),
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index ee8efe8..49332d5 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -101,9 +101,11 @@
                         intptr_t checked_argument_count,
                         const Function& interface_target,
                         const InferredTypeMetadata* result_type = NULL);
-  Fragment ClosureCall(intptr_t type_args_len,
+  Fragment ClosureCall(TokenPosition position,
+                       intptr_t type_args_len,
                        intptr_t argument_count,
-                       const Array& argument_names);
+                       const Array& argument_names,
+                       bool use_unchecked_entry = false);
   Fragment RethrowException(TokenPosition position, int catch_try_index);
   Fragment LoadClassId();
   Fragment LoadField(intptr_t offset, intptr_t class_id = kDynamicCid);
@@ -114,7 +116,8 @@
   Fragment Return(TokenPosition position, bool omit_result_type_check = false);
   Fragment CheckNull(TokenPosition position,
                      LocalVariable* receiver,
-                     const String& function_name);
+                     const String& function_name,
+                     bool clear_the_temp = true);
   void SetResultTypeForStaticCall(StaticCallInstr* call,
                                   const Function& target,
                                   intptr_t argument_count,
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 9fed0ee..d6fd7cb 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -5959,7 +5959,7 @@
   StorePointer(&raw_ptr()->code_, value.raw());
   StoreNonPointer(&raw_ptr()->entry_point_, value.EntryPoint());
   StoreNonPointer(&raw_ptr()->unchecked_entry_point_,
-                  value.unchecked_entry_point());
+                  value.UncheckedEntryPoint());
 }
 
 void Function::AttachCode(const Code& value) const {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 81a4686..9628163 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -5005,6 +5005,18 @@
     }
   }
 
+  static intptr_t function_entry_point_offset(EntryKind kind) {
+    switch (kind) {
+      case Code::EntryKind::kNormal:
+        return Function::entry_point_offset();
+      case Code::EntryKind::kUnchecked:
+        return Function::unchecked_entry_point_offset();
+      default:
+        ASSERT(false && "Invalid entry kind.");
+        UNREACHABLE();
+    }
+  }
+
   RawObjectPool* object_pool() const { return raw_ptr()->object_pool_; }
   static intptr_t object_pool_offset() {
     return OFFSET_OF(RawCode, object_pool_);
@@ -5024,9 +5036,9 @@
   uword PayloadStart() const {
     return Instructions::PayloadStart(instructions());
   }
-  uword EntryPoint() const {
-    const Instructions& instr = Instructions::Handle(instructions());
-    return instr.EntryPoint();
+  uword EntryPoint() const { return Instructions::EntryPoint(instructions()); }
+  uword UncheckedEntryPoint() const {
+    return Instructions::UncheckedEntryPoint(instructions());
   }
   uword MonomorphicEntryPoint() const {
     const Instructions& instr = Instructions::Handle(instructions());
@@ -5321,14 +5333,6 @@
 
   bool IsDisabled() const { return instructions() != active_instructions(); }
 
-  uword unchecked_entry_point() const {
-    return raw_ptr()->unchecked_entry_point_;
-  }
-
-  void set_unchecked_entry_point(uword value) const {
-    StoreNonPointer(&raw_ptr()->unchecked_entry_point_, value);
-  }
-
  private:
   void set_state_bits(intptr_t bits) const;
 
diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status
index c1330b4..76a6c49 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -1388,7 +1388,7 @@
 enum_test: Crash
 
 [ $mode == debug && ($compiler == dartk || $compiler == dartkb) && ($hot_reload || $hot_reload_rollback) ]
-enum_duplicate_test/01: Crash
+enum_duplicate_test/01: Pass, Crash
 
 [ $mode == product && $runtime == vm && ($compiler == dartk || $compiler == dartkb) ]
 deferred_load_constants_test/02: Fail