Version 2.13.0-60.0.dev

Merge commit '55766c387a3f5bd82cfff4fe9b5fb90babca3d30' into 'dev'
diff --git a/pkg/compiler/lib/src/ir/impact.dart b/pkg/compiler/lib/src/ir/impact.dart
index 0a7118a..1d8b365 100644
--- a/pkg/compiler/lib/src/ir/impact.dart
+++ b/pkg/compiler/lib/src/ir/impact.dart
@@ -2,9 +2,6 @@
 // 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.
 
-import 'package:front_end/src/api_unstable/dart2js.dart'
-    show operatorFromString;
-
 import 'package:kernel/ast.dart' as ir;
 import 'package:kernel/class_hierarchy.dart' as ir;
 import 'package:kernel/type_environment.dart' as ir;
@@ -255,13 +252,19 @@
   }
 
   @override
-  void handleStaticGet(ir.StaticGet node, ir.DartType resultType) {
-    ir.Member target = node.target;
-    if (target is ir.Procedure && target.kind == ir.ProcedureKind.Method) {
-      registerStaticTearOff(target, getDeferredImport(node));
-    } else {
-      registerStaticGet(target, getDeferredImport(node));
-    }
+  void handleStaticGet(
+      ir.Expression node, ir.Member target, ir.DartType resultType) {
+    assert(!(target is ir.Procedure && target.kind == ir.ProcedureKind.Method),
+        "Static tear off registered as static get: $node");
+    registerStaticGet(target, getDeferredImport(node));
+  }
+
+  @override
+  void handleStaticTearOff(
+      ir.Expression node, ir.Member target, ir.DartType resultType) {
+    assert(target is ir.Procedure && target.kind == ir.ProcedureKind.Method,
+        "Static get registered as static tear off: $node");
+    registerStaticTearOff(target, getDeferredImport(node));
   }
 
   @override
@@ -520,71 +523,103 @@
   }
 
   @override
-  void handleMethodInvocation(
-      ir.MethodInvocation node,
+  void handleDynamicInvocation(
+      ir.InvocationExpression node,
       ir.DartType receiverType,
       ArgumentTypes argumentTypes,
       ir.DartType returnType) {
     int positionArguments = node.arguments.positional.length;
     List<String> namedArguments = _getNamedArguments(node.arguments);
     List<ir.DartType> typeArguments = node.arguments.types;
-    ir.Expression receiver = node.receiver;
-    if (receiver is ir.VariableGet &&
-        receiver.variable.isFinal &&
-        receiver.variable.parent is ir.FunctionDeclaration) {
-      registerLocalFunctionInvocation(receiver.variable.parent,
+    ClassRelation relation = computeClassRelationFromType(receiverType);
+    registerDynamicInvocation(receiverType, relation, node.name,
+        positionArguments, namedArguments, typeArguments);
+  }
+
+  @override
+  void handleFunctionInvocation(
+      ir.InvocationExpression node,
+      ir.DartType receiverType,
+      ArgumentTypes argumentTypes,
+      ir.DartType returnType) {
+    int positionArguments = node.arguments.positional.length;
+    List<String> namedArguments = _getNamedArguments(node.arguments);
+    List<ir.DartType> typeArguments = node.arguments.types;
+    registerFunctionInvocation(
+        receiverType, positionArguments, namedArguments, typeArguments);
+  }
+
+  @override
+  void handleInstanceInvocation(
+      ir.InvocationExpression node,
+      ir.DartType receiverType,
+      ir.Member interfaceTarget,
+      ArgumentTypes argumentTypes) {
+    int positionArguments = node.arguments.positional.length;
+    List<String> namedArguments = _getNamedArguments(node.arguments);
+    List<ir.DartType> typeArguments = node.arguments.types;
+    ClassRelation relation = computeClassRelationFromType(receiverType);
+
+    if (interfaceTarget is ir.Field ||
+        interfaceTarget is ir.Procedure &&
+            interfaceTarget.kind == ir.ProcedureKind.Getter) {
+      registerInstanceInvocation(receiverType, relation, interfaceTarget,
           positionArguments, namedArguments, typeArguments);
+      registerFunctionInvocation(interfaceTarget.getterType, positionArguments,
+          namedArguments, typeArguments);
     } else {
-      ClassRelation relation = computeClassRelationFromType(receiverType);
-
-      ir.Member interfaceTarget = node.interfaceTarget;
-      if (interfaceTarget == null) {
-        registerDynamicInvocation(receiverType, relation, node.name,
-            positionArguments, namedArguments, typeArguments);
-        // TODO(johnniwinther): Avoid treating a known function call as a
-        // dynamic call when CFE provides a way to distinguish the two.
-        if (operatorFromString(node.name.text) == null &&
-            receiverType is ir.DynamicType) {
-          // We might implicitly call a getter that returns a function.
-          registerFunctionInvocation(const ir.DynamicType(), positionArguments,
-              namedArguments, typeArguments);
-        }
-      } else {
-        if (interfaceTarget is ir.Field ||
-            interfaceTarget is ir.Procedure &&
-                interfaceTarget.kind == ir.ProcedureKind.Getter) {
-          registerInstanceInvocation(receiverType, relation, interfaceTarget,
-              positionArguments, namedArguments, typeArguments);
-          registerFunctionInvocation(interfaceTarget.getterType,
-              positionArguments, namedArguments, typeArguments);
-        } else {
-          registerInstanceInvocation(receiverType, relation, interfaceTarget,
-              positionArguments, namedArguments, typeArguments);
-        }
-      }
+      registerInstanceInvocation(receiverType, relation, interfaceTarget,
+          positionArguments, namedArguments, typeArguments);
     }
   }
 
   @override
-  void handlePropertyGet(
-      ir.PropertyGet node, ir.DartType receiverType, ir.DartType resultType) {
-    ClassRelation relation = computeClassRelationFromType(receiverType);
-    if (node.interfaceTarget != null) {
-      registerInstanceGet(receiverType, relation, node.interfaceTarget);
-    } else {
-      registerDynamicGet(receiverType, relation, node.name);
-    }
+  void handleLocalFunctionInvocation(
+      ir.InvocationExpression node,
+      ir.FunctionDeclaration function,
+      ArgumentTypes argumentTypes,
+      ir.DartType returnType) {
+    int positionArguments = node.arguments.positional.length;
+    List<String> namedArguments = _getNamedArguments(node.arguments);
+    List<ir.DartType> typeArguments = node.arguments.types;
+    registerLocalFunctionInvocation(
+        function, positionArguments, namedArguments, typeArguments);
   }
 
   @override
-  void handlePropertySet(
-      ir.PropertySet node, ir.DartType receiverType, ir.DartType valueType) {
+  void handleEqualsCall(ir.Expression left, ir.DartType leftType,
+      ir.Expression right, ir.DartType rightType, ir.Member interfaceTarget) {
+    ClassRelation relation = computeClassRelationFromType(leftType);
+    registerInstanceInvocation(leftType, relation, interfaceTarget, 1,
+        const <String>[], const <ir.DartType>[]);
+  }
+
+  @override
+  void handleDynamicGet(ir.Expression node, ir.DartType receiverType,
+      ir.Name name, ir.DartType resultType) {
     ClassRelation relation = computeClassRelationFromType(receiverType);
-    if (node.interfaceTarget != null) {
-      registerInstanceSet(receiverType, relation, node.interfaceTarget);
-    } else {
-      registerDynamicSet(receiverType, relation, node.name);
-    }
+    registerDynamicGet(receiverType, relation, name);
+  }
+
+  @override
+  void handleInstanceGet(ir.Expression node, ir.DartType receiverType,
+      ir.Member interfaceTarget, ir.DartType resultType) {
+    ClassRelation relation = computeClassRelationFromType(receiverType);
+    registerInstanceGet(receiverType, relation, interfaceTarget);
+  }
+
+  @override
+  void handleDynamicSet(ir.Expression node, ir.DartType receiverType,
+      ir.Name name, ir.DartType valueType) {
+    ClassRelation relation = computeClassRelationFromType(receiverType);
+    registerDynamicSet(receiverType, relation, name);
+  }
+
+  @override
+  void handleInstanceSet(ir.Expression node, ir.DartType receiverType,
+      ir.Member interfaceTarget, ir.DartType valueType) {
+    ClassRelation relation = computeClassRelationFromType(receiverType);
+    registerInstanceSet(receiverType, relation, interfaceTarget);
   }
 
   @override
diff --git a/pkg/compiler/lib/src/ir/static_type.dart b/pkg/compiler/lib/src/ir/static_type.dart
index 28c7cfd..f419a27 100644
--- a/pkg/compiler/lib/src/ir/static_type.dart
+++ b/pkg/compiler/lib/src/ir/static_type.dart
@@ -2,6 +2,8 @@
 // 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.
 
+import 'package:front_end/src/api_unstable/dart2js.dart'
+    show operatorFromString;
 import 'package:kernel/ast.dart' as ir;
 import 'package:kernel/class_hierarchy.dart' as ir;
 import 'package:kernel/type_algebra.dart' as ir;
@@ -243,6 +245,21 @@
         .rawType(superclass, currentLibrary.nonNullable);
   }
 
+  ir.Member _resolveDynamicTarget(ir.DartType receiverType, ir.Name name) {
+    if (receiverType is ir.InterfaceType) {
+      return hierarchy.getInterfaceMember(receiverType.classNode, name);
+    }
+    return null;
+  }
+
+  ir.DartType _computeInstanceGetType(
+      ir.DartType receiverType, ir.Member interfaceTarget) {
+    ir.Class superclass = interfaceTarget.enclosingClass;
+    receiverType = getTypeAsInstanceOf(receiverType, superclass);
+    return ir.Substitution.fromInterfaceType(receiverType)
+        .substituteType(interfaceTarget.getterType);
+  }
+
   /// Computes the result type of the property access [node] on a receiver of
   /// type [receiverType].
   ///
@@ -250,16 +267,10 @@
   /// it is updated to target this member.
   ir.DartType _computePropertyGetType(
       ir.PropertyGet node, ir.DartType receiverType) {
+    node.interfaceTarget ??= _resolveDynamicTarget(receiverType, node.name);
     ir.Member interfaceTarget = node.interfaceTarget;
-    if (interfaceTarget == null && receiverType is ir.InterfaceType) {
-      interfaceTarget = node.interfaceTarget =
-          hierarchy.getInterfaceMember(receiverType.classNode, node.name);
-    }
     if (interfaceTarget != null) {
-      ir.Class superclass = interfaceTarget.enclosingClass;
-      receiverType = getTypeAsInstanceOf(receiverType, superclass);
-      return ir.Substitution.fromInterfaceType(receiverType)
-          .substituteType(interfaceTarget.getterType);
+      return _computeInstanceGetType(receiverType, interfaceTarget);
     }
     // Treat the properties of Object specially.
     String nameString = node.name.text;
@@ -271,8 +282,11 @@
     return const ir.DynamicType();
   }
 
-  void handlePropertyGet(
-      ir.PropertyGet node, ir.DartType receiverType, ir.DartType resultType) {}
+  void handleDynamicGet(ir.Expression node, ir.DartType receiverType,
+      ir.Name name, ir.DartType resultType) {}
+
+  void handleInstanceGet(ir.Expression node, ir.DartType receiverType,
+      ir.Member interfaceTarget, ir.DartType resultType) {}
 
   void handleRuntimeTypeUse(ir.PropertyGet node, RuntimeTypeUseKind kind,
       ir.DartType receiverType, ir.DartType argumentType) {}
@@ -283,63 +297,141 @@
     ir.DartType resultType = _staticTypeCache._expressionTypes[node] =
         _computePropertyGetType(node, receiverType);
     receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
-    handlePropertyGet(node, receiverType, resultType);
+    if (node.interfaceTarget != null) {
+      handleInstanceGet(node, receiverType, node.interfaceTarget, resultType);
+    } else {
+      handleDynamicGet(node, receiverType, node.name, resultType);
+    }
     if (node.name.text == Identifiers.runtimeType_) {
-      RuntimeTypeUseData data =
-          computeRuntimeTypeUse(_pendingRuntimeTypeUseData, node);
-      if (data.leftRuntimeTypeExpression == node) {
-        // [node] is the left (or single) occurrence of `.runtimeType` so we
-        // can set the static type of the receiver expression.
-        data.receiverType = receiverType;
-      } else {
-        // [node] is the right occurrence of `.runtimeType` so we
-        // can set the static type of the argument expression.
-        assert(data.rightRuntimeTypeExpression == node,
-            "Unexpected RuntimeTypeUseData for $node: $data");
-        data.argumentType = receiverType;
-      }
-      if (data.isComplete) {
-        /// We now have all need static types so we can remove the data from
-        /// the cache and handle the runtime type use.
-        _pendingRuntimeTypeUseData.remove(data.leftRuntimeTypeExpression);
-        if (data.rightRuntimeTypeExpression != null) {
-          _pendingRuntimeTypeUseData.remove(data.rightRuntimeTypeExpression);
-        }
-        handleRuntimeTypeUse(
-            node, data.kind, data.receiverType, data.argumentType);
-      }
+      handleRuntimeTypeGet(receiverType, node);
     }
     return resultType;
   }
 
-  void handlePropertySet(
-      ir.PropertySet node, ir.DartType receiverType, ir.DartType valueType) {}
+  void handleRuntimeTypeGet(ir.DartType receiverType, ir.Expression node) {
+    RuntimeTypeUseData data =
+        computeRuntimeTypeUse(_pendingRuntimeTypeUseData, node);
+    if (data.leftRuntimeTypeExpression == node) {
+      // [node] is the left (or single) occurrence of `.runtimeType` so we
+      // can set the static type of the receiver expression.
+      data.receiverType = receiverType;
+    } else {
+      // [node] is the right occurrence of `.runtimeType` so we
+      // can set the static type of the argument expression.
+      assert(data.rightRuntimeTypeExpression == node,
+          "Unexpected RuntimeTypeUseData for $node: $data");
+      data.argumentType = receiverType;
+    }
+    if (data.isComplete) {
+      /// We now have all need static types so we can remove the data from
+      /// the cache and handle the runtime type use.
+      _pendingRuntimeTypeUseData.remove(data.leftRuntimeTypeExpression);
+      if (data.rightRuntimeTypeExpression != null) {
+        _pendingRuntimeTypeUseData.remove(data.rightRuntimeTypeExpression);
+      }
+      handleRuntimeTypeUse(
+          node, data.kind, data.receiverType, data.argumentType);
+    }
+  }
+
+  @override
+  ir.DartType visitDynamicGet(ir.DynamicGet node) {
+    ir.DartType receiverType = visitNode(node.receiver);
+    ir.DartType resultType = super.visitDynamicGet(node);
+    ir.Member interfaceTarget = _resolveDynamicTarget(receiverType, node.name);
+    if (interfaceTarget != null) {
+      resultType = _computeInstanceGetType(receiverType, interfaceTarget);
+      ir.InstanceGet instanceGet = ir.InstanceGet(
+          ir.InstanceAccessKind.Instance, node.receiver, node.name,
+          interfaceTarget: interfaceTarget, resultType: resultType);
+      node.replaceWith(instanceGet);
+      handleInstanceGet(instanceGet, receiverType, interfaceTarget, resultType);
+    } else {
+      handleDynamicGet(node, receiverType, node.name, resultType);
+    }
+    if (node.name.text == Identifiers.runtimeType_) {
+      handleRuntimeTypeGet(receiverType, node);
+    }
+    return resultType;
+  }
+
+  @override
+  ir.DartType visitInstanceGet(ir.InstanceGet node) {
+    ir.DartType receiverType = visitNode(node.receiver);
+    ir.DartType resultType = super.visitInstanceGet(node);
+    handleInstanceGet(node, receiverType, node.interfaceTarget, resultType);
+    if (node.name.text == Identifiers.runtimeType_) {
+      handleRuntimeTypeGet(receiverType, node);
+    }
+    return resultType;
+  }
+
+  @override
+  ir.DartType visitInstanceTearOff(ir.InstanceTearOff node) {
+    ir.DartType receiverType = visitNode(node.receiver);
+    ir.DartType resultType = super.visitInstanceTearOff(node);
+    assert(node.name.text == Identifiers.runtimeType_,
+        "Unexpected .runtimeType instance tear-off.");
+    handleInstanceGet(node, receiverType, node.interfaceTarget, resultType);
+    return resultType;
+  }
+
+  @override
+  ir.DartType visitFunctionTearOff(ir.FunctionTearOff node) {
+    ir.DartType receiverType = visitNode(node.receiver);
+    ir.DartType resultType = super.visitFunctionTearOff(node);
+    handleDynamicGet(node, receiverType, ir.Name.callName, resultType);
+    return resultType;
+  }
+
+  void handleDynamicSet(ir.Expression node, ir.DartType receiverType,
+      ir.Name name, ir.DartType valueType) {}
+
+  void handleInstanceSet(ir.Expression node, ir.DartType receiverType,
+      ir.Member interfaceTarget, ir.DartType valueType) {}
+
+  ir.Member _resolveDynamicSet(ir.DartType receiverType, ir.Name name) {
+    if (receiverType is ir.InterfaceType) {
+      return hierarchy.getInterfaceMember(receiverType.classNode, name,
+          setter: true);
+    }
+    return null;
+  }
+
+  ir.DartType _computeInstanceSetType(
+      ir.DartType receiverType, ir.Member interfaceTarget) {
+    ir.Class superclass = interfaceTarget.enclosingClass;
+    ir.Substitution receiverSubstitution = ir.Substitution.fromInterfaceType(
+        getTypeAsInstanceOf(receiverType, superclass));
+    return receiverSubstitution.substituteType(interfaceTarget.setterType);
+  }
+
+  ir.AsExpression _createImplicitAsIfNeeded(
+      ir.Expression value, ir.DartType valueType, ir.DartType setterType) {
+    if (!typeEnvironment.isSubtypeOf(
+        valueType, setterType, ir.SubtypeCheckMode.ignoringNullabilities)) {
+      // We need to insert an implicit cast to preserve the invariant that
+      // a property set with a known interface target is also statically
+      // checked.
+      return new ir.AsExpression(value, setterType)..isTypeError = true;
+    }
+    return null;
+  }
 
   @override
   ir.DartType visitPropertySet(ir.PropertySet node) {
     ir.DartType receiverType = visitNode(node.receiver);
     ir.DartType valueType = super.visitPropertySet(node);
     ir.Member interfaceTarget = node.interfaceTarget;
-    if (interfaceTarget == null && receiverType is ir.InterfaceType) {
-      interfaceTarget = hierarchy
-          .getInterfaceMember(receiverType.classNode, node.name, setter: true);
+    if (interfaceTarget == null) {
+      interfaceTarget = _resolveDynamicSet(receiverType, node.name);
       if (interfaceTarget != null) {
-        ir.Class superclass = interfaceTarget.enclosingClass;
-        ir.Substitution receiverSubstitution =
-            ir.Substitution.fromInterfaceType(
-                getTypeAsInstanceOf(receiverType, superclass));
         ir.DartType setterType =
-            receiverSubstitution.substituteType(interfaceTarget.setterType);
-        if (!typeEnvironment.isSubtypeOf(
-            valueType, setterType, ir.SubtypeCheckMode.ignoringNullabilities)) {
-          // We need to insert an implicit cast to preserve the invariant that
-          // a property set with a known interface target is also statically
-          // checked.
-          ir.AsExpression implicitCast =
-              new ir.AsExpression(node.value, setterType)
-                ..isTypeError = true
-                ..parent = node;
-          node.value = implicitCast;
+            _computeInstanceSetType(receiverType, interfaceTarget);
+        ir.AsExpression implicitCast =
+            _createImplicitAsIfNeeded(node.value, valueType, setterType);
+        if (implicitCast != null) {
+          node.value = implicitCast..parent = node;
           // Visit the newly created as expression; the original value has
           // already been visited.
           handleAsExpression(implicitCast, valueType);
@@ -349,7 +441,49 @@
       }
     }
     receiverType = _narrowInstanceReceiver(interfaceTarget, receiverType);
-    handlePropertySet(node, receiverType, valueType);
+    if (interfaceTarget != null) {
+      handleInstanceSet(node, receiverType, node.interfaceTarget, valueType);
+    } else {
+      handleDynamicSet(node, receiverType, node.name, valueType);
+    }
+    return valueType;
+  }
+
+  @override
+  ir.DartType visitDynamicSet(ir.DynamicSet node) {
+    ir.DartType receiverType = visitNode(node.receiver);
+    ir.DartType valueType = super.visitDynamicSet(node);
+    ir.Member interfaceTarget = _resolveDynamicSet(receiverType, node.name);
+    if (interfaceTarget != null) {
+      ir.DartType setterType =
+          _computeInstanceSetType(receiverType, interfaceTarget);
+      ir.Expression value = node.value;
+      ir.AsExpression implicitCast =
+          _createImplicitAsIfNeeded(value, valueType, setterType);
+      if (implicitCast != null) {
+        value = implicitCast;
+        // Visit the newly created as expression; the original value has
+        // already been visited.
+        handleAsExpression(implicitCast, valueType);
+        valueType = setterType;
+      }
+      ir.InstanceSet instanceSet = ir.InstanceSet(
+          ir.InstanceAccessKind.Instance, node.receiver, node.name, value,
+          interfaceTarget: interfaceTarget);
+      node.replaceWith(instanceSet);
+      receiverType = _narrowInstanceReceiver(interfaceTarget, receiverType);
+      handleInstanceSet(node, receiverType, interfaceTarget, valueType);
+    } else {
+      handleDynamicSet(node, receiverType, node.name, valueType);
+    }
+    return valueType;
+  }
+
+  @override
+  ir.DartType visitInstanceSet(ir.InstanceSet node) {
+    ir.DartType receiverType = visitNode(node.receiver);
+    ir.DartType valueType = super.visitInstanceSet(node);
+    handleInstanceSet(node, receiverType, node.interfaceTarget, valueType);
     return valueType;
   }
 
@@ -465,14 +599,8 @@
   /// This inserts any implicit cast of the arguments necessary to uphold the
   /// invariant that a method invocation with an interface target handles
   /// the static types at the call site.
-  void _updateMethodInvocationTarget(
-      ir.MethodInvocation node,
-      ArgumentTypes argumentTypes,
-      ir.DartType functionType,
-      ir.Member interfaceTarget) {
-    // TODO(johnniwinther): Handle incremental target improvement.
-    if (node.interfaceTarget != null) return;
-    node.interfaceTarget = interfaceTarget;
+  void _updateMethodInvocationTarget(ir.InvocationExpression node,
+      ArgumentTypes argumentTypes, ir.DartType functionType) {
     if (functionType is! ir.FunctionType) return;
     ir.FunctionType parameterTypes = functionType;
     Map<int, ir.DartType> neededPositionalChecks = {};
@@ -562,65 +690,80 @@
     dummy.replaceWith(body);
   }
 
-  /// Computes the result type of the method invocation [node] on a receiver of
-  /// type [receiverType].
-  ///
-  /// If the `node.interfaceTarget` is `null` but matches an `Object` member
-  /// it is updated to target this member.
-  ir.DartType _computeMethodInvocationType(ir.MethodInvocation node,
-      ir.DartType receiverType, ArgumentTypes argumentTypes) {
-    ir.Member interfaceTarget = node.interfaceTarget;
+  ir.Member _resolveDynamicInvocationTarget(
+      ir.DartType receiverType, ir.Name name, ir.Arguments arguments) {
     // TODO(34602): Remove when `interfaceTarget` is set on synthetic calls to
     // ==.
-    if (interfaceTarget == null &&
-        node.name.text == '==' &&
-        node.arguments.types.isEmpty &&
-        node.arguments.positional.length == 1 &&
-        node.arguments.named.isEmpty) {
-      interfaceTarget = node.interfaceTarget = objectEquals;
+    if (name.text == '==' &&
+        arguments.types.isEmpty &&
+        arguments.positional.length == 1 &&
+        arguments.named.isEmpty) {
+      return objectEquals;
     }
-    if (interfaceTarget == null && receiverType is ir.InterfaceType) {
+    if (receiverType is ir.InterfaceType) {
       ir.Member member =
-          hierarchy.getInterfaceMember(receiverType.classNode, node.name);
-      if (_isApplicable(node.arguments, member)) {
-        interfaceTarget = member;
+          hierarchy.getInterfaceMember(receiverType.classNode, name);
+      if (_isApplicable(arguments, member)) {
+        return member;
       }
     }
-    if (interfaceTarget != null) {
-      ir.Class superclass = interfaceTarget.enclosingClass;
-      ir.Substitution receiverSubstitution = ir.Substitution.fromInterfaceType(
-          getTypeAsInstanceOf(receiverType, superclass));
-      ir.DartType getterType =
-          receiverSubstitution.substituteType(interfaceTarget.getterType);
-      if (getterType is ir.FunctionType) {
-        ir.FunctionType functionType = getterType;
-        List<ir.DartType> typeArguments = node.arguments.types;
-        if (interfaceTarget is ir.Procedure &&
-            interfaceTarget.function.typeParameters.isNotEmpty &&
-            typeArguments.isEmpty) {
-          // If this was a dynamic call the invocation does not have the
-          // inferred default type arguments so we need to create them here
-          // to perform a valid substitution.
-          typeArguments = interfaceTarget.function.typeParameters
-              .map((t) => receiverSubstitution.substituteType(t.defaultType))
-              .toList();
-        }
-        getterType = ir.Substitution.fromPairs(
-                functionType.typeParameters, typeArguments)
-            .substituteType(functionType.withoutTypeParameters);
+    return null;
+  }
+
+  /// Computes the function type of the instance invocation [node] on a receiver
+  /// of type [receiverType] on the [interfaceTarget] with the given
+  /// [argumentTypes].
+  ir.DartType _computeInvocationFunctionType(ir.DartType receiverType,
+      ir.Member interfaceTarget, ir.Arguments arguments) {
+    ir.Class superclass = interfaceTarget.enclosingClass;
+    ir.Substitution receiverSubstitution = ir.Substitution.fromInterfaceType(
+        getTypeAsInstanceOf(receiverType, superclass));
+    ir.DartType getterType =
+        receiverSubstitution.substituteType(interfaceTarget.getterType);
+    if (getterType is ir.FunctionType) {
+      ir.FunctionType functionType = getterType;
+      List<ir.DartType> typeArguments = arguments.types;
+      if (interfaceTarget is ir.Procedure &&
+          interfaceTarget.function.typeParameters.isNotEmpty &&
+          typeArguments.isEmpty) {
+        // If this was a dynamic call the invocation does not have the
+        // inferred default type arguments so we need to create them here
+        // to perform a valid substitution.
+        typeArguments = interfaceTarget.function.typeParameters
+            .map((t) => receiverSubstitution.substituteType(t.defaultType))
+            .toList();
       }
-      _updateMethodInvocationTarget(
-          node, argumentTypes, getterType, interfaceTarget);
-      if (isSpecialCasedBinaryOperator(interfaceTarget)) {
-        ir.DartType argumentType = argumentTypes.positional[0];
-        return typeEnvironment.getTypeOfSpecialCasedBinaryOperator(
-            receiverType, argumentType);
-      } else if (getterType is ir.FunctionType) {
-        return getterType.returnType;
-      } else {
-        return const ir.DynamicType();
-      }
+      getterType =
+          ir.Substitution.fromPairs(functionType.typeParameters, typeArguments)
+              .substituteType(functionType.withoutTypeParameters);
     }
+    return getterType;
+  }
+
+  /// Computes the result type of the instance invocation [node] on a receiver
+  /// of type [receiverType] on the [interfaceTarget] with the [functionType]
+  /// computed from the [receiverType], [argumentTypes] and the
+  /// [interfaceTarget].
+  ir.DartType _computeInstanceInvocationReturnTypeFromFunctionType(
+      ir.DartType receiverType,
+      ir.Member interfaceTarget,
+      ArgumentTypes argumentTypes,
+      ir.DartType functionType) {
+    if (isSpecialCasedBinaryOperator(interfaceTarget)) {
+      ir.DartType argumentType = argumentTypes.positional[0];
+      return typeEnvironment.getTypeOfSpecialCasedBinaryOperator(
+          receiverType, argumentType);
+    } else if (functionType is ir.FunctionType) {
+      return functionType.returnType;
+    } else {
+      return const ir.DynamicType();
+    }
+  }
+
+  /// Computes the result type of the dynamic invocation [node] on a receiver of
+  /// type [receiverType].
+  ir.DartType _computeDynamicInvocationReturnType(
+      ir.InvocationExpression node, ir.DartType receiverType) {
     if (node.name.text == 'call') {
       if (receiverType is ir.FunctionType) {
         if (receiverType.typeParameters.length != node.arguments.types.length) {
@@ -663,46 +806,188 @@
     return new ArgumentTypes(positional, named);
   }
 
-  void handleMethodInvocation(
-      ir.MethodInvocation node,
+  void handleDynamicInvocation(
+      ir.InvocationExpression node,
       ir.DartType receiverType,
       ArgumentTypes argumentTypes,
       ir.DartType returnType) {}
 
+  void handleFunctionInvocation(
+      ir.InvocationExpression node,
+      ir.DartType receiverType,
+      ArgumentTypes argumentTypes,
+      ir.DartType returnType) {}
+
+  void handleInstanceInvocation(
+      ir.InvocationExpression node,
+      ir.DartType receiverType,
+      ir.Member interfaceTarget,
+      ArgumentTypes argumentTypes) {}
+
+  void handleLocalFunctionInvocation(
+      ir.InvocationExpression node,
+      ir.FunctionDeclaration function,
+      ArgumentTypes argumentTypes,
+      ir.DartType returnType) {}
+
+  void handleEqualsCall(ir.Expression left, ir.DartType leftType,
+      ir.Expression right, ir.DartType rightType, ir.Member interfaceTarget) {}
+
+  void _registerEqualsNull(TypeMap afterInvocation, ir.Expression expression,
+      {bool isNot: false}) {
+    if (expression is ir.VariableGet &&
+        !_invalidatedVariables.contains(expression.variable)) {
+      // If `expression == null` is true, we promote the type of the
+      // variable to `Null` by registering that is known _not_ to be of its
+      // declared type.
+      TypeMap notOfItsDeclaredType = afterInvocation.promote(
+          expression.variable, expression.variable.type,
+          isTrue: false);
+      TypeMap ofItsDeclaredType = afterInvocation
+          .promote(expression.variable, expression.variable.type, isTrue: true);
+      if (isNot) {
+        typeMapWhenTrue = ofItsDeclaredType;
+        typeMapWhenFalse = notOfItsDeclaredType;
+      } else {
+        typeMapWhenTrue = notOfItsDeclaredType;
+        typeMapWhenFalse = ofItsDeclaredType;
+      }
+    }
+  }
+
   @override
   ir.DartType visitMethodInvocation(ir.MethodInvocation node) {
     ArgumentTypes argumentTypes = _visitArguments(node.arguments);
     ir.DartType receiverType = visitNode(node.receiver);
-    ir.DartType returnType =
-        _computeMethodInvocationType(node, receiverType, argumentTypes);
+    ir.Member interfaceTarget = node.interfaceTarget ??
+        _resolveDynamicInvocationTarget(
+            receiverType, node.name, node.arguments);
+    ir.DartType returnType;
+    if (interfaceTarget != null) {
+      ir.DartType functionType = _computeInvocationFunctionType(
+          receiverType, interfaceTarget, node.arguments);
+      if (node.interfaceTarget == null) {
+        // We change [node] from being a dynamic invocation to an instance
+        // invocation, so we need to add static type checks to the arguments to
+        // match instance invocations created by the CFE.
+        // TODO(johnniwinther): Handle incremental target improvement.
+        _updateMethodInvocationTarget(node, argumentTypes, functionType);
+        node.interfaceTarget = interfaceTarget;
+      }
+      returnType = _computeInstanceInvocationReturnTypeFromFunctionType(
+          receiverType, interfaceTarget, argumentTypes, functionType);
+    } else {
+      returnType = _computeDynamicInvocationReturnType(node, receiverType);
+    }
     receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
     if (node.name.text == '==') {
+      TypeMap afterInvocation = typeMap;
       ir.Expression left = node.receiver;
       ir.Expression right = node.arguments.positional[0];
-      TypeMap afterInvocation = typeMap;
-      if (left is ir.VariableGet &&
-          isNullLiteral(right) &&
-          !_invalidatedVariables.contains(left.variable)) {
-        // If `left == null` is true, we promote the type of the variable to
-        // `Null` by registering that is known _not_ to be of its declared type.
-        typeMapWhenTrue = afterInvocation
-            .promote(left.variable, left.variable.type, isTrue: false);
-        typeMapWhenFalse = afterInvocation
-            .promote(left.variable, left.variable.type, isTrue: true);
+      if (isNullLiteral(right)) {
+        _registerEqualsNull(afterInvocation, left);
       }
-      if (right is ir.VariableGet &&
-          isNullLiteral(left) &&
-          !_invalidatedVariables.contains(right.variable)) {
-        // If `null == right` is true, we promote the type of the variable to
-        // `Null` by registering that is known _not_ to be of its declared type.
-        typeMapWhenTrue = afterInvocation
-            .promote(right.variable, right.variable.type, isTrue: false);
-        typeMapWhenFalse = afterInvocation
-            .promote(right.variable, right.variable.type, isTrue: true);
+      if (isNullLiteral(left)) {
+        _registerEqualsNull(afterInvocation, right);
+      }
+      assert(node.interfaceTarget != null);
+      handleEqualsCall(left, receiverType, right, argumentTypes.positional[0],
+          node.interfaceTarget);
+    } else if (node.interfaceTarget != null) {
+      handleInstanceInvocation(
+          node, receiverType, interfaceTarget, argumentTypes);
+    } else {
+      ir.Expression receiver = node.receiver;
+      if (receiver is ir.VariableGet &&
+          receiver.variable.isFinal &&
+          receiver.variable.parent is ir.FunctionDeclaration) {
+        handleLocalFunctionInvocation(
+            node, receiver.variable.parent, argumentTypes, returnType);
+      } else {
+        handleDynamicInvocation(node, receiverType, argumentTypes, returnType);
+        // TODO(johnniwinther): Avoid treating a known function call as a
+        // dynamic call when CFE provides a way to distinguish the two.
+        if (operatorFromString(node.name.text) == null &&
+            receiverType is ir.DynamicType) {
+          // We might implicitly call a getter that returns a function.
+          handleFunctionInvocation(
+              node, const ir.DynamicType(), argumentTypes, returnType);
+        }
       }
     }
     _staticTypeCache._expressionTypes[node] = returnType;
-    handleMethodInvocation(node, receiverType, argumentTypes, returnType);
+    return returnType;
+  }
+
+  @override
+  ir.DartType visitDynamicInvocation(ir.DynamicInvocation node) {
+    ArgumentTypes argumentTypes = _visitArguments(node.arguments);
+    ir.DartType receiverType = visitNode(node.receiver);
+    ir.Member interfaceTarget = _resolveDynamicInvocationTarget(
+        receiverType, node.name, node.arguments);
+    if (interfaceTarget != null) {
+      // We can turn the dynamic invocation into an instance invocation.
+      ir.DartType functionType = _computeInvocationFunctionType(
+          receiverType, interfaceTarget, node.arguments);
+      ir.InstanceInvocation instanceInvocation = ir.InstanceInvocation(
+          ir.InstanceAccessKind.Instance,
+          node.receiver,
+          node.name,
+          node.arguments,
+          interfaceTarget: interfaceTarget,
+          functionType: functionType);
+      node.replaceWith(instanceInvocation);
+      _updateMethodInvocationTarget(
+          instanceInvocation, argumentTypes, functionType);
+      ir.DartType returnType =
+          _computeInstanceInvocationReturnTypeFromFunctionType(
+              receiverType, interfaceTarget, argumentTypes, functionType);
+      receiverType = _narrowInstanceReceiver(interfaceTarget, receiverType);
+      handleInstanceInvocation(
+          instanceInvocation, receiverType, interfaceTarget, argumentTypes);
+      _staticTypeCache._expressionTypes[node] = returnType;
+      return returnType;
+    } else {
+      ir.DartType returnType =
+          _computeDynamicInvocationReturnType(node, receiverType);
+      _staticTypeCache._expressionTypes[node] = returnType;
+      handleDynamicInvocation(node, receiverType, argumentTypes, returnType);
+      return returnType;
+    }
+  }
+
+  @override
+  ir.DartType visitEqualsCall(ir.EqualsCall node) {
+    ir.DartType leftType = visitNode(node.left);
+    ir.DartType rightType = visitNode(node.right);
+    handleEqualsCall(
+        node.left, leftType, node.right, rightType, node.interfaceTarget);
+    return super.visitEqualsCall(node);
+  }
+
+  @override
+  ir.DartType visitEqualsNull(ir.EqualsNull node) {
+    visitNode(node.expression);
+    _registerEqualsNull(typeMap, node.expression, isNot: node.isNot);
+    return super.visitEqualsNull(node);
+  }
+
+  @override
+  ir.DartType visitFunctionInvocation(ir.FunctionInvocation node) {
+    ArgumentTypes argumentTypes = _visitArguments(node.arguments);
+    ir.DartType receiverType = visitNode(node.receiver);
+    ir.DartType returnType = super.visitFunctionInvocation(node);
+    handleFunctionInvocation(node, receiverType, argumentTypes, returnType);
+    return returnType;
+  }
+
+  @override
+  ir.DartType visitLocalFunctionInvocation(ir.LocalFunctionInvocation node) {
+    ArgumentTypes argumentTypes = _visitArguments(node.arguments);
+    ir.FunctionDeclaration localFunction = node.variable.parent;
+    ir.DartType returnType = super.visitLocalFunctionInvocation(node);
+    handleLocalFunctionInvocation(
+        node, localFunction, argumentTypes, returnType);
     return returnType;
   }
 
@@ -742,12 +1027,30 @@
     return resultType;
   }
 
-  void handleStaticGet(ir.StaticGet node, ir.DartType resultType) {}
+  void handleStaticGet(
+      ir.Expression node, ir.Member target, ir.DartType resultType) {}
+
+  void handleStaticTearOff(
+      ir.Expression node, ir.Member target, ir.DartType resultType) {}
 
   @override
   ir.DartType visitStaticGet(ir.StaticGet node) {
     ir.DartType resultType = super.visitStaticGet(node);
-    handleStaticGet(node, resultType);
+    ir.Member target = node.target;
+    if (target is ir.Procedure && target.kind == ir.ProcedureKind.Method) {
+      // TODO(johnniwinther): Remove this when dart2js uses the new method
+      // invocation encoding.
+      handleStaticTearOff(node, target, resultType);
+    } else {
+      handleStaticGet(node, target, resultType);
+    }
+    return resultType;
+  }
+
+  @override
+  ir.DartType visitStaticTearOff(ir.StaticTearOff node) {
+    ir.DartType resultType = super.visitStaticTearOff(node);
+    handleStaticTearOff(node, node.target, resultType);
     return resultType;
   }
 
diff --git a/pkg/compiler/lib/src/ir/static_type_base.dart b/pkg/compiler/lib/src/ir/static_type_base.dart
index 67c6014..225a723 100644
--- a/pkg/compiler/lib/src/ir/static_type_base.dart
+++ b/pkg/compiler/lib/src/ir/static_type_base.dart
@@ -236,4 +236,56 @@
     // TODO(johnniwinther): Include interface exactness where applicable.
     return node.getStaticType(staticTypeContext);
   }
+
+  @override
+  ir.DartType visitEqualsNull(ir.EqualsNull node) =>
+      node.getStaticType(staticTypeContext);
+
+  @override
+  ir.DartType visitEqualsCall(ir.EqualsCall node) =>
+      node.getStaticType(staticTypeContext);
+
+  @override
+  ir.DartType visitDynamicInvocation(ir.DynamicInvocation node) =>
+      node.getStaticType(staticTypeContext);
+
+  @override
+  ir.DartType visitFunctionInvocation(ir.FunctionInvocation node) =>
+      node.getStaticType(staticTypeContext);
+
+  @override
+  ir.DartType visitLocalFunctionInvocation(ir.LocalFunctionInvocation node) =>
+      node.getStaticType(staticTypeContext);
+
+  @override
+  ir.DartType visitInstanceInvocation(ir.InstanceInvocation node) =>
+      node.getStaticType(staticTypeContext);
+
+  @override
+  ir.DartType visitFunctionTearOff(ir.FunctionTearOff node) =>
+      node.getStaticType(staticTypeContext);
+
+  @override
+  ir.DartType visitInstanceTearOff(ir.InstanceTearOff node) =>
+      node.getStaticType(staticTypeContext);
+
+  @override
+  ir.DartType visitDynamicGet(ir.DynamicGet node) =>
+      node.getStaticType(staticTypeContext);
+
+  @override
+  ir.DartType visitInstanceGet(ir.InstanceGet node) =>
+      node.getStaticType(staticTypeContext);
+
+  @override
+  ir.DartType visitDynamicSet(ir.DynamicSet node) =>
+      node.getStaticType(staticTypeContext);
+
+  @override
+  ir.DartType visitInstanceSet(ir.InstanceSet node) =>
+      node.getStaticType(staticTypeContext);
+
+  @override
+  ir.DartType visitStaticTearOff(ir.StaticTearOff node) =>
+      node.getStaticType(staticTypeContext);
 }
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart
index 3ac7e86..4a9efbf 100644
--- a/pkg/compiler/lib/src/serialization/task.dart
+++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -128,7 +128,9 @@
       api.Input<List<int>> dillInput = await _provider
           .readFromUri(_options.entryPoint, inputKind: api.InputKind.binary);
       ir.Component component = new ir.Component();
-      new ir.BinaryBuilder(dillInput.data).readComponent(component);
+      // Not using growable lists saves memory.
+      new ir.BinaryBuilder(dillInput.data, useGrowableLists: false)
+          .readComponent(component);
       return component;
     });
   }
diff --git a/pkg/compiler/test/analyses/analysis_helper.dart b/pkg/compiler/test/analyses/analysis_helper.dart
index 2b854ab..fb096c3 100644
--- a/pkg/compiler/test/analyses/analysis_helper.dart
+++ b/pkg/compiler/test/analyses/analysis_helper.dart
@@ -335,24 +335,24 @@
   }
 
   @override
-  void handlePropertyGet(
-      ir.PropertyGet node, ir.DartType receiverType, ir.DartType resultType) {
+  void handleDynamicGet(ir.Expression node, ir.DartType receiverType,
+      ir.Name name, ir.DartType resultType) {
     if (receiverType is ir.DynamicType) {
-      registerError(node, "Dynamic access of '${node.name}'.");
+      registerError(node, "Dynamic access of '${name}'.");
     }
   }
 
   @override
-  void handlePropertySet(
-      ir.PropertySet node, ir.DartType receiverType, ir.DartType valueType) {
+  void handleDynamicSet(ir.Expression node, ir.DartType receiverType,
+      ir.Name name, ir.DartType valueType) {
     if (receiverType is ir.DynamicType) {
-      registerError(node, "Dynamic update to '${node.name}'.");
+      registerError(node, "Dynamic update to '${name}'.");
     }
   }
 
   @override
-  void handleMethodInvocation(
-      ir.MethodInvocation node,
+  void handleDynamicInvocation(
+      ir.InvocationExpression node,
       ir.DartType receiverType,
       ArgumentTypes argumentTypes,
       ir.DartType returnType) {
diff --git a/pkg/compiler/test/static_type/data/if_method_call.dart b/pkg/compiler/test/static_type/data/if_method_call.dart
new file mode 100644
index 0000000..6a11b66
--- /dev/null
+++ b/pkg/compiler/test/static_type/data/if_method_call.dart
@@ -0,0 +1,36 @@
+// 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.
+
+// @dart = 2.7
+
+staticMethod(a) => true;
+
+class Super {
+  superMethod(a) => true;
+}
+
+class Class extends Super {
+  instanceMethod(a) => true;
+
+  test(a) {
+    if (super.superMethod(/*spec.dynamic*/ a is int)) {
+      /*spec.int*/ a;
+    }
+    if (this. /*spec.invoke: [Class]->dynamic*/ instanceMethod(
+        /*spec.dynamic*/ a is int)) {
+      /*spec.int*/ a;
+    }
+  }
+}
+
+ifMethodCall(a) {
+  if (staticMethod(/*spec.dynamic*/ a is int)) {
+    /*spec.int*/ a;
+  }
+}
+
+main() {
+  ifMethodCall(null);
+  new Class(). /*spec.invoke: [Class]->dynamic*/ test(null);
+}
diff --git a/pkg/compiler/test/static_type/type_promotion_data/if_method_call.dart b/pkg/compiler/test/static_type/type_promotion_data/if_method_call.dart
new file mode 100644
index 0000000..f86f00f
--- /dev/null
+++ b/pkg/compiler/test/static_type/type_promotion_data/if_method_call.dart
@@ -0,0 +1,35 @@
+// 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.
+
+// @dart = 2.7
+
+staticMethod(a) => true;
+
+class Super {
+  superMethod(a) => true;
+}
+
+class Class extends Super {
+  instanceMethod(a) => true;
+
+  test(a) {
+    if (super.superMethod(/*{}*/ a is int)) {
+      /*{a:[{true:int}|int]}*/ a;
+    }
+    if (this.instanceMethod(/*{}*/ a is int)) {
+      /*{a:[{true:int}|int]}*/ a;
+    }
+  }
+}
+
+ifMethodCall(a) {
+  if (staticMethod(/*{}*/ a is int)) {
+    /*{a:[{true:int}|int]}*/ a;
+  }
+}
+
+main() {
+  ifMethodCall(null);
+  new Class().test(null);
+}
diff --git a/pkg/front_end/lib/src/fasta/builder/class_builder.dart b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
index 725ff20..c4c4e5c 100644
--- a/pkg/front_end/lib/src/fasta/builder/class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
@@ -593,7 +593,7 @@
           null);
     }
 
-    // arguments.length == typeVariables.length
+    assert(arguments.length == typeVariablesCount);
     List<DartType> result =
         new List<DartType>.filled(arguments.length, null, growable: true);
     for (int i = 0; i < result.length; ++i) {
diff --git a/pkg/front_end/lib/src/fasta/builder/extension_builder.dart b/pkg/front_end/lib/src/fasta/builder/extension_builder.dart
index 5c150ad..1ef8de3 100644
--- a/pkg/front_end/lib/src/fasta/builder/extension_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/extension_builder.dart
@@ -6,20 +6,23 @@
 
 import 'package:kernel/ast.dart';
 import 'package:kernel/core_types.dart';
+import 'package:kernel/type_algebra.dart';
 
-import '../fasta_codes.dart' show templateInternalProblemNotFoundIn;
+import '../fasta_codes.dart'
+    show templateInternalProblemNotFoundIn, templateTypeArgumentMismatch;
 import '../scope.dart';
+import '../source/source_library_builder.dart';
 import '../problems.dart';
 import '../util/helpers.dart';
 
 import 'builder.dart';
+import 'declaration_builder.dart';
 import 'library_builder.dart';
 import 'member_builder.dart';
 import 'metadata_builder.dart';
 import 'nullability_builder.dart';
 import 'type_builder.dart';
 import 'type_variable_builder.dart';
-import 'declaration_builder.dart';
 
 abstract class ExtensionBuilder implements DeclarationBuilder {
   List<TypeVariableBuilder> get typeParameters;
@@ -86,14 +89,82 @@
   DartType buildType(LibraryBuilder library,
       NullabilityBuilder nullabilityBuilder, List<TypeBuilder> arguments,
       [bool notInstanceContext]) {
-    throw new UnsupportedError("ExtensionBuilder.buildType is not supported.");
+    if (library is SourceLibraryBuilder &&
+        library.enableExtensionTypesInLibrary) {
+      return buildTypesWithBuiltArguments(
+          library,
+          nullabilityBuilder.build(library),
+          buildTypeArguments(library, arguments, notInstanceContext));
+    } else {
+      throw new UnsupportedError("ExtensionBuilder.buildType is not supported"
+          "in library '${library.importUri}'.");
+    }
   }
 
   @override
   DartType buildTypesWithBuiltArguments(LibraryBuilder library,
       Nullability nullability, List<DartType> arguments) {
-    throw new UnsupportedError("ExtensionBuilder.buildTypesWithBuiltArguments "
-        "is not supported.");
+    if (library is SourceLibraryBuilder &&
+        library.enableExtensionTypesInLibrary) {
+      // TODO(dmitryas): Build the extension type rather than the on-type.
+      DartType builtOnType = onType.build(library);
+      if (typeParameters != null) {
+        List<TypeParameter> typeParameterNodes = <TypeParameter>[];
+        for (TypeVariableBuilder typeParameter in typeParameters) {
+          typeParameterNodes.add(typeParameter.parameter);
+        }
+        builtOnType = Substitution.fromPairs(typeParameterNodes, arguments)
+            .substituteType(builtOnType);
+      }
+      return builtOnType;
+    } else {
+      throw new UnsupportedError(
+          "ExtensionBuilder.buildTypesWithBuiltArguments "
+          "is not supported in library '${library.importUri}'.");
+    }
+  }
+
+  @override
+  int get typeVariablesCount => typeParameters?.length ?? 0;
+
+  List<DartType> buildTypeArguments(
+      LibraryBuilder library, List<TypeBuilder> arguments,
+      [bool notInstanceContext]) {
+    if (arguments == null && typeParameters == null) {
+      return <DartType>[];
+    }
+
+    if (arguments == null && typeParameters != null) {
+      List<DartType> result = new List<DartType>.filled(
+          typeParameters.length, null,
+          growable: true);
+      for (int i = 0; i < result.length; ++i) {
+        result[i] = typeParameters[i].defaultType.build(library);
+      }
+      if (library is SourceLibraryBuilder) {
+        library.inferredTypes.addAll(result);
+      }
+      return result;
+    }
+
+    if (arguments != null && arguments.length != typeVariablesCount) {
+      // That should be caught and reported as a compile-time error earlier.
+      return unhandled(
+          templateTypeArgumentMismatch
+              .withArguments(typeVariablesCount)
+              .message,
+          "buildTypeArguments",
+          -1,
+          null);
+    }
+
+    assert(arguments.length == typeVariablesCount);
+    List<DartType> result =
+        new List<DartType>.filled(arguments.length, null, growable: true);
+    for (int i = 0; i < result.length; ++i) {
+      result[i] = arguments[i].build(library);
+    }
+    return result;
   }
 
   @override
diff --git a/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart b/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
index 6004b66..c0e9971 100644
--- a/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
@@ -37,6 +37,8 @@
 
 import '../scope.dart';
 
+import '../source/source_library_builder.dart';
+
 import 'builder.dart';
 import 'builtin_type_declaration_builder.dart';
 import 'class_builder.dart';
@@ -140,7 +142,9 @@
       return;
     } else if (member is TypeDeclarationBuilder) {
       declaration = member.origin;
-      if (!declaration.isExtension) {
+      if (!declaration.isExtension ||
+          library is SourceLibraryBuilder &&
+              library.enableExtensionTypesInLibrary) {
         return;
       }
     }
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index ec1a7ec..edcd72b 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -442,6 +442,11 @@
 
   DartType get implicitTypeArgument => const ImplicitTypeArgument();
 
+  @override
+  bool get enableExtensionTypesInLibrary {
+    return libraryBuilder.enableExtensionTypesInLibrary;
+  }
+
   void _enterLocalState({bool inLateLocalInitializer: false}) {
     _localInitializerState =
         _localInitializerState.prepend(inLateLocalInitializer);
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
index d179e00..3208247 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -2975,7 +2975,7 @@
   @override
   TypeBuilder buildTypeWithResolvedArguments(
       NullabilityBuilder nullabilityBuilder, List<UnresolvedType> arguments) {
-    if (declaration.isExtension) {
+    if (declaration.isExtension && !_helper.enableExtensionTypesInLibrary) {
       // Extension declarations cannot be used as types.
       return super
           .buildTypeWithResolvedArguments(nullabilityBuilder, arguments);
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
index 8d03df8..128205c 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
@@ -62,6 +62,8 @@
   /// `true` if we are in the type of an as expression.
   bool get inIsOrAsOperatorType;
 
+  bool get enableExtensionTypesInLibrary;
+
   scopeLookup(Scope scope, String name, Token token,
       {bool isQualified: false, PrefixBuilder prefix});
 
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index b476720..11b2502 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -299,6 +299,7 @@
   bool _enableTripleShiftInLibrary;
   bool _enableExtensionMethodsInLibrary;
   bool _enableGenericMetadataInLibrary;
+  bool _enableExtensionTypesInLibrary;
 
   bool get enableVarianceInLibrary =>
       _enableVarianceInLibrary ??= loader.target.isExperimentEnabledInLibrary(
@@ -337,6 +338,10 @@
       loader.target.isExperimentEnabledInLibrary(
           ExperimentalFlag.genericMetadata, _packageUri ?? importUri);
 
+  bool get enableExtensionTypesInLibrary => _enableExtensionTypesInLibrary ??=
+      loader.target.isExperimentEnabledInLibrary(
+          ExperimentalFlag.extensionTypes, _packageUri ?? importUri);
+
   void updateLibraryNNBDSettings() {
     library.isNonNullableByDefault = isNonNullableByDefault;
     if (enableNonNullableInLibrary) {
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 30beca7e..626be69 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -492,6 +492,7 @@
 gotos
 gradually
 graphs
+growability
 gt
 guarantee
 guarded
@@ -676,6 +677,7 @@
 maintaining
 mangled
 manipulation
+manner
 markdown
 masking
 masks
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 1b92362..185fede 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -11,7 +11,10 @@
 
 a0x
 a1x
+a2i
 a2x
+a3i
+a4i
 aa
 aaa
 abc
diff --git a/pkg/front_end/testcases/extension_types/folder.options b/pkg/front_end/testcases/extension_types/folder.options
new file mode 100644
index 0000000..f936fab
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/folder.options
@@ -0,0 +1 @@
+--enable-experiment=extension-types
\ No newline at end of file
diff --git a/pkg/front_end/testcases/extension_types/simple.dart b/pkg/front_end/testcases/extension_types/simple.dart
new file mode 100644
index 0000000..b9a9997
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple.dart
@@ -0,0 +1,30 @@
+// 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.
+
+class Foo<X> {}
+class Bar<X, Y> {}
+
+extension A1 on dynamic {}
+A1 foo1(A1 a) => throw 42;
+
+extension A2<X> on Foo<X> {}
+A2 foo2(A2<int> a, A2 ai) => throw 42;
+
+extension A3<X, Y extends Function(X)> on Bar<X, Y> {}
+A3 foo3(A3<int, Function(num)> a, A3 ai) => throw 42;
+
+extension A4<X, Y extends Function(X)> on Foo<Y> {}
+A4 foo4(A4<int, Function(num)> a, A4 ai) => throw 42;
+
+bar() {
+  A1 a1;
+  A2<int> a2;
+  A2 a2i;
+  A3<int, Function(num)> a3;
+  A3 a3i;
+  A4<int, Function(num)> a4;
+  A4 a4i;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/extension_types/simple.dart.strong.expect b/pkg/front_end/testcases/extension_types/simple.dart.strong.expect
new file mode 100644
index 0000000..aa31646
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple.dart.strong.expect
@@ -0,0 +1,40 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Foo<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo<self::Foo::X%>
+    : super core::Object::•()
+    ;
+}
+class Bar<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Bar<self::Bar::X%, self::Bar::Y%>
+    : super core::Object::•()
+    ;
+}
+extension A1 on dynamic {
+}
+extension A2<X extends core::Object? = dynamic> on self::Foo<X%> {
+}
+extension A3<X extends core::Object? = dynamic, Y extends (X%) → dynamic = (Never) → dynamic> on self::Bar<X%, Y> {
+}
+extension A4<X extends core::Object? = dynamic, Y extends (X%) → dynamic = (Never) → dynamic> on self::Foo<Y> {
+}
+static method foo1(dynamic a) → dynamic
+  return throw 42;
+static method foo2(self::Foo<core::int> a, self::Foo<dynamic> ai) → self::Foo<dynamic>
+  return throw 42;
+static method foo3(self::Bar<core::int, (core::num) → dynamic> a, self::Bar<dynamic, (Never) → dynamic> ai) → self::Bar<dynamic, (Never) → dynamic>
+  return throw 42;
+static method foo4(self::Foo<(core::num) → dynamic> a, self::Foo<(Never) → dynamic> ai) → self::Foo<(Never) → dynamic>
+  return throw 42;
+static method bar() → dynamic {
+  dynamic a1;
+  self::Foo<core::int> a2;
+  self::Foo<dynamic> a2i;
+  self::Bar<core::int, (core::num) → dynamic> a3;
+  self::Bar<dynamic, (Never) → dynamic> a3i;
+  self::Foo<(core::num) → dynamic> a4;
+  self::Foo<(Never) → dynamic> a4i;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple.dart.strong.transformed.expect b/pkg/front_end/testcases/extension_types/simple.dart.strong.transformed.expect
new file mode 100644
index 0000000..aa31646
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple.dart.strong.transformed.expect
@@ -0,0 +1,40 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Foo<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo<self::Foo::X%>
+    : super core::Object::•()
+    ;
+}
+class Bar<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Bar<self::Bar::X%, self::Bar::Y%>
+    : super core::Object::•()
+    ;
+}
+extension A1 on dynamic {
+}
+extension A2<X extends core::Object? = dynamic> on self::Foo<X%> {
+}
+extension A3<X extends core::Object? = dynamic, Y extends (X%) → dynamic = (Never) → dynamic> on self::Bar<X%, Y> {
+}
+extension A4<X extends core::Object? = dynamic, Y extends (X%) → dynamic = (Never) → dynamic> on self::Foo<Y> {
+}
+static method foo1(dynamic a) → dynamic
+  return throw 42;
+static method foo2(self::Foo<core::int> a, self::Foo<dynamic> ai) → self::Foo<dynamic>
+  return throw 42;
+static method foo3(self::Bar<core::int, (core::num) → dynamic> a, self::Bar<dynamic, (Never) → dynamic> ai) → self::Bar<dynamic, (Never) → dynamic>
+  return throw 42;
+static method foo4(self::Foo<(core::num) → dynamic> a, self::Foo<(Never) → dynamic> ai) → self::Foo<(Never) → dynamic>
+  return throw 42;
+static method bar() → dynamic {
+  dynamic a1;
+  self::Foo<core::int> a2;
+  self::Foo<dynamic> a2i;
+  self::Bar<core::int, (core::num) → dynamic> a3;
+  self::Bar<dynamic, (Never) → dynamic> a3i;
+  self::Foo<(core::num) → dynamic> a4;
+  self::Foo<(Never) → dynamic> a4i;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple.dart.textual_outline.expect b/pkg/front_end/testcases/extension_types/simple.dart.textual_outline.expect
new file mode 100644
index 0000000..39e9531
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple.dart.textual_outline.expect
@@ -0,0 +1,21 @@
+class Foo<X> {}
+
+class Bar<X, Y> {}
+
+extension A1 on dynamic {}
+
+A1 foo1(A1 a) => throw 42;
+
+extension A2<X> on Foo<X> {}
+
+A2 foo2(A2<int> a, A2 ai) => throw 42;
+
+extension A3<X, Y extends Function(X)> on Bar<X, Y> {}
+
+A3 foo3(A3<int, Function(num)> a, A3 ai) => throw 42;
+
+extension A4<X, Y extends Function(X)> on Foo<Y> {}
+
+A4 foo4(A4<int, Function(num)> a, A4 ai) => throw 42;
+bar() {}
+main() {}
diff --git a/pkg/front_end/testcases/extension_types/simple.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/extension_types/simple.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..d876d2a
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple.dart.textual_outline_modelled.expect
@@ -0,0 +1,19 @@
+A1 foo1(A1 a) => throw 42;
+A2 foo2(A2<int> a, A2 ai) => throw 42;
+A3 foo3(A3<int, Function(num)> a, A3 ai) => throw 42;
+A4 foo4(A4<int, Function(num)> a, A4 ai) => throw 42;
+bar() {}
+
+class Bar<X, Y> {}
+
+class Foo<X> {}
+
+extension A1 on dynamic {}
+
+extension A2<X> on Foo<X> {}
+
+extension A3<X, Y extends Function(X)> on Bar<X, Y> {}
+
+extension A4<X, Y extends Function(X)> on Foo<Y> {}
+
+main() {}
diff --git a/pkg/front_end/testcases/extension_types/simple.dart.weak.expect b/pkg/front_end/testcases/extension_types/simple.dart.weak.expect
new file mode 100644
index 0000000..aa31646
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple.dart.weak.expect
@@ -0,0 +1,40 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Foo<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo<self::Foo::X%>
+    : super core::Object::•()
+    ;
+}
+class Bar<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Bar<self::Bar::X%, self::Bar::Y%>
+    : super core::Object::•()
+    ;
+}
+extension A1 on dynamic {
+}
+extension A2<X extends core::Object? = dynamic> on self::Foo<X%> {
+}
+extension A3<X extends core::Object? = dynamic, Y extends (X%) → dynamic = (Never) → dynamic> on self::Bar<X%, Y> {
+}
+extension A4<X extends core::Object? = dynamic, Y extends (X%) → dynamic = (Never) → dynamic> on self::Foo<Y> {
+}
+static method foo1(dynamic a) → dynamic
+  return throw 42;
+static method foo2(self::Foo<core::int> a, self::Foo<dynamic> ai) → self::Foo<dynamic>
+  return throw 42;
+static method foo3(self::Bar<core::int, (core::num) → dynamic> a, self::Bar<dynamic, (Never) → dynamic> ai) → self::Bar<dynamic, (Never) → dynamic>
+  return throw 42;
+static method foo4(self::Foo<(core::num) → dynamic> a, self::Foo<(Never) → dynamic> ai) → self::Foo<(Never) → dynamic>
+  return throw 42;
+static method bar() → dynamic {
+  dynamic a1;
+  self::Foo<core::int> a2;
+  self::Foo<dynamic> a2i;
+  self::Bar<core::int, (core::num) → dynamic> a3;
+  self::Bar<dynamic, (Never) → dynamic> a3i;
+  self::Foo<(core::num) → dynamic> a4;
+  self::Foo<(Never) → dynamic> a4i;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/simple.dart.weak.outline.expect b/pkg/front_end/testcases/extension_types/simple.dart.weak.outline.expect
new file mode 100644
index 0000000..3364bc3
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple.dart.weak.outline.expect
@@ -0,0 +1,32 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Foo<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo<self::Foo::X%>
+    ;
+}
+class Bar<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Bar<self::Bar::X%, self::Bar::Y%>
+    ;
+}
+extension A1 on dynamic {
+}
+extension A2<X extends core::Object? = dynamic> on self::Foo<X%> {
+}
+extension A3<X extends core::Object? = dynamic, Y extends (X%) → dynamic = (Never) → dynamic> on self::Bar<X%, Y> {
+}
+extension A4<X extends core::Object? = dynamic, Y extends (X%) → dynamic = (Never) → dynamic> on self::Foo<Y> {
+}
+static method foo1(dynamic a) → dynamic
+  ;
+static method foo2(self::Foo<core::int> a, self::Foo<dynamic> ai) → self::Foo<dynamic>
+  ;
+static method foo3(self::Bar<core::int, (core::num) → dynamic> a, self::Bar<dynamic, (Never) → dynamic> ai) → self::Bar<dynamic, (Never) → dynamic>
+  ;
+static method foo4(self::Foo<(core::num) → dynamic> a, self::Foo<(Never) → dynamic> ai) → self::Foo<(Never) → dynamic>
+  ;
+static method bar() → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/extension_types/simple.dart.weak.transformed.expect b/pkg/front_end/testcases/extension_types/simple.dart.weak.transformed.expect
new file mode 100644
index 0000000..aa31646
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/simple.dart.weak.transformed.expect
@@ -0,0 +1,40 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Foo<X extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Foo<self::Foo::X%>
+    : super core::Object::•()
+    ;
+}
+class Bar<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> extends core::Object {
+  synthetic constructor •() → self::Bar<self::Bar::X%, self::Bar::Y%>
+    : super core::Object::•()
+    ;
+}
+extension A1 on dynamic {
+}
+extension A2<X extends core::Object? = dynamic> on self::Foo<X%> {
+}
+extension A3<X extends core::Object? = dynamic, Y extends (X%) → dynamic = (Never) → dynamic> on self::Bar<X%, Y> {
+}
+extension A4<X extends core::Object? = dynamic, Y extends (X%) → dynamic = (Never) → dynamic> on self::Foo<Y> {
+}
+static method foo1(dynamic a) → dynamic
+  return throw 42;
+static method foo2(self::Foo<core::int> a, self::Foo<dynamic> ai) → self::Foo<dynamic>
+  return throw 42;
+static method foo3(self::Bar<core::int, (core::num) → dynamic> a, self::Bar<dynamic, (Never) → dynamic> ai) → self::Bar<dynamic, (Never) → dynamic>
+  return throw 42;
+static method foo4(self::Foo<(core::num) → dynamic> a, self::Foo<(Never) → dynamic> ai) → self::Foo<(Never) → dynamic>
+  return throw 42;
+static method bar() → dynamic {
+  dynamic a1;
+  self::Foo<core::int> a2;
+  self::Foo<dynamic> a2i;
+  self::Bar<core::int, (core::num) → dynamic> a3;
+  self::Bar<dynamic, (Never) → dynamic> a3i;
+  self::Foo<(core::num) → dynamic> a4;
+  self::Foo<(Never) → dynamic> a4i;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.1.expect b/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.1.expect
index ad8a087..41e48cc 100644
--- a/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.1.expect
@@ -6,8 +6,8 @@
 }
 library from "package:foo/foo.dart" as foo {
 additionalExports = (a::example,
-  a::example,
-  a::a)
+  a::a,
+  a::example)
 
   export "package:example/a.dart";
 
diff --git a/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.2.expect b/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.2.expect
index ad8a087..41e48cc 100644
--- a/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental/changing_modules_2.yaml.world.2.expect
@@ -6,8 +6,8 @@
 }
 library from "package:foo/foo.dart" as foo {
 additionalExports = (a::example,
-  a::example,
-  a::a)
+  a::a,
+  a::example)
 
   export "package:example/a.dart";
 
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.1.expect
index cc2970c..83a60c98 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.1.expect
@@ -10,8 +10,8 @@
 }
 library from "org-dartlang-test:///libExporter.dart" as lib2 {
 additionalExports = (lib::libField,
-  lib::libField,
-  lib::requireStuffFromLibExporter)
+  lib::requireStuffFromLibExporter,
+  lib::libField)
 
   export "org-dartlang-test:///lib.dart";
 
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.2.expect
index cc2970c..83a60c98 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.2.expect
@@ -10,8 +10,8 @@
 }
 library from "org-dartlang-test:///libExporter.dart" as lib2 {
 additionalExports = (lib::libField,
-  lib::libField,
-  lib::requireStuffFromLibExporter)
+  lib::requireStuffFromLibExporter,
+  lib::libField)
 
   export "org-dartlang-test:///lib.dart";
 
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.3.expect b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.3.expect
index cc2970c..83a60c98 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.3.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.3.expect
@@ -10,8 +10,8 @@
 }
 library from "org-dartlang-test:///libExporter.dart" as lib2 {
 additionalExports = (lib::libField,
-  lib::libField,
-  lib::requireStuffFromLibExporter)
+  lib::requireStuffFromLibExporter,
+  lib::libField)
 
   export "org-dartlang-test:///lib.dart";
 
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.4.expect b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.4.expect
index cc2970c..83a60c98 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.4.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_43.yaml.world.4.expect
@@ -10,8 +10,8 @@
 }
 library from "org-dartlang-test:///libExporter.dart" as lib2 {
 additionalExports = (lib::libField,
-  lib::libField,
-  lib::requireStuffFromLibExporter)
+  lib::requireStuffFromLibExporter,
+  lib::libField)
 
   export "org-dartlang-test:///lib.dart";
 
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 529d2c76..d9daf4b 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -147,7 +147,7 @@
 
 type ComponentFile {
   UInt32 magic = 0x90ABCDEF;
-  UInt32 formatVersion = 55;
+  UInt32 formatVersion = 56;
   Byte[10] shortSdkHash;
   List<String> problemsAsJson; // Described in problems.md.
   Library[] libraries;
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 857a8cf..cd85405 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -5460,6 +5460,7 @@
 /// An invocation of a local function declaration.
 class LocalFunctionInvocation extends InvocationExpression {
   /// The variable declaration for the function declaration.
+  // TODO(johnniwinther): Should this be the `FunctionDeclaration` instead?
   VariableDeclaration variable;
 
   @override
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index a46ee31..02d356e 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -135,13 +135,19 @@
   /// will not be resolved correctly.
   bool _disableLazyClassReading = false;
 
+  /// When creating lists that *might* be growable, use this boolean as the
+  /// setting to pass to `growable` so the dill can be loaded in a more compact
+  /// manner if the caller knows that the growability isn't needed.
+  final bool useGrowableLists;
+
   /// Note that [disableLazyClassReading] is incompatible
   /// with checkCanonicalNames on readComponent.
   BinaryBuilder(this._bytes,
       {this.filename,
       bool disableLazyReading = false,
       bool disableLazyClassReading = false,
-      bool alwaysCreateNewNamedNodes})
+      bool alwaysCreateNewNamedNodes,
+      this.useGrowableLists = true})
       : _disableLazyReading = disableLazyReading,
         _disableLazyClassReading = disableLazyReading ||
             disableLazyClassReading ||
@@ -377,7 +383,7 @@
       final Constant key = readConstantReference();
       final Constant value = readConstantReference();
       return new ConstantMapEntry(key, value);
-    }, growable: true);
+    }, growable: useGrowableLists);
     return new MapConstant(keyType, valueType, entries);
   }
 
@@ -438,7 +444,7 @@
   List<Constant> _readConstantReferenceList() {
     final int length = readUInt30();
     return new List<Constant>.generate(length, (_) => readConstantReference(),
-        growable: true);
+        growable: useGrowableLists);
   }
 
   Uri readUriReference() {
@@ -452,7 +458,7 @@
   List<String> readStringReferenceList() {
     int length = readUInt30();
     return new List<String>.generate(length, (_) => readStringReference(),
-        growable: true);
+        growable: useGrowableLists);
   }
 
   String readStringOrNullIfEmpty() {
@@ -476,7 +482,7 @@
     if (length == 0) return const <Expression>[];
     return new List<Expression>.generate(
         length, (_) => readExpression()..parent = parent,
-        growable: true);
+        growable: useGrowableLists);
   }
 
   void _fillTreeNodeList(
@@ -672,8 +678,6 @@
       for (CanonicalName child in parentChildren) {
         if (child.name != '@methods' &&
             child.name != '@typedefs' &&
-            child.name != '@fields' &&
-            child.name != '@=fields' &&
             child.name != '@getters' &&
             child.name != '@setters' &&
             child.name != '@factories' &&
@@ -874,7 +878,7 @@
     int length = readUInt30();
     if (length == 0) return null;
     return new List<String>.generate(length, (_) => readString(),
-        growable: true);
+        growable: useGrowableLists);
   }
 
   /// Read the uri-to-source part of the binary.
@@ -1201,8 +1205,13 @@
 
   List<Combinator> readCombinatorList() {
     int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      return const [];
+    }
     return new List<Combinator>.generate(length, (_) => readCombinator(),
-        growable: true);
+        growable: useGrowableLists);
   }
 
   void _readLibraryParts(Library library) {
@@ -1785,8 +1794,13 @@
 
   List<Expression> readExpressionList() {
     int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      return const [];
+    }
     return new List<Expression>.generate(length, (_) => readExpression(),
-        growable: true);
+        growable: useGrowableLists);
   }
 
   Expression readExpressionOption() {
@@ -2248,9 +2262,17 @@
       fieldValues[fieldRef] = value;
     }
     int assertCount = readUInt30();
-    List<AssertStatement> asserts = new List<AssertStatement>.generate(
-        assertCount, (_) => readStatement(),
-        growable: false);
+    List<AssertStatement> asserts;
+
+    if (!useGrowableLists && assertCount == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      asserts = const [];
+    } else {
+      asserts = new List<AssertStatement>.generate(
+          assertCount, (_) => readStatement(),
+          growable: false);
+    }
     List<Expression> unusedArguments = readExpressionList();
     return new InstanceCreation(
         classReference, typeArguments, fieldValues, asserts, unusedArguments)
@@ -2410,7 +2432,7 @@
 
   Expression _readBlockExpression() {
     int stackHeight = variableStack.length;
-    List<Statement> statements = readStatementList();
+    List<Statement> statements = readStatementListAlwaysGrowable();
     Expression value = readExpression();
     variableStack.length = stackHeight;
     return new BlockExpression(new Block(statements), value);
@@ -2432,7 +2454,7 @@
   List<MapEntry> readMapEntryList() {
     int length = readUInt30();
     return new List<MapEntry>.generate(length, (_) => readMapEntry(),
-        growable: true);
+        growable: useGrowableLists);
   }
 
   MapEntry readMapEntry() {
@@ -2441,6 +2463,17 @@
 
   List<Statement> readStatementList() {
     int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      return const [];
+    }
+    return new List<Statement>.generate(length, (_) => readStatement(),
+        growable: useGrowableLists);
+  }
+
+  List<Statement> readStatementListAlwaysGrowable() {
+    int length = readUInt30();
     return new List<Statement>.generate(length, (_) => readStatement(),
         growable: true);
   }
@@ -2579,11 +2612,18 @@
     int offset = readOffset();
     Expression expression = readExpression();
     int count = readUInt30();
-    List<SwitchCase> cases = new List<SwitchCase>.generate(
-        count,
-        (_) => new SwitchCase(<Expression>[], <int>[], dummyStatement,
-            isDefault: false),
-        growable: true);
+    List<SwitchCase> cases;
+    if (!useGrowableLists && count == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      cases = const [];
+    } else {
+      cases = new List<SwitchCase>.generate(
+          count,
+          (_) => new SwitchCase(<Expression>[], <int>[], dummyStatement,
+              isDefault: false),
+          growable: useGrowableLists);
+    }
     switchCaseStack.addAll(cases);
     for (int i = 0; i < cases.length; ++i) {
       _readSwitchCaseInto(cases[i]);
@@ -2659,7 +2699,13 @@
 
   List<Catch> readCatchList() {
     int length = readUInt30();
-    return new List<Catch>.generate(length, (_) => readCatch(), growable: true);
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      return const [];
+    }
+    return new List<Catch>.generate(length, (_) => readCatch(),
+        growable: useGrowableLists);
   }
 
   Catch readCatch() {
@@ -2678,7 +2724,7 @@
     int stackHeight = variableStack.length;
     int offset = readOffset();
     int endOffset = readOffset();
-    List<Statement> body = readStatementList();
+    List<Statement> body = readStatementListAlwaysGrowable();
     variableStack.length = stackHeight;
     return new Block(body)
       ..fileOffset = offset
@@ -2687,7 +2733,7 @@
 
   AssertBlock _readAssertBlock() {
     int stackHeight = variableStack.length;
-    List<Statement> body = readStatementList();
+    List<Statement> body = readStatementListAlwaysGrowable();
     variableStack.length = stackHeight;
     return new AssertBlock(body);
   }
@@ -2708,20 +2754,35 @@
 
   List<Supertype> readSupertypeList() {
     int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      return const [];
+    }
     return new List<Supertype>.generate(length, (_) => readSupertype(),
-        growable: true);
+        growable: useGrowableLists);
   }
 
   List<DartType> readDartTypeList() {
     int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      return const [];
+    }
     return new List<DartType>.generate(length, (_) => readDartType(),
-        growable: true);
+        growable: useGrowableLists);
   }
 
   List<NamedType> readNamedTypeList() {
     int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      return const [];
+    }
     return new List<NamedType>.generate(length, (_) => readNamedType(),
-        growable: true);
+        growable: useGrowableLists);
   }
 
   NamedType readNamedType() {
@@ -2868,7 +2929,7 @@
     if (list == null) {
       list = new List<TypeParameter>.generate(
           length, (_) => new TypeParameter(null, null)..parent = parent,
-          growable: true);
+          growable: useGrowableLists);
     } else if (list.length != length) {
       list.length = length;
       for (int i = 0; i < length; ++i) {
@@ -2907,9 +2968,14 @@
 
   List<NamedExpression> readNamedExpressionList() {
     int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      return const [];
+    }
     return new List<NamedExpression>.generate(
         length, (_) => readNamedExpression(),
-        growable: true);
+        growable: useGrowableLists);
   }
 
   NamedExpression readNamedExpression() {
@@ -2918,9 +2984,14 @@
 
   List<VariableDeclaration> readAndPushVariableDeclarationList() {
     int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      return const [];
+    }
     return new List<VariableDeclaration>.generate(
         length, (_) => readAndPushVariableDeclaration(),
-        growable: true);
+        growable: useGrowableLists);
   }
 
   VariableDeclaration readAndPushVariableDeclarationOption() {
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index 4653eec..57c0a74 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -173,7 +173,7 @@
   /// Internal version of kernel binary format.
   /// Bump it when making incompatible changes in kernel binaries.
   /// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
-  static const int BinaryFormatVersion = 55;
+  static const int BinaryFormatVersion = 56;
 }
 
 abstract class ConstantTag {
diff --git a/pkg/kernel/lib/canonical_name.dart b/pkg/kernel/lib/canonical_name.dart
index af7344e..0b9d2c6 100644
--- a/pkg/kernel/lib/canonical_name.dart
+++ b/pkg/kernel/lib/canonical_name.dart
@@ -30,9 +30,14 @@
 ///         "@constructors"
 ///         Qualified name
 ///
-///      Field:
+///      Field or the implicit getter of a field:
 ///         Canonical name of enclosing class or library
-///         "@fields"
+///         "@getters"
+///         Qualified name
+///
+///      Implicit setter of a field:
+///         Canonical name of enclosing class or library
+///         "@setters"
 ///         Qualified name
 ///
 ///      Typedef:
@@ -132,11 +137,11 @@
   }
 
   CanonicalName getChildFromField(Field field) {
-    return getChild('@fields').getChildFromQualifiedName(field.name!);
+    return getChild('@getters').getChildFromQualifiedName(field.name!);
   }
 
   CanonicalName getChildFromFieldSetter(Field field) {
-    return getChild('@=fields').getChildFromQualifiedName(field.name!);
+    return getChild('@setters').getChildFromQualifiedName(field.name!);
   }
 
   CanonicalName getChildFromConstructor(Constructor constructor) {
@@ -151,11 +156,11 @@
   }
 
   CanonicalName getChildFromFieldWithName(Name name) {
-    return getChild('@fields').getChildFromQualifiedName(name);
+    return getChild('@getters').getChildFromQualifiedName(name);
   }
 
   CanonicalName getChildFromFieldSetterWithName(Name name) {
-    return getChild('@=fields').getChildFromQualifiedName(name);
+    return getChild('@setters').getChildFromQualifiedName(name);
   }
 
   CanonicalName getChildFromTypedef(Typedef typedef_) {
diff --git a/pkg/kernel/test/convert_field_to_setter_getter.dart b/pkg/kernel/test/convert_field_to_setter_getter.dart
index 8cc17a4..1ae22d8 100644
--- a/pkg/kernel/test/convert_field_to_setter_getter.dart
+++ b/pkg/kernel/test/convert_field_to_setter_getter.dart
@@ -37,12 +37,14 @@
   List<int> writtenBytesFieldOriginal = serialize(lib1, lib2);
   // Canonical names are now set: Verify that the field is marked as such,
   // canonical-name-wise.
-  if (field.getterCanonicalName.parent.name != "@fields") {
-    throw "Expected @fields parent, but had "
+  String getterCanonicalName = '${field.getterCanonicalName}';
+  if (field.getterCanonicalName.parent.name != "@getters") {
+    throw "Expected @getters parent, but had "
         "${field.getterCanonicalName.parent.name}";
   }
-  if (field.setterCanonicalName.parent.name != "@=fields") {
-    throw "Expected @=fields parent, but had "
+  String setterCanonicalName = '${field.setterCanonicalName}';
+  if (field.setterCanonicalName.parent.name != "@setters") {
+    throw "Expected @setters parent, but had "
         "${field.setterCanonicalName.parent.name}";
   }
 
@@ -80,10 +82,18 @@
     throw "Expected @getters parent, but had "
         "${getter.canonicalName.parent.name}";
   }
+  if ('${getter.canonicalName}' != getterCanonicalName) {
+    throw "Unexpected getter canonical name. "
+        "Expected $getterCanonicalName, actual ${getter.canonicalName}.";
+  }
   if (setter.canonicalName.parent.name != "@setters") {
     throw "Expected @setters parent, but had "
         "${setter.canonicalName.parent.name}";
   }
+  if ('${setter.canonicalName}' != setterCanonicalName) {
+    throw "Unexpected setter canonical name. "
+        "Expected $setterCanonicalName, actual ${setter.canonicalName}.";
+  }
 
   // Replace getter/setter with field.
   lib1.procedures.remove(getter);
@@ -101,12 +111,12 @@
   List<int> writtenBytesFieldNew = serialize(lib1, lib2);
   // Canonical names are now set: Verify that the field is marked as such,
   // canonical-name-wise.
-  if (fieldReplacement.getterCanonicalName.parent.name != "@fields") {
-    throw "Expected @fields parent, but had "
+  if (fieldReplacement.getterCanonicalName.parent.name != "@getters") {
+    throw "Expected @getters parent, but had "
         "${fieldReplacement.getterCanonicalName.parent.name}";
   }
-  if (fieldReplacement.setterCanonicalName.parent.name != "@=fields") {
-    throw "Expected @=fields parent, but had "
+  if (fieldReplacement.setterCanonicalName.parent.name != "@setters") {
+    throw "Expected @setters parent, but had "
         "${fieldReplacement.setterCanonicalName.parent.name}";
   }
 
diff --git a/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart b/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart
index 41daa56..25be591 100644
--- a/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart
+++ b/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart
@@ -133,7 +133,7 @@
           name: '/* suppose top-level: dynamic field; */ field;',
           node: new ExpressionStatement(new StaticGet(field)),
           expectation: ''
-              '(expr (get-static "package:foo/bar.dart::@fields::field"))',
+              '(expr (get-static "package:foo/bar.dart::@getters::field"))',
           makeSerializationState: () => new SerializationState(null),
           makeDeserializationState: () =>
               new DeserializationState(null, component.root),
@@ -151,7 +151,7 @@
           name: '/* suppose top-level: dynamic field; */ field;',
           node: new ExpressionStatement(new StaticGet(field)),
           expectation: ''
-              '(expr (get-static "package:foo/bar.dart::@fields::field"))',
+              '(expr (get-static "package:foo/bar.dart::@getters::field"))',
           makeSerializationState: () => new SerializationState(null),
           makeDeserializationState: () =>
               new DeserializationState(null, component.root),
@@ -171,7 +171,7 @@
               new ExpressionStatement(new StaticSet(field, new IntLiteral(1))),
           expectation: ''
               '(expr'
-              ' (set-static "package:foo/bar.dart::@=fields::field" (int 1)))',
+              ' (set-static "package:foo/bar.dart::@setters::field" (int 1)))',
           makeSerializationState: () => new SerializationState(null),
           makeDeserializationState: () =>
               new DeserializationState(null, component.root),
diff --git a/pkg/vm/lib/transformations/ffi_definitions.dart b/pkg/vm/lib/transformations/ffi_definitions.dart
index 586d009..8371488 100644
--- a/pkg/vm/lib/transformations/ffi_definitions.dart
+++ b/pkg/vm/lib/transformations/ffi_definitions.dart
@@ -510,17 +510,29 @@
 
   List<Procedure> _generateMethodsForField(Field field, NativeTypeCfe type,
       Map<Abi, int> offsets, IndexedClass indexedClass) {
+    // TODO(johnniwinther): Avoid passing [indexedClass]. When compiling
+    // incrementally, [field] should already carry the references from
+    // [indexedClass].
     final getterStatement = type.generateGetterStatement(
         field.type, field.fileOffset, offsets, this);
+    Reference getterReference =
+        indexedClass?.lookupGetterReference(field.name) ??
+            field.getterReference;
+    assert(getterReference == field.getterReference,
+        "Unexpected getter reference for ${field}, found $getterReference.");
     final Procedure getter = Procedure(field.name, ProcedureKind.Getter,
         FunctionNode(getterStatement, returnType: field.type),
-        fileUri: field.fileUri,
-        reference: indexedClass?.lookupGetterReference(field.name))
+        fileUri: field.fileUri, reference: getterReference)
       ..fileOffset = field.fileOffset
       ..isNonNullableByDefault = field.isNonNullableByDefault;
 
     Procedure setter = null;
     if (!field.isFinal) {
+      Reference setterReference =
+          indexedClass?.lookupSetterReference(field.name) ??
+              field.setterReference;
+      assert(setterReference == field.setterReference,
+          "Unexpected setter reference for ${field}, found $setterReference.");
       final VariableDeclaration argument =
           VariableDeclaration('#v', type: field.type)
             ..fileOffset = field.fileOffset;
@@ -532,7 +544,7 @@
           FunctionNode(setterStatement,
               returnType: VoidType(), positionalParameters: [argument]),
           fileUri: field.fileUri,
-          reference: indexedClass?.lookupSetterReference(field.name))
+          reference: setterReference)
         ..fileOffset = field.fileOffset
         ..isNonNullableByDefault = field.isNonNullableByDefault;
     }
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index ac88a41..da4ca73 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -1324,7 +1324,12 @@
     if (!shaker.isMemberUsed(node) && !_preserveSpecialMember(node)) {
       // Ensure that kernel file writer will not be able to
       // write a dangling reference to the deleted member.
-      node.reference.canonicalName = null;
+      if (node is Field) {
+        node.getterCanonicalName?.reference = null;
+        node.setterCanonicalName?.reference = null;
+      } else {
+        node.canonicalName?.reference = null;
+      }
       Statistics.membersDropped++;
       return removalSentinel;
     }
diff --git a/pkg/vm_snapshot_analysis/CHANGELOG.md b/pkg/vm_snapshot_analysis/CHANGELOG.md
index 135e095..96e8132 100644
--- a/pkg/vm_snapshot_analysis/CHANGELOG.md
+++ b/pkg/vm_snapshot_analysis/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Changelog
 
+## 0.5.6
+- Fix for flutter/flutter#76313 causing issues with profiles containing
+WSRs serialized as smi-s instead of actual WSR objects.=
+
 ## 0.5.5
 - Add `deps-display-depth` (`-d`) flag for `summary` command to make the display
 depth of outputted dependency trees configurable.
diff --git a/pkg/vm_snapshot_analysis/pubspec.yaml b/pkg/vm_snapshot_analysis/pubspec.yaml
index eb64977..fdac362 100644
--- a/pkg/vm_snapshot_analysis/pubspec.yaml
+++ b/pkg/vm_snapshot_analysis/pubspec.yaml
@@ -1,6 +1,6 @@
 name: vm_snapshot_analysis
 description: Utilities for analysing AOT snapshot size.
-version: 0.5.5
+version: 0.5.6
 
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_snapshot_analysis
 
diff --git a/runtime/lib/stacktrace.cc b/runtime/lib/stacktrace.cc
index 2c75f02..545a3d3 100644
--- a/runtime/lib/stacktrace.cc
+++ b/runtime/lib/stacktrace.cc
@@ -18,25 +18,35 @@
 
 static const intptr_t kDefaultStackAllocation = 8;
 
+static StackTracePtr CreateStackTraceObject(
+    Zone* zone,
+    const GrowableObjectArray& code_list,
+    const GrowableArray<uword>& pc_offset_list) {
+  const auto& code_array =
+      Array::Handle(zone, Array::MakeFixedLength(code_list));
+  const auto& pc_offset_array = TypedData::Handle(
+      zone, TypedData::New(kUintPtrCid, pc_offset_list.length()));
+  {
+    NoSafepointScope no_safepoint;
+    memmove(pc_offset_array.DataAddr(0), pc_offset_list.data(),
+            pc_offset_list.length() * kWordSize);
+  }
+  return StackTrace::New(code_array, pc_offset_array);
+}
+
 static StackTracePtr CurrentSyncStackTraceLazy(Thread* thread,
                                                intptr_t skip_frames = 1) {
   Zone* zone = thread->zone();
 
   const auto& code_array = GrowableObjectArray::ZoneHandle(
       zone, GrowableObjectArray::New(kDefaultStackAllocation));
-  const auto& pc_offset_array = GrowableObjectArray::ZoneHandle(
-      zone, GrowableObjectArray::New(kDefaultStackAllocation));
+  GrowableArray<uword> pc_offset_array;
 
   // Collect the frames.
-  StackTraceUtils::CollectFramesLazy(thread, code_array, pc_offset_array,
+  StackTraceUtils::CollectFramesLazy(thread, code_array, &pc_offset_array,
                                      skip_frames);
 
-  const auto& code_array_fixed =
-      Array::Handle(zone, Array::MakeFixedLength(code_array));
-  const auto& pc_offset_array_fixed =
-      Array::Handle(zone, Array::MakeFixedLength(pc_offset_array));
-
-  return StackTrace::New(code_array_fixed, pc_offset_array_fixed);
+  return CreateStackTraceObject(zone, code_array, pc_offset_array);
 }
 
 static StackTracePtr CurrentSyncStackTrace(Thread* thread,
@@ -51,8 +61,8 @@
   // Allocate once.
   const Array& code_array =
       Array::ZoneHandle(zone, Array::New(stack_trace_length));
-  const Array& pc_offset_array =
-      Array::ZoneHandle(zone, Array::New(stack_trace_length));
+  const TypedData& pc_offset_array = TypedData::ZoneHandle(
+      zone, TypedData::New(kUintPtrCid, stack_trace_length));
 
   // Collect the frames.
   const intptr_t collected_frames_count = StackTraceUtils::CollectFrames(
@@ -89,7 +99,7 @@
 }
 
 static void AppendFrames(const GrowableObjectArray& code_list,
-                         const GrowableObjectArray& pc_offset_list,
+                         GrowableArray<uword>* pc_offset_list,
                          int skip_frames) {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
@@ -98,7 +108,6 @@
   StackFrame* frame = frames.NextFrame();
   ASSERT(frame != NULL);  // We expect to find a dart invocation frame.
   Code& code = Code::Handle(zone);
-  Smi& offset = Smi::Handle(zone);
   for (; frame != NULL; frame = frames.NextFrame()) {
     if (!frame->IsDartFrame()) {
       continue;
@@ -109,9 +118,9 @@
     }
 
     code = frame->LookupDartCode();
-    offset = Smi::New(frame->pc() - code.PayloadStart());
+    const intptr_t pc_offset = frame->pc() - code.PayloadStart();
     code_list.Add(code);
-    pc_offset_list.Add(offset);
+    pc_offset_list->Add(pc_offset);
   }
 }
 
@@ -119,16 +128,14 @@
 //
 // Skips the first skip_frames Dart frames.
 const StackTrace& GetCurrentStackTrace(int skip_frames) {
+  Zone* zone = Thread::Current()->zone();
   const GrowableObjectArray& code_list =
-      GrowableObjectArray::Handle(GrowableObjectArray::New());
-  const GrowableObjectArray& pc_offset_list =
-      GrowableObjectArray::Handle(GrowableObjectArray::New());
-  AppendFrames(code_list, pc_offset_list, skip_frames);
-  const Array& code_array = Array::Handle(Array::MakeFixedLength(code_list));
-  const Array& pc_offset_array =
-      Array::Handle(Array::MakeFixedLength(pc_offset_list));
-  const StackTrace& stacktrace =
-      StackTrace::Handle(StackTrace::New(code_array, pc_offset_array));
+      GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
+  GrowableArray<uword> pc_offset_list;
+  AppendFrames(code_list, &pc_offset_list, skip_frames);
+
+  const StackTrace& stacktrace = StackTrace::Handle(
+      zone, CreateStackTraceObject(zone, code_list, pc_offset_list));
   return stacktrace;
 }
 
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 1661602..fa44bea 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -6087,12 +6087,14 @@
 
   for (SerializationCluster* cluster : canonical_clusters) {
     cluster->WriteAndMeasureAlloc(this);
+    bytes_heap_allocated_ += cluster->target_memory_size();
 #if defined(DEBUG)
     Write<int32_t>(next_ref_index_);
 #endif
   }
   for (SerializationCluster* cluster : clusters) {
     cluster->WriteAndMeasureAlloc(this);
+    bytes_heap_allocated_ += cluster->target_memory_size();
 #if defined(DEBUG)
     Write<int32_t>(next_ref_index_);
 #endif
@@ -6961,11 +6963,7 @@
       vm_isolate_snapshot_size_(0),
       isolate_snapshot_size_(0),
       vm_image_writer_(vm_image_writer),
-      isolate_image_writer_(isolate_image_writer),
-      clustered_vm_size_(0),
-      clustered_isolate_size_(0),
-      mapped_data_size_(0),
-      mapped_text_size_(0) {
+      isolate_image_writer_(isolate_image_writer) {
   ASSERT(isolate_group() != NULL);
   ASSERT(heap() != NULL);
   ObjectStore* object_store = isolate_group()->object_store();
@@ -6999,6 +6997,7 @@
   ZoneGrowableArray<Object*>* objects = serializer.Serialize(&roots);
   serializer.FillHeader(serializer.kind());
   clustered_vm_size_ = serializer.bytes_written();
+  heap_vm_size_ = serializer.bytes_heap_allocated();
 
   if (Snapshot::IncludesCode(kind_)) {
     vm_image_writer_->SetProfileWriter(profile_writer_);
@@ -7043,6 +7042,7 @@
   }
   serializer.FillHeader(serializer.kind());
   clustered_isolate_size_ = serializer.bytes_written();
+  heap_isolate_size_ = serializer.bytes_heap_allocated();
 
   if (Snapshot::IncludesCode(kind_)) {
     isolate_image_writer_->SetProfileWriter(profile_writer_);
@@ -7120,6 +7120,9 @@
     OS::Print("Total(CodeSize): %" Pd "\n",
               clustered_vm_size_ + clustered_isolate_size_ + mapped_data_size_ +
                   mapped_text_size_);
+    OS::Print("VMIsolate(HeapSize): %" Pd "\n", heap_vm_size_);
+    OS::Print("Isolate(HeapSize): %" Pd "\n", heap_isolate_size_);
+    OS::Print("Total(HeapSize): %" Pd "\n", heap_vm_size_ + heap_isolate_size_);
   }
 
 #if defined(DART_PRECOMPILER)
diff --git a/runtime/vm/clustered_snapshot.h b/runtime/vm/clustered_snapshot.h
index c4bd073..147e341 100644
--- a/runtime/vm/clustered_snapshot.h
+++ b/runtime/vm/clustered_snapshot.h
@@ -245,6 +245,7 @@
 
   NonStreamingWriteStream* stream() { return stream_; }
   intptr_t bytes_written() { return stream_->bytes_written(); }
+  intptr_t bytes_heap_allocated() { return bytes_heap_allocated_; }
 
   void FlushBytesWrittenToRoot();
   void TraceStartWritingObject(const char* type, ObjectPtr obj, StringPtr name);
@@ -445,6 +446,7 @@
   FieldTable* initial_field_table_;
 
   intptr_t dispatch_table_size_ = 0;
+  intptr_t bytes_heap_allocated_ = 0;
 
   // True if writing VM snapshot, false for Isolate snapshot.
   bool vm_;
@@ -716,10 +718,12 @@
   ImageWriter* isolate_image_writer_;
 
   // Stats for benchmarking.
-  intptr_t clustered_vm_size_;
-  intptr_t clustered_isolate_size_;
-  intptr_t mapped_data_size_;
-  intptr_t mapped_text_size_;
+  intptr_t clustered_vm_size_ = 0;
+  intptr_t clustered_isolate_size_ = 0;
+  intptr_t mapped_data_size_ = 0;
+  intptr_t mapped_text_size_ = 0;
+  intptr_t heap_vm_size_ = 0;
+  intptr_t heap_isolate_size_ = 0;
 
   V8SnapshotProfileWriter* profile_writer_ = nullptr;
 
diff --git a/runtime/vm/code_patcher_arm.cc b/runtime/vm/code_patcher_arm.cc
index 0947b58..2fc5361 100644
--- a/runtime/vm/code_patcher_arm.cc
+++ b/runtime/vm/code_patcher_arm.cc
@@ -96,9 +96,8 @@
     const Code& caller_code,
     const Object& data,
     const Code& target) {
-  ASSERT(caller_code.ContainsInstructionAt(return_address));
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    BareSwitchableCallPattern call(return_address, caller_code);
+    BareSwitchableCallPattern call(return_address);
     call.SetData(data);
     call.SetTarget(target);
   } else {
@@ -110,9 +109,8 @@
 
 uword CodePatcher::GetSwitchableCallTargetEntryAt(uword return_address,
                                                   const Code& caller_code) {
-  ASSERT(caller_code.ContainsInstructionAt(return_address));
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    BareSwitchableCallPattern call(return_address, caller_code);
+    BareSwitchableCallPattern call(return_address);
     return call.target_entry();
   } else {
     SwitchableCallPattern call(return_address, caller_code);
@@ -122,9 +120,8 @@
 
 ObjectPtr CodePatcher::GetSwitchableCallDataAt(uword return_address,
                                                const Code& caller_code) {
-  ASSERT(caller_code.ContainsInstructionAt(return_address));
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    BareSwitchableCallPattern call(return_address, caller_code);
+    BareSwitchableCallPattern call(return_address);
     return call.data();
   } else {
     SwitchableCallPattern call(return_address, caller_code);
diff --git a/runtime/vm/code_patcher_arm64.cc b/runtime/vm/code_patcher_arm64.cc
index b7590db..c399fd1 100644
--- a/runtime/vm/code_patcher_arm64.cc
+++ b/runtime/vm/code_patcher_arm64.cc
@@ -132,9 +132,8 @@
     const Code& caller_code,
     const Object& data,
     const Code& target) {
-  ASSERT(caller_code.ContainsInstructionAt(return_address));
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    BareSwitchableCallPattern call(return_address, caller_code);
+    BareSwitchableCallPattern call(return_address);
     call.SetData(data);
     call.SetTarget(target);
   } else {
@@ -146,9 +145,8 @@
 
 uword CodePatcher::GetSwitchableCallTargetEntryAt(uword return_address,
                                                   const Code& caller_code) {
-  ASSERT(caller_code.ContainsInstructionAt(return_address));
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    BareSwitchableCallPattern call(return_address, caller_code);
+    BareSwitchableCallPattern call(return_address);
     return call.target_entry();
   } else {
     SwitchableCallPattern call(return_address, caller_code);
@@ -158,9 +156,8 @@
 
 ObjectPtr CodePatcher::GetSwitchableCallDataAt(uword return_address,
                                                const Code& caller_code) {
-  ASSERT(caller_code.ContainsInstructionAt(return_address));
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    BareSwitchableCallPattern call(return_address, caller_code);
+    BareSwitchableCallPattern call(return_address);
     return call.data();
   } else {
     SwitchableCallPattern call(return_address, caller_code);
diff --git a/runtime/vm/code_patcher_x64.cc b/runtime/vm/code_patcher_x64.cc
index 1ba005f..ce1d9f2 100644
--- a/runtime/vm/code_patcher_x64.cc
+++ b/runtime/vm/code_patcher_x64.cc
@@ -220,10 +220,8 @@
 //   call target.entry           call stub.entry         call stub.entry
 class SwitchableCallBase : public ValueObject {
  public:
-  explicit SwitchableCallBase(const Code& code)
-      : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
-        target_index_(-1),
-        data_index_(-1) {}
+  explicit SwitchableCallBase(const ObjectPool& object_pool)
+      : object_pool_(object_pool), target_index_(-1), data_index_(-1) {}
 
   intptr_t data_index() const { return data_index_; }
   intptr_t target_index() const { return target_index_; }
@@ -237,7 +235,7 @@
   }
 
  protected:
-  ObjectPool& object_pool_;
+  const ObjectPool& object_pool_;
   intptr_t target_index_;
   intptr_t data_index_;
 
@@ -251,8 +249,9 @@
 // monomorphic function or a stub code.
 class SwitchableCall : public SwitchableCallBase {
  public:
-  SwitchableCall(uword return_address, const Code& code)
-      : SwitchableCallBase(code) {
+  SwitchableCall(uword return_address, const Code& caller_code)
+      : SwitchableCallBase(ObjectPool::Handle(caller_code.GetObjectPool())) {
+    ASSERT(caller_code.ContainsInstructionAt(return_address));
     uword pc = return_address;
 
     // callq RCX
@@ -333,11 +332,9 @@
 // of the monomorphic function or a stub entry point.
 class BareSwitchableCall : public SwitchableCallBase {
  public:
-  BareSwitchableCall(uword return_address, const Code& code)
-      : SwitchableCallBase(code) {
-    object_pool_ = ObjectPool::RawCast(
-        IsolateGroup::Current()->object_store()->global_object_pool());
-
+  explicit BareSwitchableCall(uword return_address)
+      : SwitchableCallBase(ObjectPool::Handle(
+            IsolateGroup::Current()->object_store()->global_object_pool())) {
     uword pc = return_address;
 
     // callq RCX
@@ -489,9 +486,8 @@
     const Code& caller_code,
     const Object& data,
     const Code& target) {
-  ASSERT(caller_code.ContainsInstructionAt(return_address));
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    BareSwitchableCall call(return_address, caller_code);
+    BareSwitchableCall call(return_address);
     call.SetData(data);
     call.SetTarget(target);
   } else {
@@ -503,9 +499,8 @@
 
 uword CodePatcher::GetSwitchableCallTargetEntryAt(uword return_address,
                                                   const Code& caller_code) {
-  ASSERT(caller_code.ContainsInstructionAt(return_address));
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    BareSwitchableCall call(return_address, caller_code);
+    BareSwitchableCall call(return_address);
     return call.target_entry();
   } else {
     SwitchableCall call(return_address, caller_code);
@@ -515,9 +510,8 @@
 
 ObjectPtr CodePatcher::GetSwitchableCallDataAt(uword return_address,
                                                const Code& caller_code) {
-  ASSERT(caller_code.ContainsInstructionAt(return_address));
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    BareSwitchableCall call(return_address, caller_code);
+    BareSwitchableCall call(return_address);
     return call.data();
   } else {
     SwitchableCall call(return_address, caller_code);
diff --git a/runtime/vm/compiler/assembler/disassembler.cc b/runtime/vm/compiler/assembler/disassembler.cc
index 78ae30b..9e19290 100644
--- a/runtime/vm/compiler/assembler/disassembler.cc
+++ b/runtime/vm/compiler/assembler/disassembler.cc
@@ -457,6 +457,9 @@
 void Disassembler::DisassembleCode(const Function& function,
                                    const Code& code,
                                    bool optimized) {
+  if (code.IsUnknownDartCode()) {
+    return;
+  }
   TextBuffer buffer(128);
   const char* function_fullname = function.ToFullyQualifiedCString();
   buffer.Printf("%s", Function::KindToCString(function.kind()));
diff --git a/runtime/vm/compiler/ffi/native_calling_convention.cc b/runtime/vm/compiler/ffi/native_calling_convention.cc
index a0c1f30..79d127a 100644
--- a/runtime/vm/compiler/ffi/native_calling_convention.cc
+++ b/runtime/vm/compiler/ffi/native_calling_convention.cc
@@ -141,7 +141,7 @@
   const NativeLocation& AllocateCompound(
       const NativeCompoundType& payload_type) {
     const intptr_t size = payload_type.SizeInBytes();
-    if (size <= 16 && size > 0) {
+    if (size <= 16 && size > 0 && !payload_type.ContainsUnalignedMembers()) {
       intptr_t required_regs =
           payload_type.NumberOfWordSizeChunksNotOnlyFloat();
       intptr_t required_xmm_regs =
@@ -516,7 +516,7 @@
     Zone* zone,
     const NativeCompoundType& payload_type) {
   const intptr_t size = payload_type.SizeInBytes();
-  if (size <= 16 && size > 0) {
+  if (size <= 16 && size > 0 && !payload_type.ContainsUnalignedMembers()) {
     // Allocate the same as argument, but use return registers instead of
     // argument registers.
     NativeLocations& multiple_locations =
diff --git a/runtime/vm/compiler/ffi/native_calling_convention_test.cc b/runtime/vm/compiler/ffi/native_calling_convention_test.cc
index 82db265..0ac5e3f 100644
--- a/runtime/vm/compiler/ffi/native_calling_convention_test.cc
+++ b/runtime/vm/compiler/ffi/native_calling_convention_test.cc
@@ -398,6 +398,77 @@
   RunSignatureTest(Z, "struct8bytesx1", arguments, struct_type);
 }
 
+// The struct is only 8 bytes with packing enabled.
+//
+// Many calling conventions pass this struct in single registers or less
+// stack slots because of this.
+//
+// Non-windows x64 passes this struct on the stack instead of in a single
+// CPU register, because it contains a mis-aligned member.
+//
+// See the *.expect in ./unit_tests for this behavior.
+UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct8bytesPackedx10) {
+  const auto& int8_type = *new (Z) NativePrimitiveType(kInt8);
+  const auto& int32_type = *new (Z) NativePrimitiveType(kInt32);
+
+  auto& member_types = *new (Z) NativeTypes(Z, 5);
+  member_types.Add(&int8_type);
+  member_types.Add(&int32_type);
+  member_types.Add(&int8_type);
+  member_types.Add(&int8_type);
+  member_types.Add(&int8_type);
+  const auto& struct_type =
+      NativeCompoundType::FromNativeTypes(Z, member_types, /*packing=*/1);
+  EXPECT_EQ(8, struct_type.SizeInBytes());
+  EXPECT(struct_type.ContainsUnalignedMembers());
+
+  auto& arguments = *new (Z) NativeTypes(Z, 10);
+  arguments.Add(&struct_type);
+  arguments.Add(&struct_type);
+  arguments.Add(&struct_type);
+  arguments.Add(&struct_type);
+  arguments.Add(&struct_type);
+  arguments.Add(&struct_type);
+  arguments.Add(&struct_type);
+  arguments.Add(&struct_type);
+  arguments.Add(&struct_type);
+  arguments.Add(&struct_type);
+
+  RunSignatureTest(Z, "struct8bytesPackedx10", arguments, struct_type);
+}
+
+// Without packing, this would be a 16 byte struct. However, because of packing
+// it's 9 bytes.
+//
+// #pragma pack(push,1)
+// typedef struct  {
+//   int8_t a0;
+//   double a1
+// } StructPacked;
+// #pragma pack(pop)
+//
+// See the *.expect in ./unit_tests for this behavior.
+UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_structPacked) {
+  const auto& int8_type = *new (Z) NativePrimitiveType(kInt8);
+  const auto& double_type = *new (Z) NativePrimitiveType(kDouble);
+
+  auto& member_types = *new (Z) NativeTypes(Z, 2);
+  member_types.Add(&int8_type);
+  member_types.Add(&double_type);
+  const auto& struct_type =
+      NativeCompoundType::FromNativeTypes(Z, member_types, /*packing=*/1);
+  EXPECT_EQ(9, struct_type.SizeInBytes());
+  EXPECT(struct_type.ContainsUnalignedMembers());
+
+  auto& arguments = *new (Z) NativeTypes(Z, 11);
+  arguments.Add(&struct_type);
+  arguments.Add(&struct_type);
+  arguments.Add(&int8_type);    // Backfilling int registers.
+  arguments.Add(&double_type);  // Backfilling float registers.
+
+  RunSignatureTest(Z, "structPacked", arguments, struct_type);
+}
+
 }  // namespace ffi
 }  // namespace compiler
 }  // namespace dart
diff --git a/runtime/vm/compiler/ffi/native_type.cc b/runtime/vm/compiler/ffi/native_type.cc
index 0e0d125..f36f8af 100644
--- a/runtime/vm/compiler/ffi/native_type.cc
+++ b/runtime/vm/compiler/ffi/native_type.cc
@@ -164,7 +164,8 @@
 // pkg/vm/lib/transformations/ffi_definitions.dart:_calculateStructLayout.
 NativeCompoundType& NativeCompoundType::FromNativeTypes(
     Zone* zone,
-    const NativeTypes& members) {
+    const NativeTypes& members,
+    intptr_t member_packing) {
   intptr_t offset = 0;
 
   const intptr_t kAtLeast1ByteAligned = 1;
@@ -195,8 +196,13 @@
   for (intptr_t i = 0; i < members.length(); i++) {
     const NativeType& member = *members[i];
     const intptr_t member_size = member.SizeInBytes();
-    const intptr_t member_align_field = member.AlignmentInBytesField();
-    const intptr_t member_align_stack = member.AlignmentInBytesStack();
+    const intptr_t member_align_field =
+        Utils::Minimum(member.AlignmentInBytesField(), member_packing);
+    intptr_t member_align_stack = member.AlignmentInBytesStack();
+    if (member_align_stack > member_packing &&
+        member_packing < compiler::target::kWordSize) {
+      member_align_stack = compiler::target::kWordSize;
+    }
     offset = Utils::RoundUp(offset, member_align_field);
     member_offsets.Add(offset);
     offset += member_size;
@@ -710,6 +716,29 @@
 }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
+bool NativePrimitiveType::ContainsUnalignedMembers() const {
+  return false;
+}
+
+bool NativeArrayType::ContainsUnalignedMembers() const {
+  return element_type_.ContainsUnalignedMembers();
+}
+
+bool NativeCompoundType::ContainsUnalignedMembers() const {
+  for (intptr_t i = 0; i < members_.length(); i++) {
+    const auto& member = *members_.At(i);
+    const intptr_t member_offset = member_offsets_.At(i);
+    const intptr_t member_alignment = member.AlignmentInBytesField();
+    if (member_offset % member_alignment != 0) {
+      return true;
+    }
+    if (member.ContainsUnalignedMembers()) {
+      return true;
+    }
+  }
+  return false;
+}
+
 static void ContainsHomogenuousFloatsRecursive(const NativeTypes& types,
                                                bool* only_float,
                                                bool* only_double) {
diff --git a/runtime/vm/compiler/ffi/native_type.h b/runtime/vm/compiler/ffi/native_type.h
index 9b3df3f..8fbcbc5 100644
--- a/runtime/vm/compiler/ffi/native_type.h
+++ b/runtime/vm/compiler/ffi/native_type.h
@@ -97,6 +97,9 @@
   virtual bool ContainsOnlyFloats(Range range) const = 0;
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
+  // True iff any members are misaligned recursively due to packing.
+  virtual bool ContainsUnalignedMembers() const = 0;
+
 #if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
   // NativeTypes which are available as unboxed Representations.
   virtual bool IsExpressibleAsRepresentation() const { return false; }
@@ -185,6 +188,8 @@
   virtual bool ContainsOnlyFloats(Range range) const;
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
+  virtual bool ContainsUnalignedMembers() const;
+
 #if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
   virtual bool IsExpressibleAsRepresentation() const;
   virtual Representation AsRepresentation() const;
@@ -234,6 +239,8 @@
   virtual bool ContainsOnlyFloats(Range range) const;
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
+  virtual bool ContainsUnalignedMembers() const;
+
   virtual bool Equals(const NativeType& other) const;
 
   virtual void PrintTo(BaseTextBuffer* f,
@@ -255,8 +262,10 @@
 // TODO(dartbug.com/38491): Support unions.
 class NativeCompoundType : public NativeType {
  public:
-  static NativeCompoundType& FromNativeTypes(Zone* zone,
-                                             const NativeTypes& members);
+  static NativeCompoundType& FromNativeTypes(
+      Zone* zone,
+      const NativeTypes& members,
+      intptr_t member_packing = kMaxInt32);
 
   const NativeTypes& members() const { return members_; }
   const ZoneGrowableArray<intptr_t>& member_offsets() const {
@@ -289,6 +298,8 @@
   intptr_t NumberOfWordSizeChunksNotOnlyFloat() const;
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
+  virtual bool ContainsUnalignedMembers() const;
+
   // Whether this type has only same-size floating point members.
   //
   // Useful for determining whether struct is passed in FP registers in hardfp
diff --git a/runtime/vm/compiler/ffi/native_type_test.cc b/runtime/vm/compiler/ffi/native_type_test.cc
index d71e0e3..d6d5ece 100644
--- a/runtime/vm/compiler/ffi/native_type_test.cc
+++ b/runtime/vm/compiler/ffi/native_type_test.cc
@@ -14,9 +14,10 @@
 
 const NativeCompoundType& RunStructTest(dart::Zone* zone,
                                         const char* name,
-                                        const NativeTypes& member_types) {
+                                        const NativeTypes& member_types,
+                                        intptr_t packing = kMaxInt32) {
   const auto& struct_type =
-      NativeCompoundType::FromNativeTypes(zone, member_types);
+      NativeCompoundType::FromNativeTypes(zone, member_types, packing);
 
   const char* test_result = struct_type.ToCString(zone, /*multi_line=*/true);
 
@@ -196,6 +197,23 @@
             struct_type.NumberOfWordSizeChunksOnlyFloat());
 }
 
+UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_packed) {
+  const auto& uint8_type = *new (Z) NativePrimitiveType(kUint8);
+  const auto& uint16_type = *new (Z) NativePrimitiveType(kUint16);
+
+  auto& members = *new (Z) NativeTypes(Z, 2);
+  members.Add(&uint8_type);
+  members.Add(&uint16_type);
+  const intptr_t packing = 1;
+
+  const auto& struct_type =
+      NativeCompoundType::FromNativeTypes(Z, members, packing);
+
+  // Should be 3 bytes on every platform.
+  EXPECT_EQ(3, struct_type.SizeInBytes());
+  EXPECT(struct_type.ContainsUnalignedMembers());
+}
+
 }  // namespace ffi
 }  // namespace compiler
 }  // namespace dart
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_android.expect
new file mode 100644
index 0000000..e7b7d6e
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_android.expect
@@ -0,0 +1,12 @@
+M(r0 int64) Compound(size: 8)
+M(r1 int64) Compound(size: 8)
+M(r2 int64) Compound(size: 8)
+M(r3 int64) Compound(size: 8)
+M(r4 int64) Compound(size: 8)
+M(r5 int64) Compound(size: 8)
+M(r6 int64) Compound(size: 8)
+M(r7 int64) Compound(size: 8)
+S+0 Compound(size: 8)
+S+8 Compound(size: 8)
+=>
+M(r0 int64) Compound(size: 8)
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_ios.expect
new file mode 100644
index 0000000..e7b7d6e
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_ios.expect
@@ -0,0 +1,12 @@
+M(r0 int64) Compound(size: 8)
+M(r1 int64) Compound(size: 8)
+M(r2 int64) Compound(size: 8)
+M(r3 int64) Compound(size: 8)
+M(r4 int64) Compound(size: 8)
+M(r5 int64) Compound(size: 8)
+M(r6 int64) Compound(size: 8)
+M(r7 int64) Compound(size: 8)
+S+0 Compound(size: 8)
+S+8 Compound(size: 8)
+=>
+M(r0 int64) Compound(size: 8)
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_linux.expect
new file mode 100644
index 0000000..e7b7d6e
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_linux.expect
@@ -0,0 +1,12 @@
+M(r0 int64) Compound(size: 8)
+M(r1 int64) Compound(size: 8)
+M(r2 int64) Compound(size: 8)
+M(r3 int64) Compound(size: 8)
+M(r4 int64) Compound(size: 8)
+M(r5 int64) Compound(size: 8)
+M(r6 int64) Compound(size: 8)
+M(r7 int64) Compound(size: 8)
+S+0 Compound(size: 8)
+S+8 Compound(size: 8)
+=>
+M(r0 int64) Compound(size: 8)
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_macos.expect
new file mode 100644
index 0000000..e7b7d6e
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm64_macos.expect
@@ -0,0 +1,12 @@
+M(r0 int64) Compound(size: 8)
+M(r1 int64) Compound(size: 8)
+M(r2 int64) Compound(size: 8)
+M(r3 int64) Compound(size: 8)
+M(r4 int64) Compound(size: 8)
+M(r5 int64) Compound(size: 8)
+M(r6 int64) Compound(size: 8)
+M(r7 int64) Compound(size: 8)
+S+0 Compound(size: 8)
+S+8 Compound(size: 8)
+=>
+M(r0 int64) Compound(size: 8)
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm_android.expect
new file mode 100644
index 0000000..49e6d40
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm_android.expect
@@ -0,0 +1,12 @@
+M(r1 int32, r2 int32) Compound(size: 8)
+M(r3 int32, S+0 int32) Compound(size: 8)
+M(S+4 int32, S+8 int32) Compound(size: 8)
+M(S+12 int32, S+16 int32) Compound(size: 8)
+M(S+20 int32, S+24 int32) Compound(size: 8)
+M(S+28 int32, S+32 int32) Compound(size: 8)
+M(S+36 int32, S+40 int32) Compound(size: 8)
+M(S+44 int32, S+48 int32) Compound(size: 8)
+M(S+52 int32, S+56 int32) Compound(size: 8)
+M(S+60 int32, S+64 int32) Compound(size: 8)
+=>
+P(r0 uint32) Compound(size: 8)
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm_ios.expect
new file mode 100644
index 0000000..49e6d40
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm_ios.expect
@@ -0,0 +1,12 @@
+M(r1 int32, r2 int32) Compound(size: 8)
+M(r3 int32, S+0 int32) Compound(size: 8)
+M(S+4 int32, S+8 int32) Compound(size: 8)
+M(S+12 int32, S+16 int32) Compound(size: 8)
+M(S+20 int32, S+24 int32) Compound(size: 8)
+M(S+28 int32, S+32 int32) Compound(size: 8)
+M(S+36 int32, S+40 int32) Compound(size: 8)
+M(S+44 int32, S+48 int32) Compound(size: 8)
+M(S+52 int32, S+56 int32) Compound(size: 8)
+M(S+60 int32, S+64 int32) Compound(size: 8)
+=>
+P(r0 uint32) Compound(size: 8)
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm_linux.expect
new file mode 100644
index 0000000..49e6d40
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/arm_linux.expect
@@ -0,0 +1,12 @@
+M(r1 int32, r2 int32) Compound(size: 8)
+M(r3 int32, S+0 int32) Compound(size: 8)
+M(S+4 int32, S+8 int32) Compound(size: 8)
+M(S+12 int32, S+16 int32) Compound(size: 8)
+M(S+20 int32, S+24 int32) Compound(size: 8)
+M(S+28 int32, S+32 int32) Compound(size: 8)
+M(S+36 int32, S+40 int32) Compound(size: 8)
+M(S+44 int32, S+48 int32) Compound(size: 8)
+M(S+52 int32, S+56 int32) Compound(size: 8)
+M(S+60 int32, S+64 int32) Compound(size: 8)
+=>
+P(r0 uint32) Compound(size: 8)
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/ia32_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/ia32_android.expect
new file mode 100644
index 0000000..004cbab
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/ia32_android.expect
@@ -0,0 +1,12 @@
+S+4 Compound(size: 8)
+S+12 Compound(size: 8)
+S+20 Compound(size: 8)
+S+28 Compound(size: 8)
+S+36 Compound(size: 8)
+S+44 Compound(size: 8)
+S+52 Compound(size: 8)
+S+60 Compound(size: 8)
+S+68 Compound(size: 8)
+S+76 Compound(size: 8)
+=>
+P(S+0 uint32, ret:eax uint32) Compound(size: 8)
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/ia32_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/ia32_linux.expect
new file mode 100644
index 0000000..004cbab
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/ia32_linux.expect
@@ -0,0 +1,12 @@
+S+4 Compound(size: 8)
+S+12 Compound(size: 8)
+S+20 Compound(size: 8)
+S+28 Compound(size: 8)
+S+36 Compound(size: 8)
+S+44 Compound(size: 8)
+S+52 Compound(size: 8)
+S+60 Compound(size: 8)
+S+68 Compound(size: 8)
+S+76 Compound(size: 8)
+=>
+P(S+0 uint32, ret:eax uint32) Compound(size: 8)
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/ia32_win.expect b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/ia32_win.expect
new file mode 100644
index 0000000..632981b
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/ia32_win.expect
@@ -0,0 +1,12 @@
+S+0 Compound(size: 8)
+S+8 Compound(size: 8)
+S+16 Compound(size: 8)
+S+24 Compound(size: 8)
+S+32 Compound(size: 8)
+S+40 Compound(size: 8)
+S+48 Compound(size: 8)
+S+56 Compound(size: 8)
+S+64 Compound(size: 8)
+S+72 Compound(size: 8)
+=>
+M(eax uint32, edx uint32) Compound(size: 8)
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_ios.expect
new file mode 100644
index 0000000..b121f02
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_ios.expect
@@ -0,0 +1,12 @@
+S+0 Compound(size: 8)
+S+8 Compound(size: 8)
+S+16 Compound(size: 8)
+S+24 Compound(size: 8)
+S+32 Compound(size: 8)
+S+40 Compound(size: 8)
+S+48 Compound(size: 8)
+S+56 Compound(size: 8)
+S+64 Compound(size: 8)
+S+72 Compound(size: 8)
+=>
+P(rdi int64, ret:rax int64) Compound(size: 8)
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_linux.expect
new file mode 100644
index 0000000..b121f02
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_linux.expect
@@ -0,0 +1,12 @@
+S+0 Compound(size: 8)
+S+8 Compound(size: 8)
+S+16 Compound(size: 8)
+S+24 Compound(size: 8)
+S+32 Compound(size: 8)
+S+40 Compound(size: 8)
+S+48 Compound(size: 8)
+S+56 Compound(size: 8)
+S+64 Compound(size: 8)
+S+72 Compound(size: 8)
+=>
+P(rdi int64, ret:rax int64) Compound(size: 8)
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_macos.expect
new file mode 100644
index 0000000..b121f02
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_macos.expect
@@ -0,0 +1,12 @@
+S+0 Compound(size: 8)
+S+8 Compound(size: 8)
+S+16 Compound(size: 8)
+S+24 Compound(size: 8)
+S+32 Compound(size: 8)
+S+40 Compound(size: 8)
+S+48 Compound(size: 8)
+S+56 Compound(size: 8)
+S+64 Compound(size: 8)
+S+72 Compound(size: 8)
+=>
+P(rdi int64, ret:rax int64) Compound(size: 8)
diff --git a/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_win.expect b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_win.expect
new file mode 100644
index 0000000..3504b4a
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/struct8bytesPackedx10/x64_win.expect
@@ -0,0 +1,12 @@
+M(rcx int64) Compound(size: 8)
+M(rdx int64) Compound(size: 8)
+M(r8 int64) Compound(size: 8)
+M(r9 int64) Compound(size: 8)
+S+0 Compound(size: 8)
+S+8 Compound(size: 8)
+S+16 Compound(size: 8)
+S+24 Compound(size: 8)
+S+32 Compound(size: 8)
+S+40 Compound(size: 8)
+=>
+M(rax int64) Compound(size: 8)
diff --git a/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_android.expect b/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_android.expect
new file mode 100644
index 0000000..020f65e
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_android.expect
@@ -0,0 +1,6 @@
+M(r0 int64, r1 int64) Compound(size: 9)
+M(r2 int64, r3 int64) Compound(size: 9)
+r4 int8
+v0 double
+=>
+M(r0 int64, r1 int64) Compound(size: 9)
diff --git a/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_ios.expect
new file mode 100644
index 0000000..0d23426
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_ios.expect
@@ -0,0 +1,6 @@
+M(r0 int64, r1 int64) Compound(size: 9)
+M(r2 int64, r3 int64) Compound(size: 9)
+r4 int32[int8]
+v0 double
+=>
+M(r0 int64, r1 int64) Compound(size: 9)
diff --git a/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_linux.expect
new file mode 100644
index 0000000..020f65e
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_linux.expect
@@ -0,0 +1,6 @@
+M(r0 int64, r1 int64) Compound(size: 9)
+M(r2 int64, r3 int64) Compound(size: 9)
+r4 int8
+v0 double
+=>
+M(r0 int64, r1 int64) Compound(size: 9)
diff --git a/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_macos.expect
new file mode 100644
index 0000000..020f65e
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/structPacked/arm64_macos.expect
@@ -0,0 +1,6 @@
+M(r0 int64, r1 int64) Compound(size: 9)
+M(r2 int64, r3 int64) Compound(size: 9)
+r4 int8
+v0 double
+=>
+M(r0 int64, r1 int64) Compound(size: 9)
diff --git a/runtime/vm/compiler/ffi/unit_tests/structPacked/arm_android.expect b/runtime/vm/compiler/ffi/unit_tests/structPacked/arm_android.expect
new file mode 100644
index 0000000..d52d63e
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/structPacked/arm_android.expect
@@ -0,0 +1,6 @@
+M(r1 int32, r2 int32, r3 int32) Compound(size: 9)
+M(S+0 int32, S+4 int32, S+8 int32) Compound(size: 9)
+S+12 int32[int8]
+S+16 double
+=>
+P(r0 uint32) Compound(size: 9)
diff --git a/runtime/vm/compiler/ffi/unit_tests/structPacked/arm_ios.expect b/runtime/vm/compiler/ffi/unit_tests/structPacked/arm_ios.expect
new file mode 100644
index 0000000..17492a2
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/structPacked/arm_ios.expect
@@ -0,0 +1,6 @@
+M(r1 int32, r2 int32, r3 int32) Compound(size: 9)
+M(S+0 int32, S+4 int32, S+8 int32) Compound(size: 9)
+S+12 int32[int8]
+d0 double
+=>
+P(r0 uint32) Compound(size: 9)
diff --git a/runtime/vm/compiler/ffi/unit_tests/structPacked/arm_linux.expect b/runtime/vm/compiler/ffi/unit_tests/structPacked/arm_linux.expect
new file mode 100644
index 0000000..17492a2
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/structPacked/arm_linux.expect
@@ -0,0 +1,6 @@
+M(r1 int32, r2 int32, r3 int32) Compound(size: 9)
+M(S+0 int32, S+4 int32, S+8 int32) Compound(size: 9)
+S+12 int32[int8]
+d0 double
+=>
+P(r0 uint32) Compound(size: 9)
diff --git a/runtime/vm/compiler/ffi/unit_tests/structPacked/ia32_android.expect b/runtime/vm/compiler/ffi/unit_tests/structPacked/ia32_android.expect
new file mode 100644
index 0000000..33bf360
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/structPacked/ia32_android.expect
@@ -0,0 +1,6 @@
+S+4 Compound(size: 9)
+S+16 Compound(size: 9)
+S+28 int32[int8]
+S+32 double
+=>
+P(S+0 uint32, ret:eax uint32) Compound(size: 9)
diff --git a/runtime/vm/compiler/ffi/unit_tests/structPacked/ia32_linux.expect b/runtime/vm/compiler/ffi/unit_tests/structPacked/ia32_linux.expect
new file mode 100644
index 0000000..33bf360
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/structPacked/ia32_linux.expect
@@ -0,0 +1,6 @@
+S+4 Compound(size: 9)
+S+16 Compound(size: 9)
+S+28 int32[int8]
+S+32 double
+=>
+P(S+0 uint32, ret:eax uint32) Compound(size: 9)
diff --git a/runtime/vm/compiler/ffi/unit_tests/structPacked/ia32_win.expect b/runtime/vm/compiler/ffi/unit_tests/structPacked/ia32_win.expect
new file mode 100644
index 0000000..33bf360
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/structPacked/ia32_win.expect
@@ -0,0 +1,6 @@
+S+4 Compound(size: 9)
+S+16 Compound(size: 9)
+S+28 int32[int8]
+S+32 double
+=>
+P(S+0 uint32, ret:eax uint32) Compound(size: 9)
diff --git a/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_ios.expect
new file mode 100644
index 0000000..c4e54aa
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_ios.expect
@@ -0,0 +1,6 @@
+S+0 Compound(size: 9)
+S+16 Compound(size: 9)
+rsi int32[int8]
+xmm0 double
+=>
+P(rdi int64, ret:rax int64) Compound(size: 9)
diff --git a/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_linux.expect
new file mode 100644
index 0000000..c4e54aa
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_linux.expect
@@ -0,0 +1,6 @@
+S+0 Compound(size: 9)
+S+16 Compound(size: 9)
+rsi int32[int8]
+xmm0 double
+=>
+P(rdi int64, ret:rax int64) Compound(size: 9)
diff --git a/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_macos.expect
new file mode 100644
index 0000000..c4e54aa
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_macos.expect
@@ -0,0 +1,6 @@
+S+0 Compound(size: 9)
+S+16 Compound(size: 9)
+rsi int32[int8]
+xmm0 double
+=>
+P(rdi int64, ret:rax int64) Compound(size: 9)
diff --git a/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_win.expect b/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_win.expect
new file mode 100644
index 0000000..b4ebbc6
--- /dev/null
+++ b/runtime/vm/compiler/ffi/unit_tests/structPacked/x64_win.expect
@@ -0,0 +1,6 @@
+P(rdx int64) Compound(size: 9)
+P(r8 int64) Compound(size: 9)
+r9 int8
+S+0 double
+=>
+P(rcx int64, ret:rax int64) Compound(size: 9)
diff --git a/runtime/vm/compiler/frontend/constant_reader.cc b/runtime/vm/compiler/frontend/constant_reader.cc
index f968c45..7c0d3ad 100644
--- a/runtime/vm/compiler/frontend/constant_reader.cc
+++ b/runtime/vm/compiler/frontend/constant_reader.cc
@@ -264,7 +264,8 @@
       Field& field = Field::Handle(Z);
       Instance& constant = Instance::Handle(Z);
       for (intptr_t j = 0; j < number_of_fields; ++j) {
-        field = H.LookupFieldByKernelField(reader.ReadCanonicalNameReference());
+        field = H.LookupFieldByKernelGetterOrSetter(
+            reader.ReadCanonicalNameReference());
         // Recurse into lazily evaluating all "sub" constants
         // needed to evaluate the current constant.
         const intptr_t entry_offset = reader.ReadUInt();
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 1bb096e..7fda645 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -237,7 +237,7 @@
         ReadBool();
         const NameIndex field_name = ReadCanonicalNameReference();
         const Field& field =
-            Field::Handle(Z, H.LookupFieldByKernelField(field_name));
+            Field::Handle(Z, H.LookupFieldByKernelGetterOrSetter(field_name));
         initializer_fields[i] = &field;
         SkipExpression();
         continue;
@@ -2180,8 +2180,7 @@
   const Function* tearoff_interface_target = &Function::null_function();
   const NameIndex itarget_name =
       ReadInterfaceMemberNameReference();  // read interface_target_reference.
-  if (!H.IsRoot(itarget_name) &&
-      (H.IsGetter(itarget_name) || H.IsField(itarget_name))) {
+  if (!H.IsRoot(itarget_name) && H.IsGetter(itarget_name)) {
     interface_target = &Function::ZoneHandle(
         Z,
         H.LookupMethodByMember(itarget_name, H.DartGetterName(itarget_name)));
@@ -2549,10 +2548,11 @@
       inferred_type_metadata_helper_.GetInferredType(offset);
 
   NameIndex target = ReadCanonicalNameReference();  // read target_reference.
+  ASSERT(H.IsGetter(target));
 
-  if (H.IsField(target)) {
-    const Field& field =
-        Field::ZoneHandle(Z, H.LookupFieldByKernelField(target));
+  const Field& field = Field::ZoneHandle(
+      Z, H.LookupFieldByKernelGetterOrSetter(target, /*required=*/false));
+  if (!field.IsNull()) {
     if (field.is_const()) {
       // Since the CFE inlines all references to const variables and fields,
       // it never emits a StaticGet of a const field.
@@ -2605,44 +2605,39 @@
   if (p != NULL) *p = position;
 
   NameIndex target = ReadCanonicalNameReference();  // read target_reference.
+  ASSERT(H.IsSetter(target));
 
-  if (H.IsField(target)) {
-    const Field& field =
-        Field::ZoneHandle(Z, H.LookupFieldByKernelField(target));
-    const Class& owner = Class::Handle(Z, field.Owner());
-    const String& setter_name = H.DartSetterName(target);
-    const Function& setter =
-        Function::ZoneHandle(Z, owner.LookupStaticFunction(setter_name));
-    Fragment instructions = BuildExpression();  // read expression.
-    if (NeedsDebugStepCheck(stack(), position)) {
-      instructions = DebugStepCheck(position) + instructions;
-    }
-    LocalVariable* variable = MakeTemporary();
-    instructions += LoadLocal(variable);
-    if (!setter.IsNull() && field.NeedsSetter()) {
-      instructions += StaticCall(position, setter, 1, ICData::kStatic);
-      instructions += Drop();
-    } else {
-      instructions += StoreStaticField(position, field);
-    }
-    return instructions;
-  } else {
-    ASSERT(H.IsProcedure(target));
+  // Evaluate the expression on the right hand side.
+  Fragment instructions = BuildExpression();  // read expression.
 
-    // Evaluate the expression on the right hand side.
-    Fragment instructions = BuildExpression();  // read expression.
+  // Look up the target as a setter first and, if not present, as a field
+  // second. This order is needed to avoid looking up a final field as the
+  // target.
+  const Function& function = Function::ZoneHandle(
+      Z, H.LookupStaticMethodByKernelProcedure(target, /*required=*/false));
+
+  if (!function.IsNull()) {
     LocalVariable* variable = MakeTemporary();
 
     // Prepare argument.
     instructions += LoadLocal(variable);
 
     // Invoke the setter function.
-    const Function& function =
-        Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target));
     instructions += StaticCall(position, function, 1, ICData::kStatic);
 
     // Drop the unused result & leave the stored value on the stack.
     return instructions + Drop();
+  } else {
+    const Field& field =
+        Field::ZoneHandle(Z, H.LookupFieldByKernelGetterOrSetter(target));
+    ASSERT(!field.NeedsSetter());
+    if (NeedsDebugStepCheck(stack(), position)) {
+      instructions = DebugStepCheck(position) + instructions;
+    }
+    LocalVariable* variable = MakeTemporary();
+    instructions += LoadLocal(variable);
+    instructions += StoreStaticField(position, field);
+    return instructions;
   }
 }
 
@@ -2770,8 +2765,7 @@
   // TODO(dartbug.com/34497): Once front-end desugars calls via
   // fields/getters, filtering of field and getter interface targets here
   // can be turned into assertions.
-  if (!H.IsRoot(itarget_name) && !H.IsField(itarget_name) &&
-      !H.IsGetter(itarget_name)) {
+  if (!H.IsRoot(itarget_name) && !H.IsGetter(itarget_name)) {
     interface_target = &Function::ZoneHandle(
         Z, H.LookupMethodByMember(itarget_name,
                                   H.DartProcedureName(itarget_name)));
diff --git a/runtime/vm/compiler/frontend/kernel_fingerprints.cc b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
index 9f7415c..0e16c36 100644
--- a/runtime/vm/compiler/frontend/kernel_fingerprints.cc
+++ b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
@@ -323,7 +323,7 @@
 
 void KernelFingerprintHelper::CalculateGetterNameFingerprint() {
   const NameIndex name = ReadCanonicalNameReference();
-  if (!H.IsRoot(name) && (H.IsGetter(name) || H.IsField(name))) {
+  if (!H.IsRoot(name) && H.IsGetter(name)) {
     BuildHash(H.DartGetterName(name).Hash());
   }
   ReadCanonicalNameReference();  // read interface_target_origin_reference
@@ -340,7 +340,7 @@
 void KernelFingerprintHelper::CalculateMethodNameFingerprint() {
   const NameIndex name =
       ReadCanonicalNameReference();  // read interface_target_reference.
-  if (!H.IsRoot(name) && !H.IsField(name)) {
+  if (!H.IsRoot(name)) {
     BuildHash(H.DartProcedureName(name).Hash());
   }
   ReadCanonicalNameReference();  // read interface_target_origin_reference
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index 06aeae2..9f82df8 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -235,22 +235,7 @@
 }
 
 bool TranslationHelper::IsMember(NameIndex name) {
-  return IsConstructor(name) || IsField(name) || IsProcedure(name);
-}
-
-bool TranslationHelper::IsField(NameIndex name) {
-  // Fields with private names have the import URI of the library where they are
-  // visible as the parent and the string "@fields" as the parent's parent.
-  // Fields with non-private names have the string "@fields' as the parent.
-  if (IsRoot(name)) {
-    return false;
-  }
-  NameIndex kind = CanonicalNameParent(name);
-  if (IsPrivate(name)) {
-    kind = CanonicalNameParent(kind);
-  }
-  return StringEquals(CanonicalNameString(kind), "@fields") ||
-         StringEquals(CanonicalNameString(kind), "@=fields");
+  return IsConstructor(name) || IsProcedure(name);
 }
 
 bool TranslationHelper::IsConstructor(NameIndex name) {
@@ -330,7 +315,7 @@
 }
 
 NameIndex TranslationHelper::EnclosingName(NameIndex name) {
-  ASSERT(IsField(name) || IsConstructor(name) || IsProcedure(name));
+  ASSERT(IsConstructor(name) || IsProcedure(name));
   NameIndex enclosing = CanonicalNameParent(CanonicalNameParent(name));
   if (IsPrivate(name)) {
     enclosing = CanonicalNameParent(enclosing);
@@ -584,8 +569,10 @@
   return info_.InsertClass(thread_, name_index_handle_, klass);
 }
 
-FieldPtr TranslationHelper::LookupFieldByKernelField(NameIndex kernel_field) {
-  ASSERT(IsField(kernel_field));
+FieldPtr TranslationHelper::LookupFieldByKernelGetterOrSetter(
+    NameIndex kernel_field,
+    bool required) {
+  ASSERT(IsGetter(kernel_field) || IsSetter(kernel_field));
   NameIndex enclosing = EnclosingName(kernel_field);
 
   Class& klass = Class::Handle(Z);
@@ -601,12 +588,15 @@
   Field& field = Field::Handle(
       Z, klass.LookupFieldAllowPrivate(
              DartSymbolObfuscate(CanonicalNameString(kernel_field))));
-  CheckStaticLookup(field);
+  if (required) {
+    CheckStaticLookup(field);
+  }
   return field.ptr();
 }
 
 FunctionPtr TranslationHelper::LookupStaticMethodByKernelProcedure(
-    NameIndex procedure) {
+    NameIndex procedure,
+    bool required) {
   const String& procedure_name = DartProcedureName(procedure);
 
   // The parent is either a library or a class (in which case the procedure is a
@@ -617,7 +607,9 @@
         Library::Handle(Z, LookupLibraryByKernelLibrary(enclosing));
     Function& function =
         Function::Handle(Z, library.LookupFunctionAllowPrivate(procedure_name));
-    CheckStaticLookup(function);
+    if (required) {
+      CheckStaticLookup(function);
+    }
     return function.ptr();
   } else {
     ASSERT(IsClass(enclosing));
@@ -626,7 +618,9 @@
     ASSERT(error == Error::null());
     Function& function = Function::ZoneHandle(
         Z, klass.LookupFunctionAllowPrivate(procedure_name));
-    CheckStaticLookup(function);
+    if (required) {
+      CheckStaticLookup(function);
+    }
     return function.ptr();
   }
 }
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h
index b76603a..d5d39fe 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.h
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h
@@ -101,7 +101,6 @@
   bool IsLibrary(NameIndex name);
   bool IsClass(NameIndex name);
   bool IsMember(NameIndex name);
-  bool IsField(NameIndex name);
   bool IsConstructor(NameIndex name);
   bool IsProcedure(NameIndex name);
   bool IsMethod(NameIndex name);
@@ -164,8 +163,10 @@
   virtual LibraryPtr LookupLibraryByKernelLibrary(NameIndex library);
   virtual ClassPtr LookupClassByKernelClass(NameIndex klass);
 
-  FieldPtr LookupFieldByKernelField(NameIndex field);
-  FunctionPtr LookupStaticMethodByKernelProcedure(NameIndex procedure);
+  FieldPtr LookupFieldByKernelGetterOrSetter(NameIndex field,
+                                             bool required = true);
+  FunctionPtr LookupStaticMethodByKernelProcedure(NameIndex procedure,
+                                                  bool required = true);
   FunctionPtr LookupConstructorByKernelConstructor(NameIndex constructor);
   FunctionPtr LookupConstructorByKernelConstructor(const Class& owner,
                                                    NameIndex constructor);
diff --git a/runtime/vm/compiler/stub_code_compiler.cc b/runtime/vm/compiler/stub_code_compiler.cc
index e08b548..c4ed124 100644
--- a/runtime/vm/compiler/stub_code_compiler.cc
+++ b/runtime/vm/compiler/stub_code_compiler.cc
@@ -796,6 +796,19 @@
   GenerateRangeError(assembler, /*with_fpu_regs=*/true);
 }
 
+void StubCodeCompiler::GenerateFrameAwaitingMaterializationStub(
+    Assembler* assembler) {
+  __ Breakpoint();  // Marker stub.
+}
+
+void StubCodeCompiler::GenerateAsynchronousGapMarkerStub(Assembler* assembler) {
+  __ Breakpoint();  // Marker stub.
+}
+
+void StubCodeCompiler::GenerateUnknownDartCodeStub(Assembler* assembler) {
+  __ Breakpoint();  // Marker stub.
+}
+
 }  // 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 938b2d9..dc9b44c 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm.cc
@@ -3288,15 +3288,6 @@
       CODE_REG, target::Code::entry_point_offset(CodeEntryKind::kMonomorphic)));
 }
 
-void StubCodeCompiler::GenerateFrameAwaitingMaterializationStub(
-    Assembler* assembler) {
-  __ bkpt(0);
-}
-
-void StubCodeCompiler::GenerateAsynchronousGapMarkerStub(Assembler* assembler) {
-  __ bkpt(0);
-}
-
 void StubCodeCompiler::GenerateNotLoadedStub(Assembler* assembler) {
   __ EnterStubFrame();
   __ CallRuntime(kNotLoadedRuntimeEntry, 0);
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index 35c6bcf..022ba2c 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -3446,15 +3446,6 @@
   __ br(R1);
 }
 
-void StubCodeCompiler::GenerateFrameAwaitingMaterializationStub(
-    Assembler* assembler) {
-  __ brk(0);
-}
-
-void StubCodeCompiler::GenerateAsynchronousGapMarkerStub(Assembler* assembler) {
-  __ brk(0);
-}
-
 void StubCodeCompiler::GenerateNotLoadedStub(Assembler* assembler) {
   __ EnterStubFrame();
   __ CallRuntime(kNotLoadedRuntimeEntry, 0);
diff --git a/runtime/vm/compiler/stub_code_compiler_ia32.cc b/runtime/vm/compiler/stub_code_compiler_ia32.cc
index 7dcc08e..5c780a3 100644
--- a/runtime/vm/compiler/stub_code_compiler_ia32.cc
+++ b/runtime/vm/compiler/stub_code_compiler_ia32.cc
@@ -2784,15 +2784,6 @@
   __ int3();  // AOT only.
 }
 
-void StubCodeCompiler::GenerateFrameAwaitingMaterializationStub(
-    Assembler* assembler) {
-  __ int3();  // Marker stub.
-}
-
-void StubCodeCompiler::GenerateAsynchronousGapMarkerStub(Assembler* assembler) {
-  __ int3();  // Marker stub.
-}
-
 void StubCodeCompiler::GenerateNotLoadedStub(Assembler* assembler) {
   __ EnterStubFrame();
   __ CallRuntime(kNotLoadedRuntimeEntry, 0);
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index 9141a0c..93bd854c6 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -3378,15 +3378,6 @@
   __ jmp(RCX);
 }
 
-void StubCodeCompiler::GenerateFrameAwaitingMaterializationStub(
-    Assembler* assembler) {
-  __ int3();
-}
-
-void StubCodeCompiler::GenerateAsynchronousGapMarkerStub(Assembler* assembler) {
-  __ int3();
-}
-
 void StubCodeCompiler::GenerateNotLoadedStub(Assembler* assembler) {
   __ EnterStubFrame();
   __ CallRuntime(kNotLoadedRuntimeEntry, 0);
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index acf1abd..16cc361 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -1846,7 +1846,6 @@
   Code& code = Code::Handle(zone);
   Code& inlined_code = Code::Handle(zone);
   Array& deopt_frame = Array::Handle(zone);
-  Smi& offset = Smi::Handle(zone);
   Function& function = Function::Handle(zone);
 
   constexpr intptr_t kDefaultStackAllocation = 8;
@@ -1854,8 +1853,7 @@
 
   const auto& code_array = GrowableObjectArray::ZoneHandle(
       zone, GrowableObjectArray::New(kDefaultStackAllocation));
-  const auto& pc_offset_array = GrowableObjectArray::ZoneHandle(
-      zone, GrowableObjectArray::New(kDefaultStackAllocation));
+  GrowableArray<uword> pc_offset_array(kDefaultStackAllocation);
   bool has_async = false;
 
   std::function<void(StackFrame*)> on_sync_frame = [&](StackFrame* frame) {
@@ -1864,7 +1862,7 @@
                      &inlined_code, &deopt_frame);
   };
 
-  StackTraceUtils::CollectFramesLazy(thread, code_array, pc_offset_array,
+  StackTraceUtils::CollectFramesLazy(thread, code_array, &pc_offset_array,
                                      /*skip_frames=*/0, &on_sync_frame,
                                      &has_async);
 
@@ -1900,8 +1898,8 @@
       continue;
     }
 
-    offset ^= pc_offset_array.At(i);
-    const uword absolute_pc = code.PayloadStart() + offset.Value();
+    const uword pc_offset = pc_offset_array[i];
+    const uword absolute_pc = code.PayloadStart() + pc_offset;
     stack_trace->AddAsyncCausalFrame(absolute_pc, code);
   }
 
@@ -2136,8 +2134,7 @@
       function = code.function();
       if (function.is_visible()) {
         ASSERT(function.ptr() == code.function());
-        uword pc =
-            code.PayloadStart() + Smi::Value(ex_trace.PcOffsetAtFrame(i));
+        uword pc = code.PayloadStart() + ex_trace.PcOffsetAtFrame(i);
         if (code.is_optimized() && ex_trace.expand_inlined()) {
           // Traverse inlined frames.
           for (InlinedFunctionsIterator it(code, pc); !it.Done();
diff --git a/runtime/vm/dwarf.cc b/runtime/vm/dwarf.cc
index a818df9..35df763 100644
--- a/runtime/vm/dwarf.cc
+++ b/runtime/vm/dwarf.cc
@@ -146,7 +146,7 @@
   // Dwarf object (which is currently true).  Otherwise, need to copy.
   code_to_name_.Insert({&code, name});
 
-  if (code.IsFunctionCode()) {
+  if (code.IsFunctionCode() && !code.IsUnknownDartCode()) {
     const Function& function = Function::Handle(zone_, code.function());
     AddFunction(function);
   }
@@ -364,7 +364,7 @@
   for (intptr_t i = 0; i < codes_.length(); i++) {
     const Code& code = *(codes_[i]);
     RELEASE_ASSERT(!code.IsNull());
-    if (!code.IsFunctionCode()) {
+    if (!code.IsFunctionCode() || code.IsUnknownDartCode()) {
       continue;
     }
 
diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc
index e9464e3..1d76750 100644
--- a/runtime/vm/exceptions.cc
+++ b/runtime/vm/exceptions.cc
@@ -35,7 +35,7 @@
   StackTraceBuilder() {}
   virtual ~StackTraceBuilder() {}
 
-  virtual void AddFrame(const Object& code, const Smi& offset) = 0;
+  virtual void AddFrame(const Object& code, uword pc_offset) = 0;
 };
 
 class PreallocatedStackTraceBuilder : public StackTraceBuilder {
@@ -50,7 +50,7 @@
   }
   ~PreallocatedStackTraceBuilder() {}
 
-  virtual void AddFrame(const Object& code, const Smi& offset);
+  void AddFrame(const Object& code, uword pc_offset) override;
 
  private:
   static const int kNumTopframes = StackTrace::kPreallocatedStackdepth / 2;
@@ -63,11 +63,10 @@
 };
 
 void PreallocatedStackTraceBuilder::AddFrame(const Object& code,
-                                             const Smi& offset) {
+                                             uword pc_offset) {
   if (cur_index_ >= StackTrace::kPreallocatedStackdepth) {
     // The number of frames is overflowing the preallocated stack trace object.
     Object& frame_code = Object::Handle();
-    Smi& frame_offset = Smi::Handle();
     intptr_t start = StackTrace::kPreallocatedStackdepth - (kNumTopframes - 1);
     intptr_t null_slot = start - 2;
     // We are going to drop one frame.
@@ -80,20 +79,19 @@
       dropped_frames_++;
     }
     // Encode the number of dropped frames into the pc offset.
-    frame_offset = Smi::New(dropped_frames_);
-    stacktrace_.SetPcOffsetAtFrame(null_slot, frame_offset);
+    stacktrace_.SetPcOffsetAtFrame(null_slot, dropped_frames_);
     // Move frames one slot down so that we can accommodate the new frame.
     for (intptr_t i = start; i < StackTrace::kPreallocatedStackdepth; i++) {
       intptr_t prev = (i - 1);
       frame_code = stacktrace_.CodeAtFrame(i);
-      frame_offset = stacktrace_.PcOffsetAtFrame(i);
+      const uword frame_offset = stacktrace_.PcOffsetAtFrame(i);
       stacktrace_.SetCodeAtFrame(prev, frame_code);
       stacktrace_.SetPcOffsetAtFrame(prev, frame_offset);
     }
     cur_index_ = (StackTrace::kPreallocatedStackdepth - 1);
   }
   stacktrace_.SetCodeAtFrame(cur_index_, code);
-  stacktrace_.SetPcOffsetAtFrame(cur_index_, offset);
+  stacktrace_.SetPcOffsetAtFrame(cur_index_, pc_offset);
   cur_index_ += 1;
 }
 
@@ -104,15 +102,14 @@
   StackFrame* frame = frames.NextFrame();
   ASSERT(frame != NULL);  // We expect to find a dart invocation frame.
   Code& code = Code::Handle();
-  Smi& offset = Smi::Handle();
   for (; frame != NULL; frame = frames.NextFrame()) {
     if (!frame->IsDartFrame()) {
       continue;
     }
     code = frame->LookupDartCode();
     ASSERT(code.ContainsInstructionAt(frame->pc()));
-    offset = Smi::New(frame->pc() - code.PayloadStart());
-    builder->AddFrame(code, offset);
+    const uword pc_offset = frame->pc() - code.PayloadStart();
+    builder->AddFrame(code, pc_offset);
   }
 }
 
diff --git a/runtime/vm/instructions_arm.cc b/runtime/vm/instructions_arm.cc
index c2b7014..1c45ff9 100644
--- a/runtime/vm/instructions_arm.cc
+++ b/runtime/vm/instructions_arm.cc
@@ -263,10 +263,9 @@
   object_pool_.SetObjectAt(target_pool_index_, target_code);
 }
 
-SwitchableCallPatternBase::SwitchableCallPatternBase(const Code& code)
-    : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
-      data_pool_index_(-1),
-      target_pool_index_(-1) {}
+SwitchableCallPatternBase::SwitchableCallPatternBase(
+    const ObjectPool& object_pool)
+    : object_pool_(object_pool), data_pool_index_(-1), target_pool_index_(-1) {}
 
 ObjectPtr SwitchableCallPatternBase::data() const {
   return object_pool_.ObjectAt(data_pool_index_);
@@ -278,7 +277,7 @@
 }
 
 SwitchableCallPattern::SwitchableCallPattern(uword pc, const Code& code)
-    : SwitchableCallPatternBase(code) {
+    : SwitchableCallPatternBase(ObjectPool::Handle(code.GetObjectPool())) {
   ASSERT(code.ContainsInstructionAt(pc));
   // Last instruction: blx lr.
   ASSERT(*(reinterpret_cast<uint32_t*>(pc) - 1) == 0xe12fff3e);
@@ -302,9 +301,9 @@
   object_pool_.SetObjectAt(target_pool_index_, target);
 }
 
-BareSwitchableCallPattern::BareSwitchableCallPattern(uword pc, const Code& code)
-    : SwitchableCallPatternBase(code) {
-  ASSERT(code.ContainsInstructionAt(pc));
+BareSwitchableCallPattern::BareSwitchableCallPattern(uword pc)
+    : SwitchableCallPatternBase(ObjectPool::Handle(
+          IsolateGroup::Current()->object_store()->global_object_pool())) {
   // Last instruction: blx lr.
   ASSERT(*(reinterpret_cast<uint32_t*>(pc) - 1) == 0xe12fff3e);
 
diff --git a/runtime/vm/instructions_arm.h b/runtime/vm/instructions_arm.h
index 7b7b43b..a1e1f9b 100644
--- a/runtime/vm/instructions_arm.h
+++ b/runtime/vm/instructions_arm.h
@@ -131,7 +131,7 @@
 //   call target.entry           call stub.entry         call stub.entry
 class SwitchableCallPatternBase : public ValueObject {
  public:
-  explicit SwitchableCallPatternBase(const Code& code);
+  explicit SwitchableCallPatternBase(const ObjectPool& object_pool);
 
   ObjectPtr data() const;
   void SetData(const Object& data) const;
@@ -166,7 +166,7 @@
 // of the monomorphic function or a stub entry point.
 class BareSwitchableCallPattern : public SwitchableCallPatternBase {
  public:
-  BareSwitchableCallPattern(uword pc, const Code& code);
+  explicit BareSwitchableCallPattern(uword pc);
 
   uword target_entry() const;
   void SetTarget(const Code& target) const;
diff --git a/runtime/vm/instructions_arm64.cc b/runtime/vm/instructions_arm64.cc
index a38a07e..d193510 100644
--- a/runtime/vm/instructions_arm64.cc
+++ b/runtime/vm/instructions_arm64.cc
@@ -397,10 +397,9 @@
   // No need to flush the instruction cache, since the code is not modified.
 }
 
-SwitchableCallPatternBase::SwitchableCallPatternBase(const Code& code)
-    : object_pool_(ObjectPool::Handle(code.GetObjectPool())),
-      data_pool_index_(-1),
-      target_pool_index_(-1) {}
+SwitchableCallPatternBase::SwitchableCallPatternBase(
+    const ObjectPool& object_pool)
+    : object_pool_(object_pool), data_pool_index_(-1), target_pool_index_(-1) {}
 
 ObjectPtr SwitchableCallPatternBase::data() const {
   return object_pool_.ObjectAt(data_pool_index_);
@@ -412,7 +411,7 @@
 }
 
 SwitchableCallPattern::SwitchableCallPattern(uword pc, const Code& code)
-    : SwitchableCallPatternBase(code) {
+    : SwitchableCallPatternBase(ObjectPool::Handle(code.GetObjectPool())) {
   ASSERT(code.ContainsInstructionAt(pc));
   // Last instruction: blr lr.
   ASSERT(*(reinterpret_cast<uint32_t*>(pc) - 1) == 0xd63f03c0);
@@ -438,9 +437,9 @@
   object_pool_.SetObjectAt(target_pool_index_, target);
 }
 
-BareSwitchableCallPattern::BareSwitchableCallPattern(uword pc, const Code& code)
-    : SwitchableCallPatternBase(code) {
-  ASSERT(code.ContainsInstructionAt(pc));
+BareSwitchableCallPattern::BareSwitchableCallPattern(uword pc)
+    : SwitchableCallPatternBase(ObjectPool::Handle(
+          IsolateGroup::Current()->object_store()->global_object_pool())) {
   // Last instruction: blr lr.
   ASSERT(*(reinterpret_cast<uint32_t*>(pc) - 1) == 0xd63f03c0);
 
diff --git a/runtime/vm/instructions_arm64.h b/runtime/vm/instructions_arm64.h
index ae75539..b450522 100644
--- a/runtime/vm/instructions_arm64.h
+++ b/runtime/vm/instructions_arm64.h
@@ -141,7 +141,7 @@
 //   call target.entry           call stub.entry         call stub.entry
 class SwitchableCallPatternBase : public ValueObject {
  public:
-  explicit SwitchableCallPatternBase(const Code& code);
+  explicit SwitchableCallPatternBase(const ObjectPool& object_pool);
 
   ObjectPtr data() const;
   void SetData(const Object& data) const;
@@ -176,7 +176,7 @@
 // of the monomorphic function or a stub entry point.
 class BareSwitchableCallPattern : public SwitchableCallPatternBase {
  public:
-  BareSwitchableCallPattern(uword pc, const Code& code);
+  explicit BareSwitchableCallPattern(uword pc);
 
   uword target_entry() const;
   void SetTarget(const Code& target) const;
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index 3afc431..472de49 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -20,8 +20,8 @@
 static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
 
 // Both version numbers are inclusive.
-static const uint32_t kMinSupportedKernelFormatVersion = 55;
-static const uint32_t kMaxSupportedKernelFormatVersion = 55;
+static const uint32_t kMinSupportedKernelFormatVersion = 56;
+static const uint32_t kMaxSupportedKernelFormatVersion = 56;
 
 // Keep in sync with package:kernel/lib/binary/tag.dart
 #define KERNEL_TAG_LIST(V)                                                     \
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 1186974..286f4f1 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -75,7 +75,7 @@
 
 namespace dart {
 
-DEFINE_FLAG(int,
+DEFINE_FLAG(uint64_t,
             huge_method_cutoff_in_code_size,
             200000,
             "Huge method cutoff in unoptimized code size (in bytes).");
@@ -1059,6 +1059,11 @@
   cls.set_is_declaration_loaded();
   cls.set_is_type_finalized();
 
+  cls = Class::New<FunctionType, RTN::FunctionType>(isolate_group);
+  cls.set_is_allocate_finalized();
+  cls.set_is_declaration_loaded();
+  cls.set_is_type_finalized();
+
   cls = dynamic_class_;
   *dynamic_type_ =
       Type::New(cls, Object::null_type_arguments(), Nullability::kNullable);
@@ -1365,6 +1370,12 @@
   cls = isolate_group->class_table()->At(kForwardingCorpse);
   cls.set_name(Symbols::ForwardingCorpse());
 
+#if defined(DART_PRECOMPILER)
+  const auto& function =
+      Function::Handle(StubCode::UnknownDartCode().function());
+  function.set_name(Symbols::OptimizedOut());
+#endif  // defined(DART_PRECOMPILER)
+
   {
     ASSERT(isolate_group == Dart::vm_isolate_group());
     Thread* thread = Thread::Current();
@@ -16773,7 +16784,8 @@
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
 bool Code::SlowFindRawCodeVisitor::FindObject(ObjectPtr raw_obj) const {
-  return UntaggedCode::ContainsPC(raw_obj, pc_);
+  return UntaggedCode::ContainsPC(raw_obj, pc_) &&
+         !Code::IsUnknownDartCode(Code::RawCast(raw_obj));
 }
 
 CodePtr Code::LookupCodeInIsolateGroup(IsolateGroup* isolate_group, uword pc) {
@@ -16928,6 +16940,10 @@
   return OwnerClassId() == kFunctionCid;
 }
 
+bool Code::IsUnknownDartCode(CodePtr code) {
+  return code == StubCode::UnknownDartCode().ptr();
+}
+
 void Code::DisableDartCode() const {
   SafepointOperationScope safepoint(Thread::Current());
   ASSERT(IsFunctionCode());
@@ -17044,6 +17060,41 @@
   reader.DumpSourcePositions(relative_addresses ? 0 : PayloadStart());
 }
 
+bool Code::CanBeOmittedFromAOTSnapshot() const {
+  NoSafepointScope no_safepoint;
+
+  // Code objects are stored in stack frames if not use_bare_instructions.
+  // Code objects are used by stack traces if not dwarf_stack_traces.
+  if (!FLAG_precompiled_mode || !FLAG_use_bare_instructions ||
+      !FLAG_dwarf_stack_traces_mode) {
+    return false;
+  }
+  // Only omit Code objects corresponding to Dart functions.
+  if (!IsFunctionCode()) {
+    return false;
+  }
+  // Retain Code object if it has exception handlers or PC descriptors.
+  if ((exception_handlers() != Object::empty_exception_handlers().ptr()) ||
+      (pc_descriptors() != Object::empty_descriptors().ptr())) {
+    return false;
+  }
+  if (!owner()->IsHeapObject()) {
+    // Can drop Code if precompiler dropped the Function and only left Smi
+    // classId.
+    return true;
+  }
+  // Retain Code objects corresponding to:
+  // * invisible functions (to filter them from stack traces);
+  // * async/async* closures (to construct async stacks).
+  // * native functions (to find native implementation).
+  const auto& func = Function::Handle(function());
+  if (!func.is_visible() || func.is_native() || func.IsAsyncClosure() ||
+      func.IsAsyncGenClosure()) {
+    return false;
+  }
+  return true;
+}
+
 intptr_t Context::GetLevel() const {
   intptr_t level = 0;
   Context& parent_ctx = Context::Handle(parent());
@@ -24666,15 +24717,17 @@
   code_array.SetAt(frame_index, code);
 }
 
-SmiPtr StackTrace::PcOffsetAtFrame(intptr_t frame_index) const {
-  const Array& pc_offset_array = Array::Handle(untag()->pc_offset_array());
-  return static_cast<SmiPtr>(pc_offset_array.At(frame_index));
+uword StackTrace::PcOffsetAtFrame(intptr_t frame_index) const {
+  const TypedData& pc_offset_array =
+      TypedData::Handle(untag()->pc_offset_array());
+  return pc_offset_array.GetUintPtr(frame_index * kWordSize);
 }
 
 void StackTrace::SetPcOffsetAtFrame(intptr_t frame_index,
-                                    const Smi& pc_offset) const {
-  const Array& pc_offset_array = Array::Handle(untag()->pc_offset_array());
-  pc_offset_array.SetAt(frame_index, pc_offset);
+                                    uword pc_offset) const {
+  const TypedData& pc_offset_array =
+      TypedData::Handle(untag()->pc_offset_array());
+  pc_offset_array.SetUintPtr(frame_index * kWordSize, pc_offset);
 }
 
 void StackTrace::set_async_link(const StackTrace& async_link) const {
@@ -24685,7 +24738,7 @@
   untag()->set_code_array(code_array.ptr());
 }
 
-void StackTrace::set_pc_offset_array(const Array& pc_offset_array) const {
+void StackTrace::set_pc_offset_array(const TypedData& pc_offset_array) const {
   untag()->set_pc_offset_array(pc_offset_array.ptr());
 }
 
@@ -24698,7 +24751,7 @@
 }
 
 StackTracePtr StackTrace::New(const Array& code_array,
-                              const Array& pc_offset_array,
+                              const TypedData& pc_offset_array,
                               Heap::Space space) {
   StackTrace& result = StackTrace::Handle();
   {
@@ -24715,7 +24768,7 @@
 }
 
 StackTracePtr StackTrace::New(const Array& code_array,
-                              const Array& pc_offset_array,
+                              const TypedData& pc_offset_array,
                               const StackTrace& async_link,
                               bool skip_sync_start_in_parent_stack,
                               Heap::Space space) {
@@ -24902,9 +24955,8 @@
         if ((i < (stack_trace.Length() - 1)) &&
             (stack_trace.CodeAtFrame(i + 1) != Code::null())) {
           buffer.AddString("...\n...\n");
-          ASSERT(stack_trace.PcOffsetAtFrame(i) != Smi::null());
           // To account for gap frames.
-          frame_index += Smi::Value(stack_trace.PcOffsetAtFrame(i));
+          frame_index += stack_trace.PcOffsetAtFrame(i);
         }
         continue;
       }
@@ -24917,7 +24969,7 @@
         continue;
       }
 
-      intptr_t pc_offset = Smi::Value(stack_trace.PcOffsetAtFrame(i));
+      const uword pc_offset = stack_trace.PcOffsetAtFrame(i);
       ASSERT(code_object.IsCode());
       code ^= code_object.ptr();
       ASSERT(code.IsFunctionCode());
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 5cf9f6c..33d8942 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -5970,6 +5970,7 @@
   uword PayloadStart() const { return PayloadStartOf(ptr()); }
   static uword PayloadStartOf(const CodePtr code) {
 #if defined(DART_PRECOMPILED_RUNTIME)
+    if (IsUnknownDartCode(code)) return 0;
     const uword entry_offset = HasMonomorphicEntry(code)
                                    ? Instructions::kPolymorphicEntryOffsetAOT
                                    : 0;
@@ -6015,9 +6016,10 @@
   }
 
   // Returns the size of [instructions()].
-  intptr_t Size() const { return PayloadSizeOf(ptr()); }
-  static intptr_t PayloadSizeOf(const CodePtr code) {
+  uword Size() const { return PayloadSizeOf(ptr()); }
+  static uword PayloadSizeOf(const CodePtr code) {
 #if defined(DART_PRECOMPILED_RUNTIME)
+    if (IsUnknownDartCode(code)) return kUwordMax;
     return code->untag()->instructions_length_;
 #else
     return Instructions::Size(InstructionsOf(code));
@@ -6347,6 +6349,11 @@
   bool IsTypeTestStubCode() const;
   bool IsFunctionCode() const;
 
+  // Returns true if this Code object represents
+  // Dart function code without any additional information.
+  bool IsUnknownDartCode() const { return IsUnknownDartCode(ptr()); }
+  static bool IsUnknownDartCode(CodePtr code);
+
   void DisableDartCode() const;
 
   void DisableStubCode() const;
@@ -6371,6 +6378,11 @@
     untag()->set_object_pool(object_pool);
   }
 
+  // Returns true if given Code object can be omitted from
+  // the AOT snapshot (when corresponding instructions are
+  // included).
+  bool CanBeOmittedFromAOTSnapshot() const;
+
  private:
   void set_state_bits(intptr_t bits) const;
 
@@ -10803,9 +10815,9 @@
   ObjectPtr CodeAtFrame(intptr_t frame_index) const;
   void SetCodeAtFrame(intptr_t frame_index, const Object& code) const;
 
-  ArrayPtr pc_offset_array() const { return untag()->pc_offset_array(); }
-  SmiPtr PcOffsetAtFrame(intptr_t frame_index) const;
-  void SetPcOffsetAtFrame(intptr_t frame_index, const Smi& pc_offset) const;
+  TypedDataPtr pc_offset_array() const { return untag()->pc_offset_array(); }
+  uword PcOffsetAtFrame(intptr_t frame_index) const;
+  void SetPcOffsetAtFrame(intptr_t frame_index, uword pc_offset) const;
 
   bool skip_sync_start_in_parent_stack() const;
   void set_skip_sync_start_in_parent_stack(bool value) const;
@@ -10828,18 +10840,18 @@
     return RoundedAllocationSize(sizeof(UntaggedStackTrace));
   }
   static StackTracePtr New(const Array& code_array,
-                           const Array& pc_offset_array,
+                           const TypedData& pc_offset_array,
                            Heap::Space space = Heap::kNew);
 
   static StackTracePtr New(const Array& code_array,
-                           const Array& pc_offset_array,
+                           const TypedData& pc_offset_array,
                            const StackTrace& async_link,
                            bool skip_sync_start_in_parent_stack,
                            Heap::Space space = Heap::kNew);
 
  private:
   void set_code_array(const Array& code_array) const;
-  void set_pc_offset_array(const Array& pc_offset_array) const;
+  void set_pc_offset_array(const TypedData& pc_offset_array) const;
   bool expand_inlined() const;
 
   FINAL_HEAP_OBJECT_IMPLEMENTATION(StackTrace, Instance);
diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc
index 8fc322a..0b7cd00 100644
--- a/runtime/vm/object_graph.cc
+++ b/runtime/vm/object_graph.cc
@@ -963,7 +963,11 @@
       ScrubAndWriteUtf8(static_cast<FunctionPtr>(obj)->untag()->name_);
     } else if (cid == kCodeCid) {
       ObjectPtr owner = static_cast<CodePtr>(obj)->untag()->owner_;
-      if (owner->IsFunction()) {
+      if (!owner->IsHeapObject()) {
+        // Precompiler removed owner object from the snapshot,
+        // only leaving Smi classId.
+        writer_->WriteUnsigned(kNoData);
+      } else if (owner->IsFunction()) {
         writer_->WriteUnsigned(kNameData);
         ScrubAndWriteUtf8(static_cast<FunctionPtr>(owner)->untag()->name_);
       } else if (owner->IsClass()) {
diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc
index feb4803..57343da 100644
--- a/runtime/vm/object_store.cc
+++ b/runtime/vm/object_store.cc
@@ -53,8 +53,9 @@
 static StackTracePtr CreatePreallocatedStackTrace(Zone* zone) {
   const Array& code_array = Array::Handle(
       zone, Array::New(StackTrace::kPreallocatedStackdepth, Heap::kOld));
-  const Array& pc_offset_array = Array::Handle(
-      zone, Array::New(StackTrace::kPreallocatedStackdepth, Heap::kOld));
+  const TypedData& pc_offset_array = TypedData::Handle(
+      zone, TypedData::New(kUintPtrCid, StackTrace::kPreallocatedStackdepth,
+                           Heap::kOld));
   const StackTrace& stack_trace =
       StackTrace::Handle(zone, StackTrace::New(code_array, pc_offset_array));
   // Expansion of inlined functions requires additional memory at run time,
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
index f0d9d2c..684d7f3 100644
--- a/runtime/vm/profiler.cc
+++ b/runtime/vm/profiler.cc
@@ -1410,7 +1410,7 @@
   ~CodeLookupTableBuilder() {}
 
   void VisitObject(ObjectPtr raw_obj) {
-    if (raw_obj->IsCode()) {
+    if (raw_obj->IsCode() && !Code::IsUnknownDartCode(Code::RawCast(raw_obj))) {
       table_->Add(Code::Handle(Code::RawCast(raw_obj)));
     }
   }
diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc
index 599885b..0ba5912 100644
--- a/runtime/vm/profiler_service.cc
+++ b/runtime/vm/profiler_service.cc
@@ -70,6 +70,9 @@
     return NULL;
   }
   const Script& script = Script::Handle(function_.script());
+  if (script.IsNull()) {
+    return NULL;
+  }
   const String& uri = String::Handle(script.resolved_url());
   if (uri.IsNull()) {
     return NULL;
diff --git a/runtime/vm/profiler_test.cc b/runtime/vm/profiler_test.cc
index d4e62d2..40f1a09 100644
--- a/runtime/vm/profiler_test.cc
+++ b/runtime/vm/profiler_test.cc
@@ -2288,7 +2288,7 @@
 static uword FindPCForTokenPosition(const Code& code, TokenPosition tp) {
   GrowableArray<const Function*> functions;
   GrowableArray<TokenPosition> token_positions;
-  for (intptr_t pc_offset = 0; pc_offset < code.Size(); pc_offset++) {
+  for (uword pc_offset = 0; pc_offset < code.Size(); pc_offset++) {
     code.GetInlinedFunctionsAtInstruction(pc_offset, &functions,
                                           &token_positions);
     if (token_positions[0] == tp) {
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 1ac32d0..160185c 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -2835,10 +2835,18 @@
 // Define an aliases for intptr_t.
 #if defined(ARCH_IS_32_BIT)
 #define kIntPtrCid kTypedDataInt32ArrayCid
+#define GetIntPtr GetInt32
 #define SetIntPtr SetInt32
+#define kUintPtrCid kTypedDataUint32ArrayCid
+#define GetUintPtr GetUint32
+#define SetUintPtr SetUint32
 #elif defined(ARCH_IS_64_BIT)
 #define kIntPtrCid kTypedDataInt64ArrayCid
+#define GetIntPtr GetInt64
 #define SetIntPtr SetInt64
+#define kUintPtrCid kTypedDataUint64ArrayCid
+#define GetUintPtr GetUint64
+#define SetUintPtr SetUint64
 #else
 #error Architecture is not 32-bit or 64-bit.
 #endif  // ARCH_IS_32_BIT
@@ -2917,7 +2925,8 @@
                 async_link);  // Link to parent async stack trace.
   POINTER_FIELD(ArrayPtr,
                 code_array);  // Code object for each frame in the stack trace.
-  POINTER_FIELD(ArrayPtr, pc_offset_array);  // Offset of PC for each frame.
+  POINTER_FIELD(TypedDataPtr, pc_offset_array);  // Offset of PC for each frame.
+
   VISIT_TO(ObjectPtr, pc_offset_array)
   ObjectPtr* to_snapshot(Snapshot::Kind kind) { return to(); }
 
diff --git a/runtime/vm/reverse_pc_lookup_cache.cc b/runtime/vm/reverse_pc_lookup_cache.cc
index dad8849..70f06df 100644
--- a/runtime/vm/reverse_pc_lookup_cache.cc
+++ b/runtime/vm/reverse_pc_lookup_cache.cc
@@ -10,9 +10,9 @@
 
 namespace dart {
 
-CodePtr ReversePc::Lookup(IsolateGroup* group,
-                          uword pc,
-                          bool is_return_address) {
+CodePtr ReversePc::LookupInGroup(IsolateGroup* group,
+                                 uword pc,
+                                 bool is_return_address) {
 #if defined(DART_PRECOMPILED_RUNTIME)
   // This can run in the middle of GC and must not allocate handles.
   NoSafepointScope no_safepoint;
@@ -67,4 +67,35 @@
   return Code::null();
 }
 
+CodePtr ReversePc::Lookup(IsolateGroup* group,
+                          uword pc,
+                          bool is_return_address) {
+  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);
+  }
+  return code;
+}
+
+CompressedStackMapsPtr ReversePc::FindCompressedStackMaps(
+    IsolateGroup* group,
+    uword pc,
+    bool is_return_address,
+    uword* code_start) {
+  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();
+  }
+
+  *code_start = 0;
+  return CompressedStackMaps::null();
+}
+
 }  // namespace dart
diff --git a/runtime/vm/reverse_pc_lookup_cache.h b/runtime/vm/reverse_pc_lookup_cache.h
index 8c45bb1..9eb0558 100644
--- a/runtime/vm/reverse_pc_lookup_cache.h
+++ b/runtime/vm/reverse_pc_lookup_cache.h
@@ -13,11 +13,28 @@
 
 class IsolateGroup;
 
+// This class provides mechanism to find Code and CompressedStackMaps
+// objects corresponding to the given PC.
+// Can only be used in AOT runtime with bare instructions.
 class ReversePc : public AllStatic {
  public:
-  static CodePtr Lookup(IsolateGroup* group,
-                        uword pc,
-                        bool is_return_address = false);
+  // Looks for Code object corresponding to |pc| in the
+  // given isolate |group| and vm isolate group.
+  static CodePtr Lookup(IsolateGroup* group, uword pc, bool is_return_address);
+
+  // Looks for CompressedStackMaps corresponding to |pc| in the
+  // given isolate |group| and vm isolate group.
+  // Sets |code_start| to the beginning of the instructions corresponding
+  // to |pc| (like Code::PayloadStart()).
+  static CompressedStackMapsPtr FindCompressedStackMaps(IsolateGroup* group,
+                                                        uword pc,
+                                                        bool is_return_address,
+                                                        uword* code_start);
+
+ private:
+  static CodePtr LookupInGroup(IsolateGroup* group,
+                               uword pc,
+                               bool is_return_address);
 };
 
 }  // namespace dart
diff --git a/runtime/vm/service_test.cc b/runtime/vm/service_test.cc
index c078398..7dec0a8 100644
--- a/runtime/vm/service_test.cc
+++ b/runtime/vm/service_test.cc
@@ -259,7 +259,7 @@
   // Use the entry of the code object as it's reference.
   uword entry = code_c.PayloadStart();
   int64_t compile_timestamp = code_c.compile_timestamp();
-  EXPECT_GT(code_c.Size(), 16);
+  EXPECT_GT(code_c.Size(), 16u);
   uword last = entry + code_c.Size();
 
   // Build a mock message handler and wrap it in a dart port.
diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc
index 51c1722..037e70d 100644
--- a/runtime/vm/stack_frame.cc
+++ b/runtime/vm/stack_frame.cc
@@ -105,6 +105,9 @@
 }
 
 bool StackFrame::IsBareInstructionsDartFrame() const {
+  if (!(FLAG_precompiled_mode && FLAG_use_bare_instructions)) {
+    return false;
+  }
   NoSafepointScope no_safepoint;
 
   Code code;
@@ -115,18 +118,14 @@
     ASSERT(cid == kNullCid || cid == kClassCid || cid == kFunctionCid);
     return cid == kFunctionCid;
   }
-  code = ReversePc::Lookup(Dart::vm_isolate_group(), pc(),
-                           /*is_return_address=*/true);
-  if (!code.IsNull()) {
-    auto const cid = code.OwnerClassId();
-    ASSERT(cid == kNullCid || cid == kClassCid || cid == kFunctionCid);
-    return cid == kFunctionCid;
-  }
 
   return false;
 }
 
 bool StackFrame::IsBareInstructionsStubFrame() const {
+  if (!(FLAG_precompiled_mode && FLAG_use_bare_instructions)) {
+    return false;
+  }
   NoSafepointScope no_safepoint;
 
   Code code;
@@ -137,13 +136,6 @@
     ASSERT(cid == kNullCid || cid == kClassCid || cid == kFunctionCid);
     return cid == kNullCid || cid == kClassCid;
   }
-  code = ReversePc::Lookup(Dart::vm_isolate_group(), pc(),
-                           /*is_return_address=*/true);
-  if (!code.IsNull()) {
-    auto const cid = code.OwnerClassId();
-    ASSERT(cid == kNullCid || cid == kClassCid || cid == kFunctionCid);
-    return cid == kNullCid || cid == kClassCid;
-  }
 
   return false;
 }
@@ -225,9 +217,13 @@
   // helper functions to the raw object interface.
   NoSafepointScope no_safepoint;
   Code code;
+  CompressedStackMaps maps;
+  uword code_start;
 
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    code = GetCodeObject();
+    maps = ReversePc::FindCompressedStackMaps(isolate_group(), pc(),
+                                              /*is_return_address=*/true,
+                                              &code_start);
   } else {
     ObjectPtr pc_marker = *(reinterpret_cast<ObjectPtr*>(
         fp() + (runtime_frame_layout.code_from_fp * kWordSize)));
@@ -236,23 +232,23 @@
     visitor->VisitPointer(&pc_marker);
     if (pc_marker->IsHeapObject() && (pc_marker->GetClassId() == kCodeCid)) {
       code ^= pc_marker;
+      code_start = code.PayloadStart();
+      maps = code.compressed_stackmaps();
+      ASSERT(!maps.IsNull());
     } else {
       ASSERT(pc_marker == Object::null());
     }
   }
 
-  if (!code.IsNull()) {
+  if (!maps.IsNull()) {
     // Optimized frames have a stack map. We need to visit the frame based
     // on the stack map.
-    CompressedStackMaps maps;
-    maps = code.compressed_stackmaps();
     CompressedStackMaps global_table;
 
     global_table =
         isolate_group()->object_store()->canonicalized_stack_map_entries();
     CompressedStackMaps::Iterator it(maps, global_table);
-    const uword start = code.PayloadStart();
-    const uint32_t pc_offset = pc() - start;
+    const uint32_t pc_offset = pc() - code_start;
     if (it.Find(pc_offset)) {
       ObjectPtr* first = reinterpret_cast<ObjectPtr*>(sp());
       ObjectPtr* last = reinterpret_cast<ObjectPtr*>(
@@ -305,8 +301,14 @@
     // unoptimized code, code with no stack map information at all, or the entry
     // to an osr function. In each of these cases, all stack slots contain
     // tagged pointers, so fall through.
-    ASSERT(!code.is_optimized() || maps.IsNull() ||
-           (pc_offset == code.EntryPoint() - code.PayloadStart()));
+#if defined(DEBUG)
+    if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
+      ASSERT(IsStubFrame());
+    } else {
+      ASSERT(!code.is_optimized() ||
+             (pc_offset == code.EntryPoint() - code.PayloadStart()));
+    }
+#endif  // defined(DEBUG)
   }
 
   // For normal unoptimized Dart frames and Stub frames each slot
@@ -348,15 +350,23 @@
 CodePtr StackFrame::GetCodeObject() const {
 #if defined(DART_PRECOMPILED_RUNTIME)
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    CodePtr code = ReversePc::Lookup(isolate_group(), pc(),
-                                     /*is_return_address=*/true);
-    if (code != Code::null()) {
-      return code;
-    }
-    code = ReversePc::Lookup(Dart::vm_isolate_group(), pc(),
+    NoSafepointScope no_safepoint;
+    Code code;
+    code = ReversePc::Lookup(isolate_group(), pc(),
                              /*is_return_address=*/true);
-    if (code != Code::null()) {
-      return code;
+    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 (FLAG_dwarf_stack_traces_mode && code.CanBeOmittedFromAOTSnapshot()) {
+        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();
   }
diff --git a/runtime/vm/stack_trace.cc b/runtime/vm/stack_trace.cc
index e392260..f200269 100644
--- a/runtime/vm/stack_trace.cc
+++ b/runtime/vm/stack_trace.cc
@@ -429,19 +429,17 @@
 void StackTraceUtils::UnwindAwaiterChain(
     Zone* zone,
     const GrowableObjectArray& code_array,
-    const GrowableObjectArray& pc_offset_array,
+    GrowableArray<uword>* pc_offset_array,
     CallerClosureFinder* caller_closure_finder,
     const Closure& leaf_closure) {
   auto& code = Code::Handle(zone);
   auto& function = Function::Handle(zone);
   auto& closure = Closure::Handle(zone, leaf_closure.ptr());
   auto& pc_descs = PcDescriptors::Handle(zone);
-  auto& offset = Smi::Handle(zone);
 
   // Inject async suspension marker.
   code_array.Add(StubCode::AsynchronousGapMarker());
-  offset = Smi::New(0);
-  pc_offset_array.Add(offset);
+  pc_offset_array->Add(0);
 
   // Traverse the trail of async futures all the way up.
   for (; !closure.IsNull();
@@ -455,23 +453,22 @@
     RELEASE_ASSERT(!code.IsNull());
     code_array.Add(code);
     pc_descs = code.pc_descriptors();
-    offset = Smi::New(FindPcOffset(pc_descs, GetYieldIndex(closure)));
+    const intptr_t pc_offset = FindPcOffset(pc_descs, GetYieldIndex(closure));
     // Unlike other sources of PC offsets, the offset may be 0 here if we
     // reach a non-async closure receiving the yielded value.
-    ASSERT(offset.Value() >= 0);
-    pc_offset_array.Add(offset);
+    ASSERT(pc_offset >= 0);
+    pc_offset_array->Add(pc_offset);
 
     // Inject async suspension marker.
     code_array.Add(StubCode::AsynchronousGapMarker());
-    offset = Smi::New(0);
-    pc_offset_array.Add(offset);
+    pc_offset_array->Add(0);
   }
 }
 
 void StackTraceUtils::CollectFramesLazy(
     Thread* thread,
     const GrowableObjectArray& code_array,
-    const GrowableObjectArray& pc_offset_array,
+    GrowableArray<uword>* pc_offset_array,
     int skip_frames,
     std::function<void(StackFrame*)>* on_sync_frames,
     bool* has_async) {
@@ -489,7 +486,6 @@
   }
 
   auto& code = Code::Handle(zone);
-  auto& offset = Smi::Handle(zone);
   auto& closure = Closure::Handle(zone);
 
   CallerClosureFinder caller_closure_finder(zone);
@@ -513,10 +509,9 @@
       // Add the current synchronous frame.
       code = frame->LookupDartCode();
       code_array.Add(code);
-      const intptr_t pc_offset = frame->pc() - code.PayloadStart();
+      const uword pc_offset = frame->pc() - code.PayloadStart();
       ASSERT(pc_offset > 0 && pc_offset <= code.Size());
-      offset = Smi::New(pc_offset);
-      pc_offset_array.Add(offset);
+      pc_offset_array->Add(pc_offset);
       // Callback for sync frame.
       if (on_sync_frames != nullptr) {
         (*on_sync_frames)(frame);
@@ -593,7 +588,7 @@
 
 intptr_t StackTraceUtils::CollectFrames(Thread* thread,
                                         const Array& code_array,
-                                        const Array& pc_offset_array,
+                                        const TypedData& pc_offset_array,
                                         intptr_t array_offset,
                                         intptr_t count,
                                         int skip_frames) {
@@ -602,7 +597,6 @@
   StackFrame* frame = frames.NextFrame();
   ASSERT(frame != NULL);  // We expect to find a dart invocation frame.
   Code& code = Code::Handle(zone);
-  Smi& offset = Smi::Handle(zone);
   intptr_t collected_frames_count = 0;
   for (; (frame != NULL) && (collected_frames_count < count);
        frame = frames.NextFrame()) {
@@ -611,9 +605,9 @@
       continue;
     }
     code = frame->LookupDartCode();
-    offset = Smi::New(frame->pc() - code.PayloadStart());
+    const intptr_t pc_offset = frame->pc() - code.PayloadStart();
     code_array.SetAt(array_offset, code);
-    pc_offset_array.SetAt(array_offset, offset);
+    pc_offset_array.SetUintPtr(array_offset * kWordSize, pc_offset);
     array_offset++;
     collected_frames_count++;
   }
diff --git a/runtime/vm/stack_trace.h b/runtime/vm/stack_trace.h
index a08221f..c2eb02a 100644
--- a/runtime/vm/stack_trace.h
+++ b/runtime/vm/stack_trace.h
@@ -107,7 +107,7 @@
 
   static void UnwindAwaiterChain(Zone* zone,
                                  const GrowableObjectArray& code_array,
-                                 const GrowableObjectArray& pc_offset_array,
+                                 GrowableArray<uword>* pc_offset_array,
                                  CallerClosureFinder* caller_closure_finder,
                                  const Closure& leaf_closure);
 
@@ -130,7 +130,7 @@
   static void CollectFramesLazy(
       Thread* thread,
       const GrowableObjectArray& code_array,
-      const GrowableObjectArray& pc_offset_array,
+      GrowableArray<uword>* pc_offset_array,
       int skip_frames,
       std::function<void(StackFrame*)>* on_sync_frames = nullptr,
       bool* has_async = nullptr);
@@ -151,7 +151,7 @@
   /// Returns the number of frames collected.
   static intptr_t CollectFrames(Thread* thread,
                                 const Array& code_array,
-                                const Array& pc_offset_array,
+                                const TypedData& pc_offset_array,
                                 intptr_t array_offset,
                                 intptr_t count,
                                 int skip_frames);
diff --git a/runtime/vm/stub_code.cc b/runtime/vm/stub_code.cc
index e41c642..a78d1b0 100644
--- a/runtime/vm/stub_code.cc
+++ b/runtime/vm/stub_code.cc
@@ -60,6 +60,30 @@
   for (size_t i = 0; i < ARRAY_SIZE(entries_); i++) {
     entries_[i].code->set_object_pool(object_pool.ptr());
   }
+
+#if defined(DART_PRECOMPILER)
+  {
+    // Set Function owner for UnknownDartCode stub so it pretends to
+    // be a Dart code.
+    Zone* zone = Thread::Current()->zone();
+    const auto& signature = FunctionType::Handle(zone, FunctionType::New());
+    auto& owner = Object::Handle(zone);
+    owner = Object::void_class();
+    ASSERT(!owner.IsNull());
+    owner = Function::New(signature, Object::null_string(),
+                          UntaggedFunction::kRegularFunction,
+                          /*is_static=*/true,
+                          /*is_const=*/false,
+                          /*is_abstract=*/false,
+                          /*is_external=*/false,
+                          /*is_native=*/false, owner, TokenPosition::kNoSource);
+    StubCode::UnknownDartCode().set_owner(owner);
+    StubCode::UnknownDartCode().set_exception_handlers(
+        Object::empty_exception_handlers());
+    StubCode::UnknownDartCode().set_pc_descriptors(Object::empty_descriptors());
+    ASSERT(StubCode::UnknownDartCode().IsFunctionCode());
+  }
+#endif  // defined(DART_PRECOMPILER)
 }
 
 #undef STUB_CODE_GENERATE
diff --git a/runtime/vm/stub_code_list.h b/runtime/vm/stub_code_list.h
index c08bfd6..d99a661 100644
--- a/runtime/vm/stub_code_list.h
+++ b/runtime/vm/stub_code_list.h
@@ -128,7 +128,8 @@
   V(InstantiateTypeArguments)                                                  \
   V(InstantiateTypeArgumentsMayShareInstantiatorTA)                            \
   V(InstantiateTypeArgumentsMayShareFunctionTA)                                \
-  V(NoSuchMethodDispatcher)
+  V(NoSuchMethodDispatcher)                                                    \
+  V(UnknownDartCode)
 
 }  // namespace dart
 
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 9d54f24..1e1adac 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -52,6 +52,7 @@
 LibTest/core/int/operator_truncating_division_A01_t02: SkipByDesign # Division by zero is not an error in JavaScript
 LibTest/core/int/parse_A01_t02: SkipByDesign # big integers cannot be represented in JavaScript
 LibTest/core/int/remainder_A01_t03: SkipByDesign # Division by zero is not an error in JavaScript
+LibTest/ffi/*: SkipByDesign # dart:ffi is not supported
 LibTest/html/HttpRequest/responseText_A01_t02: Skip # https://github.com/dart-lang/co19/issues/932
 LibTest/html/HttpRequestUpload/*: Skip # https://github.com/dart-lang/co19/issues/932
 LibTest/io/*: SkipByDesign # dart:io not supported.
diff --git a/tests/co19/co19-dartdevc.status b/tests/co19/co19-dartdevc.status
index e0a2172..7f9fab3 100644
--- a/tests/co19/co19-dartdevc.status
+++ b/tests/co19/co19-dartdevc.status
@@ -103,6 +103,7 @@
 LibTest/core/int/operator_truncating_division_A01_t02: SkipByDesign # Division by zero is not an error in JavaScript
 LibTest/core/int/parse_A01_t02: SkipByDesign # big integers cannot be represented in JavaScript
 LibTest/core/int/remainder_A01_t03: SkipByDesign # Division by zero is not an error in JavaScript
+LibTest/ffi/*: SkipByDesign # dart:ffi is not supported
 LibTest/html/Element/blur_A01_t01: Skip # Times out
 LibTest/html/Element/focus_A01_t01: Skip # Times out
 LibTest/html/HttpRequest/responseText_A01_t02: Skip # https://github.com/dart-lang/co19/issues/932
diff --git a/tests/web/regress/if_method_call_test.dart b/tests/web/regress/if_method_call_test.dart
new file mode 100644
index 0000000..2938c70
--- /dev/null
+++ b/tests/web/regress/if_method_call_test.dart
@@ -0,0 +1,57 @@
+// 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.
+
+// Regression test for bug in dart2js type promotion.
+
+import 'package:expect/expect.dart';
+
+staticMethod(a) => true;
+
+class Super {
+  superMethod(a) => true;
+}
+
+class Class extends Super {
+  instanceMethod(a) => true;
+
+  test1(c) {
+    if (super.superMethod(c is Class2)) {
+      return c.method();
+    }
+    return 0;
+  }
+
+  test2(c) {
+    if (this.instanceMethod(c is Class2)) {
+      return c.method();
+    }
+    return 0;
+  }
+}
+
+class Class1 {
+  method() => 87;
+}
+
+class Class2 {
+  method() => 42;
+}
+
+test(c) {
+  if (staticMethod(c is Class2)) {
+    return c.method();
+  }
+  return 0;
+}
+
+main() {
+  Expect.equals(87, test(new Class1())); //# 01: ok
+  Expect.equals(42, test(new Class2()));
+
+  Expect.equals(87, new Class().test1(new Class1())); //# 02: ok
+  Expect.equals(42, new Class().test1(new Class2()));
+
+  Expect.equals(87, new Class().test2(new Class1())); //# 03: ok
+  Expect.equals(42, new Class().test2(new Class2()));
+}
diff --git a/tests/web_2/regress/if_method_call_test.dart b/tests/web_2/regress/if_method_call_test.dart
new file mode 100644
index 0000000..2938c70
--- /dev/null
+++ b/tests/web_2/regress/if_method_call_test.dart
@@ -0,0 +1,57 @@
+// 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.
+
+// Regression test for bug in dart2js type promotion.
+
+import 'package:expect/expect.dart';
+
+staticMethod(a) => true;
+
+class Super {
+  superMethod(a) => true;
+}
+
+class Class extends Super {
+  instanceMethod(a) => true;
+
+  test1(c) {
+    if (super.superMethod(c is Class2)) {
+      return c.method();
+    }
+    return 0;
+  }
+
+  test2(c) {
+    if (this.instanceMethod(c is Class2)) {
+      return c.method();
+    }
+    return 0;
+  }
+}
+
+class Class1 {
+  method() => 87;
+}
+
+class Class2 {
+  method() => 42;
+}
+
+test(c) {
+  if (staticMethod(c is Class2)) {
+    return c.method();
+  }
+  return 0;
+}
+
+main() {
+  Expect.equals(87, test(new Class1())); //# 01: ok
+  Expect.equals(42, test(new Class2()));
+
+  Expect.equals(87, new Class().test1(new Class1())); //# 02: ok
+  Expect.equals(42, new Class().test1(new Class2()));
+
+  Expect.equals(87, new Class().test2(new Class1())); //# 03: ok
+  Expect.equals(42, new Class().test2(new Class2()));
+}
diff --git a/tools/VERSION b/tools/VERSION
index 598505e..680a21d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 59
+PRERELEASE 60
 PRERELEASE_PATCH 0
\ No newline at end of file