Version 2.13.0-235.0.dev

Merge commit 'acd5f6e9975ed80ce64d6626e2d9a525666540a8' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 853750a6..39f52b3 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
     "constraint, update this by running tools/generate_package_config.dart."
   ],
   "configVersion": 2,
-  "generated": "2021-04-12T14:17:28.885860",
+  "generated": "2021-04-12T16:42:43.931842",
   "generator": "tools/generate_package_config.dart",
   "packages": [
     {
diff --git a/DEPS b/DEPS
index ddb3340..c5f55af 100644
--- a/DEPS
+++ b/DEPS
@@ -109,7 +109,7 @@
   "glob_rev": "a62acf590598f458d3198d9f2930c1c9dd4b1379",
   "html_rev": "00cd3c22dac0e68e6ed9e7e4945101aedb1b3109",
   "http_io_rev": "2fa188caf7937e313026557713f7feffedd4978b",
-  "http_multi_server_rev" : "2109e1b550a0eea8212da779be363a8c1c14ce63",
+  "http_multi_server_rev" : "7aca9e87d4a68374b685334f20359320054b8d7b",
   "http_parser_rev": "5dd4d16693242049dfb43b5efa429fedbf932e98",
   "http_retry_rev": "845771af7bb5ab38ab740ce4a31f3b0c7680302b",
   "http_rev": "615380db687d0649057ed8dba9d933d7d5f5051b",
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 5d1ae27..d0b3617 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -62,10 +62,6 @@
 
 /// Late lowerings which the frontend performs for dart2js.
 const List<int> _allEnabledLateLowerings = [
-  LateLowering.nullableInitializedNonFinalLocal,
-  LateLowering.nonNullableInitializedNonFinalLocal,
-  LateLowering.nullableInitializedFinalLocal,
-  LateLowering.nonNullableInitializedFinalLocal,
   LateLowering.initializedNonFinalStaticField,
   LateLowering.initializedFinalStaticField,
   LateLowering.uninitializedNonFinalInstanceField,
diff --git a/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart b/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
index 3d0d2e0..eacb5ae 100644
--- a/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
+++ b/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
@@ -6,8 +6,13 @@
 import 'package:kernel/library_index.dart';
 import 'package:kernel/type_algebra.dart';
 
-bool _shouldLowerVariable(VariableDeclaration node) =>
-    node.initializer == null && node.isLate;
+bool _shouldLowerVariable(VariableDeclaration node) => node.isLate;
+
+bool _shouldLowerUninitializedVariable(VariableDeclaration node) =>
+    _shouldLowerVariable(node) && node.initializer == null;
+
+bool _shouldLowerInitializedVariable(VariableDeclaration node) =>
+    _shouldLowerVariable(node) && node.initializer != null;
 
 bool _shouldLowerField(Field node) =>
     node.initializer == null && node.isStatic && node.isLate;
@@ -26,12 +31,19 @@
   final Class _cellClass;
   final Constructor _cellConstructor;
 
+  final Class _initializedCellClass;
+  final Constructor _initializedCellConstructor;
+
   final _Reader _readLocal;
   final _Reader _readField;
+  final _Reader _readInitialized;
+  final _Reader _readInitializedFinal;
 
   final Procedure _setValue;
   final Procedure _setFinalLocalValue;
   final Procedure _setFinalFieldValue;
+  final Procedure _setInitializedValue;
+  final Procedure _setInitializedFinalValue;
 
   // TODO(fishythefish): Remove cells when exiting their scope.
   final Map<VariableDeclaration, VariableDeclaration> _variableCells = {};
@@ -42,15 +54,27 @@
   LateLowering(LibraryIndex index)
       : _cellClass = index.getClass('dart:_late_helper', '_Cell'),
         _cellConstructor = index.getMember('dart:_late_helper', '_Cell', ''),
+        _initializedCellClass =
+            index.getClass('dart:_late_helper', '_InitializedCell'),
+        _initializedCellConstructor =
+            index.getMember('dart:_late_helper', '_InitializedCell', ''),
         _readLocal =
             _Reader(index.getMember('dart:_late_helper', '_Cell', 'readLocal')),
         _readField =
             _Reader(index.getMember('dart:_late_helper', '_Cell', 'readField')),
+        _readInitialized = _Reader(
+            index.getMember('dart:_late_helper', '_InitializedCell', 'read')),
+        _readInitializedFinal = _Reader(index.getMember(
+            'dart:_late_helper', '_InitializedCell', 'readFinal')),
         _setValue = index.getMember('dart:_late_helper', '_Cell', 'set:value'),
         _setFinalLocalValue = index.getMember(
             'dart:_late_helper', '_Cell', 'set:finalLocalValue'),
         _setFinalFieldValue = index.getMember(
-            'dart:_late_helper', '_Cell', 'set:finalFieldValue');
+            'dart:_late_helper', '_Cell', 'set:finalFieldValue'),
+        _setInitializedValue = index.getMember(
+            'dart:_late_helper', '_InitializedCell', 'set:value'),
+        _setInitializedFinalValue = index.getMember(
+            'dart:_late_helper', '_InitializedCell', 'set:finalValue');
 
   void transformAdditionalExports(Library node) {
     List<Reference> additionalExports = node.additionalExports;
@@ -69,6 +93,12 @@
           _cellConstructor, Arguments.empty()..fileOffset = fileOffset)
         ..fileOffset = fileOffset;
 
+  ConstructorInvocation _callInitializedCellConstructor(
+          Expression initializer, int fileOffset) =>
+      ConstructorInvocation(_initializedCellConstructor,
+          Arguments([initializer])..fileOffset = fileOffset)
+        ..fileOffset = fileOffset;
+
   InstanceInvocation _callReader(
       _Reader reader, Expression receiver, DartType type, int fileOffset) {
     Procedure procedure = reader._procedure;
@@ -85,34 +115,14 @@
       ..fileOffset = fileOffset;
   }
 
-  InstanceInvocation _callReadLocal(
-          Expression receiver, DartType type, int fileOffset) =>
-      _callReader(_readLocal, receiver, type, fileOffset);
-
-  InstanceInvocation _callReadField(
-          Expression receiver, DartType type, int fileOffset) =>
-      _callReader(_readField, receiver, type, fileOffset);
-
   InstanceSet _callSetter(Procedure _setter, Expression receiver,
           Expression value, int fileOffset) =>
       InstanceSet(InstanceAccessKind.Instance, receiver, _setter.name, value,
           interfaceTarget: _setter)
         ..fileOffset = fileOffset;
 
-  InstanceSet _callSetValue(
-          Expression receiver, Expression value, int fileOffset) =>
-      _callSetter(_setValue, receiver, value, fileOffset);
-
-  InstanceSet _callSetFinalLocalValue(
-          Expression receiver, Expression value, int fileOffset) =>
-      _callSetter(_setFinalLocalValue, receiver, value, fileOffset);
-
-  InstanceSet _callSetFinalFieldValue(
-          Expression receiver, Expression value, int fileOffset) =>
-      _callSetter(_setFinalFieldValue, receiver, value, fileOffset);
-
-  VariableDeclaration _variableCell(VariableDeclaration variable) {
-    assert(_shouldLowerVariable(variable));
+  VariableDeclaration _uninitializedVariableCell(VariableDeclaration variable) {
+    assert(_shouldLowerUninitializedVariable(variable));
     return _variableCells.putIfAbsent(variable, () {
       int fileOffset = variable.fileOffset;
       return VariableDeclaration(variable.name,
@@ -124,6 +134,38 @@
     });
   }
 
+  FunctionExpression _initializerClosure(
+      Expression initializer, DartType type) {
+    int fileOffset = initializer.fileOffset;
+    ReturnStatement body = ReturnStatement(initializer)
+      ..fileOffset = fileOffset;
+    FunctionNode closure = FunctionNode(body, returnType: type)
+      ..fileOffset = fileOffset;
+    return FunctionExpression(closure)..fileOffset = fileOffset;
+  }
+
+  VariableDeclaration _initializedVariableCell(VariableDeclaration variable) {
+    assert(_shouldLowerInitializedVariable(variable));
+    return _variableCells.putIfAbsent(variable, () {
+      int fileOffset = variable.fileOffset;
+      return VariableDeclaration(variable.name,
+          initializer: _callInitializedCellConstructor(
+              _initializerClosure(variable.initializer, variable.type),
+              fileOffset),
+          type: InterfaceType(_initializedCellClass,
+              _contextMember.enclosingLibrary.nonNullable),
+          isFinal: true)
+        ..fileOffset = fileOffset;
+    });
+  }
+
+  VariableDeclaration _variableCell(VariableDeclaration variable) {
+    assert(_shouldLowerVariable(variable));
+    return variable.initializer == null
+        ? _uninitializedVariableCell(variable)
+        : _initializedVariableCell(variable);
+  }
+
   VariableGet _variableCellAccess(
           VariableDeclaration variable, int fileOffset) =>
       VariableGet(_variableCell(variable))..fileOffset = fileOffset;
@@ -149,7 +191,11 @@
 
     int fileOffset = node.fileOffset;
     VariableGet cell = _variableCellAccess(variable, fileOffset);
-    return _callReadLocal(cell, node.promotedType ?? variable.type, fileOffset);
+    _Reader reader = variable.initializer == null
+        ? _readLocal
+        : (variable.isFinal ? _readInitializedFinal : _readInitialized);
+    return _callReader(
+        reader, cell, node.promotedType ?? variable.type, fileOffset);
   }
 
   TreeNode transformVariableSet(VariableSet node, Member contextMember) {
@@ -160,9 +206,10 @@
 
     int fileOffset = node.fileOffset;
     VariableGet cell = _variableCellAccess(variable, fileOffset);
-    return variable.isFinal
-        ? _callSetFinalLocalValue(cell, node.value, fileOffset)
-        : _callSetValue(cell, node.value, fileOffset);
+    Procedure setter = variable.initializer == null
+        ? (variable.isFinal ? _setFinalLocalValue : _setValue)
+        : (variable.isFinal ? _setInitializedFinalValue : _setInitializedValue);
+    return _callSetter(setter, cell, node.value, fileOffset);
   }
 
   Field _fieldCell(Field field) {
@@ -200,7 +247,7 @@
     if (target is Field && _shouldLowerField(target)) {
       int fileOffset = node.fileOffset;
       StaticGet cell = _fieldCellAccess(target, fileOffset);
-      return _callReadField(cell, target.type, fileOffset);
+      return _callReader(_readField, cell, target.type, fileOffset);
     } else {
       return node;
     }
@@ -213,9 +260,8 @@
     if (target is Field && _shouldLowerField(target)) {
       int fileOffset = node.fileOffset;
       StaticGet cell = _fieldCellAccess(target, fileOffset);
-      return target.isFinal
-          ? _callSetFinalFieldValue(cell, node.value, fileOffset)
-          : _callSetValue(cell, node.value, fileOffset);
+      Procedure setter = target.isFinal ? _setFinalFieldValue : _setValue;
+      return _callSetter(setter, cell, node.value, fileOffset);
     } else {
       return node;
     }
diff --git a/pkg/compiler/lib/src/ssa/logging.dart b/pkg/compiler/lib/src/ssa/logging.dart
index e2c63af..77c888e 100644
--- a/pkg/compiler/lib/src/ssa/logging.dart
+++ b/pkg/compiler/lib/src/ssa/logging.dart
@@ -232,18 +232,15 @@
   }
 
   void registerSubstring(HInvokeDynamic original) {
-    Features features = new Features();
-    entries.add(new OptimizationLogEntry('Substring', features));
+    _registerSpecializer(original, null, null, 'substring');
   }
 
   void registerTrim(HInvokeDynamic original) {
-    Features features = new Features();
-    entries.add(new OptimizationLogEntry('Trim', features));
+    _registerSpecializer(original, null, null, 'trim');
   }
 
   void registerPatternMatch(HInvokeDynamic original) {
-    Features features = new Features();
-    entries.add(new OptimizationLogEntry('PatternMatch', features));
+    _registerSpecializer(original, null, null, original.selector.name);
   }
 
   void registerRound(HInvokeDynamic original) {
diff --git a/pkg/compiler/test/optimization/data/string_methods.dart b/pkg/compiler/test/optimization/data/string_methods.dart
new file mode 100644
index 0000000..c2aabfc
--- /dev/null
+++ b/pkg/compiler/test/optimization/data/string_methods.dart
@@ -0,0 +1,163 @@
+// Copyright (c) 2021, 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.
+
+// ---- String.substring
+
+/*member: substring1:Specializer=[substring]*/
+@pragma('dart2js:noInline')
+substring1(String param) {
+  return param.substring(1);
+}
+
+/*member: substring2:Specializer=[substring]*/
+@pragma('dart2js:noInline')
+substring2(String param) {
+  return param.substring(1, 3);
+}
+
+/*member: substring3:Specializer=[substring]*/
+@pragma('dart2js:noInline')
+substring3(String param) {
+  dynamic s = param;
+  return s.substring(1, 2); // Not confused by static type.
+}
+
+/*member: substring4:*/
+@pragma('dart2js:noInline')
+substring4(String param) {
+  dynamic s = param;
+  return s.substring(1, 2, 3); // Arity mismatch.
+}
+
+/*member: substring5:*/
+@pragma('dart2js:noInline')
+substring5(String param) {
+  dynamic s = [param];
+  return s.substring(1); // Receiver mismatch.
+}
+
+// ---- String.trim
+
+/*member: trim1:Specializer=[trim]*/
+@pragma('dart2js:noInline')
+trim1(String param) {
+  return param.trim();
+}
+
+// ---- String.indexOf
+
+/*member: indexOf1:Specializer=[indexOf]*/
+@pragma('dart2js:noInline')
+indexOf1(String param) {
+  return param.indexOf('e');
+}
+
+/*member: indexOf2:Specializer=[indexOf]*/
+@pragma('dart2js:noInline')
+indexOf2(String param) {
+  return param.indexOf('e', 3);
+}
+
+// Specializer does not match as the pattern is not a string.
+/*member: indexOf3:*/
+@pragma('dart2js:noInline')
+indexOf3(String param) {
+  return param.indexOf(RegExp('e'));
+}
+
+// ---- String.contains
+
+/*member: contains1:Specializer=[contains]*/
+@pragma('dart2js:noInline')
+contains1(String param) {
+  return param.contains('e');
+}
+
+/*member: contains2:Specializer=[contains]*/
+@pragma('dart2js:noInline')
+contains2(String param) {
+  return param.contains('e', 3);
+}
+
+// Specializer does not match as the pattern is not a string.
+/*member: contains3:*/
+@pragma('dart2js:noInline')
+contains3(String param) {
+  return param.contains(RegExp('e'));
+}
+
+// ---- String.startsWith
+
+/*member: startsWith1:Specializer=[startsWith]*/
+@pragma('dart2js:noInline')
+startsWith1(String param) {
+  return param.startsWith('e');
+}
+
+/*member: startsWith2:Specializer=[startsWith]*/
+@pragma('dart2js:noInline')
+startsWith2(String param) {
+  return param.startsWith('e', 3);
+}
+
+// Specializer does not match as the pattern is not a string.
+/*member: startsWith3:*/
+@pragma('dart2js:noInline')
+startsWith3(String param) {
+  return param.startsWith(RegExp('e'));
+}
+
+// ---- String.endsWith
+
+/*member: endsWith1:Specializer=[endsWith]*/
+@pragma('dart2js:noInline')
+endsWith1(String param) {
+  return param.endsWith('e');
+}
+
+main() {
+  substring1('hello');
+  substring1('bye');
+  substring1('');
+  substring2('hello');
+  substring2('bye');
+  substring2('');
+  substring3('hello');
+  substring3('bye');
+  substring3('');
+  substring4('hello');
+  substring4('bye');
+  substring4('');
+  substring5('hello');
+  substring5('bye');
+  substring5('');
+
+  trim1('hello');
+  trim1(' bye ');
+  trim1('    ');
+
+  indexOf1('hello');
+  indexOf1('bye');
+  indexOf2('hello');
+  indexOf2('bye');
+  indexOf3('hello');
+  indexOf3('bye');
+
+  contains1('hello');
+  contains1('bye');
+  contains2('hello');
+  contains2('bye');
+  contains3('hello');
+  contains3('bye');
+
+  startsWith1('hello');
+  startsWith1('bye');
+  startsWith2('hello');
+  startsWith2('bye');
+  startsWith3('hello');
+  startsWith3('bye');
+
+  endsWith1('hello');
+  endsWith1('bye');
+}
diff --git a/pkg/front_end/testcases/dart2js/late_locals.dart.strong.expect b/pkg/front_end/testcases/dart2js/late_locals.dart.strong.expect
index e90385e..0ecd84b 100644
--- a/pkg/front_end/testcases/dart2js/late_locals.dart.strong.expect
+++ b/pkg/front_end/testcases/dart2js/late_locals.dart.strong.expect
@@ -1,7 +1,6 @@
 library /*isNonNullableByDefault*/;
 import self as self;
 import "dart:core" as core;
-import "dart:_internal" as _in;
 
 static method main() → void {
   self::testNullableUninitializedNonFinalLocal();
@@ -34,46 +33,28 @@
   core::print(x);
 }
 static method testNullableInitializedNonFinalLocal() → void {
-  lowered core::int? #x = _in::createSentinel<core::int?>();
-  function #x#get() → core::int?
-    return let final core::int? #t1 = #x in _in::isSentinel(#t1) ?{core::int?} #x = 1.{core::int::unary-}(){() → core::int} : #t1{core::int?};
-  function #x#set(core::int? #t2) → dynamic
-    return #x = #t2;
-  core::print(#x#get(){() → core::int?});
-  #x#set(42){(core::int?) → dynamic};
-  core::print(#x#get(){() → core::int?});
-  lowered core::int? #y = _in::createSentinel<core::int?>();
-  function #y#get() → core::int?
-    return let final core::int? #t3 = #y in _in::isSentinel(#t3) ?{core::int?} #y = null : #t3{core::int?};
-  function #y#set(core::int? #t4) → dynamic
-    return #y = #t4;
-  core::print(#y#get(){() → core::int?});
-  #y#set(42){(core::int?) → dynamic};
-  core::print(#y#get(){() → core::int?});
+  late core::int? x = 1.{core::int::unary-}(){() → core::int};
+  core::print(x{core::int});
+  x = 42;
+  core::print(x{core::int});
+  late core::int? y = null;
+  core::print(y);
+  y = 42;
+  core::print(y{core::int});
 }
 static method testNonNullableInitializedNonFinalLocal() → void {
-  lowered core::int? #x;
-  function #x#get() → core::int
-    return let final core::int? #t5 = #x in #t5 == null ?{core::int} #x = 1.{core::int::unary-}(){() → core::int} : #t5{core::int};
-  function #x#set(core::int #t6) → dynamic
-    return #x = #t6;
-  core::print(#x#get(){() → core::int});
-  #x#set(42){(core::int) → dynamic};
-  core::print(#x#get(){() → core::int});
+  late core::int x = 1.{core::int::unary-}(){() → core::int};
+  core::print(x);
+  x = 42;
+  core::print(x);
 }
 static method testNullableInitializedFinalLocal() → void {
-  lowered final core::int? #x = _in::createSentinel<core::int?>();
-  function #x#get() → core::int?
-    return let final core::int? #t7 = #x in _in::isSentinel(#t7) ?{core::int?} let final core::int? #t8 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int?} #x = #t8 : throw new _in::LateError::localADI("x") : #t7;
-  core::print(#x#get(){() → core::int?});
-  lowered final core::int? #y = _in::createSentinel<core::int?>();
-  function #y#get() → core::int?
-    return let final core::int? #t9 = #y in _in::isSentinel(#t9) ?{core::int?} let final core::int? #t10 = null in _in::isSentinel(#y) ?{core::int?} #y = #t10 : throw new _in::LateError::localADI("y") : #t9;
-  core::print(#y#get(){() → core::int?});
+  late final core::int? x = 1.{core::int::unary-}(){() → core::int};
+  core::print(x);
+  late final core::int? y = null;
+  core::print(y);
 }
 static method testNonNullableInitializedFinalLocal() → void {
-  lowered final core::int? #x;
-  function #x#get() → core::int
-    return let final core::int? #t11 = #x in #t11 == null ?{core::int} let final core::int #t12 = 1.{core::int::unary-}(){() → core::int} in #x == null ?{core::int} #x = #t12 : throw new _in::LateError::localADI("x") : #t11{core::int};
-  core::print(#x#get(){() → core::int});
+  late final core::int x = 1.{core::int::unary-}(){() → core::int};
+  core::print(x);
 }
diff --git a/pkg/front_end/testcases/dart2js/late_locals.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/late_locals.dart.strong.transformed.expect
index 54ef534..bac60ce 100644
--- a/pkg/front_end/testcases/dart2js/late_locals.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/late_locals.dart.strong.transformed.expect
@@ -2,7 +2,6 @@
 import self as self;
 import "dart:_late_helper" as _la;
 import "dart:core" as core;
-import "dart:_internal" as _in;
 
 static method main() → void {
   self::testNullableUninitializedNonFinalLocal();
@@ -35,48 +34,30 @@
   core::print(x.{_la::_Cell::readLocal}<core::int>(){() → core::int});
 }
 static method testNullableInitializedNonFinalLocal() → void {
-  lowered core::int? #x = _in::createSentinel<core::int?>();
-  function #x#get() → core::int?
-    return let final core::int? #t1 = #x in _in::isSentinel(#t1) ?{core::int?} #x = 1.{core::int::unary-}(){() → core::int} : #t1{core::int?};
-  function #x#set(core::int? #t2) → dynamic
-    return #x = #t2;
-  core::print(#x#get(){() → core::int?});
-  #x#set(42){(core::int?) → dynamic};
-  core::print(#x#get(){() → core::int?});
-  lowered core::int? #y = _in::createSentinel<core::int?>();
-  function #y#get() → core::int?
-    return let final core::int? #t3 = #y in _in::isSentinel(#t3) ?{core::int?} #y = null : #t3{core::int?};
-  function #y#set(core::int? #t4) → dynamic
-    return #y = #t4;
-  core::print(#y#get(){() → core::int?});
-  #y#set(42){(core::int?) → dynamic};
-  core::print(#y#get(){() → core::int?});
+  final _la::_InitializedCell x = new _la::_InitializedCell::•(() → core::int? => 1.{core::int::unary-}(){() → core::int});
+  core::print(x.{_la::_InitializedCell::read}<core::int>(){() → core::int});
+  x.{_la::_InitializedCell::value} = 42;
+  core::print(x.{_la::_InitializedCell::read}<core::int>(){() → core::int});
+  final _la::_InitializedCell y = new _la::_InitializedCell::•(() → core::int? => null);
+  core::print(y.{_la::_InitializedCell::read}<core::int?>(){() → core::int?});
+  y.{_la::_InitializedCell::value} = 42;
+  core::print(y.{_la::_InitializedCell::read}<core::int>(){() → core::int});
 }
 static method testNonNullableInitializedNonFinalLocal() → void {
-  lowered core::int? #x;
-  function #x#get() → core::int
-    return let final core::int? #t5 = #x in #t5 == null ?{core::int} #x = 1.{core::int::unary-}(){() → core::int} : #t5{core::int};
-  function #x#set(core::int #t6) → dynamic
-    return #x = #t6;
-  core::print(#x#get(){() → core::int});
-  #x#set(42){(core::int) → dynamic};
-  core::print(#x#get(){() → core::int});
+  final _la::_InitializedCell x = new _la::_InitializedCell::•(() → core::int => 1.{core::int::unary-}(){() → core::int});
+  core::print(x.{_la::_InitializedCell::read}<core::int>(){() → core::int});
+  x.{_la::_InitializedCell::value} = 42;
+  core::print(x.{_la::_InitializedCell::read}<core::int>(){() → core::int});
 }
 static method testNullableInitializedFinalLocal() → void {
-  lowered final core::int? #x = _in::createSentinel<core::int?>();
-  function #x#get() → core::int?
-    return let final core::int? #t7 = #x in _in::isSentinel(#t7) ?{core::int?} let final core::int? #t8 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int?} #x = #t8 : throw new _in::LateError::localADI("x") : #t7;
-  core::print(#x#get(){() → core::int?});
-  lowered final core::int? #y = _in::createSentinel<core::int?>();
-  function #y#get() → core::int?
-    return let final core::int? #t9 = #y in _in::isSentinel(#t9) ?{core::int?} let final core::int? #t10 = null in _in::isSentinel(#y) ?{core::int?} #y = #t10 : throw new _in::LateError::localADI("y") : #t9;
-  core::print(#y#get(){() → core::int?});
+  final _la::_InitializedCell x = new _la::_InitializedCell::•(() → core::int? => 1.{core::int::unary-}(){() → core::int});
+  core::print(x.{_la::_InitializedCell::readFinal}<core::int?>(){() → core::int?});
+  final _la::_InitializedCell y = new _la::_InitializedCell::•(() → core::int? => null);
+  core::print(y.{_la::_InitializedCell::readFinal}<core::int?>(){() → core::int?});
 }
 static method testNonNullableInitializedFinalLocal() → void {
-  lowered final core::int? #x;
-  function #x#get() → core::int
-    return let final core::int? #t11 = #x in #t11 == null ?{core::int} let final core::int #t12 = 1.{core::int::unary-}(){() → core::int} in #x == null ?{core::int} #x = #t12 : throw new _in::LateError::localADI("x") : #t11{core::int};
-  core::print(#x#get(){() → core::int});
+  final _la::_InitializedCell x = new _la::_InitializedCell::•(() → core::int => 1.{core::int::unary-}(){() → core::int});
+  core::print(x.{_la::_InitializedCell::readFinal}<core::int>(){() → core::int});
 }
 
 
@@ -84,8 +65,5 @@
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_locals.dart:41:17 -> DoubleConstant(-1.0)
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_locals.dart:53:16 -> DoubleConstant(-1.0)
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_locals.dart:60:23 -> DoubleConstant(-1.0)
-Evaluated: VariableGet @ org-dartlang-testcase:///late_locals.dart:60:19 -> DoubleConstant(-1.0)
-Evaluated: VariableGet @ org-dartlang-testcase:///late_locals.dart:63:19 -> NullConstant(null)
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_locals.dart:68:22 -> DoubleConstant(-1.0)
-Evaluated: VariableGet @ org-dartlang-testcase:///late_locals.dart:68:18 -> DoubleConstant(-1.0)
-Extra constant evaluation: evaluated: 130, effectively constant: 7
+Extra constant evaluation: evaluated: 81, effectively constant: 4
diff --git a/pkg/front_end/testcases/dart2js/late_locals.dart.weak.expect b/pkg/front_end/testcases/dart2js/late_locals.dart.weak.expect
index 0e44ebb..0ecd84b 100644
--- a/pkg/front_end/testcases/dart2js/late_locals.dart.weak.expect
+++ b/pkg/front_end/testcases/dart2js/late_locals.dart.weak.expect
@@ -1,7 +1,6 @@
 library /*isNonNullableByDefault*/;
 import self as self;
 import "dart:core" as core;
-import "dart:_internal" as _in;
 
 static method main() → void {
   self::testNullableUninitializedNonFinalLocal();
@@ -34,46 +33,28 @@
   core::print(x);
 }
 static method testNullableInitializedNonFinalLocal() → void {
-  lowered core::int? #x = _in::createSentinel<core::int?>();
-  function #x#get() → core::int?
-    return let final core::int? #t1 = #x in _in::isSentinel(#t1) ?{core::int?} #x = 1.{core::int::unary-}(){() → core::int} : #t1{core::int?};
-  function #x#set(core::int? #t2) → dynamic
-    return #x = #t2;
-  core::print(#x#get(){() → core::int?});
-  #x#set(42){(core::int?) → dynamic};
-  core::print(#x#get(){() → core::int?});
-  lowered core::int? #y = _in::createSentinel<core::int?>();
-  function #y#get() → core::int?
-    return let final core::int? #t3 = #y in _in::isSentinel(#t3) ?{core::int?} #y = null : #t3{core::int?};
-  function #y#set(core::int? #t4) → dynamic
-    return #y = #t4;
-  core::print(#y#get(){() → core::int?});
-  #y#set(42){(core::int?) → dynamic};
-  core::print(#y#get(){() → core::int?});
+  late core::int? x = 1.{core::int::unary-}(){() → core::int};
+  core::print(x{core::int});
+  x = 42;
+  core::print(x{core::int});
+  late core::int? y = null;
+  core::print(y);
+  y = 42;
+  core::print(y{core::int});
 }
 static method testNonNullableInitializedNonFinalLocal() → void {
-  lowered core::int? #x = _in::createSentinel<core::int>();
-  function #x#get() → core::int
-    return let final core::int? #t5 = #x in _in::isSentinel(#t5) ?{core::int} #x = 1.{core::int::unary-}(){() → core::int} : #t5{core::int};
-  function #x#set(core::int #t6) → dynamic
-    return #x = #t6;
-  core::print(#x#get(){() → core::int});
-  #x#set(42){(core::int) → dynamic};
-  core::print(#x#get(){() → core::int});
+  late core::int x = 1.{core::int::unary-}(){() → core::int};
+  core::print(x);
+  x = 42;
+  core::print(x);
 }
 static method testNullableInitializedFinalLocal() → void {
-  lowered final core::int? #x = _in::createSentinel<core::int?>();
-  function #x#get() → core::int?
-    return let final core::int? #t7 = #x in _in::isSentinel(#t7) ?{core::int?} let final core::int? #t8 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int?} #x = #t8 : throw new _in::LateError::localADI("x") : #t7;
-  core::print(#x#get(){() → core::int?});
-  lowered final core::int? #y = _in::createSentinel<core::int?>();
-  function #y#get() → core::int?
-    return let final core::int? #t9 = #y in _in::isSentinel(#t9) ?{core::int?} let final core::int? #t10 = null in _in::isSentinel(#y) ?{core::int?} #y = #t10 : throw new _in::LateError::localADI("y") : #t9;
-  core::print(#y#get(){() → core::int?});
+  late final core::int? x = 1.{core::int::unary-}(){() → core::int};
+  core::print(x);
+  late final core::int? y = null;
+  core::print(y);
 }
 static method testNonNullableInitializedFinalLocal() → void {
-  lowered final core::int? #x = _in::createSentinel<core::int>();
-  function #x#get() → core::int
-    return let final core::int #t11 = #x in _in::isSentinel(#t11) ?{core::int} let final core::int #t12 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int} #x = #t12 : throw new _in::LateError::localADI("x") : #t11;
-  core::print(#x#get(){() → core::int});
+  late final core::int x = 1.{core::int::unary-}(){() → core::int};
+  core::print(x);
 }
diff --git a/pkg/front_end/testcases/dart2js/late_locals.dart.weak.transformed.expect b/pkg/front_end/testcases/dart2js/late_locals.dart.weak.transformed.expect
index 0d8bf0e..bac60ce 100644
--- a/pkg/front_end/testcases/dart2js/late_locals.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/late_locals.dart.weak.transformed.expect
@@ -2,7 +2,6 @@
 import self as self;
 import "dart:_late_helper" as _la;
 import "dart:core" as core;
-import "dart:_internal" as _in;
 
 static method main() → void {
   self::testNullableUninitializedNonFinalLocal();
@@ -35,48 +34,30 @@
   core::print(x.{_la::_Cell::readLocal}<core::int>(){() → core::int});
 }
 static method testNullableInitializedNonFinalLocal() → void {
-  lowered core::int? #x = _in::createSentinel<core::int?>();
-  function #x#get() → core::int?
-    return let final core::int? #t1 = #x in _in::isSentinel(#t1) ?{core::int?} #x = 1.{core::int::unary-}(){() → core::int} : #t1{core::int?};
-  function #x#set(core::int? #t2) → dynamic
-    return #x = #t2;
-  core::print(#x#get(){() → core::int?});
-  #x#set(42){(core::int?) → dynamic};
-  core::print(#x#get(){() → core::int?});
-  lowered core::int? #y = _in::createSentinel<core::int?>();
-  function #y#get() → core::int?
-    return let final core::int? #t3 = #y in _in::isSentinel(#t3) ?{core::int?} #y = null : #t3{core::int?};
-  function #y#set(core::int? #t4) → dynamic
-    return #y = #t4;
-  core::print(#y#get(){() → core::int?});
-  #y#set(42){(core::int?) → dynamic};
-  core::print(#y#get(){() → core::int?});
+  final _la::_InitializedCell x = new _la::_InitializedCell::•(() → core::int? => 1.{core::int::unary-}(){() → core::int});
+  core::print(x.{_la::_InitializedCell::read}<core::int>(){() → core::int});
+  x.{_la::_InitializedCell::value} = 42;
+  core::print(x.{_la::_InitializedCell::read}<core::int>(){() → core::int});
+  final _la::_InitializedCell y = new _la::_InitializedCell::•(() → core::int? => null);
+  core::print(y.{_la::_InitializedCell::read}<core::int?>(){() → core::int?});
+  y.{_la::_InitializedCell::value} = 42;
+  core::print(y.{_la::_InitializedCell::read}<core::int>(){() → core::int});
 }
 static method testNonNullableInitializedNonFinalLocal() → void {
-  lowered core::int? #x = _in::createSentinel<core::int>();
-  function #x#get() → core::int
-    return let final core::int? #t5 = #x in _in::isSentinel(#t5) ?{core::int} #x = 1.{core::int::unary-}(){() → core::int} : #t5{core::int};
-  function #x#set(core::int #t6) → dynamic
-    return #x = #t6;
-  core::print(#x#get(){() → core::int});
-  #x#set(42){(core::int) → dynamic};
-  core::print(#x#get(){() → core::int});
+  final _la::_InitializedCell x = new _la::_InitializedCell::•(() → core::int => 1.{core::int::unary-}(){() → core::int});
+  core::print(x.{_la::_InitializedCell::read}<core::int>(){() → core::int});
+  x.{_la::_InitializedCell::value} = 42;
+  core::print(x.{_la::_InitializedCell::read}<core::int>(){() → core::int});
 }
 static method testNullableInitializedFinalLocal() → void {
-  lowered final core::int? #x = _in::createSentinel<core::int?>();
-  function #x#get() → core::int?
-    return let final core::int? #t7 = #x in _in::isSentinel(#t7) ?{core::int?} let final core::int? #t8 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int?} #x = #t8 : throw new _in::LateError::localADI("x") : #t7;
-  core::print(#x#get(){() → core::int?});
-  lowered final core::int? #y = _in::createSentinel<core::int?>();
-  function #y#get() → core::int?
-    return let final core::int? #t9 = #y in _in::isSentinel(#t9) ?{core::int?} let final core::int? #t10 = null in _in::isSentinel(#y) ?{core::int?} #y = #t10 : throw new _in::LateError::localADI("y") : #t9;
-  core::print(#y#get(){() → core::int?});
+  final _la::_InitializedCell x = new _la::_InitializedCell::•(() → core::int? => 1.{core::int::unary-}(){() → core::int});
+  core::print(x.{_la::_InitializedCell::readFinal}<core::int?>(){() → core::int?});
+  final _la::_InitializedCell y = new _la::_InitializedCell::•(() → core::int? => null);
+  core::print(y.{_la::_InitializedCell::readFinal}<core::int?>(){() → core::int?});
 }
 static method testNonNullableInitializedFinalLocal() → void {
-  lowered final core::int? #x = _in::createSentinel<core::int>();
-  function #x#get() → core::int
-    return let final core::int #t11 = #x in _in::isSentinel(#t11) ?{core::int} let final core::int #t12 = 1.{core::int::unary-}(){() → core::int} in _in::isSentinel(#x) ?{core::int} #x = #t12 : throw new _in::LateError::localADI("x") : #t11;
-  core::print(#x#get(){() → core::int});
+  final _la::_InitializedCell x = new _la::_InitializedCell::•(() → core::int => 1.{core::int::unary-}(){() → core::int});
+  core::print(x.{_la::_InitializedCell::readFinal}<core::int>(){() → core::int});
 }
 
 
@@ -84,8 +65,5 @@
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_locals.dart:41:17 -> DoubleConstant(-1.0)
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_locals.dart:53:16 -> DoubleConstant(-1.0)
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_locals.dart:60:23 -> DoubleConstant(-1.0)
-Evaluated: VariableGet @ org-dartlang-testcase:///late_locals.dart:60:19 -> DoubleConstant(-1.0)
-Evaluated: VariableGet @ org-dartlang-testcase:///late_locals.dart:63:19 -> NullConstant(null)
 Evaluated: InstanceInvocation @ org-dartlang-testcase:///late_locals.dart:68:22 -> DoubleConstant(-1.0)
-Evaluated: VariableGet @ org-dartlang-testcase:///late_locals.dart:68:18 -> DoubleConstant(-1.0)
-Extra constant evaluation: evaluated: 132, effectively constant: 7
+Extra constant evaluation: evaluated: 81, effectively constant: 4
diff --git a/runtime/vm/class_id.h b/runtime/vm/class_id.h
index ed8a237..f59c94b 100644
--- a/runtime/vm/class_id.h
+++ b/runtime/vm/class_id.h
@@ -32,6 +32,7 @@
   V(Code)                                                                      \
   V(Instructions)                                                              \
   V(InstructionsSection)                                                       \
+  V(InstructionsTable)                                                         \
   V(ObjectPool)                                                                \
   V(PcDescriptors)                                                             \
   V(CodeSourceMap)                                                             \
@@ -420,7 +421,8 @@
          IsOneByteStringClassId(index) || IsTwoByteStringClassId(index) ||
          IsTypedDataClassId(index) || (index == kContextCid) ||
          (index == kTypeArgumentsCid) || (index == kInstructionsCid) ||
-         (index == kInstructionsSectionCid) || (index == kObjectPoolCid) ||
+         (index == kInstructionsSectionCid) ||
+         (index == kInstructionsTableCid) || (index == kObjectPoolCid) ||
          (index == kPcDescriptorsCid) || (index == kCodeSourceMapCid) ||
          (index == kCompressedStackMapsCid) ||
          (index == kLocalVarDescriptorsCid) ||
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 537bfe0..585c296 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -976,8 +976,21 @@
                                      Function::InstanceSize());
       ReadFromTo(func);
 
+#if defined(DEBUG)
+      func->untag()->entry_point_ = 0;
+      func->untag()->unchecked_entry_point_ = 0;
+#endif
+
       if (kind == Snapshot::kFullAOT) {
-        func->untag()->code_ = static_cast<CodePtr>(d->ReadRef());
+        const intptr_t code_index = d->ReadUnsigned();
+        CodePtr code = static_cast<CodePtr>(d->Ref(code_index));
+        func->untag()->code_ = code;
+        if (Code::IsUnknownDartCode(code)) {
+          const uword entry_point = d->instructions_table().EntryPointAt(
+              code_index - d->code_start_index());
+          func->untag()->entry_point_ = entry_point;
+          func->untag()->unchecked_entry_point_ = entry_point;
+        }
       } else if (kind == Snapshot::kFullJIT) {
         NOT_IN_PRECOMPILED(func->untag()->unoptimized_code_ =
                                static_cast<CodePtr>(d->ReadRef()));
@@ -985,11 +998,6 @@
         func->untag()->ic_data_array_ = static_cast<ArrayPtr>(d->ReadRef());
       }
 
-#if defined(DEBUG)
-      func->untag()->entry_point_ = 0;
-      func->untag()->unchecked_entry_point_ = 0;
-#endif
-
 #if !defined(DART_PRECOMPILED_RUNTIME)
       if (kind != Snapshot::kFullAOT) {
         func->untag()->token_pos_ = d->ReadTokenPosition();
@@ -1021,13 +1029,16 @@
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         func ^= refs.At(i);
         ASSERT(func.ptr()->untag()->code()->IsCode());
-        uword entry_point = func.ptr()->untag()->code()->untag()->entry_point_;
-        ASSERT(entry_point != 0);
-        func.ptr()->untag()->entry_point_ = entry_point;
-        uword unchecked_entry_point =
-            func.ptr()->untag()->code()->untag()->unchecked_entry_point_;
-        ASSERT(unchecked_entry_point != 0);
-        func.ptr()->untag()->unchecked_entry_point_ = unchecked_entry_point;
+        if (!Code::IsUnknownDartCode(func.ptr()->untag()->code())) {
+          uword entry_point =
+              func.ptr()->untag()->code()->untag()->entry_point_;
+          ASSERT(entry_point != 0);
+          func.ptr()->untag()->entry_point_ = entry_point;
+          uword unchecked_entry_point =
+              func.ptr()->untag()->code()->untag()->unchecked_entry_point_;
+          ASSERT(unchecked_entry_point != 0);
+          func.ptr()->untag()->unchecked_entry_point_ = unchecked_entry_point;
+        }
       }
     } else if (d->kind() == Snapshot::kFullJIT) {
       Function& func = Function::Handle(d->zone());
@@ -1743,8 +1754,7 @@
 class CodeSerializationCluster : public SerializationCluster {
  public:
   explicit CodeSerializationCluster(Heap* heap)
-      : SerializationCluster("Code", compiler::target::Code::InstanceSize()),
-        array_(Array::Handle()) {}
+      : SerializationCluster("Code"), array_(Array::Handle()) {}
   ~CodeSerializationCluster() {}
 
   void Trace(Serializer* s, ObjectPtr object) {
@@ -1898,14 +1908,22 @@
     const intptr_t count = objects_.length();
     s->WriteUnsigned(count);
     for (intptr_t i = 0; i < count; i++) {
-      CodePtr code = objects_[i];
-      s->AssignRef(code);
+      WriteAlloc(s, objects_[i]);
     }
     const intptr_t deferred_count = deferred_objects_.length();
     s->WriteUnsigned(deferred_count);
     for (intptr_t i = 0; i < deferred_count; i++) {
-      CodePtr code = deferred_objects_[i];
-      s->AssignRef(code);
+      WriteAlloc(s, deferred_objects_[i]);
+    }
+  }
+
+  void WriteAlloc(Serializer* s, CodePtr code) {
+    s->AssignRef(code);
+    AutoTraceObjectName(code, MakeDisambiguatedCodeName(s, code));
+    const int32_t state_bits = code->untag()->state_bits_;
+    s->Write<int32_t>(state_bits);
+    if (!Code::DiscardedBit::decode(state_bits)) {
+      target_memory_size_ += compiler::target::Code::InstanceSize();
     }
   }
 
@@ -1951,14 +1969,6 @@
                            active_unchecked_offset, code, deferred);
     }
 
-    if (s->InCurrentLoadingUnit(code->untag()->compressed_stackmaps_)) {
-      WriteField(code, compressed_stackmaps_);
-    } else {
-      WriteFieldValue(compressed_stackmaps_, CompressedStackMaps::null());
-    }
-
-    s->Write<int32_t>(code->untag()->state_bits_);
-
 #if defined(DART_PRECOMPILER)
     if (FLAG_write_v8_snapshot_profile_to != nullptr) {
       // If we are writing V8 snapshot profile then attribute references going
@@ -2023,6 +2033,11 @@
     WriteField(code, exception_handlers_);
     WriteField(code, pc_descriptors_);
     WriteField(code, catch_entry_);
+    if (s->InCurrentLoadingUnit(code->untag()->compressed_stackmaps_)) {
+      WriteField(code, compressed_stackmaps_);
+    } else {
+      WriteFieldValue(compressed_stackmaps_, CompressedStackMaps::null());
+    }
     if (FLAG_precompiled_mode && FLAG_dwarf_stack_traces_mode) {
       WriteFieldValue(inlined_id_to_function_, Array::null());
       WriteFieldValue(code_source_map_, CodeSourceMap::null());
@@ -2097,21 +2112,33 @@
   void ReadAlloc(Deserializer* d, bool stamp_canonical) {
     PageSpace* old_space = d->heap()->old_space();
     start_index_ = d->next_index();
+    d->set_code_start_index(start_index_);
     const intptr_t count = d->ReadUnsigned();
     for (intptr_t i = 0; i < count; i++) {
-      auto code = AllocateUninitialized(old_space, Code::InstanceSize(0));
-      d->AssignRef(code);
+      ReadAllocOneCode(d, old_space);
     }
     stop_index_ = d->next_index();
     deferred_start_index_ = d->next_index();
     const intptr_t deferred_count = d->ReadUnsigned();
     for (intptr_t i = 0; i < deferred_count; i++) {
-      auto code = AllocateUninitialized(old_space, Code::InstanceSize(0));
-      d->AssignRef(code);
+      ReadAllocOneCode(d, old_space);
     }
     deferred_stop_index_ = d->next_index();
   }
 
+  void ReadAllocOneCode(Deserializer* d, PageSpace* old_space) {
+    const int32_t state_bits = d->Read<int32_t>();
+    if (Code::DiscardedBit::decode(state_bits)) {
+      ASSERT(StubCode::HasBeenInitialized());
+      d->AssignRef(StubCode::UnknownDartCode().ptr());
+    } else {
+      auto code = static_cast<CodePtr>(
+          AllocateUninitialized(old_space, Code::InstanceSize(0)));
+      d->AssignRef(code);
+      code->untag()->state_bits_ = state_bits;
+    }
+  }
+
   void ReadFill(Deserializer* d, bool stamp_canonical) {
     ASSERT(!stamp_canonical);  // Never canonical.
     for (intptr_t id = start_index_; id < stop_index_; id++) {
@@ -2124,23 +2151,19 @@
 
   void ReadFill(Deserializer* d, intptr_t id, bool deferred) {
     auto const code = static_cast<CodePtr>(d->Ref(id));
-    Deserializer::InitializeHeader(code, kCodeCid, Code::InstanceSize(0));
-
-    d->ReadInstructions(code, deferred);
-
-    code->untag()->compressed_stackmaps_ =
-        static_cast<CompressedStackMapsPtr>(d->ReadRef());
-    code->untag()->state_bits_ = d->Read<int32_t>();
 
 #if defined(DART_PRECOMPILED_RUNTIME)
-    if (Code::IsDiscarded(code)) {
-      code->untag()->owner_ = Smi::New(kFunctionCid);
+    if (Code::IsUnknownDartCode(code)) {
+      d->ReadInstructions(code, deferred, /*discarded=*/true);
       return;
     }
-#else
-    ASSERT(!Code::IsDiscarded(code));
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
 
+    Deserializer::InitializeHeader(code, kCodeCid, Code::InstanceSize(0));
+    ASSERT(!Code::IsDiscarded(code));
+
+    d->ReadInstructions(code, deferred, /*discarded=*/false);
+
     // There would be a single global pool if this is a full AOT snapshot
     // with bare instructions.
     if (!(d->kind() == Snapshot::kFullAOT && FLAG_use_bare_instructions)) {
@@ -2154,6 +2177,8 @@
     code->untag()->pc_descriptors_ =
         static_cast<PcDescriptorsPtr>(d->ReadRef());
     code->untag()->catch_entry_ = d->ReadRef();
+    code->untag()->compressed_stackmaps_ =
+        static_cast<CompressedStackMapsPtr>(d->ReadRef());
     code->untag()->inlined_id_to_function_ =
         static_cast<ArrayPtr>(d->ReadRef());
     code->untag()->code_source_map_ =
@@ -2178,7 +2203,7 @@
   }
 
   void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
-    d->EndInstructions(refs, start_index_, stop_index_);
+    d->EndInstructions();
 
 #if !defined(PRODUCT)
     if (!CodeObservers::AreActive() && !FLAG_support_disassembler) return;
@@ -5975,6 +6000,7 @@
       ASSERT(deferred_object->IsCode());
       CodePtr code = static_cast<CodePtr>(deferred_object->ptr());
       ASSERT(s->RefId(code) == (start_index + i));
+      ASSERT(!Code::IsDiscarded(code));
       s->WriteInstructions(code->untag()->instructions_,
                            code->untag()->unchecked_offset_, code, false);
       if (!FLAG_use_bare_instructions) {
@@ -6030,7 +6056,8 @@
     deferred_stop_index_ = deferred_start_index_ + d->ReadUnsigned();
     for (intptr_t id = deferred_start_index_; id < deferred_stop_index_; id++) {
       CodePtr code = static_cast<CodePtr>(d->Ref(id));
-      d->ReadInstructions(code, false);
+      ASSERT(!Code::IsUnknownDartCode(code));
+      d->ReadInstructions(code, /*deferred=*/false, /*discarded=*/false);
       if (code->untag()->owner_->IsHeapObject() &&
           code->untag()->owner_->IsFunction()) {
         FunctionPtr func = static_cast<FunctionPtr>(code->untag()->owner_);
@@ -6070,12 +6097,13 @@
     if (isolate_group->dispatch_table_snapshot() != nullptr) {
       ReadStream stream(isolate_group->dispatch_table_snapshot(),
                         isolate_group->dispatch_table_snapshot_size());
-      d->ReadDispatchTable(&stream);
+      d->ReadDispatchTable(&stream, /*deferred=*/true, deferred_start_index_,
+                           deferred_stop_index_);
     }
   }
 
   void PostLoad(Deserializer* d, const Array& refs) {
-    d->EndInstructions(refs, deferred_start_index_, deferred_stop_index_);
+    d->EndInstructions();
     unit_.set_base_objects(refs);
   }
 
@@ -6523,8 +6551,8 @@
 }
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
-void Serializer::PrepareInstructions() {
-  if (!Snapshot::IncludesCode(kind())) return;
+intptr_t Serializer::PrepareInstructions() {
+  if (!Snapshot::IncludesCode(kind())) return 0;
 
   CodeSerializationCluster* cluster =
       static_cast<CodeSerializationCluster*>(clusters_by_cid_[kCodeCid]);
@@ -6576,8 +6604,10 @@
     GrowableArray<ImageWriterCommand> writer_commands;
     RelocateCodeObjects(vm_, &code_objects, &writer_commands);
     image_writer_->PrepareForSerialization(&writer_commands);
+    return code_objects.length();
   }
 #endif  // defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
+  return 0;
 }
 
 void Serializer::WriteInstructions(InstructionsPtr instr,
@@ -6614,6 +6644,16 @@
         (unchecked_offset << 1) | (Code::HasMonomorphicEntry(code) ? 0x1 : 0x0);
     WriteUnsigned(payload_info);
     previous_text_offset_ = offset;
+
+    if (Code::IsDiscarded(code)) {
+      // Discarded Code objects are not supported in the vm isolate snapshot.
+      ASSERT(!vm_);
+      // Stack maps of discarded Code objects are written along with
+      // instructions so they can be added to instructions table during
+      // deserialization.
+      WritePropertyRef(code->untag()->compressed_stackmaps_,
+                       "compressed_stackmaps_");
+    }
     return;
   }
 #endif
@@ -6828,8 +6868,14 @@
     }
   }
   GrowableArray<SerializationCluster*> clusters;
+  // Code cluster should be deserialized before Function as
+  // FunctionDeserializationCluster::ReadFill uses instructions table
+  // which is filled in CodeDeserializationCluster::ReadFill.
+  if (clusters_by_cid_[kCodeCid] != nullptr) {
+    clusters.Add(clusters_by_cid_[kCodeCid]);
+  }
   for (intptr_t cid = 0; cid < num_cids_; cid++) {
-    if (clusters_by_cid_[cid] != nullptr) {
+    if (clusters_by_cid_[cid] != nullptr && cid != kCodeCid) {
       clusters.Add(clusters_by_cid_[cid]);
     }
   }
@@ -6842,7 +6888,7 @@
   }
 #endif
 
-  PrepareInstructions();
+  instructions_table_len_ = PrepareInstructions();
 
   intptr_t num_objects = num_base_objects_ + num_written_objects_;
 #if defined(ARCH_IS_64_BIT)
@@ -6861,6 +6907,9 @@
   } else {
     WriteUnsigned(0);
   }
+  ASSERT((instructions_table_len_ == 0) ||
+         (FLAG_precompiled_mode && FLAG_use_bare_instructions));
+  WriteUnsigned(instructions_table_len_);
 
   for (SerializationCluster* cluster : canonical_clusters) {
     cluster->WriteAndMeasureAlloc(this);
@@ -7121,6 +7170,14 @@
       clusters_by_size.Add(new (zone_) FakeSerializationCluster(
           "DispatchTable", entry_count, dispatch_table_size_));
     }
+    if (instructions_table_len_ > 0) {
+      const intptr_t memory_size =
+          compiler::target::InstructionsTable::InstanceSize(
+              instructions_table_len_) +
+          compiler::target::Array::InstanceSize(instructions_table_len_);
+      clusters_by_size.Add(new (zone_) FakeSerializationCluster(
+          "InstructionsTable", instructions_table_len_, 0, memory_size));
+    }
     clusters_by_size.Sort(CompareClusters);
     double total_size =
         static_cast<double>(bytes_written() + GetDataSize() + text_size);
@@ -7158,7 +7215,8 @@
       canonical_clusters_(nullptr),
       clusters_(nullptr),
       initial_field_table_(thread->isolate_group()->initial_field_table()),
-      is_non_root_unit_(is_non_root_unit) {
+      is_non_root_unit_(is_non_root_unit),
+      instructions_table_(InstructionsTable::Handle(thread->zone())) {
   if (Snapshot::IncludesCode(kind)) {
     ASSERT(instructions_buffer != nullptr);
     ASSERT(data_buffer != nullptr);
@@ -7308,7 +7366,10 @@
   return NULL;
 }
 
-void Deserializer::ReadDispatchTable(ReadStream* stream) {
+void Deserializer::ReadDispatchTable(ReadStream* stream,
+                                     bool deferred,
+                                     intptr_t deferred_code_start_index,
+                                     intptr_t deferred_code_end_index) {
 #if defined(DART_PRECOMPILED_RUNTIME)
   const uint8_t* table_snapshot_start = stream->AddressOfCurrentPosition();
   const intptr_t length = stream->ReadUnsigned();
@@ -7323,8 +7384,16 @@
   auto code = IG->object_store()->dispatch_table_null_error_stub();
   ASSERT(code != Code::null());
   uword null_entry = Code::EntryPointOf(code);
+  uword not_loaded_entry = StubCode::NotLoaded().EntryPoint();
 
-  auto const table = new DispatchTable(length);
+  DispatchTable* table;
+  if (deferred) {
+    table = IG->dispatch_table();
+    ASSERT(table != nullptr && table->length() == length);
+  } else {
+    ASSERT(IG->dispatch_table() == nullptr);
+    table = new DispatchTable(length);
+  }
   auto const array = table->array();
   uword value = 0;
   uword recent[kDispatchTableRecentCount] = {0};
@@ -7347,8 +7416,24 @@
       repeat_count = encoded - 1;
     } else {
       intptr_t cluster_index = encoded - kDispatchTableIndexBase;
-      code = Code::RawCast(Ref(first_code_id + cluster_index));
-      value = Code::EntryPointOf(code);
+      if (deferred) {
+        intptr_t id = first_code_id + cluster_index;
+        if ((deferred_code_start_index <= id) &&
+            (id < deferred_code_end_index)) {
+          // Deferred instructions are at the end of the instructions table.
+          value = instructions_table().EntryPointAt(
+              instructions_table().length() - deferred_code_end_index + id);
+        } else {
+          // Reuse old value from the dispatch table.
+          value = array[i];
+        }
+      } else {
+        if (cluster_index < instructions_table().length()) {
+          value = instructions_table().EntryPointAt(cluster_index);
+        } else {
+          value = not_loaded_entry;
+        }
+      }
       recent[recent_index] = value;
       recent_index = (recent_index + 1) & kDispatchTableRecentMask;
     }
@@ -7356,11 +7441,13 @@
   }
   ASSERT(repeat_count == 0);
 
-  IG->set_dispatch_table(table);
-  intptr_t table_snapshot_size =
-      stream->AddressOfCurrentPosition() - table_snapshot_start;
-  IG->set_dispatch_table_snapshot(table_snapshot_start);
-  IG->set_dispatch_table_snapshot_size(table_snapshot_size);
+  if (!deferred) {
+    IG->set_dispatch_table(table);
+    intptr_t table_snapshot_size =
+        stream->AddressOfCurrentPosition() - table_snapshot_start;
+    IG->set_dispatch_table_snapshot(table_snapshot_start);
+    IG->set_dispatch_table_snapshot_size(table_snapshot_size);
+  }
 #endif
 }
 
@@ -7481,8 +7568,11 @@
   return ApiError::New(msg, Heap::kOld);
 }
 
-void Deserializer::ReadInstructions(CodePtr code, bool deferred) {
+void Deserializer::ReadInstructions(CodePtr code,
+                                    bool deferred,
+                                    bool discarded) {
   if (deferred) {
+    ASSERT(!discarded);
 #if defined(DART_PRECOMPILED_RUNTIME)
     if (FLAG_use_bare_instructions) {
       uword entry_point = StubCode::NotLoaded().EntryPoint();
@@ -7508,8 +7598,6 @@
 
 #if defined(DART_PRECOMPILED_RUNTIME)
   if (FLAG_use_bare_instructions) {
-    // There are no serialized RawInstructions objects in this mode.
-    code->untag()->instructions_ = Instructions::null();
     previous_text_offset_ += ReadUnsigned();
     const uword payload_start =
         image_reader_->GetBareInstructionsAt(previous_text_offset_);
@@ -7528,11 +7616,23 @@
     const uword monomorphic_entry_point =
         payload_start + monomorphic_entry_offset;
 
-    code->untag()->entry_point_ = entry_point;
-    code->untag()->unchecked_entry_point_ = entry_point + unchecked_offset;
-    code->untag()->monomorphic_entry_point_ = monomorphic_entry_point;
-    code->untag()->monomorphic_unchecked_entry_point_ =
-        monomorphic_entry_point + unchecked_offset;
+    ObjectPtr code_descriptor = code;
+    if (discarded) {
+      code_descriptor = static_cast<CompressedStackMapsPtr>(ReadRef());
+    }
+
+    instructions_table_.SetEntryAt(instructions_index_++, payload_start,
+                                   has_monomorphic_entrypoint, code_descriptor);
+
+    if (!discarded) {
+      // There are no serialized RawInstructions objects in this mode.
+      code->untag()->instructions_ = Instructions::null();
+      code->untag()->entry_point_ = entry_point;
+      code->untag()->unchecked_entry_point_ = entry_point + unchecked_offset;
+      code->untag()->monomorphic_entry_point_ = monomorphic_entry_point;
+      code->untag()->monomorphic_unchecked_entry_point_ =
+          monomorphic_entry_point + unchecked_offset;
+    }
     return;
   }
 #endif
@@ -7554,38 +7654,32 @@
   Code::InitializeCachedEntryPointsFrom(code, instr, unchecked_offset);
 }
 
-void Deserializer::EndInstructions(const Array& refs,
-                                   intptr_t start_index,
-                                   intptr_t stop_index) {
+void Deserializer::EndInstructions() {
 #if defined(DART_PRECOMPILED_RUNTIME)
   if (FLAG_use_bare_instructions) {
     uword previous_end = image_reader_->GetBareInstructionsEnd();
-    for (intptr_t id = stop_index - 1; id >= start_index; id--) {
-      CodePtr code = static_cast<CodePtr>(refs.At(id));
-      uword start = Code::PayloadStartOf(code);
+    for (intptr_t i = instructions_index_ - 1; i >= 0; --i) {
+      ObjectPtr descriptor = instructions_table_.DescriptorAt(i);
+      uword start = instructions_table_.PayloadStartAt(i);
       ASSERT(start <= previous_end);
-      code->untag()->instructions_length_ = previous_end - start;
+      if (descriptor->IsCode()) {
+        CodePtr code = static_cast<CodePtr>(descriptor);
+        code->untag()->instructions_length_ = previous_end - start;
+      }
       previous_end = start;
     }
 
-    // Build an array of code objects representing the order in which the
-    // [Code]'s instructions will be located in memory.
-    const intptr_t count = stop_index - start_index;
-    const Array& order_table =
-        Array::Handle(zone_, Array::New(count, Heap::kOld));
-    Object& code = Object::Handle(zone_);
-    for (intptr_t i = 0; i < count; i++) {
-      code = refs.At(start_index + i);
-      order_table.SetAt(i, code);
-    }
     ObjectStore* object_store = IsolateGroup::Current()->object_store();
-    GrowableObjectArray& order_tables =
-        GrowableObjectArray::Handle(zone_, object_store->code_order_tables());
-    if (order_tables.IsNull()) {
-      order_tables = GrowableObjectArray::New(Heap::kOld);
-      object_store->set_code_order_tables(order_tables);
+    GrowableObjectArray& tables =
+        GrowableObjectArray::Handle(zone_, object_store->instructions_tables());
+    if (tables.IsNull()) {
+      tables = GrowableObjectArray::New(Heap::kOld);
+      object_store->set_instructions_tables(tables);
     }
-    order_tables.Add(order_table, Heap::kOld);
+    if ((tables.Length() == 0) ||
+        (tables.At(tables.Length() - 1) != instructions_table_.ptr())) {
+      tables.Add(instructions_table_, Heap::kOld);
+    }
   }
 #endif
 }
@@ -7616,6 +7710,7 @@
   num_canonical_clusters_ = ReadUnsigned();
   num_clusters_ = ReadUnsigned();
   const intptr_t initial_field_table_len = ReadUnsigned();
+  const intptr_t instructions_table_len = ReadUnsigned();
 
   canonical_clusters_ = new DeserializationCluster*[num_canonical_clusters_];
   clusters_ = new DeserializationCluster*[num_clusters_];
@@ -7625,6 +7720,18 @@
     ASSERT_EQUAL(initial_field_table_->NumFieldIds(), initial_field_table_len);
   }
 
+#if defined(DART_PRECOMPILED_RUNTIME)
+  if (instructions_table_len > 0) {
+    ASSERT(FLAG_precompiled_mode && FLAG_use_bare_instructions);
+    const uword start_pc = image_reader_->GetBareInstructionsAt(0);
+    const uword end_pc = image_reader_->GetBareInstructionsEnd();
+    instructions_table_ =
+        InstructionsTable::New(instructions_table_len, start_pc, end_pc);
+  }
+#else
+  ASSERT(instructions_table_len == 0);
+#endif  // defined(DART_PRECOMPILED_RUNTIME)
+
   bool primary;
   {
     // The deserializer initializes objects without using the write barrier,
diff --git a/runtime/vm/clustered_snapshot.h b/runtime/vm/clustered_snapshot.h
index 79ef250..cbb34de 100644
--- a/runtime/vm/clustered_snapshot.h
+++ b/runtime/vm/clustered_snapshot.h
@@ -373,7 +373,10 @@
     Write<int32_t>(cid);
   }
 
-  void PrepareInstructions();
+  // Sorts Code objects and reorders instructions before writing snapshot.
+  // Returns length of instructions table (in bare instructions mode).
+  intptr_t PrepareInstructions();
+
   void WriteInstructions(InstructionsPtr instr,
                          uint32_t unchecked_offset,
                          CodePtr code,
@@ -460,6 +463,7 @@
 
   intptr_t dispatch_table_size_ = 0;
   intptr_t bytes_heap_allocated_ = 0;
+  intptr_t instructions_table_len_ = 0;
 
   // True if writing VM snapshot, false for Isolate snapshot.
   bool vm_;
@@ -652,18 +656,21 @@
     return Read<int32_t>();
   }
 
-  void ReadInstructions(CodePtr code, bool deferred);
-  void EndInstructions(const Array& refs,
-                       intptr_t start_index,
-                       intptr_t stop_index);
+  void ReadInstructions(CodePtr code, bool deferred, bool discarded);
+  void EndInstructions();
   ObjectPtr GetObjectAt(uint32_t offset) const;
 
   void Deserialize(DeserializationRoots* roots);
 
   DeserializationCluster* ReadCluster();
 
-  void ReadDispatchTable() { ReadDispatchTable(&stream_); }
-  void ReadDispatchTable(ReadStream* stream);
+  void ReadDispatchTable() {
+    ReadDispatchTable(&stream_, /*deferred=*/false, -1, -1);
+  }
+  void ReadDispatchTable(ReadStream* stream,
+                         bool deferred,
+                         intptr_t deferred_code_start_index,
+                         intptr_t deferred_code_end_index);
 
   intptr_t next_index() const { return next_ref_index_; }
   Heap* heap() const { return heap_; }
@@ -671,6 +678,11 @@
   Snapshot::Kind kind() const { return kind_; }
   FieldTable* initial_field_table() const { return initial_field_table_; }
   bool is_non_root_unit() const { return is_non_root_unit_; }
+  void set_code_start_index(intptr_t value) { code_start_index_ = value; }
+  intptr_t code_start_index() { return code_start_index_; }
+  const InstructionsTable& instructions_table() const {
+    return instructions_table_;
+  }
 
  private:
   Heap* heap_;
@@ -685,10 +697,13 @@
   ArrayPtr refs_;
   intptr_t next_ref_index_;
   intptr_t previous_text_offset_;
+  intptr_t code_start_index_ = 0;
+  intptr_t instructions_index_ = 0;
   DeserializationCluster** canonical_clusters_;
   DeserializationCluster** clusters_;
   FieldTable* initial_field_table_;
   const bool is_non_root_unit_;
+  InstructionsTable& instructions_table_;
 };
 
 #define ReadFromTo(obj, ...) d->ReadFromTo(obj, ##__VA_ARGS__);
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index 8946b68..8dacf84 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -1581,6 +1581,7 @@
                 Function::CreateDynamicInvocationForwarderName(selector);
             function2 = function.GetDynamicInvocationForwarder(selector2);
             AddFunction(function2, RetainReasons::kDynamicInvocationForwarder);
+            functions_called_dynamically_.Insert(function2);
           }
         } else if (function.kind() == UntaggedFunction::kRegularFunction) {
           selector2 = Field::LookupGetterSymbol(selector);
@@ -1630,12 +1631,14 @@
                 function2 = function.GetDynamicInvocationForwarder(selector2);
                 AddFunction(function2,
                             RetainReasons::kDynamicInvocationForwarder);
+                functions_called_dynamically_.Insert(function2);
               }
             } else {
               if (metadata.method_or_setter_called_dynamically) {
                 function2 = function.GetDynamicInvocationForwarder(selector2);
                 AddFunction(function2,
                             RetainReasons::kDynamicInvocationForwarder);
+                functions_called_dynamically_.Insert(function2);
               }
             }
           }
@@ -2671,6 +2674,9 @@
 
 // Traverse program structure and mark Code objects
 // which do not have useful information as discarded.
+// Should be called after Precompiler::ReplaceFunctionStaticCallEntries().
+// Should be called before ProgramVisitor::Dedup() as Dedup may clear
+// static calls target table.
 void Precompiler::DiscardCodeObjects() {
   class DiscardCodeVisitor : public CodeVisitor {
    public:
@@ -2680,15 +2686,45 @@
                        const FunctionSet& functions_called_dynamically)
         : zone_(zone),
           function_(Function::Handle(zone)),
+          class_(Class::Handle(zone)),
+          library_(Library::Handle(zone)),
+          loading_unit_(LoadingUnit::Handle(zone)),
+          static_calls_target_table_(Array::Handle(zone)),
+          kind_and_offset_(Smi::Handle(zone)),
+          call_target_(Code::Handle(zone)),
+          targets_of_calls_via_code_(
+              GrowableObjectArray::Handle(zone, GrowableObjectArray::New())),
           functions_to_retain_(functions_to_retain),
           entry_point_functions_(entry_point_functions),
           functions_called_dynamically_(functions_called_dynamically) {}
 
+    // Certain static calls (e.g. between different loading units) are
+    // performed through Code objects indirectly. Such Code objects
+    // cannot be fully discarded.
+    void RecordCodeObjectsUsedForCalls(const Code& code) {
+      static_calls_target_table_ = code.static_calls_target_table();
+      if (static_calls_target_table_.IsNull()) return;
+
+      StaticCallsTable static_calls(static_calls_target_table_);
+      for (const auto& view : static_calls) {
+        kind_and_offset_ = view.Get<Code::kSCallTableKindAndOffset>();
+        auto const kind = Code::KindField::decode(kind_and_offset_.Value());
+        if (kind == Code::kCallViaCode) {
+          call_target_ =
+              Code::RawCast(view.Get<Code::kSCallTableCodeOrTypeTarget>());
+          ASSERT(!call_target_.IsNull());
+          targets_of_calls_via_code_.Add(call_target_);
+        }
+      }
+    }
+
     void VisitCode(const Code& code) override {
       ++total_code_objects_;
 
+      RecordCodeObjectsUsedForCalls(code);
+
       // Only discard Code objects corresponding to Dart functions.
-      if (!code.IsFunctionCode()) {
+      if (!code.IsFunctionCode() || code.IsUnknownDartCode()) {
         ++non_function_codes_;
         return;
       }
@@ -2740,10 +2776,39 @@
         ASSERT(!functions_called_dynamically_.ContainsKey(function_));
       }
 
+      // Retain Code objects in the non-root loading unit as
+      // they are allocated while loading root unit but filled
+      // while loading another unit.
+      class_ = function_.Owner();
+      library_ = class_.library();
+      loading_unit_ = library_.loading_unit();
+      if (loading_unit_.id() != LoadingUnit::kRootId) {
+        ++codes_with_deferred_function_;
+        return;
+      }
+
+      // Retain Code objects corresponding to FFI trampolines.
+      if (function_.IsFfiTrampoline()) {
+        ++codes_with_ffi_trampoline_function_;
+        return;
+      }
+
       code.set_is_discarded(true);
       ++discarded_codes_;
     }
 
+    void RetainCodeObjectsUsedAsCallTargets() {
+      for (intptr_t i = 0, n = targets_of_calls_via_code_.Length(); i < n;
+           ++i) {
+        call_target_ = Code::RawCast(targets_of_calls_via_code_.At(i));
+        if (call_target_.is_discarded()) {
+          call_target_.set_is_discarded(false);
+          ++codes_used_as_call_targets_;
+          --discarded_codes_;
+        }
+      }
+    }
+
     void PrintStatistics() const {
       THR_Print("Discarding Code objects:\n");
       THR_Print("    %8" Pd " non-function Codes\n", non_function_codes_);
@@ -2757,10 +2822,16 @@
                 codes_with_native_function_);
       THR_Print("    %8" Pd " Codes with async closure functions\n",
                 codes_with_async_closure_function_);
-      THR_Print("    %8" Pd " Codes with dynamically called functions\n",
-                codes_with_dynamically_called_function_);
       THR_Print("    %8" Pd " Codes with entry point functions\n",
                 codes_with_entry_point_function_);
+      THR_Print("    %8" Pd " Codes with dynamically called functions\n",
+                codes_with_dynamically_called_function_);
+      THR_Print("    %8" Pd " Codes with deferred functions\n",
+                codes_with_deferred_function_);
+      THR_Print("    %8" Pd " Codes with ffi trampoline functions\n",
+                codes_with_ffi_trampoline_function_);
+      THR_Print("    %8" Pd " Codes used as call targets\n",
+                codes_used_as_call_targets_);
       THR_Print("    %8" Pd " Codes discarded\n", discarded_codes_);
       THR_Print("    %8" Pd " Codes total\n", total_code_objects_);
     }
@@ -2768,6 +2839,13 @@
    private:
     Zone* zone_;
     Function& function_;
+    Class& class_;
+    Library& library_;
+    LoadingUnit& loading_unit_;
+    Array& static_calls_target_table_;
+    Smi& kind_and_offset_;
+    Code& call_target_;
+    GrowableObjectArray& targets_of_calls_via_code_;
     const FunctionSet& functions_to_retain_;
     const FunctionSet& entry_point_functions_;
     const FunctionSet& functions_called_dynamically_;
@@ -2780,8 +2858,11 @@
     intptr_t codes_with_invisible_function_ = 0;
     intptr_t codes_with_native_function_ = 0;
     intptr_t codes_with_async_closure_function_ = 0;
-    intptr_t codes_with_dynamically_called_function_ = 0;
     intptr_t codes_with_entry_point_function_ = 0;
+    intptr_t codes_with_dynamically_called_function_ = 0;
+    intptr_t codes_with_deferred_function_ = 0;
+    intptr_t codes_with_ffi_trampoline_function_ = 0;
+    intptr_t codes_used_as_call_targets_ = 0;
     intptr_t discarded_codes_ = 0;
   };
 
@@ -2796,6 +2877,7 @@
   DiscardCodeVisitor visitor(Z, functions_to_retain_, entry_point_functions_,
                              functions_called_dynamically_);
   ProgramVisitor::WalkProgram(Z, IG, &visitor);
+  visitor.RetainCodeObjectsUsedAsCallTargets();
 
   if (FLAG_trace_precompiler) {
     visitor.PrintStatistics();
diff --git a/runtime/vm/compiler/runtime_api.cc b/runtime/vm/compiler/runtime_api.cc
index 4db6350..0db035c 100644
--- a/runtime/vm/compiler/runtime_api.cc
+++ b/runtime/vm/compiler/runtime_api.cc
@@ -1151,6 +1151,16 @@
   return -kWordSize;
 }
 
+word InstructionsTable::InstanceSize(intptr_t length) {
+  return RoundedAllocationSize(InstructionsTable::InstanceSize() +
+                               length *
+                                   dart::InstructionsTable::kBytesPerElement);
+}
+
+word InstructionsTable::NextFieldOffset() {
+  return -kWordSize;
+}
+
 word Instructions::NextFieldOffset() {
   return -kWordSize;
 }
diff --git a/runtime/vm/compiler/runtime_api.h b/runtime/vm/compiler/runtime_api.h
index 43ec84a..de71523 100644
--- a/runtime/vm/compiler/runtime_api.h
+++ b/runtime/vm/compiler/runtime_api.h
@@ -1193,6 +1193,13 @@
   static word NextFieldOffset();
 };
 
+class InstructionsTable : public AllStatic {
+ public:
+  static word InstanceSize(intptr_t length);
+  static word InstanceSize();
+  static word NextFieldOffset();
+};
+
 class Instructions : public AllStatic {
  public:
   static const word kMonomorphicEntryOffsetJIT;
diff --git a/runtime/vm/compiler/runtime_offsets_extracted.h b/runtime/vm/compiler/runtime_offsets_extracted.h
index 0f37245..5af8a8b 100644
--- a/runtime/vm/compiler/runtime_offsets_extracted.h
+++ b/runtime/vm/compiler/runtime_offsets_extracted.h
@@ -493,6 +493,8 @@
     8;
 static constexpr dart::compiler::target::word
     InstructionsSection_UnalignedHeaderSize = 20;
+static constexpr dart::compiler::target::word InstructionsTable_InstanceSize =
+    20;
 static constexpr dart::compiler::target::word Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word Integer_InstanceSize = 4;
 static constexpr dart::compiler::target::word KernelProgramInfo_InstanceSize =
@@ -1033,6 +1035,8 @@
     16;
 static constexpr dart::compiler::target::word
     InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word InstructionsTable_InstanceSize =
+    40;
 static constexpr dart::compiler::target::word Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word KernelProgramInfo_InstanceSize =
@@ -1563,6 +1567,8 @@
     8;
 static constexpr dart::compiler::target::word
     InstructionsSection_UnalignedHeaderSize = 20;
+static constexpr dart::compiler::target::word InstructionsTable_InstanceSize =
+    20;
 static constexpr dart::compiler::target::word Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word Integer_InstanceSize = 4;
 static constexpr dart::compiler::target::word KernelProgramInfo_InstanceSize =
@@ -2104,6 +2110,8 @@
     16;
 static constexpr dart::compiler::target::word
     InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word InstructionsTable_InstanceSize =
+    40;
 static constexpr dart::compiler::target::word Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word KernelProgramInfo_InstanceSize =
@@ -2643,6 +2651,8 @@
     16;
 static constexpr dart::compiler::target::word
     InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word InstructionsTable_InstanceSize =
+    40;
 static constexpr dart::compiler::target::word Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word KernelProgramInfo_InstanceSize =
@@ -3183,6 +3193,8 @@
     16;
 static constexpr dart::compiler::target::word
     InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word InstructionsTable_InstanceSize =
+    40;
 static constexpr dart::compiler::target::word Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word KernelProgramInfo_InstanceSize =
@@ -3712,6 +3724,8 @@
     8;
 static constexpr dart::compiler::target::word
     InstructionsSection_UnalignedHeaderSize = 20;
+static constexpr dart::compiler::target::word InstructionsTable_InstanceSize =
+    20;
 static constexpr dart::compiler::target::word Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word Integer_InstanceSize = 4;
 static constexpr dart::compiler::target::word KernelProgramInfo_InstanceSize =
@@ -4246,6 +4260,8 @@
     16;
 static constexpr dart::compiler::target::word
     InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word InstructionsTable_InstanceSize =
+    40;
 static constexpr dart::compiler::target::word Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word KernelProgramInfo_InstanceSize =
@@ -4770,6 +4786,8 @@
     8;
 static constexpr dart::compiler::target::word
     InstructionsSection_UnalignedHeaderSize = 20;
+static constexpr dart::compiler::target::word InstructionsTable_InstanceSize =
+    20;
 static constexpr dart::compiler::target::word Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word Integer_InstanceSize = 4;
 static constexpr dart::compiler::target::word KernelProgramInfo_InstanceSize =
@@ -5305,6 +5323,8 @@
     16;
 static constexpr dart::compiler::target::word
     InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word InstructionsTable_InstanceSize =
+    40;
 static constexpr dart::compiler::target::word Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word KernelProgramInfo_InstanceSize =
@@ -5838,6 +5858,8 @@
     16;
 static constexpr dart::compiler::target::word
     InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word InstructionsTable_InstanceSize =
+    40;
 static constexpr dart::compiler::target::word Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word KernelProgramInfo_InstanceSize =
@@ -6372,6 +6394,8 @@
     16;
 static constexpr dart::compiler::target::word
     InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word InstructionsTable_InstanceSize =
+    40;
 static constexpr dart::compiler::target::word Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word KernelProgramInfo_InstanceSize =
@@ -6957,6 +6981,8 @@
     AOT_Instructions_UnalignedHeaderSize = 8;
 static constexpr dart::compiler::target::word
     AOT_InstructionsSection_UnalignedHeaderSize = 20;
+static constexpr dart::compiler::target::word
+    AOT_InstructionsTable_InstanceSize = 20;
 static constexpr dart::compiler::target::word AOT_Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word AOT_Integer_InstanceSize = 4;
 static constexpr dart::compiler::target::word
@@ -7555,6 +7581,8 @@
     AOT_Instructions_UnalignedHeaderSize = 16;
 static constexpr dart::compiler::target::word
     AOT_InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word
+    AOT_InstructionsTable_InstanceSize = 40;
 static constexpr dart::compiler::target::word AOT_Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word AOT_Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word
@@ -8157,6 +8185,8 @@
     AOT_Instructions_UnalignedHeaderSize = 16;
 static constexpr dart::compiler::target::word
     AOT_InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word
+    AOT_InstructionsTable_InstanceSize = 40;
 static constexpr dart::compiler::target::word AOT_Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word AOT_Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word
@@ -8755,6 +8785,8 @@
     AOT_Instructions_UnalignedHeaderSize = 16;
 static constexpr dart::compiler::target::word
     AOT_InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word
+    AOT_InstructionsTable_InstanceSize = 40;
 static constexpr dart::compiler::target::word AOT_Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word AOT_Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word
@@ -9354,6 +9386,8 @@
     AOT_Instructions_UnalignedHeaderSize = 16;
 static constexpr dart::compiler::target::word
     AOT_InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word
+    AOT_InstructionsTable_InstanceSize = 40;
 static constexpr dart::compiler::target::word AOT_Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word AOT_Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word
@@ -9943,6 +9977,8 @@
     AOT_Instructions_UnalignedHeaderSize = 8;
 static constexpr dart::compiler::target::word
     AOT_InstructionsSection_UnalignedHeaderSize = 20;
+static constexpr dart::compiler::target::word
+    AOT_InstructionsTable_InstanceSize = 20;
 static constexpr dart::compiler::target::word AOT_Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word AOT_Integer_InstanceSize = 4;
 static constexpr dart::compiler::target::word
@@ -10534,6 +10570,8 @@
     AOT_Instructions_UnalignedHeaderSize = 16;
 static constexpr dart::compiler::target::word
     AOT_InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word
+    AOT_InstructionsTable_InstanceSize = 40;
 static constexpr dart::compiler::target::word AOT_Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word AOT_Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word
@@ -11129,6 +11167,8 @@
     AOT_Instructions_UnalignedHeaderSize = 16;
 static constexpr dart::compiler::target::word
     AOT_InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word
+    AOT_InstructionsTable_InstanceSize = 40;
 static constexpr dart::compiler::target::word AOT_Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word AOT_Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word
@@ -11720,6 +11760,8 @@
     AOT_Instructions_UnalignedHeaderSize = 16;
 static constexpr dart::compiler::target::word
     AOT_InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word
+    AOT_InstructionsTable_InstanceSize = 40;
 static constexpr dart::compiler::target::word AOT_Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word AOT_Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word
@@ -12312,6 +12354,8 @@
     AOT_Instructions_UnalignedHeaderSize = 16;
 static constexpr dart::compiler::target::word
     AOT_InstructionsSection_UnalignedHeaderSize = 40;
+static constexpr dart::compiler::target::word
+    AOT_InstructionsTable_InstanceSize = 40;
 static constexpr dart::compiler::target::word AOT_Int32x4_InstanceSize = 24;
 static constexpr dart::compiler::target::word AOT_Integer_InstanceSize = 8;
 static constexpr dart::compiler::target::word
diff --git a/runtime/vm/compiler/runtime_offsets_list.h b/runtime/vm/compiler/runtime_offsets_list.h
index d2dac49..aa2e4a2 100644
--- a/runtime/vm/compiler/runtime_offsets_list.h
+++ b/runtime/vm/compiler/runtime_offsets_list.h
@@ -340,6 +340,7 @@
   SIZEOF(Instructions, UnalignedHeaderSize, UntaggedInstructions)              \
   SIZEOF(InstructionsSection, UnalignedHeaderSize,                             \
          UntaggedInstructionsSection)                                          \
+  SIZEOF(InstructionsTable, InstanceSize, UntaggedInstructionsTable)           \
   SIZEOF(Int32x4, InstanceSize, UntaggedInt32x4)                               \
   SIZEOF(Integer, InstanceSize, UntaggedInteger)                               \
   SIZEOF(KernelProgramInfo, InstanceSize, UntaggedKernelProgramInfo)           \
diff --git a/runtime/vm/compiler/stub_code_compiler.cc b/runtime/vm/compiler/stub_code_compiler.cc
index bbcdc8c..0058e1a 100644
--- a/runtime/vm/compiler/stub_code_compiler.cc
+++ b/runtime/vm/compiler/stub_code_compiler.cc
@@ -888,9 +888,17 @@
 }
 
 void StubCodeCompiler::GenerateUnknownDartCodeStub(Assembler* assembler) {
+  // Enter frame to include caller into the backtrace.
+  __ EnterStubFrame();
   __ Breakpoint();  // Marker stub.
 }
 
+void StubCodeCompiler::GenerateNotLoadedStub(Assembler* assembler) {
+  __ EnterStubFrame();
+  __ CallRuntime(kNotLoadedRuntimeEntry, 0);
+  __ Breakpoint();
+}
+
 }  // namespace compiler
 
 }  // namespace dart
diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc
index 515e680..b4901a6 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm.cc
@@ -1168,7 +1168,7 @@
 // Called when invoking Dart code from C++ (VM code).
 // Input parameters:
 //   LR : points to return address.
-//   R0 : code object of the Dart function to call.
+//   R0 : target code or entry point (in bare instructions mode).
 //   R1 : arguments descriptor array.
 //   R2 : arguments array.
 //   R3 : current thread.
@@ -1260,11 +1260,12 @@
   // Call the Dart code entrypoint.
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
     __ SetupGlobalPoolAndDispatchTable();
+    __ LoadImmediate(CODE_REG, 0);  // GC safe value into CODE_REG.
   } else {
     __ LoadImmediate(PP, 0);  // GC safe value into PP.
+    __ ldr(CODE_REG, Address(R0, target::VMHandles::kOffsetOfRawPtrInHandle));
+    __ ldr(R0, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
   }
-  __ ldr(CODE_REG, Address(R0, target::VMHandles::kOffsetOfRawPtrInHandle));
-  __ ldr(R0, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
   __ blx(R0);  // R4 is the arguments descriptor array.
 
   // Get rid of arguments pushed on the stack.
@@ -3290,12 +3291,6 @@
       CODE_REG, target::Code::entry_point_offset(CodeEntryKind::kMonomorphic)));
 }
 
-void StubCodeCompiler::GenerateNotLoadedStub(Assembler* assembler) {
-  __ EnterStubFrame();
-  __ CallRuntime(kNotLoadedRuntimeEntry, 0);
-  __ bkpt(0);
-}
-
 // Instantiate type arguments from instantiator and function type args.
 // R3 uninstantiated type arguments.
 // R2 instantiator type arguments.
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index bc5eb6a..0d7cacc 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -1304,7 +1304,7 @@
 // Called when invoking Dart code from C++ (VM code).
 // Input parameters:
 //   LR : points to return address.
-//   R0 : code object of the Dart function to call.
+//   R0 : target code or entry point (in bare instructions mode).
 //   R1 : arguments descriptor array.
 //   R2 : arguments array.
 //   R3 : current thread.
@@ -1404,16 +1404,17 @@
 
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
     __ SetupGlobalPoolAndDispatchTable();
+    __ mov(CODE_REG, ZR);  // GC-safe value into CODE_REG.
   } else {
     // We now load the pool pointer(PP) with a GC safe value as we are about to
     // invoke dart code. We don't need a real object pool here.
     // Smi zero does not work because ARM64 assumes PP to be untagged.
     __ LoadObject(PP, NullObject());
+    __ ldr(CODE_REG, Address(R0, VMHandles::kOffsetOfRawPtrInHandle));
+    __ ldr(R0, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
   }
 
   // Call the Dart code entrypoint.
-  __ ldr(CODE_REG, Address(R0, VMHandles::kOffsetOfRawPtrInHandle));
-  __ ldr(R0, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
   __ blr(R0);  // R4 is the arguments descriptor array.
   __ Comment("InvokeDartCodeStub return");
 
@@ -3450,12 +3451,6 @@
   __ br(R1);
 }
 
-void StubCodeCompiler::GenerateNotLoadedStub(Assembler* assembler) {
-  __ EnterStubFrame();
-  __ CallRuntime(kNotLoadedRuntimeEntry, 0);
-  __ brk(0);
-}
-
 // Instantiate type arguments from instantiator and function type args.
 // R3 uninstantiated type arguments.
 // R2 instantiator type arguments.
diff --git a/runtime/vm/compiler/stub_code_compiler_ia32.cc b/runtime/vm/compiler/stub_code_compiler_ia32.cc
index 2286706..ef29020 100644
--- a/runtime/vm/compiler/stub_code_compiler_ia32.cc
+++ b/runtime/vm/compiler/stub_code_compiler_ia32.cc
@@ -2784,12 +2784,6 @@
   __ int3();  // AOT only.
 }
 
-void StubCodeCompiler::GenerateNotLoadedStub(Assembler* assembler) {
-  __ EnterStubFrame();
-  __ CallRuntime(kNotLoadedRuntimeEntry, 0);
-  __ int3();
-}
-
 // Instantiate type arguments from instantiator and function type args.
 // EBX: uninstantiated type arguments.
 // EDX: instantiator type arguments.
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index 8d2987a..611e083 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -1231,7 +1231,7 @@
 // Called when invoking Dart code from C++ (VM code).
 // Input parameters:
 //   RSP : points to return address.
-//   RDI : target code
+//   RDI : target code or entry point (in bare instructions mode).
 //   RSI : arguments descriptor array.
 //   RDX : arguments array.
 //   RCX : current thread.
@@ -1239,7 +1239,7 @@
   __ pushq(Address(RSP, 0));  // Marker for the profiler.
   __ EnterFrame(0);
 
-  const Register kTargetCodeReg = CallingConventions::kArg1Reg;
+  const Register kTargetReg = CallingConventions::kArg1Reg;
   const Register kArgDescReg = CallingConventions::kArg2Reg;
   const Register kArgsReg = CallingConventions::kArg3Reg;
   const Register kThreadReg = CallingConventions::kArg4Reg;
@@ -1304,8 +1304,8 @@
   // Load arguments descriptor array into R10, which is passed to Dart code.
   __ movq(R10, Address(kArgDescReg, VMHandles::kOffsetOfRawPtrInHandle));
 
-  // Push arguments. At this point we only need to preserve kTargetCodeReg.
-  ASSERT(kTargetCodeReg != RDX);
+  // Push arguments. At this point we only need to preserve kTargetReg.
+  ASSERT(kTargetReg != RDX);
 
   // Load number of arguments into RBX and adjust count for type arguments.
   __ movq(RBX, FieldAddress(R10, target::ArgumentsDescriptor::count_offset()));
@@ -1339,14 +1339,14 @@
   // Call the Dart code entrypoint.
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
     __ movq(PP, Address(THR, target::Thread::global_object_pool_offset()));
+    __ xorq(CODE_REG, CODE_REG);  // GC-safe value into CODE_REG.
   } else {
     __ xorq(PP, PP);  // GC-safe value into PP.
+    __ movq(CODE_REG, Address(kTargetReg, VMHandles::kOffsetOfRawPtrInHandle));
+    __ movq(kTargetReg,
+            FieldAddress(CODE_REG, target::Code::entry_point_offset()));
   }
-  __ movq(CODE_REG,
-          Address(kTargetCodeReg, VMHandles::kOffsetOfRawPtrInHandle));
-  __ movq(kTargetCodeReg,
-          FieldAddress(CODE_REG, target::Code::entry_point_offset()));
-  __ call(kTargetCodeReg);  // R10 is the arguments descriptor array.
+  __ call(kTargetReg);  // R10 is the arguments descriptor array.
 
   // Read the saved number of passed arguments as Smi.
   __ movq(RDX, Address(RBP, kArgumentsDescOffset));
@@ -3380,12 +3380,6 @@
   __ jmp(RCX);
 }
 
-void StubCodeCompiler::GenerateNotLoadedStub(Assembler* assembler) {
-  __ EnterStubFrame();
-  __ CallRuntime(kNotLoadedRuntimeEntry, 0);
-  __ int3();
-}
-
 // Instantiate type arguments from instantiator and function type args.
 // RBX: uninstantiated type arguments.
 // RDX: instantiator type arguments.
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index 058d039..0b5b142 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -144,7 +144,8 @@
 
   // Now Call the invoke stub which will invoke the dart function.
   const Code& code = Code::Handle(zone, function.CurrentCode());
-  return InvokeCode(code, arguments_descriptor, arguments, thread);
+  return InvokeCode(code, function.entry_point(), arguments_descriptor,
+                    arguments, thread);
 }
 
 extern "C" {
@@ -155,10 +156,16 @@
                                           const Array& arguments_descriptor,
                                           const Array& arguments,
                                           Thread* thread);
+typedef uword /*ObjectPtr*/ (*invokestub_bare_instructions)(
+    uword entry_point,
+    const Array& arguments_descriptor,
+    const Array& arguments,
+    Thread* thread);
 }
 
 NO_SANITIZE_SAFE_STACK
 ObjectPtr DartEntry::InvokeCode(const Code& code,
+                                uword entry_point,
                                 const Array& arguments_descriptor,
                                 const Array& arguments,
                                 Thread* thread) {
@@ -166,19 +173,27 @@
   ASSERT(thread->no_callback_scope_depth() == 0);
   ASSERT(!IsolateGroup::Current()->null_safety_not_set());
 
-  invokestub entrypoint =
-      reinterpret_cast<invokestub>(StubCode::InvokeDartCode().EntryPoint());
+  const uword stub = StubCode::InvokeDartCode().EntryPoint();
   SuspendLongJumpScope suspend_long_jump_scope(thread);
   TransitionToGenerated transition(thread);
 #if defined(USING_SIMULATOR)
   return bit_copy<ObjectPtr, int64_t>(Simulator::Current()->Call(
-      reinterpret_cast<intptr_t>(entrypoint), reinterpret_cast<intptr_t>(&code),
+      static_cast<intptr_t>(stub),
+      ((FLAG_precompiled_mode && FLAG_use_bare_instructions)
+           ? static_cast<intptr_t>(entry_point)
+           : reinterpret_cast<intptr_t>(&code)),
       reinterpret_cast<intptr_t>(&arguments_descriptor),
       reinterpret_cast<intptr_t>(&arguments),
       reinterpret_cast<intptr_t>(thread)));
 #else
-  return static_cast<ObjectPtr>(
-      entrypoint(code, arguments_descriptor, arguments, thread));
+  if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
+    return static_cast<ObjectPtr>(
+        (reinterpret_cast<invokestub_bare_instructions>(stub))(
+            entry_point, arguments_descriptor, arguments, thread));
+  } else {
+    return static_cast<ObjectPtr>((reinterpret_cast<invokestub>(stub))(
+        code, arguments_descriptor, arguments, thread));
+  }
 #endif
 }
 
diff --git a/runtime/vm/dart_entry.h b/runtime/vm/dart_entry.h
index 2a33464..d91d838 100644
--- a/runtime/vm/dart_entry.h
+++ b/runtime/vm/dart_entry.h
@@ -194,6 +194,7 @@
   // Invokes the specified code as if it was a Dart function.
   // On success, returns an InstancePtr.  On failure, an ErrorPtr.
   static ObjectPtr InvokeCode(const Code& code,
+                              uword entry_point,
                               const Array& arguments_descriptor,
                               const Array& arguments,
                               Thread* thread);
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 6eff1b4..ec54a16 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -156,6 +156,7 @@
 ClassPtr Object::code_class_ = static_cast<ClassPtr>(RAW_NULL);
 ClassPtr Object::instructions_class_ = static_cast<ClassPtr>(RAW_NULL);
 ClassPtr Object::instructions_section_class_ = static_cast<ClassPtr>(RAW_NULL);
+ClassPtr Object::instructions_table_class_ = static_cast<ClassPtr>(RAW_NULL);
 ClassPtr Object::object_pool_class_ = static_cast<ClassPtr>(RAW_NULL);
 ClassPtr Object::pc_descriptors_class_ = static_cast<ClassPtr>(RAW_NULL);
 ClassPtr Object::code_source_map_class_ = static_cast<ClassPtr>(RAW_NULL);
@@ -824,6 +825,9 @@
       Class::New<InstructionsSection, RTN::InstructionsSection>(isolate_group);
   instructions_section_class_ = cls.ptr();
 
+  cls = Class::New<InstructionsTable, RTN::InstructionsTable>(isolate_group);
+  instructions_table_class_ = cls.ptr();
+
   cls = Class::New<ObjectPool, RTN::ObjectPool>(isolate_group);
   object_pool_class_ = cls.ptr();
 
@@ -1245,6 +1249,7 @@
   code_class_ = static_cast<ClassPtr>(RAW_NULL);
   instructions_class_ = static_cast<ClassPtr>(RAW_NULL);
   instructions_section_class_ = static_cast<ClassPtr>(RAW_NULL);
+  instructions_table_class_ = static_cast<ClassPtr>(RAW_NULL);
   object_pool_class_ = static_cast<ClassPtr>(RAW_NULL);
   pc_descriptors_class_ = static_cast<ClassPtr>(RAW_NULL);
   code_source_map_class_ = static_cast<ClassPtr>(RAW_NULL);
@@ -1345,6 +1350,7 @@
   SET_CLASS_NAME(code, Code);
   SET_CLASS_NAME(instructions, Instructions);
   SET_CLASS_NAME(instructions_section, InstructionsSection);
+  SET_CLASS_NAME(instructions_table, InstructionsTable);
   SET_CLASS_NAME(object_pool, ObjectPool);
   SET_CLASS_NAME(code_source_map, CodeSourceMap);
   SET_CLASS_NAME(pc_descriptors, PcDescriptors);
@@ -4869,6 +4875,8 @@
       return Symbols::Instructions().ToCString();
     case kInstructionsSectionCid:
       return Symbols::InstructionsSection().ToCString();
+    case kInstructionsTableCid:
+      return Symbols::InstructionsTable().ToCString();
     case kObjectPoolCid:
       return Symbols::ObjectPool().ToCString();
     case kCodeSourceMapCid:
@@ -9757,7 +9765,10 @@
   kernel::ProcedureAttributesMetadata metadata;
   metadata = kernel::ProcedureAttributesOf(*this, zone);
   if (IsGetterFunction() || IsImplicitGetterFunction() || IsMethodExtractor()) {
-    return metadata.getter_called_dynamically;
+    // Dynamic method call through field/getter involves dynamic call of
+    // the field/getter.
+    return metadata.getter_called_dynamically ||
+           metadata.method_or_setter_called_dynamically;
   } else {
     return metadata.method_or_setter_called_dynamically;
   }
@@ -14149,6 +14160,125 @@
   return "InstructionsSection";
 }
 
+void InstructionsTable::set_length(intptr_t value) const {
+  StoreNonPointer(&untag()->length_, value);
+}
+
+void InstructionsTable::set_start_pc(uword value) const {
+  StoreNonPointer(&untag()->start_pc_, value);
+}
+
+void InstructionsTable::set_end_pc(uword value) const {
+  StoreNonPointer(&untag()->end_pc_, value);
+}
+
+void InstructionsTable::set_descriptors(const Array& value) const {
+  untag()->set_descriptors(value.ptr());
+}
+
+InstructionsTablePtr InstructionsTable::New(intptr_t length,
+                                            uword start_pc,
+                                            uword end_pc) {
+  ASSERT(Object::instructions_table_class() != Class::null());
+  ASSERT(length >= 0);
+  ASSERT(start_pc <= end_pc);
+  ASSERT(Utils::IsAligned(start_pc, kPayloadAlignment));
+  Thread* thread = Thread::Current();
+  InstructionsTable& result = InstructionsTable::Handle(thread->zone());
+  {
+    uword size = InstructionsTable::InstanceSize(length);
+    ObjectPtr raw = Object::Allocate(InstructionsTable::kClassId, size,
+                                     Heap::kOld, /*compressed*/ false);
+    NoSafepointScope no_safepoint;
+    result ^= raw;
+    result.set_length(length);
+  }
+  const Array& descriptors =
+      (length == 0) ? Object::empty_array()
+                    : Array::Handle(Array::New(length, Heap::kOld));
+  result.set_descriptors(descriptors);
+  result.set_start_pc(start_pc);
+  result.set_end_pc(end_pc);
+  return result.ptr();
+}
+
+void InstructionsTable::SetEntryAt(intptr_t index,
+                                   uword payload_start,
+                                   bool has_monomorphic_entrypoint,
+                                   ObjectPtr descriptor) const {
+  ASSERT((0 <= index) && (index < length()));
+  ASSERT(ContainsPc(payload_start));
+  ASSERT(Utils::IsAligned(payload_start, kPayloadAlignment));
+
+  const uint32_t pc_offset = ConvertPcToOffset(payload_start);
+  ASSERT((index == 0) || (PcOffsetAt(index - 1) <= pc_offset));
+  ASSERT((pc_offset & kHasMonomorphicEntrypointFlag) == 0);
+
+  untag()->data()[index] =
+      pc_offset |
+      (has_monomorphic_entrypoint ? kHasMonomorphicEntrypointFlag : 0);
+  descriptors()->untag()->set_element(index, descriptor);
+}
+
+bool InstructionsTable::ContainsPc(InstructionsTablePtr table, uword pc) {
+  return (InstructionsTable::start_pc(table) <= pc) &&
+         (pc < InstructionsTable::end_pc(table));
+}
+
+uint32_t InstructionsTable::ConvertPcToOffset(InstructionsTablePtr table,
+                                              uword pc) {
+  ASSERT(InstructionsTable::ContainsPc(table, pc));
+  const uint32_t pc_offset =
+      static_cast<uint32_t>(pc - InstructionsTable::start_pc(table));
+  ASSERT(InstructionsTable::start_pc(table) + pc_offset == pc);  // No overflow.
+  return pc_offset;
+}
+
+intptr_t InstructionsTable::FindEntry(InstructionsTablePtr table, uword pc) {
+  // This can run in the middle of GC and must not allocate handles.
+  NoSafepointScope no_safepoint;
+  if (!InstructionsTable::ContainsPc(table, pc)) return -1;
+  const uint32_t pc_offset = InstructionsTable::ConvertPcToOffset(table, pc);
+  intptr_t lo = 0;
+  intptr_t hi = InstructionsTable::length(table) - 1;
+  while (lo <= hi) {
+    intptr_t mid = (hi - lo + 1) / 2 + lo;
+    ASSERT(mid >= lo);
+    ASSERT(mid <= hi);
+    if (pc_offset < InstructionsTable::PcOffsetAt(table, mid)) {
+      hi = mid - 1;
+    } else if ((mid != hi) &&
+               (pc_offset >= InstructionsTable::PcOffsetAt(table, mid + 1))) {
+      lo = mid + 1;
+    } else {
+      return mid;
+    }
+  }
+  return -1;
+}
+
+ObjectPtr InstructionsTable::DescriptorAt(InstructionsTablePtr table,
+                                          intptr_t index) {
+  ASSERT((0 <= index) && (index < InstructionsTable::length(table)));
+  return table->untag()->descriptors()->untag()->element(index);
+}
+
+uword InstructionsTable::PayloadStartAt(InstructionsTablePtr table,
+                                        intptr_t index) {
+  return InstructionsTable::start_pc(table) +
+         InstructionsTable::PcOffsetAt(table, index);
+}
+
+uword InstructionsTable::EntryPointAt(intptr_t index) const {
+  return PayloadStartAt(index) + (HasMonomorphicEntryPointAt(index)
+                                      ? Instructions::kPolymorphicEntryOffsetAOT
+                                      : 0);
+}
+
+const char* InstructionsTable::ToCString() const {
+  return "InstructionsTable";
+}
+
 ObjectPoolPtr ObjectPool::New(intptr_t len) {
   ASSERT(Object::object_pool_class() != Class::null());
   if (len < 0 || len > kMaxElements) {
@@ -16979,7 +17109,8 @@
 }
 
 bool Code::IsUnknownDartCode(CodePtr code) {
-  return code == StubCode::UnknownDartCode().ptr();
+  return StubCode::HasBeenInitialized() &&
+         (code == StubCode::UnknownDartCode().ptr());
 }
 
 void Code::DisableDartCode() const {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 4f4fb6c..8c000c9 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -479,6 +479,9 @@
   static ClassPtr instructions_section_class() {
     return instructions_section_class_;
   }
+  static ClassPtr instructions_table_class() {
+    return instructions_table_class_;
+  }
   static ClassPtr object_pool_class() { return object_pool_class_; }
   static ClassPtr pc_descriptors_class() { return pc_descriptors_class_; }
   static ClassPtr code_source_map_class() { return code_source_map_class_; }
@@ -790,6 +793,7 @@
 
   static ClassPtr instructions_class_;  // Class of the Instructions vm object.
   static ClassPtr instructions_section_class_;  // Class of InstructionsSection.
+  static ClassPtr instructions_table_class_;    // Class of InstructionsTable.
   static ClassPtr object_pool_class_;      // Class of the ObjectPool vm object.
   static ClassPtr pc_descriptors_class_;   // Class of PcDescriptors vm object.
   static ClassPtr code_source_map_class_;  // Class of CodeSourceMap vm object.
@@ -2662,6 +2666,8 @@
 
   static intptr_t code_offset() { return OFFSET_OF(UntaggedFunction, code_); }
 
+  uword entry_point() const { return untag()->entry_point_; }
+
   static intptr_t entry_point_offset(
       CodeEntryKind entry_kind = CodeEntryKind::kNormal) {
     switch (entry_kind) {
@@ -5402,6 +5408,107 @@
   friend class Class;
 };
 
+// Table which maps ranges of machine code to [Code] or
+// [CompressedStackMaps] objects.
+// Used in AOT in bare instructions mode.
+class InstructionsTable : public Object {
+ public:
+  static const intptr_t kBytesPerElement = sizeof(uint32_t);
+  static const intptr_t kMaxElements = kIntptrMax / kBytesPerElement;
+
+  static const uint32_t kHasMonomorphicEntrypointFlag = 0x1;
+  static const uint32_t kPayloadAlignment = Instructions::kBarePayloadAlignment;
+  static const uint32_t kPayloadMask = ~(kPayloadAlignment - 1);
+  COMPILE_ASSERT((kPayloadMask & kHasMonomorphicEntrypointFlag) == 0);
+
+  static intptr_t InstanceSize() {
+    ASSERT_EQUAL(sizeof(UntaggedInstructionsTable),
+                 OFFSET_OF_RETURNED_VALUE(UntaggedInstructionsTable, data));
+    return 0;
+  }
+  static intptr_t InstanceSize(intptr_t len) {
+    ASSERT(0 <= len && len <= kMaxElements);
+    return RoundedAllocationSize(sizeof(UntaggedInstructionsTable) +
+                                 len * kBytesPerElement);
+  }
+
+  static InstructionsTablePtr New(intptr_t length,
+                                  uword start_pc,
+                                  uword end_pc);
+
+  void SetEntryAt(intptr_t index,
+                  uword payload_start,
+                  bool has_monomorphic_entrypoint,
+                  ObjectPtr descriptor) const;
+
+  bool ContainsPc(uword pc) const { return ContainsPc(ptr(), pc); }
+  static bool ContainsPc(InstructionsTablePtr table, uword pc);
+
+  // Looks for the entry in the [table] by the given [pc].
+  // Returns index of an entry which contains [pc], or -1 if not found.
+  static intptr_t FindEntry(InstructionsTablePtr table, uword pc);
+
+  intptr_t length() const { return InstructionsTable::length(this->ptr()); }
+  static intptr_t length(InstructionsTablePtr table) {
+    return table->untag()->length_;
+  }
+
+  // Returns descriptor object for the entry with given index.
+  ObjectPtr DescriptorAt(intptr_t index) const {
+    return InstructionsTable::DescriptorAt(this->ptr(), index);
+  }
+  static ObjectPtr DescriptorAt(InstructionsTablePtr table, intptr_t index);
+
+  // Returns start address of the instructions entry with given index.
+  uword PayloadStartAt(intptr_t index) const {
+    return InstructionsTable::PayloadStartAt(this->ptr(), index);
+  }
+  static uword PayloadStartAt(InstructionsTablePtr table, intptr_t index);
+
+  // Returns entry point of the instructions with given index.
+  uword EntryPointAt(intptr_t index) const;
+
+ private:
+  uword start_pc() const { return InstructionsTable::start_pc(this->ptr()); }
+  static uword start_pc(InstructionsTablePtr table) {
+    return table->untag()->start_pc_;
+  }
+
+  uword end_pc() const { return InstructionsTable::end_pc(this->ptr()); }
+  static uword end_pc(InstructionsTablePtr table) {
+    return table->untag()->end_pc_;
+  }
+
+  ArrayPtr descriptors() const { return untag()->descriptors_; }
+
+  static uint32_t DataAt(InstructionsTablePtr table, intptr_t index) {
+    ASSERT((0 <= index) && (index < InstructionsTable::length(table)));
+    return table->untag()->data()[index];
+  }
+  uint32_t PcOffsetAt(intptr_t index) const {
+    return InstructionsTable::PcOffsetAt(this->ptr(), index);
+  }
+  static uint32_t PcOffsetAt(InstructionsTablePtr table, intptr_t index) {
+    return DataAt(table, index) & kPayloadMask;
+  }
+  bool HasMonomorphicEntryPointAt(intptr_t index) const {
+    return (DataAt(this->ptr(), index) & kHasMonomorphicEntrypointFlag) != 0;
+  }
+
+  void set_length(intptr_t value) const;
+  void set_start_pc(uword value) const;
+  void set_end_pc(uword value) const;
+  void set_descriptors(const Array& value) const;
+
+  uint32_t ConvertPcToOffset(uword pc) const {
+    return InstructionsTable::ConvertPcToOffset(this->ptr(), pc);
+  }
+  static uint32_t ConvertPcToOffset(InstructionsTablePtr table, uword pc);
+
+  FINAL_HEAP_OBJECT_IMPLEMENTATION(InstructionsTable, Object);
+  friend class Class;
+};
+
 class LocalVarDescriptors : public Object {
  public:
   intptr_t Length() const;
@@ -6412,8 +6519,8 @@
 
   // Set by precompiler if this Code object doesn't contain
   // useful information besides instructions and compressed stack map.
-  // Such object is serialized in a shorter form. (In future such
-  // Code objects will not be re-created during snapshot deserialization.)
+  // Such objects are serialized in a shorter form and replaced with
+  // StubCode::UnknownDartCode() during snapshot deserialization.
   class DiscardedBit : public BitField<int32_t, bool, kDiscardedBit, 1> {};
 
   class PtrOffBits
diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc
index c9b0bf3..9df16ee 100644
--- a/runtime/vm/object_graph.cc
+++ b/runtime/vm/object_graph.cc
@@ -1308,6 +1308,7 @@
     case kImmutableArrayCid:
     case kInstructionsCid:
     case kInstructionsSectionCid:
+    case kInstructionsTableCid:
     case kLinkedHashMapCid:
     case kMintCid:
     case kNeverCid:
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index 9edd135..7f3da19 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -623,6 +623,10 @@
   Object::PrintJSONImpl(stream, ref);
 }
 
+void InstructionsTable::PrintJSONImpl(JSONStream* stream, bool ref) const {
+  Object::PrintJSONImpl(stream, ref);
+}
+
 void WeakSerializationReference::PrintJSONImpl(JSONStream* stream,
                                                bool ref) const {
   JSONObject jsobj(stream);
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index a452fd3..e741716 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -231,7 +231,7 @@
   RW(Code, unreachable_tts_stub)                                               \
   RW(Code, slow_tts_stub)                                                      \
   RW(Array, dispatch_table_code_entries)                                       \
-  RW(GrowableObjectArray, code_order_tables)                                   \
+  RW(GrowableObjectArray, instructions_tables)                                 \
   RW(Array, obfuscation_map)                                                   \
   RW(GrowableObjectArray, ffi_callback_functions)                              \
   RW(Class, ffi_pointer_class)                                                 \
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index 7fb0ce7..7e32ade 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -115,6 +115,13 @@
       instance_size = InstructionsSection::InstanceSize(section_size);
       break;
     }
+    case kInstructionsTableCid: {
+      const InstructionsTablePtr raw_instructions_table =
+          static_cast<const InstructionsTablePtr>(this);
+      intptr_t length = raw_instructions_table->untag()->length_;
+      instance_size = InstructionsTable::InstanceSize(length);
+      break;
+    }
     case kContextCid: {
       const ContextPtr raw_context = static_cast<const ContextPtr>(this);
       intptr_t num_variables = raw_context->untag()->num_variables_;
@@ -575,6 +582,7 @@
     TypedData::ElementSizeInBytes(raw_obj->GetClassId()) *
         Smi::Value(raw_obj->untag()->length()))
 VARIABLE_COMPRESSED_VISITOR(ContextScope, raw_obj->untag()->num_variables_)
+VARIABLE_VISITOR(InstructionsTable, raw_obj->untag()->length_)
 NULL_VISITOR(Mint)
 NULL_VISITOR(Double)
 NULL_VISITOR(Float32x4)
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index bbec4f1..4163c3e 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -1835,6 +1835,22 @@
   friend class Image;
 };
 
+class UntaggedInstructionsTable : public UntaggedObject {
+  RAW_HEAP_OBJECT_IMPLEMENTATION(InstructionsTable);
+
+  VISIT_FROM(ObjectPtr, descriptors)
+  POINTER_FIELD(ArrayPtr, descriptors)
+  VISIT_TO_LENGTH(ObjectPtr, &descriptors_)
+
+  intptr_t length_;
+  uword start_pc_;
+  uword end_pc_;
+
+  // Variable length data follows here.
+  uint32_t* data() { OPEN_ARRAY_START(uint32_t, uint32_t); }
+  const uint32_t* data() const { OPEN_ARRAY_START(uint32_t, uint32_t); }
+};
+
 class UntaggedPcDescriptors : public UntaggedObject {
  public:
 // The macro argument V is passed two arguments, the raw name of the enum value
diff --git a/runtime/vm/raw_object_fields.cc b/runtime/vm/raw_object_fields.cc
index 07f24db..7329337 100644
--- a/runtime/vm/raw_object_fields.cc
+++ b/runtime/vm/raw_object_fields.cc
@@ -106,6 +106,7 @@
   F(CallSiteData, args_descriptor_)                                            \
   F(ICData, entries_)                                                          \
   F(ICData, owner_)                                                            \
+  F(InstructionsTable, descriptors_)                                           \
   F(MegamorphicCache, buckets_)                                                \
   F(MegamorphicCache, mask_)                                                   \
   F(SubtypeTestCache, cache_)                                                  \
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index 414430b..9f8b670 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -549,6 +549,7 @@
 MESSAGE_SNAPSHOT_UNREACHABLE(ICData);
 MESSAGE_SNAPSHOT_UNREACHABLE(Instructions);
 MESSAGE_SNAPSHOT_UNREACHABLE(InstructionsSection);
+MESSAGE_SNAPSHOT_UNREACHABLE(InstructionsTable);
 MESSAGE_SNAPSHOT_UNREACHABLE(KernelProgramInfo);
 MESSAGE_SNAPSHOT_UNREACHABLE(Library);
 MESSAGE_SNAPSHOT_UNREACHABLE(LibraryPrefix);
diff --git a/runtime/vm/reverse_pc_lookup_cache.cc b/runtime/vm/reverse_pc_lookup_cache.cc
index 70f06df..517d8a1 100644
--- a/runtime/vm/reverse_pc_lookup_cache.cc
+++ b/runtime/vm/reverse_pc_lookup_cache.cc
@@ -7,12 +7,14 @@
 #include "vm/isolate.h"
 #include "vm/object.h"
 #include "vm/object_store.h"
+#include "vm/stub_code.h"
 
 namespace dart {
 
-CodePtr ReversePc::LookupInGroup(IsolateGroup* group,
-                                 uword pc,
-                                 bool is_return_address) {
+ObjectPtr ReversePc::FindCodeDescriptorInGroup(IsolateGroup* group,
+                                               uword pc,
+                                               bool is_return_address,
+                                               uword* code_start) {
 #if defined(DART_PRECOMPILED_RUNTIME)
   // This can run in the middle of GC and must not allocate handles.
   NoSafepointScope no_safepoint;
@@ -21,50 +23,40 @@
     pc--;
   }
 
-  // This expected number of tables is low, so we go through them linearly. If
-  // this changes, would could sort the table list during deserialization and
-  // binary search for the table.
-  GrowableObjectArrayPtr tables = group->object_store()->code_order_tables();
+  // This expected number of tables is low (one per loading unit), so we go
+  // through them linearly. If this changes, would could sort the table list
+  // during deserialization and binary search for the table.
+  GrowableObjectArrayPtr tables = group->object_store()->instructions_tables();
   intptr_t tables_length = Smi::Value(tables->untag()->length_);
   for (intptr_t i = 0; i < tables_length; i++) {
-    ArrayPtr table =
-        static_cast<ArrayPtr>(tables->untag()->data_->untag()->data()[i]);
-    intptr_t lo = 0;
-    intptr_t hi = Smi::Value(table->untag()->length_) - 1;
-
-    // Fast check if pc belongs to this table.
-    if (lo > hi) {
-      continue;
-    }
-    CodePtr first = static_cast<CodePtr>(table->untag()->data()[lo]);
-    if (pc < Code::PayloadStartOf(first)) {
-      continue;
-    }
-    CodePtr last = static_cast<CodePtr>(table->untag()->data()[hi]);
-    if (pc >= (Code::PayloadStartOf(last) + Code::PayloadSizeOf(last))) {
-      continue;
-    }
-
-    // Binary search within the table for the matching Code.
-    while (lo <= hi) {
-      intptr_t mid = (hi - lo + 1) / 2 + lo;
-      ASSERT(mid >= lo);
-      ASSERT(mid <= hi);
-      CodePtr code = static_cast<CodePtr>(table->untag()->data()[mid]);
-      uword code_start = Code::PayloadStartOf(code);
-      uword code_end = code_start + Code::PayloadSizeOf(code);
-      if (pc < code_start) {
-        hi = mid - 1;
-      } else if (pc >= code_end) {
-        lo = mid + 1;
-      } else {
-        return code;
-      }
+    InstructionsTablePtr table = static_cast<InstructionsTablePtr>(
+        tables->untag()->data_->untag()->data()[i]);
+    intptr_t index = InstructionsTable::FindEntry(table, pc);
+    if (index >= 0) {
+      *code_start = InstructionsTable::PayloadStartAt(table, index);
+      return InstructionsTable::DescriptorAt(table, index);
     }
   }
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
 
-  return Code::null();
+  *code_start = 0;
+  return Object::null();
+}
+
+ObjectPtr ReversePc::FindCodeDescriptor(IsolateGroup* group,
+                                        uword pc,
+                                        bool is_return_address,
+                                        uword* code_start) {
+  ASSERT(FLAG_precompiled_mode && FLAG_use_bare_instructions);
+  NoSafepointScope no_safepoint;
+
+  ObjectPtr code_descriptor =
+      FindCodeDescriptorInGroup(group, pc, is_return_address, code_start);
+  if (code_descriptor == Object::null()) {
+    code_descriptor = FindCodeDescriptorInGroup(Dart::vm_isolate_group(), pc,
+                                                is_return_address, code_start);
+  }
+  return code_descriptor;
 }
 
 CodePtr ReversePc::Lookup(IsolateGroup* group,
@@ -73,11 +65,19 @@
   ASSERT(FLAG_precompiled_mode && FLAG_use_bare_instructions);
   NoSafepointScope no_safepoint;
 
-  CodePtr code = LookupInGroup(group, pc, is_return_address);
-  if (code == Code::null()) {
-    code = LookupInGroup(Dart::vm_isolate_group(), pc, is_return_address);
+  uword code_start;
+  ObjectPtr code_descriptor =
+      FindCodeDescriptor(group, pc, is_return_address, &code_start);
+  if (code_descriptor != Object::null()) {
+    if (!code_descriptor->IsCode()) {
+      ASSERT(StubCode::UnknownDartCode().PayloadStart() == 0);
+      ASSERT(StubCode::UnknownDartCode().Size() == kUwordMax);
+      ASSERT(StubCode::UnknownDartCode().IsFunctionCode());
+      ASSERT(StubCode::UnknownDartCode().IsUnknownDartCode());
+      code_descriptor = StubCode::UnknownDartCode().ptr();
+    }
   }
-  return code;
+  return static_cast<CodePtr>(code_descriptor);
 }
 
 CompressedStackMapsPtr ReversePc::FindCompressedStackMaps(
@@ -88,10 +88,17 @@
   ASSERT(FLAG_precompiled_mode && FLAG_use_bare_instructions);
   NoSafepointScope no_safepoint;
 
-  CodePtr code = Lookup(group, pc, is_return_address);
-  if (code != Code::null()) {
-    *code_start = Code::PayloadStartOf(code);
-    return code->untag()->compressed_stackmaps();
+  ObjectPtr code_descriptor =
+      FindCodeDescriptor(group, pc, is_return_address, code_start);
+  if (code_descriptor != Object::null()) {
+    if (code_descriptor->IsCode()) {
+      CodePtr code = static_cast<CodePtr>(code_descriptor);
+      ASSERT(*code_start == Code::PayloadStartOf(code));
+      return code->untag()->compressed_stackmaps();
+    } else {
+      ASSERT(code_descriptor->IsCompressedStackMaps());
+      return static_cast<CompressedStackMapsPtr>(code_descriptor);
+    }
   }
 
   *code_start = 0;
diff --git a/runtime/vm/reverse_pc_lookup_cache.h b/runtime/vm/reverse_pc_lookup_cache.h
index 9eb0558..e214747 100644
--- a/runtime/vm/reverse_pc_lookup_cache.h
+++ b/runtime/vm/reverse_pc_lookup_cache.h
@@ -32,9 +32,14 @@
                                                         uword* code_start);
 
  private:
-  static CodePtr LookupInGroup(IsolateGroup* group,
-                               uword pc,
-                               bool is_return_address);
+  static ObjectPtr FindCodeDescriptorInGroup(IsolateGroup* group,
+                                             uword pc,
+                                             bool is_return_address,
+                                             uword* code_start);
+  static ObjectPtr FindCodeDescriptor(IsolateGroup* group,
+                                      uword pc,
+                                      bool is_return_address,
+                                      uword* code_start);
 };
 
 }  // namespace dart
diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc
index f5df240..b083155 100644
--- a/runtime/vm/stack_frame.cc
+++ b/runtime/vm/stack_frame.cc
@@ -351,24 +351,10 @@
 #if defined(DART_PRECOMPILED_RUNTIME)
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
     NoSafepointScope no_safepoint;
-    Code code;
-    code = ReversePc::Lookup(isolate_group(), pc(),
-                             /*is_return_address=*/true);
-    if (!code.IsNull()) {
-      // This is needed in order to test stack traces with the future
-      // behavior of ReversePc::Lookup which will return
-      // StubCode::UnknownDartCode() if code object is omitted from
-      // the snapshot.
-      if (code.is_discarded()) {
-        ASSERT(StubCode::UnknownDartCode().PayloadStart() == 0);
-        ASSERT(StubCode::UnknownDartCode().Size() == kUwordMax);
-        ASSERT(StubCode::UnknownDartCode().IsFunctionCode());
-        ASSERT(StubCode::UnknownDartCode().IsUnknownDartCode());
-        return StubCode::UnknownDartCode().ptr();
-      }
-      return code.ptr();
-    }
-    UNREACHABLE();
+    CodePtr code = ReversePc::Lookup(isolate_group(), pc(),
+                                     /*is_return_address=*/true);
+    ASSERT(code != Code::null());
+    return code;
   }
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
 
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index dc6d663..4921334 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -186,6 +186,7 @@
   V(InitPrefix, "init:")                                                       \
   V(Instructions, "Instructions")                                              \
   V(InstructionsSection, "InstructionsSection")                                \
+  V(InstructionsTable, "InstructionsTable")                                    \
   V(Int, "int")                                                                \
   V(Int16List, "Int16List")                                                    \
   V(Int32List, "Int32List")                                                    \
diff --git a/runtime/vm/tagged_pointer.h b/runtime/vm/tagged_pointer.h
index b0cb20d..4df36b4 100644
--- a/runtime/vm/tagged_pointer.h
+++ b/runtime/vm/tagged_pointer.h
@@ -300,6 +300,7 @@
 DEFINE_TAGGED_POINTER(ObjectPool, Object)
 DEFINE_TAGGED_POINTER(Instructions, Object)
 DEFINE_TAGGED_POINTER(InstructionsSection, Object)
+DEFINE_TAGGED_POINTER(InstructionsTable, Object)
 DEFINE_TAGGED_POINTER(PcDescriptors, Object)
 DEFINE_TAGGED_POINTER(CodeSourceMap, Object)
 DEFINE_TAGGED_POINTER(CompressedStackMaps, Object)
diff --git a/runtime/vm/type_testing_stubs_test.cc b/runtime/vm/type_testing_stubs_test.cc
index e00f107..18c90d1 100644
--- a/runtime/vm/type_testing_stubs_test.cc
+++ b/runtime/vm/type_testing_stubs_test.cc
@@ -224,8 +224,8 @@
   auto& stc2 = SubtypeTestCache::Handle();
 
   // First invocation will a) specialize the TTS b) may create SubtypeTestCache
-  result = DartEntry::InvokeCode(invoke_tts, arguments_descriptor, arguments,
-                                 thread);
+  result = DartEntry::InvokeCode(invoke_tts, invoke_tts.EntryPoint(),
+                                 arguments_descriptor, arguments, thread);
   stc ^= pool.ObjectAt(kSubtypeTestCacheIndex);
   tts = instantiated_dst_type.type_test_stub();
   if (!result.IsError()) {
@@ -234,8 +234,8 @@
   lazy(result, stc);
 
   // Second invocation will a) keep TTS b) keep optional SubtypeTestCache
-  result2 = DartEntry::InvokeCode(invoke_tts, arguments_descriptor, arguments,
-                                  thread);
+  result2 = DartEntry::InvokeCode(invoke_tts, invoke_tts.EntryPoint(),
+                                  arguments_descriptor, arguments, thread);
   stc2 ^= pool.ObjectAt(kSubtypeTestCacheIndex);
   tts2 = instantiated_dst_type.type_test_stub();
   abi_regs_modified ^= abi_regs_modified_box.At(0);
@@ -252,8 +252,8 @@
   TypeTestingStubGenerator::SpecializeStubFor(thread, instantiated_dst_type);
   tts = instantiated_dst_type.type_test_stub();
 
-  result2 = DartEntry::InvokeCode(invoke_tts, arguments_descriptor, arguments,
-                                  thread);
+  result2 = DartEntry::InvokeCode(invoke_tts, invoke_tts.EntryPoint(),
+                                  arguments_descriptor, arguments, thread);
   stc2 ^= pool.ObjectAt(kSubtypeTestCacheIndex);
   tts2 = instantiated_dst_type.type_test_stub();
   abi_regs_modified ^= abi_regs_modified_box.At(0);
diff --git a/sdk/lib/_internal/js_runtime/lib/late_helper.dart b/sdk/lib/_internal/js_runtime/lib/late_helper.dart
index e6136e8..a7adcb8 100644
--- a/sdk/lib/_internal/js_runtime/lib/late_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/late_helper.dart
@@ -8,10 +8,11 @@
 
 void throwLateFieldADI(String fieldName) => throw LateError.fieldADI(fieldName);
 
-/// A boxed variable used for lowering `late` variables when they are local or
-/// uninitialized statics.
+/// A boxed variable used for lowering uninitialized `late` variables when they
+/// are locals or statics.
 ///
 /// The [LateError]s produced have empty variable names.
+// TODO(fishythefish): Support variable names depending on flags/compiler.
 class _Cell {
   Object? _value;
 
@@ -43,16 +44,58 @@
   }
 
   void set finalLocalValue(Object? v) {
-    // TODO(fishythefish): Throw [LateError.localADI] if this occurs during
-    // recursive initialization.
     if (!identical(_value, this)) throw LateError.localAI('');
     _value = v;
   }
 
   void set finalFieldValue(Object? v) {
-    // TODO(fishythefish): Throw [LateError.fieldADI] if this occurs during
-    // recursive initialization.
     if (!identical(_value, this)) throw LateError.fieldAI('');
     _value = v;
   }
 }
+
+/// A boxed variable used for lowering `late` variables when they are
+/// initialized locals.
+///
+/// The [LateError]s produced have empty variable names.
+// TODO(fishythefish): Support variable names depending on flags/compiler.
+class _InitializedCell {
+  Object? _value;
+  Object? Function() _initializer;
+
+  _InitializedCell(this._initializer) {
+    // `this` is a unique sentinel.
+    _value = this;
+  }
+
+  @pragma('dart2js:tryInline')
+  @pragma('dart2js:as:trust')
+  T read<T>() => _read() as T;
+
+  @pragma('dart2js:tryInline')
+  @pragma('dart2js:as:trust')
+  T readFinal<T>() => _readFinal() as T;
+
+  Object? _read() {
+    if (identical(_value, this)) _value = _initializer();
+    return _value;
+  }
+
+  Object? _readFinal() {
+    if (identical(_value, this)) {
+      final result = _initializer();
+      if (!identical(_value, this)) throw LateError.localADI('');
+      _value = result;
+    }
+    return _value;
+  }
+
+  void set value(Object? v) {
+    _value = v;
+  }
+
+  void set finalValue(Object? v) {
+    if (!identical(_value, this)) throw LateError.localAI('');
+    _value = v;
+  }
+}
diff --git a/tests/corelib/apply2_test.dart b/tests/corelib/apply2_test.dart
index 4973e87..23805a1 100644
--- a/tests/corelib/apply2_test.dart
+++ b/tests/corelib/apply2_test.dart
@@ -2,6 +2,9 @@
 // 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=
+// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
+
 import "package:expect/expect.dart";
 
 apply(Function function, List? positional, Map<Symbol, dynamic>? named) {
diff --git a/tests/corelib/bigint_from_test.dart b/tests/corelib/bigint_from_test.dart
index babceff..a12abd9 100644
--- a/tests/corelib/bigint_from_test.dart
+++ b/tests/corelib/bigint_from_test.dart
@@ -7,6 +7,7 @@
 // VMOptions=--intrinsify --enable-asserts
 // VMOptions=--no-intrinsify --enable-asserts
 // VMOptions=--optimization-counter-threshold=5 --no-background-compilation
+// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
 
 import "package:expect/expect.dart";
 
diff --git a/tests/corelib_2/apply2_test.dart b/tests/corelib_2/apply2_test.dart
index 557aa16..507b2a2 100644
--- a/tests/corelib_2/apply2_test.dart
+++ b/tests/corelib_2/apply2_test.dart
@@ -2,6 +2,9 @@
 // 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=
+// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
+
 import "package:expect/expect.dart";
 
 apply(Function function, List positional, Map<Symbol, dynamic> named) {
diff --git a/tests/corelib_2/bigint_from_test.dart b/tests/corelib_2/bigint_from_test.dart
index 1ef06b3..52bd855 100644
--- a/tests/corelib_2/bigint_from_test.dart
+++ b/tests/corelib_2/bigint_from_test.dart
@@ -7,6 +7,7 @@
 // VMOptions=--intrinsify --enable-asserts
 // VMOptions=--no-intrinsify --enable-asserts
 // VMOptions=--optimization-counter-threshold=5 --no-background-compilation
+// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
 
 import "package:expect/expect.dart";
 
diff --git a/tests/language/async/identifier_test.dart b/tests/language/async/identifier_test.dart
index dec9c55..0707259 100644
--- a/tests/language/async/identifier_test.dart
+++ b/tests/language/async/identifier_test.dart
@@ -2,6 +2,9 @@
 // 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=
+// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
+
 import 'dart:async' as async;
 import 'lib.dart' as l; // Minimal library containing "int async;".
 
diff --git a/tests/language/deferred/function_type_test.dart b/tests/language/deferred/function_type_test.dart
index 49e151b..b69eae6 100644
--- a/tests/language/deferred/function_type_test.dart
+++ b/tests/language/deferred/function_type_test.dart
@@ -2,6 +2,9 @@
 // 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=
+// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
+
 import 'function_type_lib.dart' deferred as lib;
 
 main() {
diff --git a/tests/language/deferred/shared_and_unshared_classes_test.dart b/tests/language/deferred/shared_and_unshared_classes_test.dart
index a815c74..43c8ecd 100644
--- a/tests/language/deferred/shared_and_unshared_classes_test.dart
+++ b/tests/language/deferred/shared_and_unshared_classes_test.dart
@@ -2,6 +2,9 @@
 // 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=
+// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
+
 import "package:expect/expect.dart";
 import "package:async_helper/async_helper.dart";
 import "shared_and_unshared_classes_lib1.dart" deferred as lib1;
diff --git a/tests/language_2/async/identifier_test.dart b/tests/language_2/async/identifier_test.dart
index cc299a1..ad935d2 100644
--- a/tests/language_2/async/identifier_test.dart
+++ b/tests/language_2/async/identifier_test.dart
@@ -2,6 +2,9 @@
 // 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=
+// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
+
 import 'dart:async' as async;
 import 'lib.dart' as l; // Minimal library containing "int async;".
 
diff --git a/tests/language_2/deferred/function_type_test.dart b/tests/language_2/deferred/function_type_test.dart
index 49e151b..b69eae6 100644
--- a/tests/language_2/deferred/function_type_test.dart
+++ b/tests/language_2/deferred/function_type_test.dart
@@ -2,6 +2,9 @@
 // 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=
+// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
+
 import 'function_type_lib.dart' deferred as lib;
 
 main() {
diff --git a/tests/language_2/deferred/shared_and_unshared_classes_test.dart b/tests/language_2/deferred/shared_and_unshared_classes_test.dart
index a815c74..43c8ecd 100644
--- a/tests/language_2/deferred/shared_and_unshared_classes_test.dart
+++ b/tests/language_2/deferred/shared_and_unshared_classes_test.dart
@@ -2,6 +2,9 @@
 // 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=
+// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
+
 import "package:expect/expect.dart";
 import "package:async_helper/async_helper.dart";
 import "shared_and_unshared_classes_lib1.dart" deferred as lib1;
diff --git a/tools/VERSION b/tools/VERSION
index 5635e4e..10f522a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 234
+PRERELEASE 235
 PRERELEASE_PATCH 0
\ No newline at end of file