Version 2.13.0-131.0.dev

Merge commit 'f13285482df8f30462410851a6b90f0cb14bb8f3' into 'dev'
diff --git a/WATCHLISTS b/WATCHLISTS
index 195162c..23100a3 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -73,7 +73,6 @@
   },
 
   'WATCHLISTS': {
-    'build': [ 'keertip@google.com' ],
     'dart2js': [ 'dart2js-team+reviews@google.com' ],
     'dartdevc': [ 'dart-dc-team+reviews@google.com' ],
     'front_end': [ 'dart-fe-team+reviews@google.com' ],
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
index 25287e5..d8b48dc 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
@@ -164,7 +164,9 @@
   /// offset is within the given [node], or `null` if the context does not
   /// impose any type.
   DartType computeContextType(AstNode node, int offset) {
-    var type = node.accept(_ContextTypeVisitor(typeProvider, offset));
+    var type = node
+        .accept(_ContextTypeVisitor(typeProvider, offset))
+        ?.resolveToBound(typeProvider.objectType);
     if (type == null || type.isDynamic) {
       return null;
     }
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart
index e3eee98..2ffa0b5 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart
@@ -47,7 +47,7 @@
     }
 
     // Determine the target expression's type.
-    var type = expression.staticType;
+    var type = expression.staticType?.resolveToBound(request.objectType);
     if (type == null || type.isDynamic) {
       // If the expression does not provide a good type, then attempt to get a
       // better type from the element.
diff --git a/pkg/analysis_server/test/completion_test.dart b/pkg/analysis_server/test/completion_test.dart
index 26306df..6a3b7d3 100644
--- a/pkg/analysis_server/test/completion_test.dart
+++ b/pkg/analysis_server/test/completion_test.dart
@@ -2351,9 +2351,7 @@
         <String>['1+HttpServer', '1-HttpClient'],
         failingTests: '1');
 
-    buildTests(
-        'test038',
-        '''
+    buildTests('test038', '''
 class X {
   x(){}
 }
@@ -2367,9 +2365,7 @@
     ay.!1y;
     az.!2x;
   }
-}''',
-        <String>['1+y', '1-x', '2+x', '2-y'],
-        failingTests: '2');
+}''', <String>['1+y', '1-x', '2+x', '2-y']);
 
     // test analysis of untyped fields and top-level vars
     buildTests(
diff --git a/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
index 894b3d0..982f671 100644
--- a/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
@@ -3067,6 +3067,21 @@
     assertNotSuggested('==');
   }
 
+  Future<void> test_methodInvocation_typeParameter() async {
+    addTestSource('''
+class A {
+  void a() {}
+}
+class C<T extends A> {
+  void c(T t) {
+    t.^;
+  }
+}
+''');
+    await computeSuggestions();
+    assertSuggestMethod('a', 'A', 'void');
+  }
+
   Future<void> test_mixin() async {
     addTestSource(r'''
 class A {
diff --git a/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart b/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
index 2a6276b..c2a5bb6 100644
--- a/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
+++ b/pkg/analysis_server/test/src/services/completion/dart/feature_computer_test.dart
@@ -357,6 +357,31 @@
 ''', 'String');
   }
 
+  Future<void> test_argumentList_typeParameter_resolved() async {
+    await assertContextType('''
+class A {}
+class B {}
+class C<T extends A> {
+  void m(T t) {}
+}
+void f(C<B> c) {
+  c.m(^);
+}
+''', 'B');
+  }
+
+  Future<void> test_argumentList_typeParameter_unresolved() async {
+    await assertContextType('''
+class A {}
+class C<T extends A> {
+  void m1(T t) {}
+  void m2() {
+    m1(^);
+  }
+}
+''', 'A');
+  }
+
   Future<void> test_assertInitializer_with_identifier() async {
     await assertContextType('''
 class C {
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 498c87c..07ede06b 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -44,12 +44,6 @@
 import 'package:analyzer/src/util/file_paths.dart' as file_paths;
 import 'package:meta/meta.dart';
 
-/// TODO(scheglov) We could use generalized Function in
-/// [AnalysisDriverTestView], but this breaks `AnalysisContext` and code
-/// generation. So, for now let's work around them, and rewrite generators to
-/// [AnalysisDriver].
-typedef WorkToWaitAfterComputingResult = Future<void> Function(String path);
-
 /// This class computes [AnalysisResult]s for Dart files.
 ///
 /// Let the set of "explicitly analyzed files" denote the set of paths that have
@@ -528,7 +522,7 @@
     }
     _discoverAvailableFiles();
     _scheduler.notify(this);
-    return _discoverAvailableFilesTask!.completer!.future;
+    return _discoverAvailableFilesTask!.completer.future;
   }
 
   @override
@@ -2214,28 +2208,31 @@
 
   final AnalysisDriver driver;
 
-  bool isCompleted = false;
-  Completer<void>? completer = Completer<void>();
+  final Completer<void> completer = Completer<void>();
 
   Iterator<Folder>? folderIterator;
-  List<String>? files = [];
+
+  final List<String> files = [];
+
   int fileIndex = 0;
 
   _DiscoverAvailableFilesTask(this.driver);
 
+  bool get isCompleted => completer.isCompleted;
+
   /// Perform the next piece of work, and set [isCompleted] to `true` to
   /// indicate that the task is done, or keeps it `false` to indicate that the
   /// task should continue to be run.
   void perform() {
     if (folderIterator == null) {
-      files!.addAll(driver.addedFiles);
+      files.addAll(driver.addedFiles);
 
       // Discover SDK libraries.
       var dartSdk = driver._sourceFactory.dartSdk;
       if (dartSdk != null) {
         for (var sdkLibrary in dartSdk.sdkLibraries) {
           var file = dartSdk.mapDartUri(sdkLibrary.shortName)!.fullName;
-          files!.add(file);
+          files.add(file);
         }
       }
 
@@ -2262,22 +2259,18 @@
     }
 
     // Get know files one by one.
-    while (fileIndex < files!.length) {
+    while (fileIndex < files.length) {
       if (timer.elapsedMilliseconds > _MS_WORK_INTERVAL) {
         return;
       }
-      var file = files![fileIndex++];
+      var file = files[fileIndex++];
       driver._fsState.getFileForPath(file);
     }
 
     // The task is done, clean up.
     folderIterator = null;
-    files = null;
-
-    // Complete and clean up.
-    isCompleted = true;
-    completer!.complete();
-    completer = null;
+    files.clear();
+    completer.complete();
   }
 
   void _appendFilesRecursively(Folder folder) {
@@ -2287,7 +2280,7 @@
         if (child is File) {
           var path = child.path;
           if (file_paths.isDart(pathContext, path)) {
-            files!.add(path);
+            files.add(path);
           }
         } else if (child is Folder) {
           _appendFilesRecursively(child);
diff --git a/pkg/analyzer/lib/src/dart/ast/extensions.dart b/pkg/analyzer/lib/src/dart/ast/extensions.dart
index 7451b12..03225d1 100644
--- a/pkg/analyzer/lib/src/dart/ast/extensions.dart
+++ b/pkg/analyzer/lib/src/dart/ast/extensions.dart
@@ -143,3 +143,18 @@
     return cast<FormalParameterImpl>();
   }
 }
+
+extension TypeAnnotationExtension on TypeAnnotation {
+  /// Return the static type of this type annotation.
+  ///
+  /// This accessor should be used on expressions that are expected to
+  /// be already resolved. Every such expression must have the type set,
+  /// at least `dynamic`.
+  DartType get typeOrThrow {
+    var type = this.type;
+    if (type == null) {
+      throw StateError('No type: $this');
+    }
+    return type;
+  }
+}
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index e0f5421..5413fb9 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -17,6 +17,7 @@
 import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer/src/dart/analysis/session.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
 import 'package:analyzer/src/dart/constant/compute.dart';
 import 'package:analyzer/src/dart/constant/evaluation.dart';
 import 'package:analyzer/src/dart/constant/value.dart';
@@ -3873,7 +3874,7 @@
     if (linkedNode != null) {
       var linkedNode = this.linkedNode as ExtensionDeclaration;
       linkedContext!.applyResolution(linkedNode);
-      return _extendedType = linkedNode.extendedType.type!;
+      return _extendedType = linkedNode.extendedType.typeOrThrow;
     }
 
     return _extendedType!;
diff --git a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
index 25bd1d7..93cc5b6 100644
--- a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
@@ -199,7 +199,7 @@
     if (typeArguments != null) {
       var arguments = typeArguments.arguments;
       if (arguments.length == typeParameters.length) {
-        typeArgumentTypes = arguments.map((a) => a.type!).toList();
+        typeArgumentTypes = arguments.map((a) => a.typeOrThrow).toList();
       } else {
         typeArgumentTypes = _listOfDynamic(typeParameters);
       }
@@ -392,7 +392,7 @@
         if (typeParameters.isEmpty) {
           return const <DartType>[];
         }
-        return arguments.map((a) => a.type!).toList();
+        return arguments.map((a) => a.typeOrThrow).toList();
       } else {
         _errorReporter.reportErrorForNode(
           CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS_EXTENSION,
diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
index 658adba..2478a00 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
@@ -9,6 +9,7 @@
 import 'package:analyzer/dart/ast/visitor.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/type_system.dart' show TypeSystemImpl;
 import 'package:analyzer/src/generated/migration.dart';
@@ -86,7 +87,7 @@
     var expression = node.expression;
     var typeAnnotation = node.type;
 
-    flow!.asExpression_end(expression, typeAnnotation.type!);
+    flow!.asExpression_end(expression, typeAnnotation.typeOrThrow);
   }
 
   void assignmentExpression(AssignmentExpression node) {
@@ -200,7 +201,7 @@
       node,
       expression,
       node.notOperator != null,
-      typeAnnotation.type!,
+      typeAnnotation.typeOrThrow,
     );
   }
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
index 4dfeb7d..24ce9b9 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
@@ -7,6 +7,7 @@
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart';
 import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
@@ -153,7 +154,7 @@
     var typeArguments = node.typeArguments;
     if (typeArguments != null) {
       node.typeArgumentTypes = typeArguments.arguments
-          .map((typeArgument) => typeArgument.type!)
+          .map((typeArgument) => typeArgument.typeOrThrow)
           .toList();
     } else {
       node.typeArgumentTypes = const <DartType>[];
diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
index 0b960b5..8239ccd 100644
--- a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
@@ -144,7 +144,8 @@
     // for FunctionExpressionInvocation(s), so set it here, if not inferred.
     if (node is FunctionExpressionInvocationImpl) {
       if (typeArguments != null) {
-        var typeArgs = typeArguments.arguments.map((n) => n.type!).toList();
+        var typeArgs =
+            typeArguments.arguments.map((n) => n.typeOrThrow).toList();
         node.typeArgumentTypes = typeArgs;
       } else {
         node.typeArgumentTypes = const <DartType>[];
@@ -420,7 +421,7 @@
       );
     } else {
       typeArguments = typeArgumentList.arguments
-          .map((typeArgument) => typeArgument.type!)
+          .map((typeArgument) => typeArgument.typeOrThrow)
           .toList(growable: true);
     }
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
index 77429cb..c2d6338 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -789,7 +789,8 @@
     var typeArgumentList = _invocation!.typeArguments;
     if (typeArgumentList != null) {
       var arguments = typeArgumentList.arguments;
-      _invocation!.typeArgumentTypes = arguments.map((n) => n.type!).toList();
+      _invocation!.typeArgumentTypes =
+          arguments.map((n) => n.typeOrThrow).toList();
     } else {
       _invocation!.typeArgumentTypes = [];
     }
diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
index 49c88e6..666cce9 100644
--- a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
@@ -13,6 +13,7 @@
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/scope.dart';
 import 'package:analyzer/src/dart/element/type.dart';
@@ -180,7 +181,7 @@
           element.type = type;
           exceptionNode.staticType = type;
         } else {
-          element.type = exceptionTypeNode.type!;
+          element.type = exceptionTypeNode.typeOrThrow;
           exceptionNode.staticType = exceptionTypeNode.type;
         }
 
@@ -321,7 +322,7 @@
       element.type = _dynamicType;
     } else {
       node.type!.accept(this);
-      element.type = node.type!.type!;
+      element.type = node.type!.typeOrThrow;
     }
 
     _setCodeRange(element, node);
@@ -1192,7 +1193,7 @@
     visitTypeName(typeName);
     _typeNameResolver.classHierarchy_typeName = null;
 
-    DartType type = typeName.type!;
+    DartType type = typeName.typeOrThrow;
     if (type is InterfaceType) {
       ClassElement element = type.element;
       if (element.isEnum || element.isMixin && asClass) {
diff --git a/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
index 9c2328a..c4c1dfe 100644
--- a/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
@@ -11,6 +11,7 @@
 import 'package:analyzer/dart/element/type_provider.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
@@ -140,7 +141,7 @@
 
     return List.generate(
       parameterCount,
-      (i) => arguments[i].type!,
+      (i) => arguments[i].typeOrThrow,
     );
   }
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart
index 624a27b..ee009b4 100644
--- a/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/typed_literal_resolver.dart
@@ -79,7 +79,7 @@
     var typeArguments = node.typeArguments?.arguments;
     if (typeArguments != null) {
       if (typeArguments.length == 1) {
-        DartType elementType = typeArguments[0].type!;
+        DartType elementType = typeArguments[0].typeOrThrow;
         if (!elementType.isDynamic) {
           listType = _typeProvider.listType(elementType);
         }
@@ -109,7 +109,7 @@
     var literalResolution = _computeSetOrMapResolution(node);
     if (literalResolution.kind == _LiteralResolutionKind.set) {
       if (typeArguments != null && typeArguments.length == 1) {
-        var elementType = typeArguments[0].type!;
+        var elementType = typeArguments[0].typeOrThrow;
         literalType = _typeProvider.setType(elementType);
       } else {
         literalType =
@@ -117,8 +117,8 @@
       }
     } else if (literalResolution.kind == _LiteralResolutionKind.map) {
       if (typeArguments != null && typeArguments.length == 2) {
-        var keyType = typeArguments[0].type!;
-        var valueType = typeArguments[1].type!;
+        var keyType = typeArguments[0].typeOrThrow;
+        var valueType = typeArguments[1].typeOrThrow;
         literalType = _typeProvider.mapType(keyType, valueType);
       } else {
         literalType =
@@ -313,10 +313,12 @@
     if (arguments != null) {
       if (arguments.length == 1) {
         return _LiteralResolution(_LiteralResolutionKind.set,
-            _typeProvider.setType(arguments[0].type!));
+            _typeProvider.setType(arguments[0].typeOrThrow));
       } else if (arguments.length == 2) {
-        return _LiteralResolution(_LiteralResolutionKind.map,
-            _typeProvider.mapType(arguments[0].type!, arguments[1].type!));
+        return _LiteralResolution(
+            _LiteralResolutionKind.map,
+            _typeProvider.mapType(
+                arguments[0].typeOrThrow, arguments[1].typeOrThrow));
       }
     }
     return _LiteralResolution(_LiteralResolutionKind.ambiguous, null);
@@ -669,7 +671,7 @@
     if (typeArguments != null) {
       DartType elementType = _dynamicType;
       if (typeArguments.length == 1) {
-        elementType = typeArguments[0].type!;
+        elementType = typeArguments[0].typeOrThrow;
       }
       _recordStaticType(
         node,
@@ -706,7 +708,7 @@
     if (typeArguments != null) {
       if (typeArguments.length == 1) {
         node.becomeSet();
-        var elementType = typeArguments[0].type!;
+        var elementType = typeArguments[0].typeOrThrow;
         _recordStaticType(
           node,
           _typeProvider.setElement.instantiate(
@@ -717,8 +719,8 @@
         return;
       } else if (typeArguments.length == 2) {
         node.becomeMap();
-        var keyType = typeArguments[0].type!;
-        var valueType = typeArguments[1].type!;
+        var keyType = typeArguments[0].typeOrThrow;
+        var valueType = typeArguments[1].typeOrThrow;
         _recordStaticType(
           node,
           _typeProvider.mapElement.instantiate(
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index e68cf25..388384f 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -1207,7 +1207,7 @@
       return;
     }
 
-    if (_typeSystem.isPotentiallyNullable(type.type!)) {
+    if (_typeSystem.isPotentiallyNullable(type.typeOrThrow)) {
       _errorReporter.reportErrorForNode(
         HintCode.NULLABLE_TYPE_IN_CATCH_CLAUSE,
         type,
@@ -1596,7 +1596,7 @@
   /// on [node].  See [HintCode.UNNECESSARY_CAST].
   static bool isUnnecessaryCast(AsExpression node, TypeSystemImpl typeSystem) {
     var leftType = node.expression.typeOrThrow;
-    var rightType = node.type.type!;
+    var rightType = node.type.typeOrThrow;
 
     // `dynamicValue as SomeType` is a valid use case.
     if (leftType.isDynamic) {
diff --git a/pkg/analyzer/lib/src/error/inheritance_override.dart b/pkg/analyzer/lib/src/error/inheritance_override.dart
index 4b4e865..e9fbe20 100644
--- a/pkg/analyzer/lib/src/error/inheritance_override.dart
+++ b/pkg/analyzer/lib/src/error/inheritance_override.dart
@@ -10,6 +10,7 @@
 import 'package:analyzer/dart/element/type_provider.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
@@ -330,7 +331,7 @@
       return false;
     }
 
-    DartType type = typeName.type!;
+    DartType type = typeName.typeOrThrow;
     if (type is InterfaceType &&
         typeProvider.nonSubtypableClasses.contains(type.element)) {
       reporter.reportErrorForNode(errorCode, typeName, [type]);
diff --git a/pkg/analyzer/lib/src/error/type_arguments_verifier.dart b/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
index 6d4276d..9e5545c 100644
--- a/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
+++ b/pkg/analyzer/lib/src/error/type_arguments_verifier.dart
@@ -175,7 +175,7 @@
       return;
     }
     if (_isMissingTypeArguments(
-        node, node.type!, node.name.staticElement, null)) {
+        node, node.typeOrThrow, node.name.staticElement, null)) {
       AstNode unwrappedParent = parentEscapingTypeArguments(node);
       if (unwrappedParent is AsExpression || unwrappedParent is IsExpression) {
         // Do not report a "Strict raw type" error in this case; too noisy.
@@ -190,7 +190,7 @@
   /// Verify that the type arguments in the given [typeName] are all within
   /// their bounds.
   void _checkForTypeArgumentNotMatchingBounds(TypeName typeName) {
-    var type = typeName.type!;
+    var type = typeName.typeOrThrow;
 
     List<TypeParameterElement> typeParameters;
     List<DartType> typeArguments;
@@ -336,7 +336,7 @@
     var instantiatedType = node.staticInvokeType;
     if (genericType is FunctionType && instantiatedType is FunctionType) {
       var fnTypeParams = genericType.typeFormals;
-      var typeArgs = typeArgumentList.map((t) => t.type!).toList();
+      var typeArgs = typeArgumentList.map((t) => t.typeOrThrow).toList();
 
       // If the amount mismatches, clean up the lists to be substitutable. The
       // mismatch in size is reported elsewhere, but we must successfully
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index 7b163a73..921c948 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -8,6 +8,7 @@
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/type_provider.dart';
 import 'package:analyzer/src/dart/resolver/method_invocation_resolver.dart';
@@ -245,7 +246,7 @@
 
   @override
   void visitConstructorName(covariant ConstructorNameImpl node) {
-    DartType type = node.type.type!;
+    DartType type = node.type.typeOrThrow;
     if (type.isDynamic) {
       // Nothing to do.
     } else if (type is InterfaceType) {
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 4a304c1..071f068 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -870,7 +870,7 @@
   void visitInstanceCreationExpression(InstanceCreationExpression node) {
     ConstructorName constructorName = node.constructorName;
     TypeName typeName = constructorName.type;
-    DartType type = typeName.type!;
+    DartType type = typeName.typeOrThrow;
     if (type is InterfaceType) {
       _checkForConstOrNewWithAbstractClass(node, typeName, type);
       _checkForConstOrNewWithEnum(node, typeName, type);
@@ -1400,7 +1400,7 @@
         mixinNameIndex < withClause.mixinTypes.length;
         mixinNameIndex++) {
       TypeName mixinName = withClause.mixinTypes[mixinNameIndex];
-      DartType mixinType = mixinName.type!;
+      DartType mixinType = mixinName.typeOrThrow;
       if (mixinType is InterfaceType) {
         mixinTypeIndex++;
         if (_checkForExtendsOrImplementsDisallowedClass(
@@ -1450,7 +1450,7 @@
       // If the element is null, we check for the
       // REDIRECT_TO_MISSING_CONSTRUCTOR case
       TypeName constructorTypeName = redirectedConstructor.type;
-      DartType redirectedType = constructorTypeName.type!;
+      DartType redirectedType = constructorTypeName.typeOrThrow;
       if (redirectedType.element != null && !redirectedType.isDynamic) {
         // Prepare the constructor name
         String constructorStrName = constructorTypeName.name.name;
@@ -2199,7 +2199,7 @@
     if (constructorName.staticElement != null) {
       return;
     }
-    DartType type = typeName.type!;
+    DartType type = typeName.typeOrThrow;
     if (type is InterfaceType) {
       ClassElement element = type.element;
       if (element.isEnum) {
@@ -2492,7 +2492,8 @@
       return false;
     }
     return typeName.type is InterfaceType &&
-        _typeProvider.nonSubtypableClasses.contains(typeName.type!.element);
+        _typeProvider.nonSubtypableClasses
+            .contains(typeName.typeOrThrow.element);
   }
 
   void _checkForExtensionDeclaresMemberOfObject(MethodDeclaration node) {
@@ -2687,7 +2688,7 @@
     if (_featureSet?.isEnabled(Feature.generic_metadata) ?? false) {
       return;
     }
-    DartType type = node.type!;
+    DartType type = node.typeOrThrow;
     if (type is FunctionType && type.typeFormals.isNotEmpty) {
       _errorReporter.reportErrorForNode(
           CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND,
@@ -2758,7 +2759,7 @@
         (node is TypeName && node.typeArguments != null)) {
       return;
     }
-    DartType type = node.type!;
+    DartType type = node.typeOrThrow;
     if (type is ParameterizedType &&
         type.typeArguments.isNotEmpty &&
         type.typeArguments.any((t) => t.isDynamic)) {
@@ -3419,7 +3420,7 @@
     }
 
     for (TypeName mixinType in withClause.mixinTypes) {
-      DartType type = mixinType.type!;
+      DartType type = mixinType.typeOrThrow;
       if (type is InterfaceType) {
         LibraryElement library = type.element.library;
         if (library != _currentLibrary) {
@@ -3486,7 +3487,7 @@
     if (constructorName.staticElement != null) {
       return;
     }
-    DartType type = typeName.type!;
+    DartType type = typeName.typeOrThrow;
     if (type is InterfaceType) {
       ClassElement element = type.element;
       if (element.isEnum || element.isMixin) {
@@ -3632,7 +3633,7 @@
     // check return type
     var annotation = declaration.returnType;
     if (annotation != null) {
-      DartType type = annotation.type!;
+      DartType type = annotation.typeOrThrow;
       if (!type.isVoid) {
         _errorReporter.reportErrorForNode(
             CompileTimeErrorCode.NON_VOID_RETURN_FOR_OPERATOR, annotation);
@@ -3646,7 +3647,7 @@
   /// See [StaticWarningCode.NON_VOID_RETURN_FOR_SETTER].
   void _checkForNonVoidReturnTypeForSetter(TypeAnnotation? typeName) {
     if (typeName != null) {
-      DartType type = typeName.type!;
+      DartType type = typeName.typeOrThrow;
       if (!type.isVoid) {
         _errorReporter.reportErrorForNode(
             CompileTimeErrorCode.NON_VOID_RETURN_FOR_SETTER, typeName);
@@ -3720,7 +3721,7 @@
     if (node.type == null) {
       return;
     }
-    var type = node.type!.type!;
+    var type = node.type!.typeOrThrow;
 
     if (!_typeSystem.isPotentiallyNonNullable(type)) {
       return;
@@ -3747,7 +3748,7 @@
     }
     bool problemReported = false;
     for (TypeName typeName in onClause.superclassConstraints) {
-      DartType type = typeName.type!;
+      DartType type = typeName.typeOrThrow;
       if (type is InterfaceType) {
         if (_checkForExtendsOrImplementsDisallowedClass(
             typeName,
@@ -4723,7 +4724,7 @@
             continue;
           }
           var methodTypeParameterVariance = Variance.invariant.combine(
-            Variance(typeParameter, methodTypeParameter.bound!.type!),
+            Variance(typeParameter, methodTypeParameter.bound!.typeOrThrow),
           );
           _checkForWrongVariancePosition(
               methodTypeParameterVariance, typeParameter, methodTypeParameter);
@@ -4748,7 +4749,7 @@
       var returnType = method.returnType;
       if (returnType != null) {
         var methodReturnTypeVariance =
-            Variance(typeParameter, returnType.type!);
+            Variance(typeParameter, returnType.typeOrThrow);
         _checkForWrongVariancePosition(
             methodReturnTypeVariance, typeParameter, returnType);
       }
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index 99f293a..d65781b 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -546,7 +546,7 @@
       _errorReporter.reportErrorForNode(
           FfiCode.MISSING_FIELD_TYPE_IN_STRUCT, fields.variables[0].name);
     } else {
-      DartType declaredType = fieldType.type!;
+      DartType declaredType = fieldType.typeOrThrow;
       if (declaredType.isDartCoreInt) {
         _validateAnnotations(fieldType, annotations, _PrimitiveDartType.int);
       } else if (declaredType.isDartCoreDouble) {
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index 45632ba..fb518cd 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -198,7 +198,7 @@
   void visitInstanceCreationExpression(
       covariant InstanceCreationExpressionImpl node) {
     _inferInstanceCreationExpression(node);
-    recordStaticType(node, node.constructorName.type.type!);
+    recordStaticType(node, node.constructorName.type.typeOrThrow);
   }
 
   /// <blockquote>
diff --git a/pkg/analyzer/lib/src/generated/type_promotion_manager.dart b/pkg/analyzer/lib/src/generated/type_promotion_manager.dart
index de3e0a0..965b30f 100644
--- a/pkg/analyzer/lib/src/generated/type_promotion_manager.dart
+++ b/pkg/analyzer/lib/src/generated/type_promotion_manager.dart
@@ -259,7 +259,7 @@
       }
     } else if (condition is IsExpression) {
       if (condition.notOperator == null) {
-        _promote(condition.expression, condition.type.type!);
+        _promote(condition.expression, condition.type.typeOrThrow);
       }
     } else if (condition is ParenthesizedExpression) {
       _promoteTypes(condition.expression);
diff --git a/pkg/analyzer/lib/src/summary2/default_types_builder.dart b/pkg/analyzer/lib/src/summary2/default_types_builder.dart
index 4401ee5..6138667 100644
--- a/pkg/analyzer/lib/src/summary2/default_types_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/default_types_builder.dart
@@ -6,6 +6,7 @@
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/replacement_visitor.dart';
 import 'package:analyzer/src/dart/element/type.dart';
@@ -73,7 +74,7 @@
 
       var cycles = _findRawTypePathsToDeclaration(
         parameter,
-        boundNode.type!,
+        boundNode.typeOrThrow,
         declarationElement,
         Set<Element>.identity(),
       );
@@ -244,7 +245,7 @@
               if (bound != null) {
                 var tails = _findRawTypePathsToDeclaration(
                   parameterNode,
-                  bound.type!,
+                  bound.typeOrThrow,
                   end,
                   visited,
                 );
diff --git a/pkg/analyzer/lib/src/summary2/function_type_builder.dart b/pkg/analyzer/lib/src/summary2/function_type_builder.dart
index bdbc0ba..faeefe2 100644
--- a/pkg/analyzer/lib/src/summary2/function_type_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/function_type_builder.dart
@@ -168,7 +168,7 @@
     if (node == null) {
       return _dynamicType;
     } else {
-      return node.type!;
+      return node.typeOrThrow;
     }
   }
 
diff --git a/pkg/analyzer/lib/src/summary2/named_type_builder.dart b/pkg/analyzer/lib/src/summary2/named_type_builder.dart
index 47ea3f0..56056ce 100644
--- a/pkg/analyzer/lib/src/summary2/named_type_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/named_type_builder.dart
@@ -57,7 +57,7 @@
     List<DartType> arguments;
     var argumentList = node.typeArguments;
     if (argumentList != null) {
-      arguments = argumentList.arguments.map((n) => n.type!).toList();
+      arguments = argumentList.arguments.map((n) => n.typeOrThrow).toList();
     } else {
       arguments = <DartType>[];
     }
@@ -154,13 +154,13 @@
   DartType _buildAliasedType(TypeAnnotation? node) {
     if (_isNonFunctionTypeAliasesEnabled) {
       if (node != null) {
-        return _buildType(node.type!);
+        return _buildType(node.typeOrThrow);
       } else {
         return _dynamicType;
       }
     } else {
       if (node is GenericFunctionType) {
-        return _buildType(node.type!);
+        return _buildType(node.typeOrThrow);
       } else {
         return FunctionTypeImpl(
           typeFormals: const <TypeParameterElement>[],
@@ -233,7 +233,7 @@
     if (node == null) {
       return _dynamicType;
     } else {
-      return _buildType(node.type!);
+      return _buildType(node.typeOrThrow);
     }
   }
 
diff --git a/pkg/analyzer/lib/src/summary2/types_builder.dart b/pkg/analyzer/lib/src/summary2/types_builder.dart
index f049832d..af20893 100644
--- a/pkg/analyzer/lib/src/summary2/types_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/types_builder.dart
@@ -264,7 +264,7 @@
   }
 
   InterfaceType _inferSingle(TypeNameImpl mixinNode) {
-    var mixinType = _interfaceType(mixinNode.type!);
+    var mixinType = _interfaceType(mixinNode.typeOrThrow);
 
     if (mixinNode.typeArguments != null) {
       return mixinType;
diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart
index 0c4053d..02e11bc 100644
--- a/pkg/analyzer/lib/src/task/strong/checker.dart
+++ b/pkg/analyzer/lib/src/task/strong/checker.dart
@@ -376,7 +376,7 @@
     if (typeArgumentList != null) {
       var typeArguments = typeArgumentList.arguments;
       if (typeArguments.isNotEmpty) {
-        type = typeArguments[0].type!;
+        type = typeArguments[0].typeOrThrow;
       }
     } else {
       DartType staticType = node.typeOrThrow;
@@ -447,10 +447,10 @@
       if (typeArgumentsList != null) {
         NodeList<TypeAnnotation> typeArguments = typeArgumentsList.arguments;
         if (typeArguments.isNotEmpty) {
-          keyType = typeArguments[0].type!;
+          keyType = typeArguments[0].typeOrThrow;
         }
         if (typeArguments.length > 1) {
-          valueType = typeArguments[1].type!;
+          valueType = typeArguments[1].typeOrThrow;
         }
       } else {
         DartType staticType = node.typeOrThrow;
@@ -467,7 +467,7 @@
       if (typeArgumentsList != null) {
         NodeList<TypeAnnotation> typeArguments = typeArgumentsList.arguments;
         if (typeArguments.isNotEmpty) {
-          type = typeArguments[0].type!;
+          type = typeArguments[0].typeOrThrow;
         }
       } else {
         DartType staticType = node.typeOrThrow;
@@ -525,7 +525,7 @@
         var initializer = variable.initializer;
         if (initializer != null) {
           checkForCast(initializer,
-              from: initializer.typeOrThrow, to: type.type!);
+              from: initializer.typeOrThrow, to: type.typeOrThrow);
         }
       }
     }
diff --git a/pkg/analyzer/test/generated/strong_mode_test.dart b/pkg/analyzer/test/generated/strong_mode_test.dart
index 9a88a32..aa67fe2 100644
--- a/pkg/analyzer/test/generated/strong_mode_test.dart
+++ b/pkg/analyzer/test/generated/strong_mode_test.dart
@@ -372,7 +372,7 @@
     var exp = stmt.expression as InstanceCreationExpression;
     ClassElement elementB = AstFinder.getClass(unit, "B").declaredElement!;
     ClassElement elementA = AstFinder.getClass(unit, "A").declaredElement!;
-    expect(exp.constructorName.type.type!.element, elementB);
+    expect(exp.constructorName.type.typeOrThrow.element, elementB);
     _isInstantiationOf(_hasElement(elementB))([
       _isType(elementA.typeParameters[0]
           .instantiate(nullabilitySuffix: NullabilitySuffix.star))
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index e4a7455..9ddda1e 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -13,6 +13,7 @@
 import 'package:analyzer/src/dart/analysis/file_state.dart';
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
 import 'package:analyzer/src/dart/analysis/status.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
 import 'package:analyzer/src/dart/constant/evaluation.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/error/codes.dart';
@@ -1404,7 +1405,7 @@
 
     var f = result.unit!.declarations[0] as FunctionDeclaration;
     assertType(f.declaredElement!.type, 'int Function()');
-    assertType(f.returnType!.type!, 'int');
+    assertType(f.returnType!.typeOrThrow, 'int');
 
     // The same result is also received through the stream.
     await waitForIdleWithoutExceptions();
@@ -3131,7 +3132,7 @@
 
     var f = result.unit!.declarations[0] as FunctionDeclaration;
     assertType(f.declaredElement!.type, 'int Function()');
-    assertType(f.returnType!.type!, 'int');
+    assertType(f.returnType!.typeOrThrow, 'int');
   }
 
   test_results_priorityFirst() async {
diff --git a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
index 89086df..1e0723b 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
@@ -2,6 +2,7 @@
 // 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:analyzer/src/dart/ast/extensions.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/test_utilities/find_element.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -615,7 +616,7 @@
     assertTypeName(typeName, element, 'int* Function(bool*)*');
 
     assertTypeAlias(
-      typeName.type!,
+      typeName.typeOrThrow,
       element: element,
       typeArguments: [],
     );
@@ -639,7 +640,7 @@
     assertTypeName(typeName, element, 'dynamic Function(bool*)*');
 
     assertTypeAlias(
-      typeName.type!,
+      typeName.typeOrThrow,
       element: element,
       typeArguments: ['dynamic'],
     );
@@ -663,7 +664,7 @@
     assertTypeName(typeName, element, 'num* Function(bool*)*');
 
     assertTypeAlias(
-      typeName.type!,
+      typeName.typeOrThrow,
       element: element,
       typeArguments: ['num*'],
     );
@@ -687,7 +688,7 @@
     assertTypeName(typeName, element, 'int* Function(bool*)*');
 
     assertTypeAlias(
-      typeName.type!,
+      typeName.typeOrThrow,
       element: element,
       typeArguments: ['int*'],
     );
diff --git a/pkg/dartdev/lib/src/analysis_server.dart b/pkg/dartdev/lib/src/analysis_server.dart
index 0f616f8..4102f12 100644
--- a/pkg/dartdev/lib/src/analysis_server.dart
+++ b/pkg/dartdev/lib/src/analysis_server.dart
@@ -16,10 +16,10 @@
 
 /// A class to provide an API wrapper around an analysis server process.
 class AnalysisServer {
-  AnalysisServer(this.sdkPath, this.directory);
+  AnalysisServer(this.sdkPath, this.analysisRoot);
 
   final Directory sdkPath;
-  final Directory directory;
+  final FileSystemEntity analysisRoot;
 
   Process _process;
 
@@ -98,8 +98,8 @@
     //
     // The call to absolute.resolveSymbolicLinksSync() canonicalizes the path to
     // be passed to the analysis server.
-    var dirPath = trimEnd(
-      directory.absolute.resolveSymbolicLinksSync(),
+    var analysisRootPath = trimEnd(
+      analysisRoot.absolute.resolveSymbolicLinksSync(),
       path.context.separator,
     );
 
@@ -115,7 +115,7 @@
 
     // ignore: unawaited_futures
     _sendCommand('analysis.setAnalysisRoots', params: <String, dynamic>{
-      'included': [dirPath],
+      'included': [analysisRootPath],
       'excluded': <String>[]
     });
   }
diff --git a/pkg/dartdev/lib/src/commands/analyze.dart b/pkg/dartdev/lib/src/commands/analyze.dart
index cbb4270..4bdcd62 100644
--- a/pkg/dartdev/lib/src/commands/analyze.dart
+++ b/pkg/dartdev/lib/src/commands/analyze.dart
@@ -68,15 +68,26 @@
   @override
   FutureOr<int> run() async {
     if (argResults.rest.length > 1) {
-      usageException('Only one directory is expected.');
+      usageException('Only one directory or file is expected.');
     }
 
-    // find directory from argResults.rest
-    var dir = argResults.rest.isEmpty
-        ? io.Directory.current
-        : io.Directory(argResults.rest.single);
-    if (!dir.existsSync()) {
-      usageException("Directory doesn't exist: ${dir.path}");
+    // find target from argResults.rest
+    io.FileSystemEntity target;
+    io.Directory relativeToDir;
+    if (argResults.rest.isEmpty) {
+      target = io.Directory.current;
+      relativeToDir = target;
+    } else {
+      var targetPath = argResults.rest.single;
+      if (io.Directory(targetPath).existsSync()) {
+        target = io.Directory(targetPath);
+        relativeToDir = target;
+      } else if (io.File(targetPath).existsSync()) {
+        target = io.File(targetPath);
+        relativeToDir = target.parent;
+      } else {
+        usageException("Directory or file doesn't exist: $targetPath");
+      }
     }
 
     final List<AnalysisError> errors = <AnalysisError>[];
@@ -85,11 +96,11 @@
 
     var progress = machineFormat
         ? null
-        : log.progress('Analyzing ${path.basename(dir.path)}');
+        : log.progress('Analyzing ${path.basename(target.path)}');
 
     final AnalysisServer server = AnalysisServer(
       io.Directory(sdk.sdkPath),
-      dir,
+      target,
     );
 
     server.onErrors.listen((FileAnalysisErrors fileErrors) {
@@ -128,7 +139,8 @@
     if (machineFormat) {
       emitMachineFormat(log, errors);
     } else {
-      emitDefaultFormat(log, errors, relativeToDir: dir, verbose: verbose);
+      emitDefaultFormat(log, errors,
+          relativeToDir: relativeToDir, verbose: verbose);
     }
 
     bool hasErrors = false;
@@ -180,7 +192,7 @@
       if (error.isError) {
         severity = ansi.error(severity);
       }
-      var filePath = _relativePath(error.file, relativeToDir?.path);
+      var filePath = _relativePath(error.file, relativeToDir);
       var codeRef = error.code;
       // If we're in verbose mode, write any error urls instead of error codes.
       if (error.url != null && verbose) {
@@ -207,7 +219,7 @@
 
       // Add any context messages as bullet list items.
       for (var message in error.contextMessages) {
-        var contextPath = _relativePath(error.file, relativeToDir?.path);
+        var contextPath = _relativePath(error.file, relativeToDir);
         var messageSentenceFragment = trimEnd(message.message, '.');
 
         log.stdout('$_bodyIndent'
@@ -222,8 +234,9 @@
     log.stdout('$errorCount ${pluralize('issue', errorCount)} found.');
   }
 
-  /// Return a relative path if it is a shorter reference than the given path.
-  static String _relativePath(String givenPath, String fromPath) {
+  /// Return a relative path if it is a shorter reference than the given dir.
+  static String _relativePath(String givenPath, io.Directory fromDir) {
+    String fromPath = fromDir?.absolute?.resolveSymbolicLinksSync();
     String relative = path.relative(givenPath, from: fromPath);
     return relative.length <= givenPath.length ? relative : givenPath;
   }
diff --git a/pkg/dartdev/test/commands/analyze_test.dart b/pkg/dartdev/test/commands/analyze_test.dart
index 6d0709d..8a23601 100644
--- a/pkg/dartdev/test/commands/analyze_test.dart
+++ b/pkg/dartdev/test/commands/analyze_test.dart
@@ -78,7 +78,7 @@
 
     expect(result.exitCode, 64);
     expect(result.stdout, isEmpty);
-    expect(result.stderr, contains('Only one directory is expected.'));
+    expect(result.stderr, contains('Only one directory or file is expected.'));
     expect(result.stderr, contains(_analyzeUsageText));
   });
 
@@ -88,7 +88,8 @@
 
     expect(result.exitCode, 64);
     expect(result.stdout, isEmpty);
-    expect(result.stderr, contains("Directory doesn't exist: /no/such/dir1/"));
+    expect(result.stderr,
+        contains("Directory or file doesn't exist: /no/such/dir1/"));
     expect(result.stderr, contains(_analyzeUsageText));
   });
 
@@ -102,34 +103,59 @@
     expect(result.stdout, contains('No issues found!'));
   });
 
-  test('no errors', () {
-    p = project(mainSrc: 'int get foo => 1;\n');
-    var result = p.runSync(['analyze', p.dirPath]);
+  group('single directory', () {
+    test('no errors', () {
+      p = project(mainSrc: 'int get foo => 1;\n');
+      var result = p.runSync(['analyze', p.dirPath]);
 
-    expect(result.exitCode, 0);
-    expect(result.stderr, isEmpty);
-    expect(result.stdout, contains('No issues found!'));
+      expect(result.exitCode, 0);
+      expect(result.stderr, isEmpty);
+      expect(result.stdout, contains('No issues found!'));
+    });
+
+    test('one error', () {
+      p = project(mainSrc: "int get foo => 'str';\n");
+      var result = p.runSync(['analyze', p.dirPath]);
+
+      expect(result.exitCode, 3);
+      expect(result.stderr, isEmpty);
+      expect(result.stdout, contains('A value of type '));
+      expect(result.stdout, contains('lib/main.dart:1:16 '));
+      expect(result.stdout, contains('return_of_invalid_type'));
+      expect(result.stdout, contains('1 issue found.'));
+    });
+
+    test('two errors', () {
+      p = project(mainSrc: "int get foo => 'str';\nint get bar => 'str';\n");
+      var result = p.runSync(['analyze', p.dirPath]);
+
+      expect(result.exitCode, 3);
+      expect(result.stderr, isEmpty);
+      expect(result.stdout, contains('2 issues found.'));
+    });
   });
 
-  test('one error', () {
-    p = project(mainSrc: "int get foo => 'str';\n");
-    var result = p.runSync(['analyze', p.dirPath]);
+  group('single file', () {
+    test('no errors', () {
+      p = project(mainSrc: 'int get foo => 1;\n');
+      var result = p.runSync(['analyze', p.mainPath]);
 
-    expect(result.exitCode, 3);
-    expect(result.stderr, isEmpty);
-    expect(result.stdout, contains('A value of type '));
-    expect(result.stdout, contains('lib/main.dart:1:16 '));
-    expect(result.stdout, contains('return_of_invalid_type'));
-    expect(result.stdout, contains('1 issue found.'));
-  });
+      expect(result.exitCode, 0);
+      expect(result.stderr, isEmpty);
+      expect(result.stdout, contains('No issues found!'));
+    });
 
-  test('two errors', () {
-    p = project(mainSrc: "int get foo => 'str';\nint get bar => 'str';\n");
-    var result = p.runSync(['analyze', p.dirPath]);
+    test('one error', () {
+      p = project(mainSrc: "int get foo => 'str';\n");
+      var result = p.runSync(['analyze', p.mainPath]);
 
-    expect(result.exitCode, 3);
-    expect(result.stderr, isEmpty);
-    expect(result.stdout, contains('2 issues found.'));
+      expect(result.exitCode, 3);
+      expect(result.stderr, isEmpty);
+      expect(result.stdout, contains('A value of type '));
+      expect(result.stdout, contains('main.dart:1:16 '));
+      expect(result.stdout, contains('return_of_invalid_type'));
+      expect(result.stdout, contains('1 issue found.'));
+    });
   });
 
   test('warning --fatal-warnings', () {
diff --git a/pkg/dartdev/test/utils.dart b/pkg/dartdev/test/utils.dart
index 117aec2..1f3db17 100644
--- a/pkg/dartdev/test/utils.dart
+++ b/pkg/dartdev/test/utils.dart
@@ -38,6 +38,8 @@
 
   String get dirPath => dir.path;
 
+  String get mainPath => path.join(dirPath, relativeFilePath);
+
   final String name;
 
   String get relativeFilePath => 'lib/main.dart';
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 2fba597..1089515 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -543,7 +543,7 @@
       if (node.function != null) {
         node.function = transform(node.function)..parent = node;
       }
-      constantEvaluator.env.updateVariableValue(
+      constantEvaluator.env.addVariableValue(
           node.variable, new IntermediateValue(node.function));
     } else {
       return super.visitFunctionDeclaration(node, removalSentinel);
@@ -559,7 +559,7 @@
     if (node.initializer != null) {
       if (node.isConst) {
         final Constant constant = evaluateWithContext(node, node.initializer);
-        constantEvaluator.env.updateVariableValue(node, constant);
+        constantEvaluator.env.addVariableValue(node, constant);
         node.initializer = makeConstantExpression(constant, node.initializer)
           ..parent = node;
 
@@ -1669,14 +1669,14 @@
             // TODO(johnniwinther): This should call [_evaluateSubexpression].
             : _evaluateNullableSubexpression(parameter.initializer);
         if (value is AbortConstant) return value;
-        env.updateVariableValue(parameter, value);
+        env.addVariableValue(parameter, value);
       }
       for (final VariableDeclaration parameter in function.namedParameters) {
         final Constant value = namedArguments[parameter.name] ??
             // TODO(johnniwinther): This should call [_evaluateSubexpression].
             _evaluateNullableSubexpression(parameter.initializer);
         if (value is AbortConstant) return value;
-        env.updateVariableValue(parameter, value);
+        env.addVariableValue(parameter, value);
       }
 
       // Step 2) Run all initializers (including super calls) with environment
@@ -1697,7 +1697,7 @@
           final VariableDeclaration variable = init.variable;
           Constant constant = _evaluateSubexpression(variable.initializer);
           if (constant is AbortConstant) return constant;
-          env.updateVariableValue(variable, constant);
+          env.addVariableValue(variable, constant);
         } else if (init is SuperInitializer) {
           AbortConstant error = checkConstructorConst(init, constructor);
           if (error != null) return error;
@@ -2412,7 +2412,7 @@
   Constant visitLet(Let node) {
     Constant value = _evaluateSubexpression(node.variable.initializer);
     if (value is AbortConstant) return value;
-    env.updateVariableValue(node.variable, value);
+    env.addVariableValue(node.variable, value);
     return _evaluateSubexpression(node.body);
   }
 
@@ -2445,6 +2445,19 @@
         node, 'Variable get of a non-const variable.');
   }
 
+  @override
+  Constant visitVariableSet(VariableSet node) {
+    if (enableConstFunctions) {
+      final VariableDeclaration variable = node.variable;
+      Constant value = _evaluateSubexpression(node.value);
+      if (value is AbortConstant) return value;
+      return env.updateVariableValue(variable, value) ??
+          createInvalidExpressionConstant(
+              node, 'Variable set of an unknown value.');
+    }
+    return defaultExpression(node);
+  }
+
   /// Computes the constant for [expression] defined in the context of [member].
   ///
   /// This compute the constant as seen in the current evaluation mode even when
@@ -2772,14 +2785,14 @@
             // TODO(johnniwinther): This should call [_evaluateSubexpression].
             : _evaluateNullableSubexpression(parameter.initializer);
         if (value is AbortConstant) return value;
-        env.updateVariableValue(parameter, value);
+        env.addVariableValue(parameter, value);
       }
       for (final VariableDeclaration parameter in function.namedParameters) {
         final Constant value = namedArguments[parameter.name] ??
             // TODO(johnniwinther): This should call [_evaluateSubexpression].
             _evaluateNullableSubexpression(parameter.initializer);
         if (value is AbortConstant) return value;
-        env.updateVariableValue(parameter, value);
+        env.addVariableValue(parameter, value);
       }
       return execute(function.body);
     });
@@ -3295,9 +3308,6 @@
 
   @override
   Constant visitThrow(Throw node) => defaultExpression(node);
-
-  @override
-  Constant visitVariableSet(VariableSet node) => defaultExpression(node);
 }
 
 class StatementConstantEvaluator extends StatementVisitor<ExecutionStatus> {
@@ -3330,6 +3340,13 @@
   }
 
   @override
+  ExecutionStatus visitExpressionStatement(ExpressionStatement node) {
+    Constant value = evaluate(node.expression);
+    if (value is AbortConstant) return new ReturnStatus(value);
+    return const ProceedStatus();
+  }
+
+  @override
   ExecutionStatus visitReturnStatement(ReturnStatement node) =>
       new ReturnStatus(evaluate(node.expression));
 
@@ -3337,7 +3354,7 @@
   ExecutionStatus visitVariableDeclaration(VariableDeclaration node) {
     Constant value = evaluate(node.initializer);
     if (value is AbortConstant) return new ReturnStatus(value);
-    exprEvaluator.env.updateVariableValue(node, value);
+    exprEvaluator.env.addVariableValue(node, value);
     return const ProceedStatus();
   }
 }
@@ -3426,17 +3443,21 @@
     _typeVariables[parameter] = value;
   }
 
-  void updateVariableValue(VariableDeclaration variable, Constant value) {
-    if (!_variables.containsKey(variable)) {
-      _variables[variable] = new EvaluationReference(value);
-    } else {
-      _variables[variable].value = value;
-    }
+  void addVariableValue(VariableDeclaration variable, Constant value) {
+    _variables[variable] = new EvaluationReference(value);
     if (value is UnevaluatedConstant) {
       _unreadUnevaluatedVariables.add(variable);
     }
   }
 
+  Constant updateVariableValue(VariableDeclaration variable, Constant value) {
+    if (_variables.containsKey(variable)) {
+      _variables[variable].value = value;
+      return value;
+    }
+    return _parent?.updateVariableValue(variable, value);
+  }
+
   Constant lookupVariable(VariableDeclaration variable) {
     Constant value = _variables[variable]?.value;
     if (value is UnevaluatedConstant) {
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart
new file mode 100644
index 0000000..3a3fc6f
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart
@@ -0,0 +1,42 @@
+// 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.
+
+// Tests variable assignments for const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = varAssignmentTest(1);
+int varAssignmentTest(int a) {
+  int x = 4;
+  {
+    x = 3;
+  }
+  return x;
+}
+
+int function() {
+  int varAssignmentTest2() {
+    int x = 2;
+    x += 1;
+    return x;
+  }
+
+  const var2 = varAssignmentTest2();
+  return var2;
+}
+
+const var3 = varAssignmentTest3(1);
+int varAssignmentTest3(int a) {
+  int x = 4;
+  x = a + 1;
+  return x;
+}
+
+void main() {
+  Expect.equals(var1, 3);
+  Expect.equals(function(), 3);
+  Expect.equals(var3, 2);
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.expect
new file mode 100644
index 0000000..337c656
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.expect
@@ -0,0 +1,39 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var3 = #C2;
+static method varAssignmentTest(core::int a) → core::int {
+  core::int x = 4;
+  {
+    x = 3;
+  }
+  return x;
+}
+static method function() → core::int {
+  function varAssignmentTest2() → core::int {
+    core::int x = 2;
+    x = x.{core::num::+}(1);
+    return x;
+  }
+  return #C1;
+}
+static method varAssignmentTest3(core::int a) → core::int {
+  core::int x = 4;
+  x = a.{core::num::+}(1);
+  return x;
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 3);
+  exp::Expect::equals(self::function(), 3);
+  exp::Expect::equals(#C2, 2);
+}
+
+constants  {
+  #C1 = 3
+  #C2 = 2
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.transformed.expect
new file mode 100644
index 0000000..337c656
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.transformed.expect
@@ -0,0 +1,39 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var3 = #C2;
+static method varAssignmentTest(core::int a) → core::int {
+  core::int x = 4;
+  {
+    x = 3;
+  }
+  return x;
+}
+static method function() → core::int {
+  function varAssignmentTest2() → core::int {
+    core::int x = 2;
+    x = x.{core::num::+}(1);
+    return x;
+  }
+  return #C1;
+}
+static method varAssignmentTest3(core::int a) → core::int {
+  core::int x = 4;
+  x = a.{core::num::+}(1);
+  return x;
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 3);
+  exp::Expect::equals(self::function(), 3);
+  exp::Expect::equals(#C2, 2);
+}
+
+constants  {
+  #C1 = 3
+  #C2 = 2
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline.expect
new file mode 100644
index 0000000..801ab46
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline.expect
@@ -0,0 +1,8 @@
+import "package:expect/expect.dart";
+
+const var1 = varAssignmentTest(1);
+int varAssignmentTest(int a) {}
+int function() {}
+const var3 = varAssignmentTest3(1);
+int varAssignmentTest3(int a) {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..5f57ea4
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline_modelled.expect
@@ -0,0 +1,8 @@
+import "package:expect/expect.dart";
+
+const var1 = varAssignmentTest(1);
+const var3 = varAssignmentTest3(1);
+int function() {}
+int varAssignmentTest(int a) {}
+int varAssignmentTest3(int a) {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.expect
new file mode 100644
index 0000000..337c656
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.expect
@@ -0,0 +1,39 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var3 = #C2;
+static method varAssignmentTest(core::int a) → core::int {
+  core::int x = 4;
+  {
+    x = 3;
+  }
+  return x;
+}
+static method function() → core::int {
+  function varAssignmentTest2() → core::int {
+    core::int x = 2;
+    x = x.{core::num::+}(1);
+    return x;
+  }
+  return #C1;
+}
+static method varAssignmentTest3(core::int a) → core::int {
+  core::int x = 4;
+  x = a.{core::num::+}(1);
+  return x;
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 3);
+  exp::Expect::equals(self::function(), 3);
+  exp::Expect::equals(#C2, 2);
+}
+
+constants  {
+  #C1 = 3
+  #C2 = 2
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.outline.expect
new file mode 100644
index 0000000..71847ee
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.outline.expect
@@ -0,0 +1,16 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = self::varAssignmentTest(1);
+static const field core::int var3 = self::varAssignmentTest3(1);
+static method varAssignmentTest(core::int a) → core::int
+  ;
+static method function() → core::int
+  ;
+static method varAssignmentTest3(core::int a) → core::int
+  ;
+static method main() → void
+  ;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.transformed.expect
new file mode 100644
index 0000000..337c656
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.transformed.expect
@@ -0,0 +1,39 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var3 = #C2;
+static method varAssignmentTest(core::int a) → core::int {
+  core::int x = 4;
+  {
+    x = 3;
+  }
+  return x;
+}
+static method function() → core::int {
+  function varAssignmentTest2() → core::int {
+    core::int x = 2;
+    x = x.{core::num::+}(1);
+    return x;
+  }
+  return #C1;
+}
+static method varAssignmentTest3(core::int a) → core::int {
+  core::int x = 4;
+  x = a.{core::num::+}(1);
+  return x;
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 3);
+  exp::Expect::equals(self::function(), 3);
+  exp::Expect::equals(#C2, 2);
+}
+
+constants  {
+  #C1 = 3
+  #C2 = 2
+}
diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart
index 139e557..2c45796c 100644
--- a/pkg/frontend_server/lib/frontend_server.dart
+++ b/pkg/frontend_server/lib/frontend_server.dart
@@ -10,7 +10,11 @@
 
 import 'package:args/args.dart';
 import 'package:dev_compiler/dev_compiler.dart'
-    show DevCompilerTarget, ExpressionCompiler, parseModuleFormat;
+    show
+        DevCompilerTarget,
+        ExpressionCompiler,
+        parseModuleFormat,
+        ProgramCompiler;
 
 // front_end/src imports below that require lint `ignore_for_file`
 // are a temporary state of things until frontend team builds better api
@@ -631,7 +635,7 @@
     final sourceMapsFileSink = sourceMapsFile.openWrite();
     final metadataFileSink =
         emitDebugMetadata ? metadataFile.openWrite() : null;
-    await _bundler.compile(
+    final kernel2JsCompilers = await _bundler.compile(
         results.classHierarchy,
         results.coreTypes,
         results.loadedLibraries,
@@ -639,6 +643,7 @@
         manifestFileSink,
         sourceMapsFileSink,
         metadataFileSink);
+    cachedProgramCompilers.addAll(kernel2JsCompilers);
     await Future.wait([
       sourceFileSink.close(),
       manifestFileSink.close(),
@@ -806,6 +811,12 @@
     }
   }
 
+  /// Program compilers per module.
+  ///
+  /// Produced suring initial compilation of the module to JavaScript,
+  /// cached to be used for expression compilation in [compileExpressionToJs].
+  final Map<String, ProgramCompiler> cachedProgramCompilers = {};
+
   @override
   Future<Null> compileExpressionToJs(
       String libraryUri,
@@ -822,7 +833,7 @@
       reportError('JavaScript bundler is null');
       return;
     }
-    if (!_bundler.compilers.containsKey(moduleName)) {
+    if (!cachedProgramCompilers.containsKey(moduleName)) {
       reportError('Cannot find kernel2js compiler for $moduleName.');
       return;
     }
@@ -833,13 +844,13 @@
     _processedOptions.ticker
         .logMs('Compiling expression to JavaScript in $moduleName');
 
-    var kernel2jsCompiler = _bundler.compilers[moduleName];
+    final kernel2jsCompiler = cachedProgramCompilers[moduleName];
     Component component = _generator.lastKnownGoodComponent;
     component.computeCanonicalNames();
 
     _processedOptions.ticker.logMs('Computed component');
 
-    var expressionCompiler = new ExpressionCompiler(
+    final expressionCompiler = new ExpressionCompiler(
       _compilerOptions,
       parseModuleFormat(_options['dartdevc-module-format'] as String),
       errors,
@@ -848,10 +859,10 @@
       component,
     );
 
-    var procedure = await expressionCompiler.compileExpressionToJs(
+    final procedure = await expressionCompiler.compileExpressionToJs(
         libraryUri, line, column, jsFrameValues, expression);
 
-    var result = errors.length > 0 ? errors[0] : procedure;
+    final result = errors.length > 0 ? errors[0] : procedure;
 
     // TODO(annagrin): kernelBinaryFilename is too specific
     // rename to _outputFileName?
diff --git a/pkg/frontend_server/lib/src/javascript_bundle.dart b/pkg/frontend_server/lib/src/javascript_bundle.dart
index 346b4d8..9829c57 100644
--- a/pkg/frontend_server/lib/src/javascript_bundle.dart
+++ b/pkg/frontend_server/lib/src/javascript_bundle.dart
@@ -29,8 +29,7 @@
       this.emitDebugMetadata = false,
       this.soundNullSafety = false,
       String moduleFormat})
-      : compilers = <String, ProgramCompiler>{},
-        _moduleFormat = parseModuleFormat(moduleFormat ?? 'amd') {
+      : _moduleFormat = parseModuleFormat(moduleFormat ?? 'amd') {
     _summaries = <Component>[];
     _summaryUris = <Uri>[];
     _moduleImportForSummary = <Uri, String>{};
@@ -64,7 +63,6 @@
   final PackageConfig _packageConfig;
   final bool useDebuggerModuleNames;
   final bool emitDebugMetadata;
-  final Map<String, ProgramCompiler> compilers;
   final ModuleFormat _moduleFormat;
   final bool soundNullSafety;
 
@@ -75,7 +73,7 @@
   Map<Uri, Component> _uriToComponent;
 
   /// Compile each component into a single JavaScript module.
-  Future<void> compile(
+  Future<Map<String, ProgramCompiler>> compile(
       ClassHierarchy classHierarchy,
       CoreTypes coreTypes,
       Set<Library> loadedLibraries,
@@ -88,6 +86,7 @@
     var metadataOffset = 0;
     final manifest = <String, Map<String, List<int>>>{};
     final Set<Uri> visited = <Uri>{};
+    final Map<String, ProgramCompiler> kernel2JsCompilers = {};
 
     final importToSummary = Map<Library, Component>.identity();
     final summaryToModule = Map<Component, String>.identity();
@@ -150,9 +149,8 @@
       // so it can map dart symbols to js symbols
       // [issue 40273](https://github.com/dart-lang/sdk/issues/40273)
 
-      // program compiler is used by ExpressionCompiler to evaluate expressions
-      // on demand
-      compilers[moduleName] = compiler;
+      // Save program compiler to reuse for expression evaluation.
+      kernel2JsCompilers[moduleName] = compiler;
 
       final moduleUrl = urlForComponentUri(moduleUri);
       String sourceMapBase;
@@ -201,6 +199,8 @@
       };
     }
     manifestSink.add(utf8.encode(json.encode(manifest)));
+
+    return kernel2JsCompilers;
   }
 }
 
diff --git a/pkg/frontend_server/test/src/javascript_bundle_test.dart b/pkg/frontend_server/test/src/javascript_bundle_test.dart
index 26a7c21..02cd813 100644
--- a/pkg/frontend_server/test/src/javascript_bundle_test.dart
+++ b/pkg/frontend_server/test/src/javascript_bundle_test.dart
@@ -106,8 +106,14 @@
     final metadataSink = _MemorySink();
     final coreTypes = CoreTypes(testComponent);
 
-    await javaScriptBundler.compile(ClassHierarchy(testComponent, coreTypes),
-        coreTypes, {}, codeSink, manifestSink, sourcemapSink, metadataSink);
+    final compilers = await javaScriptBundler.compile(
+        ClassHierarchy(testComponent, coreTypes),
+        coreTypes,
+        {},
+        codeSink,
+        manifestSink,
+        sourcemapSink,
+        metadataSink);
 
     final Map manifest = json.decode(utf8.decode(manifestSink.buffer));
     final String code = utf8.decode(codeSink.buffer);
@@ -122,6 +128,9 @@
 
     // verify source map url is correct.
     expect(code, contains('sourceMappingURL=c.dart.lib.js.map'));
+
+    // verify program compilers are created.
+    expect(compilers.keys, equals([urlForComponentUri(library.importUri)]));
   });
 
   test('converts package: uris into /packages/ uris', () async {
diff --git a/pkg/test_runner/lib/src/options.dart b/pkg/test_runner/lib/src/options.dart
index af07581a..1d2b814 100644
--- a/pkg/test_runner/lib/src/options.dart
+++ b/pkg/test_runner/lib/src/options.dart
@@ -833,7 +833,7 @@
       // Expand architectures.
       var architectures = data["arch"] as String;
       if (architectures == "all") {
-        architectures = "ia32,x64,simarm,simarm64";
+        architectures = "ia32,x64,x64c,simarm,simarm64,simarm64c";
       }
 
       for (var architectureName in architectures.split(",")) {
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index 4413911..00b1716 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -2703,7 +2703,7 @@
   FieldRef? decl;
 
   /// [value] can be one of [InstanceRef] or [Sentinel].
-  dynamic? value;
+  dynamic value;
 
   BoundField({
     required this.decl,
@@ -2747,7 +2747,7 @@
   String? name;
 
   /// [value] can be one of [InstanceRef], [TypeArgumentsRef] or [Sentinel].
-  dynamic? value;
+  dynamic value;
 
   /// The token position where this variable was declared.
   int? declarationTokenPos;
@@ -2822,7 +2822,7 @@
   /// a breakpoint is not resolved.
   ///
   /// [location] can be one of [SourceLocation] or [UnresolvedSourceLocation].
-  dynamic? location;
+  dynamic location;
 
   Breakpoint({
     required this.breakpointNumber,
@@ -3331,7 +3331,7 @@
       json == null ? null : ContextElement._fromJson(json);
 
   /// [value] can be one of [InstanceRef] or [Sentinel].
-  dynamic? value;
+  dynamic value;
 
   ContextElement({
     required this.value,
@@ -4052,7 +4052,7 @@
   ///
   /// [staticValue] can be one of [InstanceRef] or [Sentinel].
   @optional
-  dynamic? staticValue;
+  dynamic staticValue;
 
   /// The location of this field in the source code.
   @optional
@@ -4082,7 +4082,7 @@
     isFinal = json['final'] ?? false;
     isStatic = json['static'] ?? false;
     staticValue = createServiceObject(
-        json['staticValue'], const ['InstanceRef', 'Sentinel']) as dynamic?;
+        json['staticValue'], const ['InstanceRef', 'Sentinel']) as dynamic;
     location = createServiceObject(json['location'], const ['SourceLocation'])
         as SourceLocation?;
   }
@@ -4276,7 +4276,7 @@
   /// The owner of this function, which can be a Library, Class, or a Function.
   ///
   /// [owner] can be one of [LibraryRef], [ClassRef] or [FuncRef].
-  dynamic? owner;
+  dynamic owner;
 
   /// Is this function static?
   bool? isStatic;
@@ -4338,7 +4338,7 @@
   /// The owner of this function, which can be a Library, Class, or a Function.
   ///
   /// [owner] can be one of [LibraryRef], [ClassRef] or [FuncRef].
-  dynamic? owner;
+  dynamic owner;
 
   /// Is this function static?
   bool? isStatic;
@@ -5788,10 +5788,10 @@
       json == null ? null : MapAssociation._fromJson(json);
 
   /// [key] can be one of [InstanceRef] or [Sentinel].
-  dynamic? key;
+  dynamic key;
 
   /// [value] can be one of [InstanceRef] or [Sentinel].
-  dynamic? value;
+  dynamic value;
 
   MapAssociation({
     required this.key,
@@ -6241,7 +6241,7 @@
   String? resolvedUrl;
 
   /// The function captured during profiling.
-  dynamic? function;
+  dynamic function;
 
   ProfileFunction({
     required this.kind,
diff --git a/pkg/vm_service/tool/dart/generate_dart.dart b/pkg/vm_service/tool/dart/generate_dart.dart
index 774f271..b57d248 100644
--- a/pkg/vm_service/tool/dart/generate_dart.dart
+++ b/pkg/vm_service/tool/dart/generate_dart.dart
@@ -1200,7 +1200,7 @@
     gen.write('Future<${returnType.name}> ${name}(');
     bool startedOptional = false;
     gen.write(args.map((MethodArg arg) {
-      String? typeName;
+      String typeName;
       if (api.isEnumName(arg.type.name)) {
         if (arg.type.isArray) {
           typeName = typeName = '/*${arg.type}*/ List<String>';
@@ -1268,7 +1268,7 @@
     }
   }
 
-  String? get name {
+  String get name {
     if (types.isEmpty) return '';
     if (types.length == 1) return types.first.ref;
     if (isReturnType) return 'Response';
@@ -1286,7 +1286,7 @@
 
   bool get isArray => types.length == 1 && types.first.isArray;
 
-  void generate(DartGenerator gen) => gen.write(name!);
+  void generate(DartGenerator gen) => gen.write(name);
 }
 
 class TypeRef {
@@ -1296,7 +1296,7 @@
 
   TypeRef(this.name);
 
-  String? get ref {
+  String get ref {
     if (arrayDepth == 2) {
       return 'List<List<${name}>>';
     } else if (arrayDepth == 1) {
@@ -1304,7 +1304,7 @@
     } else if (genericTypes != null) {
       return '$name<${genericTypes!.join(', ')}>';
     } else {
-      return name!.startsWith('_') ? name!.substring(1) : name;
+      return name!.startsWith('_') ? name!.substring(1) : name!;
     }
   }
 
@@ -1340,7 +1340,7 @@
           name == 'double' ||
           name == 'ByteData');
 
-  String toString() => ref!;
+  String toString() => ref;
 }
 
 class MethodArg extends Member {
@@ -1639,9 +1639,11 @@
         }
       } else {
         String typesList = _typeRefListToString(field.type.types);
+        String nullable =
+            field.optional && field.type.name != 'dynamic' ? '?' : '';
         gen.writeln("${field.generatableName} = "
             "createServiceObject(json['${field.name}']${field.optional ? '' : '!'}, "
-            "$typesList) as ${field.type.name}${field.optional ? '?' : ''};");
+            "$typesList) as ${field.type.name}$nullable;");
       }
     });
     if (fields.isNotEmpty) {
@@ -1851,8 +1853,8 @@
           gen.writeln('}');
         } else {
           String assertMethodName = 'assert' +
-              type.name!.substring(0, 1).toUpperCase() +
-              type.name!.substring(1);
+              type.name.substring(0, 1).toUpperCase() +
+              type.name.substring(1);
           gen.writeln('$assertMethodName(obj.${field.generatableName}!);');
         }
       }
@@ -1943,7 +1945,9 @@
     {
       String? typeName =
           api.isEnumName(type.name) ? '/*${type.name}*/ String' : type.name;
-      typeName = '$typeName?';
+      if (typeName != 'dynamic') {
+        typeName = '$typeName?';
+      }
       gen.writeStatement('${typeName} ${generatableName};');
       if (parent.fields.any((field) => field.hasDocs)) gen.writeln();
     }
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index b303c7d..d390576 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -572,6 +572,8 @@
       for (auto gap : gaps_) {
         s->WriteUnsigned(gap);
       }
+      target_memory_size_ +=
+          compiler::target::Array::InstanceSize(table_length_);
     }
   }
 
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
index 3176eea..059575b 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
@@ -94,28 +94,16 @@
 
 void AsmIntrinsifier::Integer_add(Assembler* assembler, Label* normal_ir_body) {
   TestBothArgumentsSmis(assembler, normal_ir_body);  // Checks two smis.
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ adds(R0, R0, Operand(R1));  // Add.
+  __ adds(R0, R0, Operand(R1), kObjectBytes);        // Add.
   __ b(normal_ir_body, VS);  // Fall-through on overflow.
-#else
-  __ addsw(R0, R0, Operand(R1));  // Add (32-bit).
-  __ b(normal_ir_body, VS);       // Fall-through on overflow.
-  __ sxtw(R0, R0);                // Sign extend.
-#endif
   __ ret();
   __ Bind(normal_ir_body);
 }
 
 void AsmIntrinsifier::Integer_sub(Assembler* assembler, Label* normal_ir_body) {
   TestBothArgumentsSmis(assembler, normal_ir_body);
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ subs(R0, R1, Operand(R0));  // Subtract.
-  __ b(normal_ir_body, VS);      // Fall-through on overflow.
-#else
-  __ subsw(R0, R1, Operand(R0));  // Subtract (32-bit).
-  __ b(normal_ir_body, VS);       // Fall-through on overflow.
-  __ sxtw(R0, R0);                // Sign extend.
-#endif
+  __ subs(R0, R1, Operand(R0), compiler::kObjectBytes);  // Subtract.
+  __ b(normal_ir_body, VS);  // Fall-through on overflow.
   __ ret();
   __ Bind(normal_ir_body);
 }
@@ -160,16 +148,16 @@
   ASSERT(left == result);
 
   // Check for quick zero results.
-  __ CompareRegisters(left, ZR);
+  __ CompareObjectRegisters(left, ZR);
   __ b(&return_zero, EQ);
-  __ CompareRegisters(left, right);
+  __ CompareObjectRegisters(left, right);
   __ b(&return_zero, EQ);
 
   // Check if result should be left.
-  __ CompareRegisters(left, ZR);
+  __ CompareObjectRegisters(left, ZR);
   __ b(&modulo, LT);
   // left is positive.
-  __ CompareRegisters(left, right);
+  __ CompareObjectRegisters(left, right);
   // left is less than right, result is left.
   __ b(&modulo, GT);
   __ mov(R0, left);
@@ -184,8 +172,9 @@
   __ SmiUntag(left);
   __ SmiUntag(right);
 
-  __ sdiv(tmp, left, right);
-  __ msub(result, right, tmp, left);  // result <- left - right * tmp
+  __ sdiv(tmp, left, right, kObjectBytes);
+  __ msub(result, right, tmp, left,
+          kObjectBytes);  // result <- left - right * tmp
 }
 
 // Implementation:
@@ -207,21 +196,21 @@
   // R1: Tagged left (dividend).
   // R0: Tagged right (divisor).
   // Check if modulo by zero -> exception thrown in main function.
-  __ CompareRegisters(R0, ZR);
+  __ CompareObjectRegisters(R0, ZR);
   __ b(normal_ir_body, EQ);
   EmitRemainderOperation(assembler);
   // Untagged right in R0. Untagged remainder result in R1.
 
-  __ CompareRegisters(R1, ZR);
+  __ CompareObjectRegisters(R1, ZR);
   __ b(&neg_remainder, LT);
   __ SmiTag(R0, R1);  // Tag and move result to R0.
   __ ret();
 
   __ Bind(&neg_remainder);
   // Result is negative, adjust it.
-  __ CompareRegisters(R0, ZR);
-  __ sub(TMP, R1, Operand(R0));
-  __ add(TMP2, R1, Operand(R0));
+  __ CompareObjectRegisters(R0, ZR);
+  __ sub(TMP, R1, Operand(R0), kObjectBytes);
+  __ add(TMP2, R1, Operand(R0), kObjectBytes);
   __ csel(R0, TMP2, TMP, GE);
   __ SmiTag(R0);
   __ ret();
@@ -234,20 +223,20 @@
   // Check to see if we have integer division
 
   TestBothArgumentsSmis(assembler, normal_ir_body);
-  __ CompareRegisters(R0, ZR);
+  __ CompareObjectRegisters(R0, ZR);
   __ b(normal_ir_body, EQ);  // If b is 0, fall through.
 
   __ SmiUntag(R0);
   __ SmiUntag(R1);
 
-  __ sdiv(R0, R1, R0);
+  __ sdiv(R0, R1, R0, kObjectBytes);
 
   // Check the corner case of dividing the 'MIN_SMI' with -1, in which case we
   // cannot tag the result.
 #if !defined(DART_COMPRESSED_POINTERS)
   __ CompareImmediate(R0, 0x4000000000000000);
 #else
-  __ CompareImmediate(R0, 0x40000000);
+  __ CompareImmediate(R0, 0x40000000, compiler::kFourBytes);
 #endif
   __ b(normal_ir_body, EQ);
   __ SmiTag(R0);  // Not equal. Okay to tag and return.
@@ -259,14 +248,8 @@
                                      Label* normal_ir_body) {
   __ ldr(R0, Address(SP, +0 * target::kWordSize));  // Grab first argument.
   __ BranchIfNotSmi(R0, normal_ir_body);
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ negs(R0, R0);
+  __ negs(R0, R0, kObjectBytes);
   __ b(normal_ir_body, VS);
-#else
-  __ negsw(R0, R0);
-  __ b(normal_ir_body, VS);
-  __ sxtw(R0, R0);
-#endif
   __ ret();
   __ Bind(normal_ir_body);
 }
@@ -304,25 +287,20 @@
   const Register result = R0;
 
   TestBothArgumentsSmis(assembler, normal_ir_body);
-  __ CompareImmediate(right, target::ToRawSmi(target::kSmiBits));
+  __ CompareImmediate(right, target::ToRawSmi(target::kSmiBits),
+                      compiler::kObjectBytes);
   __ b(normal_ir_body, CS);
 
   // Left is not a constant.
   // Check if count too large for handling it inlined.
   __ SmiUntag(TMP, right);  // SmiUntag right into TMP.
   // Overflow test (preserve left, right, and TMP);
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ lslv(temp, left, TMP);
-  __ asrv(TMP2, temp, TMP);
-  __ CompareRegisters(left, TMP2);
-#else
-  __ lslvw(temp, left, TMP);
-  __ asrvw(TMP2, temp, TMP);
-  __ cmpw(left, Operand(TMP2));
-#endif
+  __ lslv(temp, left, TMP, kObjectBytes);
+  __ asrv(TMP2, temp, TMP, kObjectBytes);
+  __ cmp(left, Operand(TMP2), kObjectBytes);
   __ b(normal_ir_body, NE);  // Overflow.
   // Shift for result now we know there is no overflow.
-  __ lslv(result, left, TMP);
+  __ lslv(result, left, TMP, kObjectBytes);
   __ ret();
   __ Bind(normal_ir_body);
 }
@@ -333,7 +311,7 @@
   Label true_label;
   TestBothArgumentsSmis(assembler, normal_ir_body);
   // R0 contains the right argument, R1 the left.
-  __ CompareRegisters(R1, R0);
+  __ CompareObjectRegisters(R1, R0);
   __ LoadObject(R0, CastHandle<Object>(FalseObject()));
   __ LoadObject(TMP, CastHandle<Object>(TrueObject()));
   __ csel(R0, TMP, R0, true_condition);
@@ -369,7 +347,7 @@
   // For integer receiver '===' check first.
   __ ldr(R0, Address(SP, 0 * target::kWordSize));
   __ ldr(R1, Address(SP, 1 * target::kWordSize));
-  __ cmp(R0, Operand(R1));
+  __ CompareObjectRegisters(R0, R1);
   __ b(&true_label, EQ);
 
   __ orr(R2, R0, Operand(R1));
@@ -424,15 +402,19 @@
 
   // Fall through if shift amount is negative.
   __ SmiUntag(R0);
-  __ CompareRegisters(R0, ZR);
+  __ CompareObjectRegisters(R0, ZR);
   __ b(normal_ir_body, LT);
 
-  // If shift amount is bigger than 63, set to 63.
+  // If shift amount is bigger than 63/31, set to 63/31.
+#if !defined(DART_COMPRESSED_POINTERS)
   __ LoadImmediate(TMP, 0x3F);
+#else
+  __ LoadImmediate(TMP, 0x1F);
+#endif
   __ CompareRegisters(R0, TMP);
   __ csel(R0, TMP, R0, GT);
   __ SmiUntag(R1);
-  __ asrv(R0, R1, R0);
+  __ asrv(R0, R1, R0, kObjectBytes);
   __ SmiTag(R0);
   __ ret();
   __ Bind(normal_ir_body);
@@ -451,9 +433,15 @@
   __ ldr(R0, Address(SP, 0 * target::kWordSize));
   __ SmiUntag(R0);
   // XOR with sign bit to complement bits if value is negative.
+#if !defined(DART_COMPRESSED_POINTERS)
   __ eor(R0, R0, Operand(R0, ASR, 63));
   __ clz(R0, R0);
   __ LoadImmediate(R1, 64);
+#else
+  __ eorw(R0, R0, Operand(R0, ASR, 31));
+  __ clzw(R0, R0);
+  __ LoadImmediate(R1, 32);
+#endif
   __ sub(R0, R1, Operand(R0));
   __ SmiTag(R0);
   __ ret();
@@ -1349,14 +1337,14 @@
   __ fcmpd(V0, V0);
   __ b(normal_ir_body, VS);
 
-#if !defined(DART_COMPRESSED_POINTERS)
   __ fcvtzdsx(R0, V0);
+
+#if !defined(DART_COMPRESSED_POINTERS)
   // Overflow is signaled with minint.
   // Check for overflow and that it fits into Smi.
   __ CompareImmediate(R0, 0xC000000000000000);
   __ b(normal_ir_body, MI);
 #else
-  __ fcvtzdsw(R0, V0);
   // Overflow is signaled with minint.
   // Check for overflow and that it fits into Smi.
   __ AsrImmediate(TMP, R0, 30);
@@ -1397,14 +1385,8 @@
   // overflow is signalled by fcvt through clamping R0 to either
   // INT64_MAX or INT64_MIN (saturation).
   ASSERT(kSmiTag == 0 && kSmiTagShift == 1);
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ adds(R0, R0, Operand(R0));
+  __ adds(R0, R0, Operand(R0), kObjectBytes);
   __ b(normal_ir_body, VS);
-#else
-  __ addsw(R0, R0, Operand(R0));
-  __ b(normal_ir_body, VS);
-  __ sxtw(R0, R0);  // Sign extend.
-#endif
 
   // Compare the two double values. If they are equal, we return the
   // Smi tagged result immediately as the hash code.
@@ -1476,7 +1458,7 @@
                                    Label* normal_ir_body) {
   __ ldr(R0, Address(SP, 0 * target::kWordSize));
   __ ldr(R1, Address(SP, 1 * target::kWordSize));
-  __ cmp(R0, Operand(R1));
+  __ CompareObjectRegisters(R0, R1);
   __ LoadObject(R0, CastHandle<Object>(FalseObject()));
   __ LoadObject(TMP, CastHandle<Object>(TrueObject()));
   __ csel(R0, TMP, R0, EQ);
@@ -1704,7 +1686,7 @@
   Label equal, not_equal, equiv_cids, check_legacy;
 
   __ ldp(R1, R2, Address(SP, 0 * target::kWordSize, Address::PairOffset));
-  __ cmp(R1, Operand(R2));
+  __ CompareObjectRegisters(R1, R2);
   __ b(&equal, EQ);
 
   // R1 might not be a Type object, so check that first (R2 should be though,
@@ -1766,7 +1748,7 @@
 void AsmIntrinsifier::FunctionType_equality(Assembler* assembler,
                                             Label* normal_ir_body) {
   __ ldp(R1, R2, Address(SP, 0 * target::kWordSize, Address::PairOffset));
-  __ cmp(R1, Operand(R2));
+  __ CompareObjectRegisters(R1, R2);
   __ b(normal_ir_body, NE);
 
   __ LoadObject(R0, CastHandle<Object>(TrueObject()));
@@ -1955,7 +1937,7 @@
                                         Label* normal_ir_body) {
   __ ldr(R0, Address(SP, 0 * target::kWordSize));
   __ ldr(R0, FieldAddress(R0, target::String::length_offset()));
-  __ cmp(R0, Operand(target::ToRawSmi(0)));
+  __ cmp(R0, Operand(target::ToRawSmi(0)), kObjectBytes);
   __ LoadObject(R0, CastHandle<Object>(TrueObject()));
   __ LoadObject(TMP, CastHandle<Object>(FalseObject()));
   __ csel(R0, TMP, R0, NE);
@@ -2234,7 +2216,7 @@
   __ ldr(R1, Address(SP, 0 * target::kWordSize));  // Other.
 
   // Are identical?
-  __ cmp(R0, Operand(R1));
+  __ CompareObjectRegisters(R0, R1);
   __ b(&is_true, EQ);
 
   // Is other OneByteString?
@@ -2245,7 +2227,7 @@
   // Have same length?
   __ ldr(R2, FieldAddress(R0, target::String::length_offset()));
   __ ldr(R3, FieldAddress(R1, target::String::length_offset()));
-  __ cmp(R2, Operand(R3));
+  __ CompareObjectRegisters(R2, R3);
   __ b(&is_false, NE);
 
   // Check contents, no fall-through possible.
diff --git a/runtime/vm/compiler/asm_intrinsifier_x64.cc b/runtime/vm/compiler/asm_intrinsifier_x64.cc
index 3254745..a2b59ef 100644
--- a/runtime/vm/compiler/asm_intrinsifier_x64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_x64.cc
@@ -93,14 +93,8 @@
 void AsmIntrinsifier::Integer_add(Assembler* assembler, Label* normal_ir_body) {
   TestBothArgumentsSmis(assembler, normal_ir_body);
   // RAX contains right argument.
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ addq(RAX, Address(RSP, +2 * target::kWordSize));
+  __ OBJ(add)(RAX, Address(RSP, +2 * target::kWordSize));
   __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-#else
-  __ addl(RAX, Address(RSP, +2 * target::kWordSize));
-  __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-  __ movsxd(RAX, RAX);
-#endif
   // Result is in RAX.
   __ ret();
   __ Bind(normal_ir_body);
@@ -111,14 +105,8 @@
   // RAX contains right argument, which is the actual subtrahend of subtraction.
   __ movq(RCX, RAX);
   __ movq(RAX, Address(RSP, +2 * target::kWordSize));
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ subq(RAX, RCX);
+  __ OBJ(sub)(RAX, RCX);
   __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-#else
-  __ subl(RAX, RCX);
-  __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-  __ movsxd(RAX, RAX);
-#endif
   // Result is in RAX.
   __ ret();
   __ Bind(normal_ir_body);
@@ -129,14 +117,8 @@
   // RAX is the right argument.
   ASSERT(kSmiTag == 0);  // Adjust code below if not the case.
   __ SmiUntag(RAX);
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ imulq(RAX, Address(RSP, +2 * target::kWordSize));
+  __ OBJ(imul)(RAX, Address(RSP, +2 * target::kWordSize));
   __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-#else
-  __ imull(RAX, Address(RSP, +2 * target::kWordSize));
-  __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-  __ movsxd(RAX, RAX);
-#endif
   // Result is in RAX.
   __ ret();
   __ Bind(normal_ir_body);
@@ -156,16 +138,16 @@
 static void EmitRemainderOperation(Assembler* assembler) {
   Label return_zero, try_modulo, not_32bit, done;
   // Check for quick zero results.
-  __ cmpq(RAX, Immediate(0));
+  __ OBJ(cmp)(RAX, Immediate(0));
   __ j(EQUAL, &return_zero, Assembler::kNearJump);
-  __ cmpq(RAX, RCX);
+  __ OBJ(cmp)(RAX, RCX);
   __ j(EQUAL, &return_zero, Assembler::kNearJump);
 
   // Check if result equals left.
-  __ cmpq(RAX, Immediate(0));
+  __ OBJ(cmp)(RAX, Immediate(0));
   __ j(LESS, &try_modulo, Assembler::kNearJump);
   // left is positive.
-  __ cmpq(RAX, RCX);
+  __ OBJ(cmp)(RAX, RCX);
   __ j(GREATER, &try_modulo, Assembler::kNearJump);
   // left is less than right, result is left (RAX).
   __ ret();
@@ -229,11 +211,11 @@
   __ movq(RCX, Address(RSP, +1 * target::kWordSize));
   // RAX: Tagged left (dividend).
   // RCX: Tagged right (divisor).
-  __ cmpq(RCX, Immediate(0));
+  __ OBJ(cmp)(RCX, Immediate(0));
   __ j(EQUAL, normal_ir_body);
   EmitRemainderOperation(assembler);
   // Untagged remainder result in RAX.
-  __ cmpq(RAX, Immediate(0));
+  __ OBJ(cmp)(RAX, Immediate(0));
   __ j(LESS, &negative_result, Assembler::kNearJump);
   __ SmiTag(RAX);
   __ ret();
@@ -242,14 +224,14 @@
   Label subtract;
   // RAX: Untagged result.
   // RCX: Untagged right.
-  __ cmpq(RCX, Immediate(0));
+  __ OBJ(cmp)(RCX, Immediate(0));
   __ j(LESS, &subtract, Assembler::kNearJump);
-  __ addq(RAX, RCX);
+  __ OBJ(add)(RAX, RCX);
   __ SmiTag(RAX);
   __ ret();
 
   __ Bind(&subtract);
-  __ subq(RAX, RCX);
+  __ OBJ(sub)(RAX, RCX);
   __ SmiTag(RAX);
   __ ret();
 
@@ -261,7 +243,7 @@
   Label not_32bit;
   TestBothArgumentsSmis(assembler, normal_ir_body);
   // RAX: right argument (divisor)
-  __ cmpq(RAX, Immediate(0));
+  __ OBJ(cmp)(RAX, Immediate(0));
   __ j(EQUAL, normal_ir_body, Assembler::kNearJump);
   __ movq(RCX, RAX);
   __ movq(RAX,
@@ -325,14 +307,8 @@
   __ movq(RAX, Address(RSP, +1 * target::kWordSize));
   __ testq(RAX, Immediate(kSmiTagMask));
   __ j(NOT_ZERO, normal_ir_body, Assembler::kNearJump);  // Non-smi value.
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ negq(RAX);
+  __ OBJ(neg)(RAX);
   __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-#else
-  __ negl(RAX);
-  __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-  __ movsxd(RAX, RAX);
-#endif
   // Result is in RAX.
   __ ret();
   __ Bind(normal_ir_body);
@@ -374,7 +350,7 @@
   Label overflow;
   TestBothArgumentsSmis(assembler, normal_ir_body);
   // Shift value is in RAX. Compare with tagged Smi.
-  __ cmpq(RAX, Immediate(target::ToRawSmi(target::kSmiBits)));
+  __ OBJ(cmp)(RAX, Immediate(target::ToRawSmi(target::kSmiBits)));
   __ j(ABOVE_EQUAL, normal_ir_body, Assembler::kNearJump);
 
   __ SmiUntag(RAX);
@@ -383,18 +359,12 @@
 
   // Overflow test - all the shifted-out bits must be same as the sign bit.
   __ movq(RDI, RAX);
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ shlq(RAX, RCX);
-  __ sarq(RAX, RCX);
-#else
-  __ shll(RAX, RCX);
-  __ sarl(RAX, RCX);
-  __ movsxd(RAX, RAX);
-#endif
-  __ cmpq(RAX, RDI);
+  __ OBJ(shl)(RAX, RCX);
+  __ OBJ(sar)(RAX, RCX);
+  __ OBJ(cmp)(RAX, RDI);
   __ j(NOT_EQUAL, &overflow, Assembler::kNearJump);
 
-  __ shlq(RAX, RCX);  // Shift for result now we know there is no overflow.
+  __ OBJ(shl)(RAX, RCX);  // Shift for result now we know there is no overflow.
 
   // RAX is a correctly tagged Smi.
   __ ret();
@@ -411,7 +381,7 @@
   Label true_label;
   TestBothArgumentsSmis(assembler, normal_ir_body);
   // RAX contains the right argument.
-  __ cmpq(Address(RSP, +2 * target::kWordSize), RAX);
+  __ OBJ(cmp)(Address(RSP, +2 * target::kWordSize), RAX);
   __ j(true_condition, &true_label, Assembler::kNearJump);
   __ LoadObject(RAX, CastHandle<Object>(FalseObject()));
   __ ret();
@@ -452,7 +422,7 @@
   // For integer receiver '===' check first.
   __ movq(RAX, Address(RSP, +kArgumentOffset * target::kWordSize));
   __ movq(RCX, Address(RSP, +kReceiverOffset * target::kWordSize));
-  __ cmpq(RAX, RCX);
+  __ OBJ(cmp)(RAX, RCX);
   __ j(EQUAL, &true_label, Assembler::kNearJump);
   __ orq(RAX, RCX);
   __ testq(RAX, Immediate(kSmiTagMask));
@@ -504,22 +474,26 @@
 void AsmIntrinsifier::Integer_sar(Assembler* assembler, Label* normal_ir_body) {
   Label shift_count_ok;
   TestBothArgumentsSmis(assembler, normal_ir_body);
+#if !defined(DART_COMPRESSED_POINTERS)
   const Immediate& count_limit = Immediate(0x3F);
+#else
+  const Immediate& count_limit = Immediate(0x1F);
+#endif
   // Check that the count is not larger than what the hardware can handle.
   // For shifting right a Smi the result is the same for all numbers
   // >= count_limit.
   __ SmiUntag(RAX);
   // Negative counts throw exception.
-  __ cmpq(RAX, Immediate(0));
+  __ OBJ(cmp)(RAX, Immediate(0));
   __ j(LESS, normal_ir_body, Assembler::kNearJump);
-  __ cmpq(RAX, count_limit);
+  __ OBJ(cmp)(RAX, count_limit);
   __ j(LESS_EQUAL, &shift_count_ok, Assembler::kNearJump);
   __ movq(RAX, count_limit);
   __ Bind(&shift_count_ok);
   __ movq(RCX, RAX);  // Shift amount must be in RCX.
   __ movq(RAX, Address(RSP, +2 * target::kWordSize));  // Value.
   __ SmiUntag(RAX);                                    // Value.
-  __ sarq(RAX, RCX);
+  __ OBJ(sar)(RAX, RCX);
   __ SmiTag(RAX);
   __ ret();
   __ Bind(normal_ir_body);
@@ -529,7 +503,7 @@
 void AsmIntrinsifier::Smi_bitNegate(Assembler* assembler,
                                     Label* normal_ir_body) {
   __ movq(RAX, Address(RSP, +1 * target::kWordSize));  // Index.
-  __ notq(RAX);
+  __ OBJ(not )(RAX);
   __ andq(RAX, Immediate(~kSmiTagMask));  // Remove inverted smi-tag.
   __ ret();
 }
@@ -538,10 +512,13 @@
                                     Label* normal_ir_body) {
   ASSERT(kSmiTagShift == 1);
   __ movq(RAX, Address(RSP, +1 * target::kWordSize));  // Index.
+#if defined(DART_COMPRESSED_POINTERS)
+  __ movsxd(RAX, RAX);
+#endif
   // XOR with sign bit to complement bits if value is negative.
   __ movq(RCX, RAX);
   __ sarq(RCX, Immediate(63));  // All 0 or all 1.
-  __ xorq(RAX, RCX);
+  __ OBJ (xor)(RAX, RCX);
   // BSR does not write the destination register if source is zero.  Put a 1 in
   // the Smi tag bit to ensure BSR writes to destination register.
   __ orq(RAX, Immediate(kSmiTagMask));
@@ -1111,7 +1088,7 @@
   __ ret();
   __ Bind(&is_smi);
   __ SmiUntag(RAX);
-  __ cvtsi2sdq(XMM1, RAX);
+  __ OBJ(cvtsi2sd)(XMM1, RAX);
   __ jmp(&double_op);
   __ Bind(normal_ir_body);
 }
@@ -1177,7 +1154,7 @@
   __ ret();
   __ Bind(&is_smi);
   __ SmiUntag(RAX);
-  __ cvtsi2sdq(XMM1, RAX);
+  __ OBJ(cvtsi2sd)(XMM1, RAX);
   __ jmp(&double_op);
   __ Bind(normal_ir_body);
 }
@@ -1206,7 +1183,7 @@
   __ j(NOT_ZERO, normal_ir_body);
   // Is Smi.
   __ SmiUntag(RAX);
-  __ cvtsi2sdq(XMM1, RAX);
+  __ OBJ(cvtsi2sd)(XMM1, RAX);
   __ movq(RAX, Address(RSP, +2 * target::kWordSize));
   __ movsd(XMM0, FieldAddress(RAX, target::Double::value_offset()));
   __ mulsd(XMM0, XMM1);
@@ -1227,11 +1204,7 @@
   __ j(NOT_ZERO, normal_ir_body);
   // Is Smi.
   __ SmiUntag(RAX);
-#if !defined(DART_COMPRESSED_POINTER)
-  __ cvtsi2sdq(XMM0, RAX);
-#else
-  __ cvtsi2sdl(XMM0, RAX);
-#endif
+  __ OBJ(cvtsi2sd)(XMM0, RAX);
   const Class& double_class = DoubleClass();
   __ TryAllocate(double_class, normal_ir_body, Assembler::kFarJump,
                  RAX,  // Result register.
@@ -1303,26 +1276,13 @@
                                       Label* normal_ir_body) {
   __ movq(RAX, Address(RSP, +1 * target::kWordSize));
   __ movsd(XMM0, FieldAddress(RAX, target::Double::value_offset()));
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ cvttsd2siq(RAX, XMM0);
-#else
-  __ cvttsd2sil(RAX, XMM0);
-#endif
+  __ OBJ(cvttsd2si)(RAX, XMM0);
   // Overflow is signalled with minint.
   // Check for overflow and that it fits into Smi.
   __ movq(RCX, RAX);
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ shlq(RCX, Immediate(1));
-#else
-  __ shll(RCX, Immediate(1));
-#endif
+  __ OBJ(shl)(RCX, Immediate(1));
   __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-#if !defined(DART_COMPRESSED_POINTERS)
   __ SmiTag(RAX);
-#else
-  ASSERT((kSmiTagShift == 1) && (kSmiTag == 0));
-  __ movsxd(RAX, RCX);
-#endif
   __ ret();
   __ Bind(normal_ir_body);
 }
@@ -1335,26 +1295,15 @@
   // back to a double in XMM1.
   __ movq(RCX, Address(RSP, +1 * target::kWordSize));
   __ movsd(XMM0, FieldAddress(RCX, target::Double::value_offset()));
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ cvttsd2siq(RAX, XMM0);
-  __ cvtsi2sdq(XMM1, RAX);
-#else
-  __ cvttsd2sil(RAX, XMM0);
-  __ cvtsi2sdl(XMM1, RAX);
-#endif
+  __ OBJ(cvttsd2si)(RAX, XMM0);
+  __ OBJ(cvtsi2sd)(XMM1, RAX);
 
   // Tag the int as a Smi, making sure that it fits; this checks for
   // overflow and NaN in the conversion from double to int. Conversion
   // overflow from cvttsd2si is signalled with an INT64_MIN value.
   ASSERT(kSmiTag == 0 && kSmiTagShift == 1);
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ addq(RAX, RAX);
+  __ OBJ(add)(RAX, RAX);
   __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-#else
-  __ addl(RAX, RAX);
-  __ j(OVERFLOW, normal_ir_body, Assembler::kNearJump);
-  __ movsxd(RAX, RAX);
-#endif
 
   // Compare the two double values. If they are equal, we return the
   // Smi tagged result immediately as the hash code.
@@ -1392,7 +1341,7 @@
   __ ret();
   __ Bind(&is_smi);
   __ SmiUntag(RAX);
-  __ cvtsi2sdq(XMM1, RAX);
+  __ OBJ(cvtsi2sd)(XMM1, RAX);
   __ jmp(&double_op);
   __ Bind(normal_ir_body);
 }
@@ -1437,7 +1386,7 @@
   const intptr_t kArgumentOffset = 1;
 
   __ movq(RAX, Address(RSP, +kArgumentOffset * target::kWordSize));
-  __ cmpq(RAX, Address(RSP, +kReceiverOffset * target::kWordSize));
+  __ OBJ(cmp)(RAX, Address(RSP, +kReceiverOffset * target::kWordSize));
   __ j(EQUAL, &is_true, Assembler::kNearJump);
   __ LoadObject(RAX, CastHandle<Object>(FalseObject()));
   __ ret();
@@ -1665,7 +1614,7 @@
 
   __ movq(RCX, Address(RSP, +1 * target::kWordSize));
   __ movq(RDX, Address(RSP, +2 * target::kWordSize));
-  __ cmpq(RCX, RDX);
+  __ OBJ(cmp)(RCX, RDX);
   __ j(EQUAL, &equal);
 
   // RCX might not be a Type object, so check that first (RDX should be though,
@@ -1729,7 +1678,7 @@
                                             Label* normal_ir_body) {
   __ movq(RCX, Address(RSP, +1 * target::kWordSize));
   __ movq(RDX, Address(RSP, +2 * target::kWordSize));
-  __ cmpq(RCX, RDX);
+  __ OBJ(cmp)(RCX, RDX);
   __ j(NOT_EQUAL, normal_ir_body);
 
   __ LoadObject(RAX, CastHandle<Object>(TrueObject()));
@@ -1763,8 +1712,11 @@
                                             intptr_t other_cid,
                                             Label* return_true,
                                             Label* return_false) {
+  __ SmiUntag(RBX);
   __ movq(R8, FieldAddress(RAX, target::String::length_offset()));
+  __ SmiUntag(R8);
   __ movq(R9, FieldAddress(RCX, target::String::length_offset()));
+  __ SmiUntag(R9);
 
   // if (other.length == 0) return true;
   __ testq(R9, R9);
@@ -1780,8 +1732,6 @@
   __ cmpq(R11, R8);
   __ j(GREATER, return_false);
 
-  __ SmiUntag(RBX);                     // start
-  __ SmiUntag(R9);                      // other.length
   __ LoadImmediate(R11, Immediate(0));  // i = 0
 
   // do
@@ -1911,7 +1861,7 @@
   // Get length.
   __ movq(RAX, Address(RSP, +1 * target::kWordSize));  // String object.
   __ movq(RAX, FieldAddress(RAX, target::String::length_offset()));
-  __ cmpq(RAX, Immediate(target::ToRawSmi(0)));
+  __ OBJ(cmp)(RAX, Immediate(target::ToRawSmi(0)));
   __ j(EQUAL, &is_true, Assembler::kNearJump);
   __ LoadObject(RAX, CastHandle<Object>(FalseObject()));
   __ ret();
@@ -2195,7 +2145,7 @@
   __ movq(RCX, Address(RSP, +1 * target::kWordSize));  // Other.
 
   // Are identical?
-  __ cmpq(RAX, RCX);
+  __ OBJ(cmp)(RAX, RCX);
   __ j(EQUAL, &is_true, Assembler::kNearJump);
 
   // Is other target::OneByteString?
@@ -2206,7 +2156,7 @@
 
   // Have same length?
   __ movq(RDI, FieldAddress(RAX, target::String::length_offset()));
-  __ cmpq(RDI, FieldAddress(RCX, target::String::length_offset()));
+  __ OBJ(cmp)(RDI, FieldAddress(RCX, target::String::length_offset()));
   __ j(NOT_EQUAL, &is_false, Assembler::kNearJump);
 
   // Check contents, no fall-through possible.
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.cc b/runtime/vm/compiler/assembler/assembler_arm64.cc
index 91c5e85..ff0820b 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm64.cc
@@ -529,16 +529,16 @@
   ASSERT(IsOriginalObject(object));
   word offset = 0;
   if (IsSameObject(compiler::NullObject(), object)) {
-    CompareRegisters(reg, NULL_REG);
+    CompareObjectRegisters(reg, NULL_REG);
   } else if (target::CanLoadFromThread(object, &offset)) {
     ldr(TMP, Address(THR, offset));
-    CompareRegisters(reg, TMP);
+    CompareObjectRegisters(reg, TMP);
   } else if (CanLoadFromObjectPool(object)) {
     LoadObject(TMP, object);
-    CompareRegisters(reg, TMP);
+    CompareObjectRegisters(reg, TMP);
   } else {
     ASSERT(target::IsSmi(object));
-    CompareImmediate(reg, target::ToRawSmi(object));
+    CompareImmediate(reg, target::ToRawSmi(object), kObjectBytes);
   }
 }
 
@@ -667,7 +667,12 @@
   Call(FieldAddress(CODE_REG, target::Code::entry_point_offset(entry_kind)));
 }
 
-void Assembler::AddImmediate(Register dest, Register rn, int64_t imm) {
+void Assembler::AddImmediate(Register dest,
+                             Register rn,
+                             int64_t imm,
+                             OperandSize sz) {
+  ASSERT(sz == kEightBytes || sz == kFourBytes);
+  int width = sz == kEightBytes ? kXRegSizeInBits : kWRegSizeInBits;
   Operand op;
   if (imm == 0) {
     if (dest != rn) {
@@ -675,16 +680,15 @@
     }
     return;
   }
-  if (Operand::CanHold(imm, kXRegSizeInBits, &op) == Operand::Immediate) {
-    add(dest, rn, op);
-  } else if (Operand::CanHold(-imm, kXRegSizeInBits, &op) ==
-             Operand::Immediate) {
-    sub(dest, rn, op);
+  if (Operand::CanHold(imm, width, &op) == Operand::Immediate) {
+    add(dest, rn, op, sz);
+  } else if (Operand::CanHold(-imm, width, &op) == Operand::Immediate) {
+    sub(dest, rn, op, sz);
   } else {
     // TODO(zra): Try adding top 12 bits, then bottom 12 bits.
     ASSERT(rn != TMP2);
     LoadImmediate(TMP2, imm);
-    add(dest, rn, Operand(TMP2));
+    add(dest, rn, Operand(TMP2), sz);
   }
 }
 
@@ -783,7 +787,7 @@
     tsti(rn, Immediate(imm), sz);
   } else {
     LoadImmediate(TMP, imm);
-    tst(rn, Operand(TMP));
+    tst(rn, Operand(TMP), sz);
   }
 }
 
@@ -926,17 +930,8 @@
 #if !defined(DART_COMPRESSED_POINTERS)
   ldr(dest, slot);
 #else
-  Label done;
-  ldr(dest, slot, kFourBytes);  // Sign-extension.
-  if (can_value_be_smi == kValueCanBeSmi) {
-    BranchIfSmi(dest, &done);
-  }
+  ldr(dest, slot, kUnsignedFourBytes);  // Zero-extension.
   add(dest, dest, Operand(HEAP_BASE));
-  Bind(&done);
-
-  // After further Smi changes:
-  // ldr(dest, slot, kUnsignedFourBytes);  // Zero-extension.
-  // add(dest, dest, Operand(HEAP_BASE));
 #endif
 }
 
@@ -1899,15 +1894,33 @@
   const intptr_t boxing_shift = index_unboxed ? 0 : -kSmiTagShift;
   const intptr_t shift = Utils::ShiftForPowerOfTwo(index_scale) + boxing_shift;
   const int32_t offset = HeapDataOffset(is_external, cid);
+#if !defined(DART_COMPRESSED_POINTERS)
+  const bool index_is_32bit = false;
+#else
+  const bool index_is_32bit = !index_unboxed;
+#endif
   ASSERT(array != temp);
   ASSERT(index != temp);
   if ((offset == 0) && (shift == 0)) {
-    return Address(array, index, UXTX, Address::Unscaled);
+    if (index_is_32bit) {
+      return Address(array, index, SXTW, Address::Unscaled);
+    } else {
+      return Address(array, index, UXTX, Address::Unscaled);
+    }
   } else if (shift < 0) {
     ASSERT(shift == -1);
-    add(temp, array, Operand(index, ASR, 1));
+    if (index_is_32bit) {
+      AsrImmediate(temp, index, 1, kFourBytes);
+      add(temp, array, Operand(temp, SXTW, 0));
+    } else {
+      add(temp, array, Operand(index, ASR, 1));
+    }
   } else {
-    add(temp, array, Operand(index, LSL, shift));
+    if (index_is_32bit) {
+      add(temp, array, Operand(index, SXTW, shift));
+    } else {
+      add(temp, array, Operand(index, LSL, shift));
+    }
   }
   ASSERT(Address::CanHoldOffset(offset, Address::Offset, size));
   return Address(temp, offset, Address::Offset, size);
@@ -1924,13 +1937,31 @@
   const intptr_t boxing_shift = index_unboxed ? 0 : -kSmiTagShift;
   const intptr_t shift = Utils::ShiftForPowerOfTwo(index_scale) + boxing_shift;
   const int32_t offset = HeapDataOffset(is_external, cid);
+#if !defined(DART_COMPRESSED_POINTERS)
+  const bool index_is_32bit = false;
+#else
+  const bool index_is_32bit = !index_unboxed;
+#endif
   if (shift == 0) {
-    add(address, array, Operand(index));
+    if (index_is_32bit) {
+      add(address, array, Operand(index, SXTW, 0));
+    } else {
+      add(address, array, Operand(index));
+    }
   } else if (shift < 0) {
     ASSERT(shift == -1);
-    add(address, array, Operand(index, ASR, 1));
+    if (index_is_32bit) {
+      sxtw(index, index);
+      add(address, array, Operand(index, ASR, 1));
+    } else {
+      add(address, array, Operand(index, ASR, 1));
+    }
   } else {
-    add(address, array, Operand(index, LSL, shift));
+    if (index_is_32bit) {
+      add(address, array, Operand(index, SXTW, shift));
+    } else {
+      add(address, array, Operand(index, LSL, shift));
+    }
   }
   if (offset != 0) {
     AddImmediate(address, offset);
@@ -2046,7 +2077,7 @@
   }
 }
 
-bool Assembler::CanGenerateXCbzTbz(Register rn, Condition cond) {
+bool Assembler::CanGenerateCbzTbz(Register rn, Condition cond) {
   if (rn == CSP) {
     return false;
   }
@@ -2063,9 +2094,12 @@
   }
 }
 
-void Assembler::GenerateXCbzTbz(Register rn, Condition cond, Label* label) {
-  constexpr int32_t bit_no = 63;
-  constexpr OperandSize sz = kEightBytes;
+void Assembler::GenerateCbzTbz(Register rn,
+                               Condition cond,
+                               Label* label,
+                               OperandSize sz) {
+  ASSERT((sz == kEightBytes) || (sz == kFourBytes));
+  const int32_t sign_bit = sz == kEightBytes ? 63 : 31;
   ASSERT(rn != CSP);
   switch (cond) {
     case EQ:  // equal
@@ -2076,11 +2110,11 @@
       return;
     case MI:  // minus/negative
     case LT:  // signed less than
-      tbnz(label, rn, bit_no);
+      tbnz(label, rn, sign_bit);
       return;
     case PL:  // plus/positive or zero
     case GE:  // signed greater than or equal
-      tbz(label, rn, bit_no);
+      tbz(label, rn, sign_bit);
       return;
     default:
       // Only conditions above allow single instruction emission.
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h
index 14ac5aa..0417af4 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.h
+++ b/runtime/vm/compiler/assembler/assembler_arm64.h
@@ -897,29 +897,47 @@
   }
 
   // Misc. arithmetic.
-  void udiv(Register rd, Register rn, Register rm) {
-    EmitMiscDP2Source(UDIV, rd, rn, rm, kEightBytes);
+  void udiv(Register rd,
+            Register rn,
+            Register rm,
+            OperandSize sz = kEightBytes) {
+    EmitMiscDP2Source(UDIV, rd, rn, rm, sz);
   }
-  void sdiv(Register rd, Register rn, Register rm) {
-    EmitMiscDP2Source(SDIV, rd, rn, rm, kEightBytes);
+  void sdiv(Register rd,
+            Register rn,
+            Register rm,
+            OperandSize sz = kEightBytes) {
+    EmitMiscDP2Source(SDIV, rd, rn, rm, sz);
   }
-  void lslv(Register rd, Register rn, Register rm) {
-    EmitMiscDP2Source(LSLV, rd, rn, rm, kEightBytes);
+  void lslv(Register rd,
+            Register rn,
+            Register rm,
+            OperandSize sz = kEightBytes) {
+    EmitMiscDP2Source(LSLV, rd, rn, rm, sz);
   }
-  void lsrv(Register rd, Register rn, Register rm) {
-    EmitMiscDP2Source(LSRV, rd, rn, rm, kEightBytes);
+  void lsrv(Register rd,
+            Register rn,
+            Register rm,
+            OperandSize sz = kEightBytes) {
+    EmitMiscDP2Source(LSRV, rd, rn, rm, sz);
   }
-  void asrv(Register rd, Register rn, Register rm) {
-    EmitMiscDP2Source(ASRV, rd, rn, rm, kEightBytes);
+  void asrv(Register rd,
+            Register rn,
+            Register rm,
+            OperandSize sz = kEightBytes) {
+    EmitMiscDP2Source(ASRV, rd, rn, rm, sz);
+  }
+  void sdivw(Register rd, Register rn, Register rm) {
+    sdiv(rd, rn, rm, kFourBytes);
   }
   void lslvw(Register rd, Register rn, Register rm) {
-    EmitMiscDP2Source(LSLV, rd, rn, rm, kFourBytes);
+    lslv(rd, rn, rm, kFourBytes);
   }
   void lsrvw(Register rd, Register rn, Register rm) {
-    EmitMiscDP2Source(LSRV, rd, rn, rm, kFourBytes);
+    lsrv(rd, rn, rm, kFourBytes);
   }
   void asrvw(Register rd, Register rn, Register rm) {
-    EmitMiscDP2Source(ASRV, rd, rn, rm, kFourBytes);
+    asrv(rd, rn, rm, kFourBytes);
   }
   void madd(Register rd,
             Register rn,
@@ -1115,6 +1133,11 @@
     }
   }
 
+  void CompareObjectRegisters(Register rn, Register rm) {
+    ASSERT(rn != CSP);
+    cmp(rn, Operand(rm), kObjectBytes);
+  }
+
   // Conditional branch.
   void b(Label* label, Condition cond = AL) {
     EmitConditionalBranch(BCOND, cond, label);
@@ -1149,10 +1172,13 @@
     EmitCompareAndBranch(CBNZ, rt, label, sz);
   }
 
-  // Generate 64-bit compare with zero and branch when condition allows to use
-  // a single instruction: cbz/cbnz/tbz/tbnz.
-  bool CanGenerateXCbzTbz(Register rn, Condition cond);
-  void GenerateXCbzTbz(Register rn, Condition cond, Label* label);
+  // Generate 64/32-bit compare with zero and branch when condition allows to
+  // use a single instruction: cbz/cbnz/tbz/tbnz.
+  bool CanGenerateCbzTbz(Register rn, Condition cond);
+  void GenerateCbzTbz(Register rn,
+                      Condition cond,
+                      Label* label,
+                      OperandSize sz = kEightBytes);
 
   // Test bit and branch if zero.
   void tbz(Label* label, Register rt, intptr_t bit_number) {
@@ -1442,8 +1468,10 @@
   void mvn(Register rd, Register rm) { orn(rd, ZR, Operand(rm)); }
   void mvnw(Register rd, Register rm) { ornw(rd, ZR, Operand(rm)); }
   void neg(Register rd, Register rm) { sub(rd, ZR, Operand(rm)); }
-  void negs(Register rd, Register rm) { subs(rd, ZR, Operand(rm)); }
-  void negsw(Register rd, Register rm) { subsw(rd, ZR, Operand(rm)); }
+  void negs(Register rd, Register rm, OperandSize sz = kEightBytes) {
+    subs(rd, ZR, Operand(rm), sz);
+  }
+  void negsw(Register rd, Register rm) { negs(rd, rm, kFourBytes); }
   void mul(Register rd, Register rn, Register rm) {
     madd(rd, rn, rm, ZR, kEightBytes);
   }
@@ -1538,9 +1566,11 @@
   void VRecps(VRegister vd, VRegister vn);
   void VRSqrts(VRegister vd, VRegister vn);
 
-  void SmiUntag(Register reg) { AsrImmediate(reg, reg, kSmiTagSize); }
+  void SmiUntag(Register reg) {
+    sbfm(reg, reg, kSmiTagSize, target::kSmiBits + 1);
+  }
   void SmiUntag(Register dst, Register src) {
-    AsrImmediate(dst, src, kSmiTagSize);
+    sbfm(dst, src, kSmiTagSize, target::kSmiBits + 1);
   }
   void SmiTag(Register reg) { LslImmediate(reg, reg, kSmiTagSize); }
   void SmiTag(Register dst, Register src) {
@@ -1617,7 +1647,10 @@
   // the object pool when possible. Unless you are sure that the untagged object
   // pool pointer is in another register, or that it is not available at all,
   // PP should be passed for pp. `dest` can be TMP2, `rn` cannot.
-  void AddImmediate(Register dest, Register rn, int64_t imm);
+  void AddImmediate(Register dest,
+                    Register rn,
+                    int64_t imm,
+                    OperandSize sz = kEightBytes);
   void AddImmediateSetFlags(Register dest,
                             Register rn,
                             int64_t imm,
diff --git a/runtime/vm/compiler/assembler/assembler_base.h b/runtime/vm/compiler/assembler/assembler_base.h
index a650f43..1ce7b58 100644
--- a/runtime/vm/compiler/assembler/assembler_base.h
+++ b/runtime/vm/compiler/assembler/assembler_base.h
@@ -191,6 +191,12 @@
   kRegList,
   // 64-bit ARM specific constants.
   kQWord,
+
+#if defined(TARGET_ARCH_IS_64_BIT) && !defined(DART_COMPRESSED_POINTERS)
+  kObjectBytes = kEightBytes,
+#else
+  kObjectBytes = kFourBytes,
+#endif
 };
 
 // Forward declarations.
diff --git a/runtime/vm/compiler/assembler/assembler_x64.cc b/runtime/vm/compiler/assembler/assembler_x64.cc
index 7a616d9..d1ab4a7 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.cc
+++ b/runtime/vm/compiler/assembler/assembler_x64.cc
@@ -533,22 +533,36 @@
   EmitSimple(0xDD, 0xC0 + value);
 }
 
-void Assembler::CompareImmediate(Register reg, const Immediate& imm) {
-  if (imm.is_int32()) {
-    cmpq(reg, imm);
+void Assembler::CompareImmediate(Register reg,
+                                 const Immediate& imm,
+                                 OperandSize width) {
+  if (width == kEightBytes) {
+    if (imm.is_int32()) {
+      cmpq(reg, imm);
+    } else {
+      ASSERT(reg != TMP);
+      LoadImmediate(TMP, imm);
+      cmpq(reg, TMP);
+    }
   } else {
-    ASSERT(reg != TMP);
-    LoadImmediate(TMP, imm);
-    cmpq(reg, TMP);
+    ASSERT(width == kFourBytes);
+    cmpl(reg, imm);
   }
 }
 
-void Assembler::CompareImmediate(const Address& address, const Immediate& imm) {
-  if (imm.is_int32()) {
-    cmpq(address, imm);
+void Assembler::CompareImmediate(const Address& address,
+                                 const Immediate& imm,
+                                 OperandSize width) {
+  if (width == kEightBytes) {
+    if (imm.is_int32()) {
+      cmpq(address, imm);
+    } else {
+      LoadImmediate(TMP, imm);
+      cmpq(address, TMP);
+    }
   } else {
-    LoadImmediate(TMP, imm);
-    cmpq(address, TMP);
+    ASSERT(width == kFourBytes);
+    cmpl(address, imm);
   }
 }
 
@@ -607,13 +621,20 @@
   }
 }
 
-void Assembler::TestImmediate(Register dst, const Immediate& imm) {
-  if (imm.is_int32() || imm.is_uint32()) {
-    testq(dst, imm);
+void Assembler::TestImmediate(Register dst,
+                              const Immediate& imm,
+                              OperandSize width) {
+  if (width == kEightBytes) {
+    if (imm.is_int32() || imm.is_uint32()) {
+      testq(dst, imm);
+    } else {
+      ASSERT(dst != TMP);
+      LoadImmediate(TMP, imm);
+      testq(dst, TMP);
+    }
   } else {
-    ASSERT(dst != TMP);
-    LoadImmediate(TMP, imm);
-    testq(dst, TMP);
+    ASSERT(width == kFourBytes);
+    testl(dst, imm);
   }
 }
 
@@ -1303,15 +1324,15 @@
 
   intptr_t offset_from_thread;
   if (target::CanLoadFromThread(object, &offset_from_thread)) {
-    cmpq(reg, Address(THR, offset_from_thread));
+    OBJ(cmp)(reg, Address(THR, offset_from_thread));
   } else if (CanLoadFromObjectPool(object)) {
     const intptr_t idx = object_pool_builder().FindObject(
         object, ObjectPoolBuilderEntry::kNotPatchable);
     const int32_t offset = target::ObjectPool::element_offset(idx);
-    cmpq(reg, Address(PP, offset - kHeapObjectTag));
+    OBJ(cmp)(reg, Address(PP, offset - kHeapObjectTag));
   } else {
     ASSERT(target::IsSmi(object));
-    CompareImmediate(reg, Immediate(target::ToRawSmi(object)));
+    CompareImmediate(reg, Immediate(target::ToRawSmi(object)), kObjectBytes);
   }
 }
 
@@ -1345,17 +1366,8 @@
 #if !defined(DART_COMPRESSED_POINTERS)
   movq(dest, slot);
 #else
-  Label done;
-  movsxd(dest, slot);  // (movslq) Sign-extension.
-  if (can_value_be_smi == kValueCanBeSmi) {
-    BranchIfSmi(dest, &done, kNearJump);
-  }
+  movl(dest, slot);  // Zero-extension.
   addq(dest, Address(THR, target::Thread::heap_base_offset()));
-  Bind(&done);
-
-  // After further Smi changes:
-  // movl(dest, slot);  // Zero-extension.
-  // addq(dest, Address(THR, target::Thread::heap_base_offset());
 #endif
 }
 
@@ -1888,7 +1900,11 @@
   j(NOT_EQUAL, &miss, Assembler::kNearJump);
   addl(FieldAddress(RBX, count_offset), Immediate(target::ToRawSmi(1)));
   xorq(R10, R10);  // GC-safe for OptimizeInvokedFunction.
+#if !defined(DART_COMPRESSED_POINTERS)
   nop(1);
+#else
+  nop(2);
+#endif
 
   // Fall through to unchecked entry.
   ASSERT_EQUAL(CodeSize() - start,
@@ -1921,7 +1937,11 @@
 
   // Ensure the unchecked entry is 2-byte aligned (so GC can see them if we
   // store them in ICData / MegamorphicCache arrays).
+#if !defined(DART_COMPRESSED_POINTERS)
   nop(1);
+#else
+  nop(2);
+#endif
 
   // Fall through to unchecked entry.
   ASSERT_EQUAL(CodeSize() - start,
@@ -2230,6 +2250,7 @@
 void Assembler::SmiUntagOrCheckClass(Register object,
                                      intptr_t class_id,
                                      Label* is_smi) {
+#if !defined(DART_COMPRESSED_POINTERS)
   ASSERT(kSmiTagShift == 1);
   ASSERT(target::UntaggedObject::kClassIdTagPos == 16);
   ASSERT(target::UntaggedObject::kClassIdTagSize == 16);
@@ -2244,6 +2265,11 @@
   // factor in the addressing mode to compensate for this.
   movzxw(TMP, Address(object, TIMES_2, class_id_offset));
   cmpl(TMP, Immediate(class_id));
+#else
+  // Cannot speculatively untag compressed Smis because it erases upper address
+  // bits.
+  UNREACHABLE();
+#endif
 }
 
 void Assembler::LoadClassIdMayBeSmi(Register result, Register object) {
diff --git a/runtime/vm/compiler/assembler/assembler_x64.h b/runtime/vm/compiler/assembler/assembler_x64.h
index cffa9e9..82d7b89 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.h
+++ b/runtime/vm/compiler/assembler/assembler_x64.h
@@ -286,6 +286,12 @@
   }
 };
 
+#if !defined(DART_COMPRESSED_POINTERS)
+#define OBJ(op) op##q
+#else
+#define OBJ(op) op##l
+#endif
+
 class Assembler : public AssemblerBase {
  public:
   explicit Assembler(ObjectPoolBuilder* object_pool_builder,
@@ -544,10 +550,16 @@
   };
   void roundsd(XmmRegister dst, XmmRegister src, RoundingMode mode);
 
-  void CompareImmediate(Register reg, const Immediate& imm);
-  void CompareImmediate(const Address& address, const Immediate& imm);
-  void CompareImmediate(Register reg, int32_t immediate) {
-    return CompareImmediate(reg, Immediate(immediate));
+  void CompareImmediate(Register reg,
+                        const Immediate& imm,
+                        OperandSize width = kEightBytes);
+  void CompareImmediate(const Address& address,
+                        const Immediate& imm,
+                        OperandSize width = kEightBytes);
+  void CompareImmediate(Register reg,
+                        int32_t immediate,
+                        OperandSize width = kEightBytes) {
+    return CompareImmediate(reg, Immediate(immediate), width);
   }
 
   void testl(Register reg, const Immediate& imm) { testq(reg, imm); }
@@ -555,7 +567,9 @@
   void testb(const Address& address, Register reg);
 
   void testq(Register reg, const Immediate& imm);
-  void TestImmediate(Register dst, const Immediate& imm);
+  void TestImmediate(Register dst,
+                     const Immediate& imm,
+                     OperandSize width = kEightBytes);
 
   void AndImmediate(Register dst, const Immediate& imm);
   void OrImmediate(Register dst, const Immediate& imm);
@@ -678,6 +692,7 @@
   // Methods for High-level operations and implemented on all architectures.
   void Ret() { ret(); }
   void CompareRegisters(Register a, Register b);
+  void CompareObjectRegisters(Register a, Register b) { OBJ(cmp)(a, b); }
   void BranchIf(Condition condition,
                 Label* label,
                 JumpDistance distance = kFarJump) {
@@ -864,9 +879,21 @@
   void SmiUntagOrCheckClass(Register object, intptr_t class_id, Label* smi);
 
   // Misc. functionality.
-  void SmiTag(Register reg) { addq(reg, reg); }
+  void SmiTag(Register reg) { OBJ(add)(reg, reg); }
 
-  void SmiUntag(Register reg) { sarq(reg, Immediate(kSmiTagSize)); }
+  void SmiUntag(Register reg) { OBJ(sar)(reg, Immediate(kSmiTagSize)); }
+
+  void SmiUntagAndSignExtend(Register reg) {
+#if !defined(DART_COMPRESSED_POINTERS)
+    sarq(reg, Immediate(kSmiTagSize));
+#else
+    // This is shorter than
+    // shlq reg, 32
+    // sraq reg, 33
+    sarl(reg, Immediate(1));
+    movsxd(reg, reg);
+#endif
+  }
 
   void BranchIfNotSmi(Register reg,
                       Label* label,
@@ -950,7 +977,7 @@
   }
 
   void CompareWithFieldValue(Register value, FieldAddress address) {
-    cmpq(value, address);
+    OBJ(cmp)(value, address);
   }
 
   void CompareTypeNullabilityWith(Register type, int8_t value) {
diff --git a/runtime/vm/compiler/assembler/assembler_x64_test.cc b/runtime/vm/compiler/assembler/assembler_x64_test.cc
index 39d492d..9704c22 100644
--- a/runtime/vm/compiler/assembler/assembler_x64_test.cc
+++ b/runtime/vm/compiler/assembler/assembler_x64_test.cc
@@ -4971,6 +4971,7 @@
 ASSEMBLER_TEST_RUN(TestObjectCompare, test) {
   bool res = test->InvokeWithCodeAndThread<bool>();
   EXPECT_EQ(true, res);
+#if !defined(DART_COMPRESSED_POINTERS)
   EXPECT_DISASSEMBLY_NOT_WINDOWS(
       "push rbp\n"
       "movq rbp,rsp\n"
@@ -5014,6 +5015,51 @@
       "movq rsp,rbp\n"
       "pop rbp\n"
       "ret\n");
+#else
+  EXPECT_DISASSEMBLY_NOT_WINDOWS(
+      "push rbp\n"
+      "movq rbp,rsp\n"
+      "push r12\n"
+      "push pp\n"
+      "push thr\n"
+      "movq r12,[rdi+0x8]\n"
+      "movq thr,rsi\n"
+      "movq pp,[r12+0x27]\n"
+      "movq rax,[pp+0xf]\n"
+      "cmpl rax,[pp+0xf]\n"
+      "jnz 0x................\n"
+      "movq rcx,[pp+0xf]\n"
+      "cmpl rcx,[pp+0xf]\n"
+      "jnz 0x................\n"
+      "movl rcx,0x1e\n"
+      "cmpl rcx,0x1e\n"
+      "jnz 0x................\n"
+      "push rax\n"
+      "movq r11,[pp+0xf]\n"
+      "movq [rsp],r11\n"
+      "pop rcx\n"
+      "cmpl rcx,[pp+0xf]\n"
+      "jnz 0x................\n"
+      "push rax\n"
+      "movq [rsp],0x1e\n"
+      "pop rcx\n"
+      "cmpl rcx,0x1e\n"
+      "jnz 0x................\n"
+      "movl rax,1\n"
+      "pop thr\n"
+      "pop pp\n"
+      "pop r12\n"
+      "movq rsp,rbp\n"
+      "pop rbp\n"
+      "ret\n"
+      "movl rax,0\n"
+      "pop thr\n"
+      "pop pp\n"
+      "pop r12\n"
+      "movq rsp,rbp\n"
+      "pop rbp\n"
+      "ret\n");
+#endif
 }
 
 ASSEMBLER_TEST_GENERATE(TestNop, assembler) {
diff --git a/runtime/vm/compiler/backend/constant_propagator_test.cc b/runtime/vm/compiler/backend/constant_propagator_test.cc
index 3eb3218..5265ea0 100644
--- a/runtime/vm/compiler/backend/constant_propagator_test.cc
+++ b/runtime/vm/compiler/backend/constant_propagator_test.cc
@@ -51,7 +51,7 @@
 
   {
     BlockBuilder builder(H.flow_graph(), b2);
-    v1 = H.Phi(b2, {{b1, v0}, {b3, FlowGraphBuilderHelper::kPhiSelfReference}});
+    v1 = H.Phi(b2, {{b1, v0}, {b3, &v1}});
     builder.AddPhi(v1);
     auto v2 = builder.AddDefinition(
         new EqualityCompareInstr(InstructionSource(), Token::kEQ, new Value(v1),
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index 69b2bd0..639a7cd 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -1901,7 +1901,7 @@
   }
 }
 
-static void UnboxPhi(PhiInstr* phi) {
+static void UnboxPhi(PhiInstr* phi, bool is_aot) {
   Representation unboxed = phi->representation();
 
   switch (phi->Type()->ToCid()) {
@@ -2004,10 +2004,22 @@
     }
   }
 
+#if defined(TARGET_ARCH_IS_64_BIT)
+  // In AOT mode on 64-bit platforms always unbox integer typed phis (similar
+  // to how we treat doubles and other boxed numeric types).
+  // In JIT mode only unbox phis which are not fully known to be Smi.
+  if ((unboxed == kTagged) && phi->Type()->IsInt() &&
+      (is_aot || phi->Type()->ToCid() != kSmiCid)) {
+    unboxed = kUnboxedInt64;
+  }
+#endif
+
   phi->set_representation(unboxed);
 }
 
 void FlowGraph::SelectRepresentations() {
+  const auto is_aot = CompilerState::Current().is_aot();
+
   // First we decide for each phi if it is beneficial to unbox it. If so, we
   // change it's `phi->representation()`
   for (BlockIterator block_it = reverse_postorder_iterator(); !block_it.Done();
@@ -2016,7 +2028,7 @@
     if (join_entry != NULL) {
       for (PhiIterator it(join_entry); !it.Done(); it.Advance()) {
         PhiInstr* phi = it.Current();
-        UnboxPhi(phi);
+        UnboxPhi(phi, is_aot);
       }
     }
   }
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
index 89f2a24..6b3f7c3 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
@@ -708,7 +708,7 @@
 
   if (obj.IsSmi() && (Smi::Cast(obj).Value() == 0)) {
     ASSERT(!needs_number_check);
-    __ testq(reg, reg);
+    __ OBJ(test)(reg, reg);
     return EQUAL;
   }
 
diff --git a/runtime/vm/compiler/backend/flow_graph_test.cc b/runtime/vm/compiler/backend/flow_graph_test.cc
new file mode 100644
index 0000000..55323bb
--- /dev/null
+++ b/runtime/vm/compiler/backend/flow_graph_test.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2012, 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.
+
+#include "vm/compiler/backend/flow_graph.h"
+
+#include <vector>
+
+#include "platform/utils.h"
+#include "vm/compiler/backend/block_builder.h"
+#include "vm/compiler/backend/il_printer.h"
+#include "vm/compiler/backend/il_test_helper.h"
+#include "vm/compiler/backend/type_propagator.h"
+#include "vm/unit_test.h"
+
+namespace dart {
+
+#if defined(TARGET_ARCH_IS_64_BIT)
+ISOLATE_UNIT_TEST_CASE(FlowGraph_UnboxInt64Phi) {
+  using compiler::BlockBuilder;
+
+  CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
+
+  FlowGraphBuilderHelper H;
+
+  // Add a variable into the scope which would provide static type for the
+  // parameter.
+  LocalVariable* v0_var =
+      new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
+                        String::Handle(Symbols::New(thread, "v0")),
+                        AbstractType::ZoneHandle(Type::IntType()),
+                        new CompileType(CompileType::Int()));
+  v0_var->set_type_check_mode(LocalVariable::kTypeCheckedByCaller);
+  H.flow_graph()->parsed_function().scope()->AddVariable(v0_var);
+
+  auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
+  auto loop_header = H.JoinEntry();
+  auto loop_body = H.TargetEntry();
+  auto loop_exit = H.TargetEntry();
+
+  Definition* v0;
+  PhiInstr* loop_var;
+  Definition* add1;
+  ReturnInstr* ret;
+
+  {
+    BlockBuilder builder(H.flow_graph(), normal_entry);
+    v0 = builder.AddParameter(0, 0, /*with_frame=*/true, kTagged);
+    builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
+  }
+
+  {
+    BlockBuilder builder(H.flow_graph(), loop_header);
+    loop_var = H.Phi(loop_header, {{normal_entry, v0}, {loop_body, &add1}});
+    builder.AddPhi(loop_var);
+    builder.AddBranch(new RelationalOpInstr(
+                          InstructionSource(), Token::kLT, new Value(loop_var),
+                          new Value(H.IntConstant(50)), kMintCid,
+                          S.GetNextDeoptId(), Instruction::kNotSpeculative),
+                      loop_body, loop_exit);
+  }
+
+  {
+    BlockBuilder builder(H.flow_graph(), loop_body);
+    add1 = builder.AddDefinition(new BinaryInt64OpInstr(
+        Token::kADD, new Value(loop_var), new Value(H.IntConstant(1)),
+        S.GetNextDeoptId(), Instruction::kNotSpeculative));
+    builder.AddInstruction(new GotoInstr(loop_header, S.GetNextDeoptId()));
+  }
+
+  {
+    BlockBuilder builder(H.flow_graph(), loop_exit);
+    ret = builder.AddReturn(new Value(loop_var));
+  }
+
+  H.FinishGraph();
+
+  FlowGraphTypePropagator::Propagate(H.flow_graph());
+  H.flow_graph()->SelectRepresentations();
+
+  EXPECT_PROPERTY(loop_var, it.representation() == kUnboxedInt64);
+}
+#endif  // defined(TARGET_ARCH_IS_64_BIT)
+
+}  // namespace dart
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index d3c8abb..0e88877 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -93,14 +93,22 @@
     case kTagged:
     case kUnboxedInt64: {
       const auto out = locs()->out(0).reg();
+#if !defined(DART_COMPRESSED_POINTERS)
       __ add(out, base_reg(), compiler::Operand(index, LSL, 2));
+#else
+      __ add(out, base_reg(), compiler::Operand(index, SXTW, 2));
+#endif
       __ LoadFromOffset(out, out, offset());
       break;
     }
     case kUnboxedDouble: {
       const auto tmp = locs()->temp(0).reg();
       const auto out = locs()->out(0).fpu_reg();
+#if !defined(DART_COMPRESSED_POINTERS)
       __ add(tmp, base_reg(), compiler::Operand(index, LSL, 2));
+#else
+      __ add(tmp, base_reg(), compiler::Operand(index, SXTW, 2));
+#endif
       __ LoadDFromOffset(out, tmp, offset());
       break;
     }
@@ -114,7 +122,11 @@
                (NoLocation, Register index, Register value)) {
   ASSERT(instr->RequiredInputRepresentation(
              StoreIndexedUnsafeInstr::kIndexPos) == kTagged);  // It is a Smi.
+#if !defined(DART_COMPRESSED_POINTERS)
   __ add(TMP, instr->base_reg(), compiler::Operand(index, LSL, 2));
+#else
+  __ add(TMP, instr->base_reg(), compiler::Operand(index, SXTW, 2));
+#endif
   __ str(value, compiler::Address(TMP, instr->offset()));
 
   ASSERT(kSmiTag == 0);
@@ -810,21 +822,22 @@
                                       Register rn,
                                       Condition cond,
                                       BranchLabels labels) {
-  return !AreLabelsNull(labels) && __ CanGenerateXCbzTbz(rn, cond);
+  return !AreLabelsNull(labels) && __ CanGenerateCbzTbz(rn, cond);
 }
 
 static void EmitCbzTbz(Register reg,
                        FlowGraphCompiler* compiler,
                        Condition true_condition,
-                       BranchLabels labels) {
+                       BranchLabels labels,
+                       compiler::OperandSize sz) {
   ASSERT(CanUseCbzTbzForComparison(compiler, reg, true_condition, labels));
   if (labels.fall_through == labels.false_label) {
     // If the next block is the false successor we will fall through to it.
-    __ GenerateXCbzTbz(reg, true_condition, labels.true_label);
+    __ GenerateCbzTbz(reg, true_condition, labels.true_label, sz);
   } else {
     // If the next block is not the false successor we will branch to it.
     Condition false_condition = InvertCondition(true_condition);
-    __ GenerateXCbzTbz(reg, false_condition, labels.false_label);
+    __ GenerateCbzTbz(reg, false_condition, labels.false_label, sz);
 
     // Fall through or jump to the true successor.
     if (labels.fall_through != labels.true_label) {
@@ -833,6 +846,44 @@
   }
 }
 
+static Condition EmitSmiComparisonOp(FlowGraphCompiler* compiler,
+                                     LocationSummary* locs,
+                                     Token::Kind kind,
+                                     BranchLabels labels) {
+  Location left = locs->in(0);
+  Location right = locs->in(1);
+  ASSERT(!left.IsConstant() || !right.IsConstant());
+
+  Condition true_condition = TokenKindToSmiCondition(kind);
+  if (left.IsConstant() || right.IsConstant()) {
+    // Ensure constant is on the right.
+    ConstantInstr* constant = nullptr;
+    if (left.IsConstant()) {
+      constant = left.constant_instruction();
+      Location tmp = right;
+      right = left;
+      left = tmp;
+      true_condition = FlipCondition(true_condition);
+    } else {
+      constant = right.constant_instruction();
+    }
+
+    ASSERT(constant->representation() == kTagged);
+    int64_t value;
+    if (compiler::HasIntegerValue(constant->value(), &value) && (value == 0) &&
+        CanUseCbzTbzForComparison(compiler, left.reg(), true_condition,
+                                  labels)) {
+      EmitCbzTbz(left.reg(), compiler, true_condition, labels,
+                 compiler::kObjectBytes);
+      return kInvalidCondition;
+    }
+    __ CompareObject(left.reg(), right.constant());
+  } else {
+    __ CompareObjectRegisters(left.reg(), right.reg());
+  }
+  return true_condition;
+}
+
 // Similar to ComparisonInstr::EmitComparisonCode, may either:
 //   - emit comparison code and return a valid condition in which case the
 //     caller is expected to emit a branch to the true label based on that
@@ -867,13 +918,13 @@
       RELEASE_ASSERT(ok);
       if (value == 0 && CanUseCbzTbzForComparison(compiler, left.reg(),
                                                   true_condition, labels)) {
-        EmitCbzTbz(left.reg(), compiler, true_condition, labels);
+        EmitCbzTbz(left.reg(), compiler, true_condition, labels,
+                   compiler::kEightBytes);
         return kInvalidCondition;
       }
       __ CompareImmediate(left.reg(), value);
     } else {
-      ASSERT(constant->representation() == kTagged);
-      __ CompareObject(left.reg(), right.constant());
+      UNREACHABLE();
     }
   } else {
     __ CompareRegisters(left.reg(), right.reg());
@@ -949,7 +1000,9 @@
 
 Condition EqualityCompareInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
                                                    BranchLabels labels) {
-  if (operation_cid() == kSmiCid || operation_cid() == kMintCid) {
+  if (operation_cid() == kSmiCid) {
+    return EmitSmiComparisonOp(compiler, locs(), kind(), labels);
+  } else if (operation_cid() == kMintCid) {
     return EmitInt64ComparisonOp(compiler, locs(), kind(), labels);
   } else {
     ASSERT(operation_cid() == kDoubleCid);
@@ -976,9 +1029,9 @@
   if (right.IsConstant()) {
     ASSERT(right.constant().IsSmi());
     const int64_t imm = static_cast<int64_t>(right.constant().ptr());
-    __ TestImmediate(left, imm);
+    __ TestImmediate(left, imm, compiler::kObjectBytes);
   } else {
-    __ tst(left, compiler::Operand(right.reg()));
+    __ tst(left, compiler::Operand(right.reg()), compiler::kObjectBytes);
   }
   Condition true_condition = (kind() == Token::kNE) ? NE : EQ;
   return true_condition;
@@ -1070,7 +1123,9 @@
 
 Condition RelationalOpInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
                                                 BranchLabels labels) {
-  if (operation_cid() == kSmiCid || operation_cid() == kMintCid) {
+  if (operation_cid() == kSmiCid) {
+    return EmitSmiComparisonOp(compiler, locs(), kind(), labels);
+  } else if (operation_cid() == kMintCid) {
     return EmitInt64ComparisonOp(compiler, locs(), kind(), labels);
   } else {
     ASSERT(operation_cid() == kDoubleCid);
@@ -3383,20 +3438,12 @@
     ASSERT((0 < value) && (value < kCountLimit));
     if (shift_left->can_overflow()) {
       // Check for overflow (preserve left).
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ LslImmediate(TMP, left, value);
-      __ cmp(left, compiler::Operand(TMP, ASR, value));
-#else
-      __ LslImmediate(TMP, left, value, compiler::kFourBytes);
-      __ cmpw(left, compiler::Operand(TMP, ASR, value));
-#endif
+      __ LslImmediate(TMP, left, value, compiler::kObjectBytes);
+      __ cmp(left, compiler::Operand(TMP, ASR, value), compiler::kObjectBytes);
       __ b(deopt, NE);  // Overflow.
     }
     // Shift for result now we know there is no overflow.
-    __ LslImmediate(result, left, value);
-#if defined(DART_COMPRESSED_POINTERS)
-    __ sxtw(result, result);
-#endif
+    __ LslImmediate(result, left, value, compiler::kObjectBytes);
     return;
   }
 
@@ -3410,7 +3457,7 @@
     if (obj.IsSmi()) {
       const intptr_t left_int = Smi::Cast(obj).Value();
       if (left_int == 0) {
-        __ CompareRegisters(right, ZR);
+        __ CompareObjectRegisters(right, ZR);
         __ b(deopt, MI);
         __ mov(result, ZR);
         return;
@@ -3420,14 +3467,11 @@
       const bool right_needs_check =
           !RangeUtils::IsWithin(right_range, 0, max_right - 1);
       if (right_needs_check) {
-        __ CompareImmediate(right, static_cast<int64_t>(Smi::New(max_right)));
+        __ CompareObject(right, Smi::ZoneHandle(Smi::New(max_right)));
         __ b(deopt, CS);
       }
       __ SmiUntag(TMP, right);
-      __ lslv(result, left, TMP);
-#if defined(DART_COMPRESSED_POINTERS)
-      __ sxtw(result, result);
-#endif
+      __ lslv(result, left, TMP, compiler::kObjectBytes);
     }
     return;
   }
@@ -3438,23 +3482,23 @@
     if (right_needs_check) {
       if (!RangeUtils::IsPositive(right_range)) {
         ASSERT(shift_left->CanDeoptimize());
-        __ CompareRegisters(right, ZR);
+        __ CompareObjectRegisters(right, ZR);
         __ b(deopt, MI);
       }
 
-      __ CompareImmediate(right, static_cast<int64_t>(Smi::New(Smi::kBits)));
+      __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
       __ csel(result, ZR, result, CS);
       __ SmiUntag(TMP, right);
-      __ lslv(TMP, left, TMP);
+      __ lslv(TMP, left, TMP, compiler::kObjectBytes);
       __ csel(result, TMP, result, CC);
     } else {
       __ SmiUntag(TMP, right);
-      __ lslv(result, left, TMP);
+      __ lslv(result, left, TMP, compiler::kObjectBytes);
     }
   } else {
     if (right_needs_check) {
       ASSERT(shift_left->CanDeoptimize());
-      __ CompareImmediate(right, static_cast<int64_t>(Smi::New(Smi::kBits)));
+      __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
       __ b(deopt, CS);
     }
     // Left is not a constant.
@@ -3462,18 +3506,12 @@
     __ SmiUntag(TMP, right);
     // Overflow test (preserve left, right, and TMP);
     const Register temp = locs.temp(0).reg();
-#if !defined(DART_COMPRESSED_POINTERS)
-    __ lslv(temp, left, TMP);
-    __ asrv(TMP2, temp, TMP);
-    __ CompareRegisters(left, TMP2);
-#else
-    __ lslvw(temp, left, TMP);
-    __ asrvw(TMP2, temp, TMP);
-    __ cmpw(left, compiler::Operand(TMP2));
-#endif
+    __ lslv(temp, left, TMP, compiler::kObjectBytes);
+    __ asrv(TMP2, temp, TMP, compiler::kObjectBytes);
+    __ cmp(left, compiler::Operand(TMP2), compiler::kObjectBytes);
     __ b(deopt, NE);  // Overflow.
     // Shift for result now we know there is no overflow.
-    __ lslv(result, left, TMP);
+    __ lslv(result, left, TMP, compiler::kObjectBytes);
   }
 }
 
@@ -3554,21 +3592,11 @@
 
   switch (op_kind()) {
     case Token::kADD:
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ adds(result, left, compiler::Operand(right));
-#else
-      __ addsw(result, left, compiler::Operand(right));
-      __ sxtw(result, result);
-#endif
+      __ adds(result, left, compiler::Operand(right), compiler::kObjectBytes);
       __ b(slow_path->entry_label(), VS);
       break;
     case Token::kSUB:
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ subs(result, left, compiler::Operand(right));
-#else
-      __ subsw(result, left, compiler::Operand(right));
-      __ sxtw(result, result);
-#endif
+      __ subs(result, left, compiler::Operand(right), compiler::kObjectBytes);
       __ b(slow_path->entry_label(), VS);
       break;
     case Token::kMUL:
@@ -3600,37 +3628,36 @@
     case Token::kSHL:
       ASSERT(result != left);
       ASSERT(result != right);
-      __ CompareImmediate(right, static_cast<int64_t>(Smi::New(Smi::kBits)));
+      __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
       __ b(slow_path->entry_label(), CS);
 
       __ SmiUntag(TMP, right);
-      __ lslv(result, left, TMP);
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ asrv(TMP2, result, TMP);
-      __ CompareRegisters(left, TMP2);
-#else
-      __ asrvw(TMP2, result, TMP);
-      __ cmp(left, compiler::Operand(TMP2, SXTW, 0));
-#endif
+      __ lslv(result, left, TMP, compiler::kObjectBytes);
+      __ asrv(TMP2, result, TMP, compiler::kObjectBytes);
+      __ cmp(left, compiler::Operand(TMP2), compiler::kObjectBytes);
       __ b(slow_path->entry_label(), NE);  // Overflow.
       break;
     case Token::kSHR:
+      ASSERT(result != left);
+      ASSERT(result != right);
+      __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
+      __ b(slow_path->entry_label(), CS);
+
+      __ SmiUntag(result, right);
+      __ SmiUntag(TMP, left);
+      __ asrv(result, TMP, result, compiler::kObjectBytes);
+      __ SmiTag(result);
+      break;
     case Token::kUSHR:
       ASSERT(result != left);
       ASSERT(result != right);
-      __ CompareImmediate(right, static_cast<int64_t>(Smi::New(kBitsPerInt64)));
+      __ CompareObject(right, Smi::ZoneHandle(Smi::New(kBitsPerInt64)));
       __ b(slow_path->entry_label(), UNSIGNED_GREATER_EQUAL);
 
       __ SmiUntag(result, right);
       __ SmiUntag(TMP, left);
-      if (op_kind() == Token::kSHR) {
-        __ asrv(result, TMP, result);
-        __ SmiTag(result);
-      } else {
-        ASSERT(op_kind() == Token::kUSHR);
-        __ lsrv(result, TMP, result);
-        __ SmiTagAndBranchIfOverflow(result, slow_path->entry_label());
-      }
+      __ lsrv(result, TMP, result);
+      __ SmiTagAndBranchIfOverflow(result, slow_path->entry_label());
       break;
     default:
       UNIMPLEMENTED();
@@ -3722,7 +3749,7 @@
 Condition CheckedSmiComparisonInstr::EmitComparisonCode(
     FlowGraphCompiler* compiler,
     BranchLabels labels) {
-  return EmitInt64ComparisonOp(compiler, locs(), kind(), labels);
+  return EmitSmiComparisonOp(compiler, locs(), kind(), labels);
 }
 
 #define EMIT_SMI_CHECK                                                         \
@@ -3848,37 +3875,21 @@
     switch (op_kind()) {
       case Token::kADD: {
         if (deopt == NULL) {
-          // When we can't overflow, prefer 64-bit op to 32-bit op followed by
-          // sign extension.
-          __ AddImmediate(result, left, imm);
+          __ AddImmediate(result, left, imm, compiler::kObjectBytes);
         } else {
-#if !defined(DART_COMPRESSED_POINTERS)
-          __ AddImmediateSetFlags(result, left, imm);
+          __ AddImmediateSetFlags(result, left, imm, compiler::kObjectBytes);
           __ b(deopt, VS);
-#else
-          __ AddImmediateSetFlags(result, left, imm, compiler::kFourBytes);
-          __ b(deopt, VS);
-          __ sxtw(result, result);
-#endif
         }
         break;
       }
       case Token::kSUB: {
         if (deopt == NULL) {
-          // When we can't overflow, prefer 64-bit op to 32-bit op followed by
-          // sign extension.
           __ AddImmediate(result, left, -imm);
         } else {
           // Negating imm and using AddImmediateSetFlags would not detect the
           // overflow when imm == kMinInt64.
-#if !defined(DART_COMPRESSED_POINTERS)
-          __ SubImmediateSetFlags(result, left, imm);
+          __ SubImmediateSetFlags(result, left, imm, compiler::kObjectBytes);
           __ b(deopt, VS);
-#else
-          __ SubImmediateSetFlags(result, left, imm, compiler::kFourBytes);
-          __ b(deopt, VS);
-          __ sxtw(result, result);
-#endif
         }
         break;
       }
@@ -3911,14 +3922,22 @@
         const intptr_t shift_count =
             Utils::ShiftForPowerOfTwo(Utils::Abs(value)) + kSmiTagSize;
         ASSERT(kSmiTagSize == 1);
+#if !defined(DART_COMPRESSED_POINTERS)
         __ AsrImmediate(TMP, left, 63);
+#else
+        __ AsrImmediate(TMP, left, 31, compiler::kFourBytes);
+#endif
         ASSERT(shift_count > 1);  // 1, -1 case handled above.
         const Register temp = TMP2;
+#if !defined(DART_COMPRESSED_POINTERS)
         __ add(temp, left, compiler::Operand(TMP, LSR, 64 - shift_count));
+#else
+        __ addw(temp, left, compiler::Operand(TMP, LSR, 32 - shift_count));
+#endif
         ASSERT(shift_count > 0);
-        __ AsrImmediate(result, temp, shift_count);
+        __ AsrImmediate(result, temp, shift_count, compiler::kObjectBytes);
         if (value < 0) {
-          __ sub(result, ZR, compiler::Operand(result));
+          __ sub(result, ZR, compiler::Operand(result), compiler::kObjectBytes);
         }
         __ SmiTag(result);
         break;
@@ -3936,12 +3955,18 @@
         __ XorImmediate(result, left, imm);
         break;
       case Token::kSHR: {
-        // Asr operation masks the count to 6 bits.
+        // Asr operation masks the count to 6/5 bits.
+#if !defined(DART_COMPRESSED_POINTERS)
         const intptr_t kCountLimit = 0x3F;
+#else
+        const intptr_t kCountLimit = 0x1F;
+#endif
         intptr_t value = Smi::Cast(constant).Value();
         __ AsrImmediate(result, left,
-                        Utils::Minimum(value + kSmiTagSize, kCountLimit));
+                        Utils::Minimum(value + kSmiTagSize, kCountLimit),
+                        compiler::kObjectBytes);
         __ SmiTag(result);
+        // BOGUS: this could be one sbfiz
         break;
       }
       case Token::kUSHR: {
@@ -3971,31 +3996,19 @@
   switch (op_kind()) {
     case Token::kADD: {
       if (deopt == NULL) {
-        __ add(result, left, compiler::Operand(right));
+        __ add(result, left, compiler::Operand(right), compiler::kObjectBytes);
       } else {
-#if !defined(DART_COMPRESSED_POINTERS)
-        __ adds(result, left, compiler::Operand(right));
+        __ adds(result, left, compiler::Operand(right), compiler::kObjectBytes);
         __ b(deopt, VS);
-#else
-        __ addsw(result, left, compiler::Operand(right));
-        __ b(deopt, VS);
-        __ sxtw(result, result);
-#endif
       }
       break;
     }
     case Token::kSUB: {
       if (deopt == NULL) {
-        __ sub(result, left, compiler::Operand(right));
+        __ sub(result, left, compiler::Operand(right), compiler::kObjectBytes);
       } else {
-#if !defined(DART_COMPRESSED_POINTERS)
-        __ subs(result, left, compiler::Operand(right));
+        __ subs(result, left, compiler::Operand(right), compiler::kObjectBytes);
         __ b(deopt, VS);
-#else
-        __ subsw(result, left, compiler::Operand(right));
-        __ b(deopt, VS);
-        __ sxtw(result, result);
-#endif
       }
       break;
     }
@@ -4037,20 +4050,20 @@
     case Token::kTRUNCDIV: {
       if (RangeUtils::CanBeZero(right_range())) {
         // Handle divide by zero in runtime.
-        __ cbz(deopt, right);
+        __ cbz(deopt, right, compiler::kObjectBytes);
       }
       const Register temp = TMP2;
       __ SmiUntag(temp, left);
       __ SmiUntag(TMP, right);
 
-      __ sdiv(result, temp, TMP);
+      __ sdiv(result, temp, TMP, compiler::kObjectBytes);
       if (RangeUtils::Overlaps(right_range(), -1, -1)) {
         // Check the corner case of dividing the 'MIN_SMI' with -1, in which
         // case we cannot tag the result.
 #if !defined(DART_COMPRESSED_POINTERS)
         __ CompareImmediate(result, 0x4000000000000000LL);
 #else
-        __ CompareImmediate(result, 0x40000000LL);
+        __ CompareImmediate(result, 0x40000000LL, compiler::kFourBytes);
 #endif
         __ b(deopt, EQ);
       }
@@ -4060,16 +4073,17 @@
     case Token::kMOD: {
       if (RangeUtils::CanBeZero(right_range())) {
         // Handle divide by zero in runtime.
-        __ cbz(deopt, right);
+        __ cbz(deopt, right, compiler::kObjectBytes);
       }
       const Register temp = TMP2;
       __ SmiUntag(temp, left);
       __ SmiUntag(TMP, right);
 
-      __ sdiv(result, temp, TMP);
+      __ sdiv(result, temp, TMP, compiler::kObjectBytes);
 
       __ SmiUntag(TMP, right);
-      __ msub(result, TMP, result, temp);  // result <- left - right * result
+      __ msub(result, TMP, result, temp,
+              compiler::kObjectBytes);  // result <- left - right * result
       __ SmiTag(result);
       //  res = left % right;
       //  if (res < 0) {
@@ -4080,12 +4094,12 @@
       //    }
       //  }
       compiler::Label done;
-      __ CompareRegisters(result, ZR);
+      __ CompareObjectRegisters(result, ZR);
       __ b(&done, GE);
       // Result is negative, adjust it.
-      __ CompareRegisters(right, ZR);
-      __ sub(TMP, result, compiler::Operand(right));
-      __ add(result, result, compiler::Operand(right));
+      __ CompareObjectRegisters(right, ZR);
+      __ sub(TMP, result, compiler::Operand(right), compiler::kObjectBytes);
+      __ add(result, result, compiler::Operand(right), compiler::kObjectBytes);
       __ csel(result, TMP, result, LT);
       __ Bind(&done);
       break;
@@ -4095,16 +4109,20 @@
         __ tbnz(deopt, right, compiler::target::kSmiBits + kSmiTagSize);
       }
       __ SmiUntag(TMP, right);
-      // asrv operation masks the count to 6 bits.
+      // asrv[w] operation masks the count to 6/5 bits.
+#if !defined(DART_COMPRESSED_POINTERS)
       const intptr_t kCountLimit = 0x3F;
+#else
+      const intptr_t kCountLimit = 0x1F;
+#endif
       if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(), kCountLimit)) {
         __ LoadImmediate(TMP2, kCountLimit);
-        __ CompareRegisters(TMP, TMP2);
+        __ CompareObjectRegisters(TMP, TMP2);
         __ csel(TMP, TMP2, TMP, GT);
       }
       const Register temp = locs()->temp(0).reg();
       __ SmiUntag(temp, left);
-      __ asrv(result, temp, TMP);
+      __ asrv(result, temp, TMP, compiler::kObjectBytes);
       __ SmiTag(result);
       break;
     }
@@ -4291,7 +4309,11 @@
     case kUnboxedDouble: {
       const VRegister result = locs()->out(0).fpu_reg();
       __ SmiUntag(TMP, box);
+#if !defined(DART_COMPRESSED_POINTERS)
       __ scvtfdx(result, TMP);
+#else
+      __ scvtfdw(result, TMP);
+#endif
       break;
     }
 
@@ -4400,7 +4422,8 @@
     ASSERT(from_representation() == kUnboxedUint32);
     // A 32 bit positive Smi has one tag bit and one unused sign bit,
     // leaving only 30 bits for the payload.
-    __ ubfiz(out, value, kSmiTagSize, compiler::target::kSmiBits);
+    // __ ubfiz(out, value, kSmiTagSize, compiler::target::kSmiBits);
+    __ LslImmediate(out, value, kSmiTagSize, compiler::kFourBytes);
     if (ValueFitsSmi()) {
       return;
     }
@@ -4414,7 +4437,7 @@
   if (from_representation() == kUnboxedInt32) {
     __ sxtw(temp, value);  // Sign-extend.
   } else {
-    __ ubfiz(temp, value, 0, 32);  // Zero extend word.
+    __ uxtw(temp, value);  // Zero-extend.
   }
   __ StoreToOffset(temp, out, Mint::value_offset() - kHeapObjectTag);
   __ Bind(&done);
@@ -4474,9 +4497,7 @@
   // which causes the overflow flag to be set.
   __ b(&done, NO_OVERFLOW);
 #else
-  __ LslImmediate(out, in, kSmiTagSize,
-                  compiler::kFourBytes);  // SmiTag (32-bit);
-  __ sxtw(out, out);                      // Sign-extend.
+  __ sbfiz(out, in, kSmiTagSize, 31);  // SmiTag + sign-extend.
   __ cmp(in, compiler::Operand(out, ASR, kSmiTagSize));
   __ b(&done, EQ);
 #endif
@@ -4910,7 +4931,7 @@
 
   const Register vs[] = {v0, v1, v2, v3};
   for (intptr_t i = 0; i < 4; i++) {
-    __ CompareRegisters(vs[i], TMP2);
+    __ CompareObjectRegisters(vs[i], TMP2);
     __ csel(TMP, temp, ZR, EQ);
     __ vinsw(result, i, TMP);
   }
@@ -5207,7 +5228,7 @@
   const Register left = locs()->in(0).reg();
   const Register right = locs()->in(1).reg();
   const Register result = locs()->out(0).reg();
-  __ CompareRegisters(left, right);
+  __ CompareObjectRegisters(left, right);
   ASSERT(result == left);
   if (is_min) {
     __ csel(result, right, left, GT);
@@ -5236,12 +5257,7 @@
     case Token::kNEGATE: {
       compiler::Label* deopt =
           compiler->AddDeoptStub(deopt_id(), ICData::kDeoptUnaryOp);
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ subs(result, ZR, compiler::Operand(value));
-#else
-      __ subsw(result, ZR, compiler::Operand(value));
-      __ sxtw(result, result);
-#endif
+      __ subs(result, ZR, compiler::Operand(value), compiler::kObjectBytes);
       __ b(deopt, VS);
       break;
     }
@@ -5407,13 +5423,13 @@
   __ fcmpd(value, value);
   __ b(deopt, VS);
 
-#if !defined(DART_COMPRESSED_POINTERS)
   __ fcvtzdsx(result, value);
+
+#if !defined(DART_COMPRESSED_POINTERS)
   // Check for overflow and that it fits into Smi.
   __ CompareImmediate(result, 0xC000000000000000);
   __ b(deopt, MI);
 #else
-  __ fcvtzdsw(result, value);
   // Check for overflow and that it fits into Smi.
   __ AsrImmediate(TMP, result, 30);
   __ cmp(TMP, compiler::Operand(result, ASR, 63));
@@ -5678,25 +5694,25 @@
   const Register result_mod = pair->At(1).reg();
   if (RangeUtils::CanBeZero(divisor_range())) {
     // Handle divide by zero in runtime.
-    __ CompareRegisters(right, ZR);
+    __ CompareObjectRegisters(right, ZR);
     __ b(deopt, EQ);
   }
 
   __ SmiUntag(result_mod, left);
   __ SmiUntag(TMP, right);
 
-  __ sdiv(result_div, result_mod, TMP);
-
   // Check the corner case of dividing the 'MIN_SMI' with -1, in which
   // case we cannot tag the result.
 #if !defined(DART_COMPRESSED_POINTERS)
+  __ sdiv(result_div, result_mod, TMP);
   __ CompareImmediate(result_div, 0x4000000000000000);
 #else
-  __ CompareImmediate(result_div, 0x40000000);
+  __ sdivw(result_div, result_mod, TMP);
+  __ CompareImmediate(result_div, 0x40000000, compiler::kFourBytes);
 #endif
   __ b(deopt, EQ);
   // result_mod <- left - right * result_div.
-  __ msub(result_mod, TMP, result_div, result_mod);
+  __ msub(result_mod, TMP, result_div, result_mod, compiler::kObjectBytes);
   __ SmiTag(result_div);
   __ SmiTag(result_mod);
   // Correct MOD result:
@@ -5709,12 +5725,12 @@
   //    }
   //  }
   compiler::Label done;
-  __ CompareRegisters(result_mod, ZR);
+  __ CompareObjectRegisters(result_mod, ZR);
   __ b(&done, GE);
   // Result is negative, adjust it.
-  __ CompareRegisters(right, ZR);
-  __ sub(TMP2, result_mod, compiler::Operand(right));
-  __ add(TMP, result_mod, compiler::Operand(right));
+  __ CompareObjectRegisters(right, ZR);
+  __ sub(TMP2, result_mod, compiler::Operand(right), compiler::kObjectBytes);
+  __ add(TMP, result_mod, compiler::Operand(right), compiler::kObjectBytes);
   __ csel(result_mod, TMP, TMP2, GE);
   __ Bind(&done);
 }
@@ -5898,7 +5914,7 @@
   if (index_loc.IsConstant()) {
     const Register length = length_loc.reg();
     const Smi& index = Smi::Cast(index_loc.constant());
-    __ CompareImmediate(length, static_cast<int64_t>(index.ptr()));
+    __ CompareObject(length, index);
     __ b(deopt, LS);
   } else if (length_loc.IsConstant()) {
     const Smi& length = Smi::Cast(length_loc.constant());
@@ -5907,10 +5923,10 @@
       __ BranchIfNotSmi(index, deopt);
     }
     if (length.Value() == Smi::kMaxValue) {
-      __ tst(index, compiler::Operand(index));
+      __ tst(index, compiler::Operand(index), compiler::kObjectBytes);
       __ b(deopt, MI);
     } else {
-      __ CompareImmediate(index, static_cast<int64_t>(length.ptr()));
+      __ CompareObject(index, length);
       __ b(deopt, CS);
     }
   } else {
@@ -5919,7 +5935,7 @@
     if (index_cid != kSmiCid) {
       __ BranchIfNotSmi(index, deopt);
     }
-    __ CompareRegisters(index, length);
+    __ CompareObjectRegisters(index, length);
     __ b(deopt, CS);
   }
 }
@@ -6759,7 +6775,6 @@
     }
     case kUnboxedInt64: {
       ASSERT(to() == kUnboxedDouble);
-
       const Register from_reg = locs()->in(0).reg();
       const FpuRegister to_reg = locs()->out(0).fpu_reg();
       __ fmovdr(to_reg, from_reg);
@@ -6895,7 +6910,7 @@
   if (!needs_number_check() && compiler::target::IsSmi(obj) &&
       compiler::target::ToRawSmi(obj) == 0 &&
       CanUseCbzTbzForComparison(compiler, reg, orig_cond, labels)) {
-    EmitCbzTbz(reg, compiler, orig_cond, labels);
+    EmitCbzTbz(reg, compiler, orig_cond, labels, compiler::kObjectBytes);
     return kInvalidCondition;
   } else {
     return compiler->EmitEqualityRegConstCompare(reg, obj, needs_number_check(),
diff --git a/runtime/vm/compiler/backend/il_test_helper.cc b/runtime/vm/compiler/backend/il_test_helper.cc
index ab32cd4..b860562 100644
--- a/runtime/vm/compiler/backend/il_test_helper.cc
+++ b/runtime/vm/compiler/backend/il_test_helper.cc
@@ -21,8 +21,6 @@
 
 namespace dart {
 
-Definition* const FlowGraphBuilderHelper::kPhiSelfReference = nullptr;
-
 LibraryPtr LoadTestScript(const char* script,
                           Dart_NativeEntryResolver resolver,
                           const char* lib_uri) {
diff --git a/runtime/vm/compiler/backend/il_test_helper.h b/runtime/vm/compiler/backend/il_test_helper.h
index e34a5fe..7ef8db3 100644
--- a/runtime/vm/compiler/backend/il_test_helper.h
+++ b/runtime/vm/compiler/backend/il_test_helper.h
@@ -5,6 +5,7 @@
 #ifndef RUNTIME_VM_COMPILER_BACKEND_IL_TEST_HELPER_H_
 #define RUNTIME_VM_COMPILER_BACKEND_IL_TEST_HELPER_H_
 
+#include <type_traits>
 #include <utility>
 #include <vector>
 
@@ -277,20 +278,48 @@
     return flow_graph_.GetConstant(Double::Handle(Double::NewCanonical(value)));
   }
 
-  static Definition* const kPhiSelfReference;
+  enum class IncomingDefKind {
+    kImmediate,
+    kDelayed,
+  };
+
+  class IncomingDef {
+   public:
+    IncomingDef(BlockEntryInstr* from, Definition* defn)
+        : kind_(IncomingDefKind::kImmediate), from_(from), defn_(defn) {}
+
+    template <typename T,
+              typename = typename std::enable_if<
+                  std::is_base_of<Definition, T>::value>::type>
+    IncomingDef(BlockEntryInstr* from, T** defn_source)
+        : kind_(IncomingDefKind::kDelayed),
+          from_(from),
+          defn_source_(reinterpret_cast<Definition**>(defn_source)) {}
+
+    BlockEntryInstr* from() const { return from_; }
+    Definition* defn() const {
+      return kind_ == IncomingDefKind::kImmediate ? defn_ : *defn_source_;
+    }
+
+   private:
+    IncomingDefKind kind_;
+    BlockEntryInstr* from_;
+    union {
+      Definition* defn_;
+      Definition** defn_source_;
+    };
+  };
 
   PhiInstr* Phi(JoinEntryInstr* join,
-                std::initializer_list<std::pair<BlockEntryInstr*, Definition*>>
-                    incoming) {
+                std::initializer_list<IncomingDef> incoming) {
     auto phi = new PhiInstr(join, incoming.size());
     for (size_t i = 0; i < incoming.size(); i++) {
       auto input = new Value(flow_graph_.constant_dead());
       phi->SetInputAt(i, input);
       input->definition()->AddInputUse(input);
     }
-    for (auto pair : incoming) {
-      pending_phis_.Add({phi, pair.first,
-                         pair.second == kPhiSelfReference ? phi : pair.second});
+    for (auto def : incoming) {
+      pending_phis_.Add({phi, def});
     }
     return phi;
   }
@@ -303,9 +332,9 @@
     for (auto& pending : pending_phis_) {
       auto join = pending.phi->block();
       EXPECT(pending.phi->InputCount() == join->PredecessorCount());
-      auto pred_index = join->IndexOfPredecessor(pending.pred);
+      auto pred_index = join->IndexOfPredecessor(pending.incoming.from());
       EXPECT(pred_index != -1);
-      pending.phi->InputAt(pred_index)->BindTo(pending.defn);
+      pending.phi->InputAt(pred_index)->BindTo(pending.incoming.defn());
     }
   }
 
@@ -347,8 +376,7 @@
 
   struct PendingPhiInput {
     PhiInstr* phi;
-    BlockEntryInstr* pred;
-    Definition* defn;
+    IncomingDef incoming;
   };
   GrowableArray<PendingPhiInput> pending_phis_;
 };
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 971d6cc..681164a 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -88,6 +88,14 @@
   ASSERT(kSmiTagSize == 1);
 
   const Register index = locs()->in(0).reg();
+#if defined(DART_COMPRESSED_POINTERS)
+  // No addressing mode will ignore the upper bits. Cannot use the shorter `orl`
+  // to clear the upper bits as this instructions uses negative indices as part
+  // of FP-relative loads.
+  // TODO(compressed-pointers): Can we guarentee the index is already
+  // sign-extended if always comes for an args-descriptor load?
+  __ movsxd(index, index);
+#endif
 
   switch (representation()) {
     case kTagged:
@@ -111,6 +119,14 @@
                (NoLocation, Register index, Register value)) {
   ASSERT(instr->RequiredInputRepresentation(
              StoreIndexedUnsafeInstr::kIndexPos) == kTagged);  // It is a Smi.
+#if defined(DART_COMPRESSED_POINTERS)
+  // No addressing mode will ignore the upper bits. Cannot use the shorter `orl`
+  // to clear the upper bits as this instructions uses negative indices as part
+  // of FP-relative stores.
+  // TODO(compressed-pointers): Can we guarentee the index is already
+  // sign-extended if always comes for an args-descriptor load?
+  __ movsxd(index, index);
+#endif
   __ movq(compiler::Address(instr->base_reg(), index, TIMES_4, instr->offset()),
           value);
 
@@ -788,6 +804,44 @@
   }
 }
 
+static Condition EmitSmiComparisonOp(FlowGraphCompiler* compiler,
+                                     const LocationSummary& locs,
+                                     Token::Kind kind) {
+  Location left = locs.in(0);
+  Location right = locs.in(1);
+  ASSERT(!left.IsConstant() || !right.IsConstant());
+
+  Condition true_condition = TokenKindToIntCondition(kind);
+  if (left.IsConstant() || right.IsConstant()) {
+    // Ensure constant is on the right.
+    ConstantInstr* constant = NULL;
+    if (left.IsConstant()) {
+      constant = left.constant_instruction();
+      Location tmp = right;
+      right = left;
+      left = tmp;
+      true_condition = FlipCondition(true_condition);
+    } else {
+      constant = right.constant_instruction();
+    }
+
+    if (RepresentationUtils::IsUnboxedInteger(constant->representation())) {
+      int64_t value;
+      const bool ok = compiler::HasIntegerValue(constant->value(), &value);
+      RELEASE_ASSERT(ok);
+      __ OBJ(cmp)(left.reg(), compiler::Immediate(value));
+    } else {
+      ASSERT(constant->representation() == kTagged);
+      __ CompareObject(left.reg(), right.constant());
+    }
+  } else if (right.IsStackSlot()) {
+    __ OBJ(cmp)(left.reg(), LocationToStackSlotAddress(right));
+  } else {
+    __ OBJ(cmp)(left.reg(), right.reg());
+  }
+  return true_condition;
+}
+
 static Condition EmitInt64ComparisonOp(FlowGraphCompiler* compiler,
                                        const LocationSummary& locs,
                                        Token::Kind kind) {
@@ -815,8 +869,7 @@
       RELEASE_ASSERT(ok);
       __ cmpq(left.reg(), compiler::Immediate(value));
     } else {
-      ASSERT(constant->representation() == kTagged);
-      __ CompareObject(left.reg(), right.constant());
+      UNREACHABLE();
     }
   } else if (right.IsStackSlot()) {
     __ cmpq(left.reg(), LocationToStackSlotAddress(right));
@@ -864,7 +917,9 @@
 
 Condition EqualityCompareInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
                                                    BranchLabels labels) {
-  if ((operation_cid() == kSmiCid) || (operation_cid() == kMintCid)) {
+  if (operation_cid() == kSmiCid) {
+    return EmitSmiComparisonOp(compiler, *locs(), kind());
+  } else if (operation_cid() == kMintCid) {
     return EmitInt64ComparisonOp(compiler, *locs(), kind());
   } else {
     ASSERT(operation_cid() == kDoubleCid);
@@ -917,9 +972,10 @@
   if (right.IsConstant()) {
     ASSERT(right.constant().IsSmi());
     const int64_t imm = static_cast<int64_t>(right.constant().ptr());
-    __ TestImmediate(left_reg, compiler::Immediate(imm));
+    __ TestImmediate(left_reg, compiler::Immediate(imm),
+                     compiler::kObjectBytes);
   } else {
-    __ testq(left_reg, right.reg());
+    __ OBJ(test)(left_reg, right.reg());
   }
   Condition true_condition = (kind() == Token::kNE) ? NOT_ZERO : ZERO;
   return true_condition;
@@ -1010,7 +1066,9 @@
 
 Condition RelationalOpInstr::EmitComparisonCode(FlowGraphCompiler* compiler,
                                                 BranchLabels labels) {
-  if (operation_cid() == kSmiCid || operation_cid() == kMintCid) {
+  if (operation_cid() == kSmiCid) {
+    return EmitSmiComparisonOp(compiler, *locs(), kind());
+  } else if (operation_cid() == kMintCid) {
     return EmitInt64ComparisonOp(compiler, *locs(), kind());
   } else {
     ASSERT(operation_cid() == kDoubleCid);
@@ -1614,7 +1672,7 @@
       LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
   locs->set_in(0, Location::RequiresRegister());
   // For tagged index with index_scale=1 as well as untagged index with
-  // index_scale=16 we need a writable register due to assdressing mode
+  // index_scale=16 we need a writable register due to addressing mode
   // restrictions on X64.
   const bool need_writable_index_register =
       (index_scale() == 1 && !index_unboxed_) ||
@@ -1648,6 +1706,16 @@
       // X64 does not support addressing mode using TIMES_16.
       __ SmiTag(index.reg());
       index_scale >>= 1;
+    } else if (!index_unboxed_) {
+#if defined(DART_COMPRESSED_POINTERS)
+      // The upper half of a compressed Smi contains undefined bits, but no x64
+      // addressing mode will ignore these bits. Assume that the index is
+      // non-negative and clear the upper bits, which is shorter than
+      // sign-extension (movsxd). Note: we don't bother to ensure index is a
+      // writable input because any other instructions using it must also not
+      // rely on the upper bits.
+      __ orl(index.reg(), index.reg());
+#endif
     }
   } else {
     ASSERT(index.IsConstant());
@@ -1754,6 +1822,16 @@
 
   if ((index_scale() == 1)) {
     __ SmiUntag(index.reg());
+  } else {
+#if defined(DART_COMPRESSED_POINTERS)
+    // The upper half of a compressed Smi contains undefined bits, but no x64
+    // addressing mode will ignore these bits. Assume that the index is
+    // non-negative and clear the upper bits, which is shorter than
+    // sign-extension (movsxd). Note: we don't bother to ensure index is a
+    // writable input because any other instructions using it must also not
+    // rely on the upper bits.
+    __ orl(index.reg(), index.reg());
+#endif
   }
   Register result = locs()->out(0).reg();
   switch (class_id()) {
@@ -1851,7 +1929,7 @@
       LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
   locs->set_in(0, Location::RequiresRegister());
   // For tagged index with index_scale=1 as well as untagged index with
-  // index_scale=16 we need a writable register due to assdressing mode
+  // index_scale=16 we need a writable register due to addressing mode
   // restrictions on X64.
   const bool need_writable_index_register =
       (index_scale() == 1 && !index_unboxed_) ||
@@ -1923,6 +2001,16 @@
       // X64 does not support addressing mode using TIMES_16.
       __ SmiTag(index.reg());
       index_scale >>= 1;
+    } else if (!index_unboxed_) {
+#if defined(DART_COMPRESSED_POINTERS)
+      // The upper half of a compressed Smi contains undefined bits, but no x64
+      // addressing mode will ignore these bits. Assume that the index is
+      // non-negative and clear the upper bits, which is shorter than
+      // sign-extension (movsxd). Note: we don't bother to ensure index is a
+      // writable input because any other instructions using it must also not
+      // rely on the upper bits.
+      __ orl(index.reg(), index.reg());
+#endif
     }
   } else {
     ASSERT(index.IsConstant());
@@ -3401,37 +3489,20 @@
     if (shift_left->can_overflow()) {
       if (value == 1) {
         // Use overflow flag.
-#if !defined(DART_COMPRESSED_POINTERS)
-        __ shlq(left, compiler::Immediate(1));
+        __ OBJ(shl)(left, compiler::Immediate(1));
         __ j(OVERFLOW, deopt);
-#else
-        __ shll(left, compiler::Immediate(1));
-        __ j(OVERFLOW, deopt);
-        __ movsxd(left, left);
-#endif
         return;
       }
       // Check for overflow.
       Register temp = locs.temp(0).reg();
-      __ movq(temp, left);
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ shlq(left, compiler::Immediate(value));
-      __ sarq(left, compiler::Immediate(value));
-#else
-      __ shll(left, compiler::Immediate(value));
-      __ sarl(left, compiler::Immediate(value));
-      __ movsxd(left, left);
-#endif
-      __ cmpq(left, temp);
+      __ OBJ(mov)(temp, left);
+      __ OBJ(shl)(left, compiler::Immediate(value));
+      __ OBJ(sar)(left, compiler::Immediate(value));
+      __ OBJ(cmp)(left, temp);
       __ j(NOT_EQUAL, deopt);  // Overflow.
     }
     // Shift for result now we know there is no overflow.
-    __ shlq(left, compiler::Immediate(value));
-#if defined(DART_COMPRESSED_POINTERS)
-    if (shift_left->is_truncating()) {
-      __ movsxd(left, left);
-    }
-#endif
+    __ OBJ(shl)(left, compiler::Immediate(value));
     return;
   }
 
@@ -3445,7 +3516,8 @@
     if (obj.IsSmi()) {
       const intptr_t left_int = Smi::Cast(obj).Value();
       if (left_int == 0) {
-        __ CompareImmediate(right, compiler::Immediate(0));
+        __ CompareImmediate(right, compiler::Immediate(0),
+                            compiler::kObjectBytes);
         __ j(NEGATIVE, deopt);
         return;
       }
@@ -3453,20 +3525,11 @@
       const bool right_needs_check =
           !RangeUtils::IsWithin(right_range, 0, max_right - 1);
       if (right_needs_check) {
-        __ CompareImmediate(
-            right,
-            compiler::Immediate(static_cast<int64_t>(Smi::New(max_right))));
+        __ CompareObject(right, Smi::ZoneHandle(Smi::New(max_right)));
         __ j(ABOVE_EQUAL, deopt);
       }
       __ SmiUntag(right);
-      __ shlq(left, right);
-#if defined(DART_COMPRESSED_POINTERS)
-      if (shift_left->is_truncating()) {
-        __ movsxd(left, left);
-      }
-#endif
-    } else {
-      __ int3();  //???
+      __ OBJ(shl)(left, right);
     }
     return;
   }
@@ -3480,59 +3543,41 @@
           (right_range == NULL) || !right_range->IsPositive();
       if (right_may_be_negative) {
         ASSERT(shift_left->CanDeoptimize());
-        __ CompareImmediate(right, compiler::Immediate(0));
+        __ CompareImmediate(right, compiler::Immediate(0),
+                            compiler::kObjectBytes);
         __ j(NEGATIVE, deopt);
       }
       compiler::Label done, is_not_zero;
-      __ CompareImmediate(
-          right,
-          compiler::Immediate(static_cast<int64_t>(Smi::New(Smi::kBits))));
+      __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
       __ j(BELOW, &is_not_zero, compiler::Assembler::kNearJump);
       __ xorq(left, left);
       __ jmp(&done, compiler::Assembler::kNearJump);
       __ Bind(&is_not_zero);
       __ SmiUntag(right);
-      __ shlq(left, right);
+      __ OBJ(shl)(left, right);
       __ Bind(&done);
     } else {
       __ SmiUntag(right);
-      __ shlq(left, right);
+      __ OBJ(shl)(left, right);
     }
-#if defined(DART_COMPRESSED_POINTERS)
-    if (shift_left->is_truncating()) {
-      __ movsxd(left, left);
-    }
-#endif
   } else {
     if (right_needs_check) {
       ASSERT(shift_left->CanDeoptimize());
-      __ CompareImmediate(
-          right,
-          compiler::Immediate(static_cast<int64_t>(Smi::New(Smi::kBits))));
+      __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
       __ j(ABOVE_EQUAL, deopt);
     }
     // Left is not a constant.
     Register temp = locs.temp(0).reg();
     // Check if count too large for handling it inlined.
-#if !defined(DART_COMPRESSED_POINTERS)
-    __ movq(temp, left);
-#else
-    __ movl(temp, left);
-#endif
+    __ OBJ(mov)(temp, left);
     __ SmiUntag(right);
     // Overflow test (preserve temp and right);
-#if !defined(DART_COMPRESSED_POINTERS)
-    __ shlq(left, right);
-    __ sarq(left, right);
-    __ cmpq(left, temp);
-#else
-    __ shll(temp, right);
-    __ sarl(temp, right);
-    __ cmpl(temp, left);
-#endif
+    __ OBJ(shl)(left, right);
+    __ OBJ(sar)(left, right);
+    __ OBJ(cmp)(left, temp);
     __ j(NOT_EQUAL, deopt);  // Overflow.
     // Shift for result now we know there is no overflow.
-    __ shlq(left, right);
+    __ OBJ(shl)(left, right);
     ASSERT(!shift_left->is_truncating());
   }
 }
@@ -3637,37 +3682,19 @@
   switch (op_kind()) {
     case Token::kADD:
       __ movq(result, left);
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ addq(result, right);
+      __ OBJ(add)(result, right);
       __ j(OVERFLOW, slow_path->entry_label());
-#else
-      __ addl(result, right);
-      __ j(OVERFLOW, slow_path->entry_label());
-      __ movsxd(result, result);
-#endif
       break;
     case Token::kSUB:
       __ movq(result, left);
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ subq(result, right);
+      __ OBJ(sub)(result, right);
       __ j(OVERFLOW, slow_path->entry_label());
-#else
-      __ subl(result, right);
-      __ j(OVERFLOW, slow_path->entry_label());
-      __ movsxd(result, result);
-#endif
       break;
     case Token::kMUL:
       __ movq(result, left);
       __ SmiUntag(result);
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ imulq(result, right);
+      __ OBJ(imul)(result, right);
       __ j(OVERFLOW, slow_path->entry_label());
-#else
-      __ imull(result, right);
-      __ j(OVERFLOW, slow_path->entry_label());
-      __ movsxd(result, result);
-#endif
       break;
     case Token::kBIT_OR:
       ASSERT(left == result);
@@ -3684,54 +3711,50 @@
     case Token::kSHL:
       ASSERT(result != right);
       ASSERT(locs()->temp(0).reg() == RCX);
-      __ cmpq(right, compiler::Immediate(Smi::RawValue(Smi::kBits)));
+      __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
       __ j(ABOVE_EQUAL, slow_path->entry_label());
 
       __ movq(RCX, right);
       __ SmiUntag(RCX);
       __ movq(result, left);
-#if !defined(DART_COMPRESSED_POINTERS)
       __ shlq(result, RCX);
       __ movq(TMP, result);
-      __ sarq(TMP, RCX);
-      __ cmpq(TMP, left);
+      __ OBJ(sar)(TMP, RCX);
+      __ OBJ(cmp)(TMP, left);
       __ j(NOT_EQUAL, slow_path->entry_label());
-#else
-      __ shll(result, RCX);
-      __ movq(TMP, result);
-      __ sarl(TMP, RCX);
-      __ cmpl(TMP, left);
-      __ j(NOT_EQUAL, slow_path->entry_label());
-      __ movsxd(result, result);
-#endif
       break;
     case Token::kSHR:
-    case Token::kUSHR: {
-      compiler::Label shift_count_ok;
       ASSERT(result != right);
       ASSERT(locs()->temp(0).reg() == RCX);
-      __ cmpq(right, compiler::Immediate(Smi::RawValue(kBitsPerInt64)));
+      __ CompareObject(right, Smi::ZoneHandle(Smi::New(Smi::kBits)));
       __ j(ABOVE_EQUAL, slow_path->entry_label());
 
       __ movq(RCX, right);
       __ SmiUntag(RCX);
       __ movq(result, left);
       __ SmiUntag(result);
-      if (op_kind() == Token::kSHR) {
-        __ sarq(result, RCX);
-        __ SmiTag(result);
-      } else {
-        ASSERT(op_kind() == Token::kUSHR);
-        __ shrq(result, RCX);
-        __ SmiTag(result);
-        __ j(OVERFLOW, slow_path->entry_label());
+      __ OBJ(sar)(result, RCX);
+      __ SmiTag(result);
+      break;
+    case Token::kUSHR: {
+      ASSERT(result != right);
+      ASSERT(locs()->temp(0).reg() == RCX);
+      __ CompareObject(right, Smi::ZoneHandle(Smi::New(kBitsPerInt64)));
+      __ j(ABOVE_EQUAL, slow_path->entry_label());
+
+      __ movq(RCX, right);
+      __ SmiUntag(RCX);
+      __ movq(result, left);
+      __ SmiUntagAndSignExtend(result);
+      __ shrq(result, RCX);
+      __ shlq(result, compiler::Immediate(1));  // SmiTag, keep hi bits.
+      __ j(OVERFLOW, slow_path->entry_label());
 #if defined(DART_COMPRESSED_POINTERS)
-        const Register temp = locs()->temp(0).reg();
-        __ movsxd(temp, result);
-        __ cmpq(temp, result);
-        __ j(NOT_EQUAL, slow_path->entry_label());
+      const Register temp = locs()->temp(0).reg();
+      __ movsxd(temp, result);
+      __ cmpq(temp, result);
+      __ j(NOT_EQUAL, slow_path->entry_label());
 #endif  // defined(DART_COMPRESSED_POINTERS)
-      }
       break;
     }
     default:
@@ -3826,7 +3849,7 @@
 Condition CheckedSmiComparisonInstr::EmitComparisonCode(
     FlowGraphCompiler* compiler,
     BranchLabels labels) {
-  return EmitInt64ComparisonOp(compiler, *locs(), kind());
+  return EmitSmiComparisonOp(compiler, *locs(), kind());
 }
 
 #define EMIT_SMI_CHECK                                                         \
@@ -4030,57 +4053,21 @@
     const int64_t imm = static_cast<int64_t>(constant.ptr());
     switch (op_kind()) {
       case Token::kADD: {
-        if (deopt == NULL) {
-          // When we can't overflow, prefer 64-bit op to 32-bit op followed by
-          // sign extension.
-          __ AddImmediate(left, compiler::Immediate(imm));
-        } else {
-#if !defined(DART_COMPRESSED_POINTERS)
-          __ AddImmediate(left, compiler::Immediate(imm));
-          __ j(OVERFLOW, deopt);
-#else
-          __ AddImmediate(left, compiler::Immediate(imm), compiler::kFourBytes);
-          __ j(OVERFLOW, deopt);
-          __ movsxd(left, left);
-#endif
-        }
+        __ AddImmediate(left, compiler::Immediate(imm), compiler::kObjectBytes);
+        if (deopt != NULL) __ j(OVERFLOW, deopt);
         break;
       }
       case Token::kSUB: {
-        if (deopt == NULL) {
-          // When we can't overflow, prefer 64-bit op to 32-bit op followed by
-          // sign extension.
-          __ SubImmediate(left, compiler::Immediate(imm));
-        } else {
-#if !defined(DART_COMPRESSED_POINTERS)
-          __ SubImmediate(left, compiler::Immediate(imm));
-          __ j(OVERFLOW, deopt);
-#else
-          __ SubImmediate(left, compiler::Immediate(imm), compiler::kFourBytes);
-          __ j(OVERFLOW, deopt);
-          __ movsxd(left, left);
-#endif
-        }
+        __ SubImmediate(left, compiler::Immediate(imm), compiler::kObjectBytes);
+        if (deopt != NULL) __ j(OVERFLOW, deopt);
         break;
       }
       case Token::kMUL: {
         // Keep left value tagged and untag right value.
         const intptr_t value = Smi::Cast(constant).Value();
-        if (deopt == NULL) {
-          // When we can't overflow, prefer 64-bit op to 32-bit op followed by
-          // sign extension.
-          __ MulImmediate(left, compiler::Immediate(value));
-        } else {
-#if !defined(DART_COMPRESSED_POINTERS)
-          __ MulImmediate(left, compiler::Immediate(value));
-          __ j(OVERFLOW, deopt);
-#else
-          __ MulImmediate(left, compiler::Immediate(value),
-                          compiler::kFourBytes);
-          __ j(OVERFLOW, deopt);
-          __ movsxd(left, left);
-#endif
-        }
+        __ MulImmediate(left, compiler::Immediate(value),
+                        compiler::kObjectBytes);
+        if (deopt != NULL) __ j(OVERFLOW, deopt);
         break;
       }
       case Token::kTRUNCDIV: {
@@ -4095,16 +4082,19 @@
 #if !defined(DART_COMPRESSED_POINTERS)
         __ sarq(temp, compiler::Immediate(63));
 #else
-        // Assumes Smis are sign extended.
-        __ sarq(temp, compiler::Immediate(31));
+        __ sarl(temp, compiler::Immediate(31));
 #endif
         ASSERT(shift_count > 1);  // 1, -1 case handled above.
+#if !defined(DART_COMPRESSED_POINTERS)
         __ shrq(temp, compiler::Immediate(64 - shift_count));
-        __ addq(left, temp);
+#else
+        __ shrl(temp, compiler::Immediate(32 - shift_count));
+#endif
+        __ OBJ(add)(left, temp);
         ASSERT(shift_count > 0);
-        __ sarq(left, compiler::Immediate(shift_count));
+        __ OBJ(sar)(left, compiler::Immediate(shift_count));
         if (value < 0) {
-          __ negq(left);
+          __ OBJ(neg)(left);
         }
         __ SmiTag(left);
         break;
@@ -4126,15 +4116,15 @@
       }
 
       case Token::kSHR: {
-        // sarq operation masks the count to 6 bits.
+        // sarq/l operation masks the count to 6/5 bits.
 #if !defined(DART_COMPRESSED_POINTERS)
         const intptr_t kCountLimit = 0x3F;
 #else
         const intptr_t kCountLimit = 0x1F;
 #endif
         const intptr_t value = Smi::Cast(constant).Value();
-        __ sarq(left, compiler::Immediate(
-                          Utils::Minimum(value + kSmiTagSize, kCountLimit)));
+        __ OBJ(sar)(left, compiler::Immediate(Utils::Minimum(
+                              value + kSmiTagSize, kCountLimit)));
         __ SmiTag(left);
         break;
       }
@@ -4146,9 +4136,9 @@
         const intptr_t kCountLimit = 0x3F;
         const intptr_t value = Smi::Cast(constant).Value();
         ASSERT((value >= 0) && (value <= kCountLimit));
-        __ SmiUntag(left);
+        __ SmiUntagAndSignExtend(left);
         __ shrq(left, compiler::Immediate(value));
-        __ SmiTag(left);
+        __ shlq(left, compiler::Immediate(1));  // SmiTag, keep hi bits.
         if (deopt != nullptr) {
           __ j(OVERFLOW, deopt);
 #if defined(DART_COMPRESSED_POINTERS)
@@ -4172,55 +4162,19 @@
     const compiler::Address& right = LocationToStackSlotAddress(locs()->in(1));
     switch (op_kind()) {
       case Token::kADD: {
-        if (deopt == NULL) {
-          // When we can't overflow, prefer 64-bit op to 32-bit op followed by
-          // sign extension.
-          __ addq(left, right);
-        } else {
-#if !defined(DART_COMPRESSED_POINTERS)
-          __ addq(left, right);
-          __ j(OVERFLOW, deopt);
-#else
-          __ addl(left, right);
-          __ j(OVERFLOW, deopt);
-          __ movsxd(left, left);
-#endif
-        }
+        __ OBJ(add)(left, right);
+        if (deopt != NULL) __ j(OVERFLOW, deopt);
         break;
       }
       case Token::kSUB: {
-        if (deopt == NULL) {
-          // When we can't overflow, prefer 64-bit op to 32-bit op followed by
-          // sign extension.
-          __ subq(left, right);
-        } else {
-#if !defined(DART_COMPRESSED_POINTERS)
-          __ subq(left, right);
-          __ j(OVERFLOW, deopt);
-#else
-          __ subl(left, right);
-          __ j(OVERFLOW, deopt);
-          __ movsxd(left, left);
-#endif
-        }
+        __ OBJ(sub)(left, right);
+        if (deopt != NULL) __ j(OVERFLOW, deopt);
         break;
       }
       case Token::kMUL: {
         __ SmiUntag(left);
-        if (deopt == NULL) {
-          // When we can't overflow, prefer 64-bit op to 32-bit op followed by
-          // sign extension.
-          __ imulq(left, right);
-        } else {
-#if !defined(DART_COMPRESSED_POINTERS)
-          __ imulq(left, right);
-          __ j(OVERFLOW, deopt);
-#else
-          __ imull(left, right);
-          __ j(OVERFLOW, deopt);
-          __ movsxd(left, left);
-#endif
-        }
+        __ OBJ(imul)(left, right);
+        if (deopt != NULL) __ j(OVERFLOW, deopt);
         break;
       }
       case Token::kBIT_AND: {
@@ -4249,55 +4203,19 @@
   Register right = locs()->in(1).reg();
   switch (op_kind()) {
     case Token::kADD: {
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ addq(left, right);
+      __ OBJ(add)(left, right);
       if (deopt != NULL) __ j(OVERFLOW, deopt);
-#else
-      if (deopt == NULL) {
-        // When we can't overflow, prefer 64-bit op to 32-bit op followed by
-        // sign extension.
-        __ addq(left, right);
-      } else {
-        __ addl(left, right);
-        __ j(OVERFLOW, deopt);
-        __ movsxd(left, left);
-      }
-#endif
       break;
     }
     case Token::kSUB: {
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ subq(left, right);
+      __ OBJ(sub)(left, right);
       if (deopt != NULL) __ j(OVERFLOW, deopt);
-#else
-      if (deopt == NULL) {
-        // When we can't overflow, prefer 64-bit op to 32-bit op followed by
-        // sign extension.
-        __ subq(left, right);
-      } else {
-        __ subl(left, right);
-        __ j(OVERFLOW, deopt);
-        __ movsxd(left, left);
-      }
-#endif
       break;
     }
     case Token::kMUL: {
       __ SmiUntag(left);
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ imulq(left, right);
+      __ OBJ(imul)(left, right);
       if (deopt != NULL) __ j(OVERFLOW, deopt);
-#else
-      if (deopt == NULL) {
-        // When we can't overflow, prefer 64-bit op to 32-bit op followed by
-        // sign extension.
-        __ imulq(left, right);
-      } else {
-        __ imull(left, right);
-        __ j(OVERFLOW, deopt);
-        __ movsxd(left, left);
-      }
-#endif
       break;
     }
     case Token::kBIT_AND: {
@@ -4325,7 +4243,7 @@
       ASSERT(result == RAX);
       if (RangeUtils::CanBeZero(right_range())) {
         // Handle divide by zero in runtime.
-        __ testq(right, right);
+        __ OBJ(test)(right, right);
         __ j(ZERO, deopt);
       }
 #if !defined(DART_COMPRESSED_POINTERS)
@@ -4390,7 +4308,7 @@
       ASSERT(result == RDX);
       if (RangeUtils::CanBeZero(right_range())) {
         // Handle divide by zero in runtime.
-        __ testq(right, right);
+        __ OBJ(test)(right, right);
         __ j(ZERO, deopt);
       }
 #if !defined(DART_COMPRESSED_POINTERS)
@@ -4434,23 +4352,23 @@
       //    }
       //  }
       compiler::Label all_done;
-      __ cmpq(result, compiler::Immediate(0));
+      __ OBJ(cmp)(result, compiler::Immediate(0));
       __ j(GREATER_EQUAL, &all_done, compiler::Assembler::kNearJump);
       // Result is negative, adjust it.
       if (RangeUtils::Overlaps(right_range(), -1, 1)) {
         compiler::Label subtract;
-        __ cmpq(right, compiler::Immediate(0));
+        __ OBJ(cmp)(right, compiler::Immediate(0));
         __ j(LESS, &subtract, compiler::Assembler::kNearJump);
-        __ addq(result, right);
+        __ OBJ(add)(result, right);
         __ jmp(&all_done, compiler::Assembler::kNearJump);
         __ Bind(&subtract);
-        __ subq(result, right);
+        __ OBJ(sub)(result, right);
       } else if (right_range()->IsPositive()) {
         // Right is positive.
-        __ addq(result, right);
+        __ OBJ(add)(result, right);
       } else {
         // Right is negative.
-        __ subq(result, right);
+        __ OBJ(sub)(result, right);
       }
       __ Bind(&all_done);
       __ SmiTag(result);
@@ -4458,12 +4376,17 @@
     }
     case Token::kSHR: {
       if (CanDeoptimize()) {
-        __ CompareImmediate(right, compiler::Immediate(0));
+        __ CompareImmediate(right, compiler::Immediate(0),
+                            compiler::kObjectBytes);
         __ j(LESS, deopt);
       }
       __ SmiUntag(right);
-      // sarq operation masks the count to 6 bits.
+      // sarq/l operation masks the count to 6/5 bits.
+#if !defined(DART_COMPRESSED_POINTERS)
       const intptr_t kCountLimit = 0x3F;
+#else
+      const intptr_t kCountLimit = 0x1F;
+#endif
       if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(), kCountLimit)) {
         __ CompareImmediate(right, compiler::Immediate(kCountLimit));
         compiler::Label count_ok;
@@ -4473,13 +4396,14 @@
       }
       ASSERT(right == RCX);  // Count must be in RCX
       __ SmiUntag(left);
-      __ sarq(left, right);
+      __ OBJ(sar)(left, right);
       __ SmiTag(left);
       break;
     }
     case Token::kUSHR: {
       if (deopt != nullptr) {
-        __ CompareImmediate(right, compiler::Immediate(0));
+        __ CompareImmediate(right, compiler::Immediate(0),
+                            compiler::kObjectBytes);
         __ j(LESS, deopt);
       }
       __ SmiUntag(right);
@@ -4488,7 +4412,8 @@
       COMPILE_ASSERT(kCountLimit + 1 == kBitsPerInt64);
       compiler::Label done;
       if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(), kCountLimit)) {
-        __ CompareImmediate(right, compiler::Immediate(kCountLimit));
+        __ CompareImmediate(right, compiler::Immediate(kCountLimit),
+                            compiler::kObjectBytes);
         compiler::Label count_ok;
         __ j(LESS_EQUAL, &count_ok, compiler::Assembler::kNearJump);
         __ xorq(left, left);
@@ -4496,9 +4421,9 @@
         __ Bind(&count_ok);
       }
       ASSERT(right == RCX);  // Count must be in RCX
-      __ SmiUntag(left);
+      __ SmiUntagAndSignExtend(left);
       __ shrq(left, right);
-      __ SmiTag(left);
+      __ shlq(left, compiler::Immediate(1));  // SmiTag, keep hi bits.
       if (deopt != nullptr) {
         __ j(OVERFLOW, deopt);
 #if defined(DART_COMPRESSED_POINTERS)
@@ -4674,14 +4599,14 @@
     case kUnboxedInt64: {
       const Register result = locs()->out(0).reg();
       ASSERT(result == box);
-      __ SmiUntag(box);
+      __ SmiUntagAndSignExtend(box);
       break;
     }
 
     case kUnboxedDouble: {
       const FpuRegister result = locs()->out(0).fpu_reg();
       __ SmiUntag(box);
-      __ cvtsi2sdq(result, box);
+      __ OBJ(cvtsi2sd)(result, box);
       break;
     }
 
@@ -4696,9 +4621,22 @@
   const Register result = locs()->out(0).reg();
   ASSERT(value == result);
   compiler::Label done;
+#if !defined(DART_COMPRESSED_POINTERS)
   __ SmiUntag(value);
   __ j(NOT_CARRY, &done, compiler::Assembler::kNearJump);
   __ movsxw(result, compiler::Address(value, TIMES_2, Mint::value_offset()));
+#else
+  // Cannot speculatively untag because it erases the upper bits needed to
+  // dereference when it is a Mint.
+  // TODO(compressed-pointers): It would probably be cheaper to make
+  // result != value with compressed pointers.
+  compiler::Label not_smi;
+  __ BranchIfNotSmi(value, &not_smi, compiler::Assembler::kNearJump);
+  __ SmiUntagAndSignExtend(value);
+  __ jmp(&done, compiler::Assembler::kNearJump);
+  __ Bind(&not_smi);
+  __ movsxw(result, compiler::FieldAddress(value, Mint::value_offset()));
+#endif
   __ Bind(&done);
 }
 
@@ -4707,10 +4645,22 @@
   const Register result = locs()->out(0).reg();
   ASSERT(value == result);
   compiler::Label done;
+#if !defined(DART_COMPRESSED_POINTERS)
   __ SmiUntag(value);
   __ j(NOT_CARRY, &done, compiler::Assembler::kNearJump);
   __ movq(value, compiler::Address(value, TIMES_2, Mint::value_offset()));
   __ Bind(&done);
+#else
+  // Cannot speculatively untag because it erases the upper bits needed to
+  // dereference when it is a Mint.
+  compiler::Label not_smi;
+  __ BranchIfNotSmi(value, &not_smi, compiler::Assembler::kNearJump);
+  __ SmiUntagAndSignExtend(value);
+  __ jmp(&done, compiler::Assembler::kNearJump);
+  __ Bind(&not_smi);
+  __ movq(value, compiler::FieldAddress(value, Mint::value_offset()));
+  __ Bind(&done);
+#endif
 }
 
 LocationSummary* UnboxInteger32Instr::MakeLocationSummary(Zone* zone,
@@ -4746,23 +4696,42 @@
     // or mint value.
     ASSERT(is_truncating());
     compiler::Label done;
+#if !defined(DART_COMPRESSED_POINTERS)
     __ SmiUntag(value);
     __ j(NOT_CARRY, &done, compiler::Assembler::kNearJump);
     __ movq(value, compiler::Address(value, TIMES_2, Mint::value_offset()));
-    __ Bind(&done);
-#if defined(DART_COMPRESSED_POINTERS)
-    if (is_truncating()) {
-      __ movsxd(value, value);
-    }
+#else
+    // Cannot speculatively untag because it erases the upper bits needed to
+    // dereference when it is a Mint.
+    compiler::Label not_smi;
+    __ BranchIfNotSmi(value, &not_smi, compiler::Assembler::kNearJump);
+    __ SmiUntagAndSignExtend(value);
+    __ jmp(&done, compiler::Assembler::kNearJump);
+    __ Bind(&not_smi);
+    __ movq(value, compiler::FieldAddress(value, Mint::value_offset()));
 #endif
+    __ Bind(&done);
     return;
   } else {
     compiler::Label done;
+#if !defined(DART_COMPRESSED_POINTERS)
     // Optimistically untag value.
     __ SmiUntagOrCheckClass(value, kMintCid, &done);
     __ j(NOT_EQUAL, deopt);
     // Undo untagging by multiplying value with 2.
     __ movq(value, compiler::Address(value, TIMES_2, Mint::value_offset()));
+#else
+    // Cannot speculatively untag because it erases the upper bits needed to
+    // dereference when it is a Mint.
+    compiler::Label not_smi;
+    __ BranchIfNotSmi(value, &not_smi, compiler::Assembler::kNearJump);
+    __ SmiUntagAndSignExtend(value);
+    __ jmp(&done, compiler::Assembler::kNearJump);
+    __ Bind(&not_smi);
+    __ CompareClassId(value, kMintCid);
+    __ j(NOT_EQUAL, deopt);
+    __ movq(value, compiler::FieldAddress(value, Mint::value_offset()));
+#endif
     __ Bind(&done);
   }
 
@@ -5523,12 +5492,7 @@
     case Token::kNEGATE: {
       compiler::Label* deopt =
           compiler->AddDeoptStub(deopt_id(), ICData::kDeoptUnaryOp);
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ negq(value);
-#else
-      __ negl(value);
-      __ movsxd(value, value);
-#endif
+      __ OBJ(neg)(value);
       __ j(OVERFLOW, deopt);
       break;
     }
@@ -5639,7 +5603,7 @@
   Register left = locs()->in(0).reg();
   Register right = locs()->in(1).reg();
   Register result = locs()->out(0).reg();
-  __ cmpq(left, right);
+  __ OBJ(cmp)(left, right);
   ASSERT(result == left);
   if (is_min) {
     __ cmovgeq(result, right);
@@ -5680,11 +5644,7 @@
   Register value = locs()->in(0).reg();
   FpuRegister result = locs()->out(0).fpu_reg();
   __ SmiUntag(value);
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ cvtsi2sdq(result, value);
-#else
-  __ cvtsi2sdl(result, value);
-#endif
+  __ OBJ(cvtsi2sd)(result, value);
 }
 
 DEFINE_BACKEND(Int64ToDouble, (FpuRegister result, Register value)) {
@@ -5713,25 +5673,14 @@
   ASSERT(result != temp);
   __ movsd(value_double,
            compiler::FieldAddress(value_obj, Double::value_offset()));
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ cvttsd2siq(result, value_double);
-#else
-  __ cvttsd2sil(result, value_double);
-#endif
+  __ OBJ(cvttsd2si)(result, value_double);
   // Overflow is signalled with minint.
   compiler::Label do_call, done;
   // Check for overflow and that it fits into Smi.
-#if !defined(DART_COMPRESSED_POINTERS)
   __ movq(temp, result);
-  __ shlq(temp, compiler::Immediate(1));
+  __ OBJ(shl)(temp, compiler::Immediate(1));
   __ j(OVERFLOW, &do_call, compiler::Assembler::kNearJump);
   __ SmiTag(result);
-#else
-  __ movl(temp, result);
-  __ shll(temp, compiler::Immediate(1));
-  __ j(OVERFLOW, &do_call, compiler::Assembler::kNearJump);
-  __ movsxd(result, temp);
-#endif
   __ jmp(&done);
   __ Bind(&do_call);
   __ pushq(value_obj);
@@ -5770,26 +5719,14 @@
   XmmRegister value = locs()->in(0).fpu_reg();
   Register temp = locs()->temp(0).reg();
 
-#if !defined(DART_COMPRESSED_POINTERS)
-  __ cvttsd2siq(result, value);
-#else
-  __ cvttsd2sil(result, value);
-#endif
+  __ OBJ(cvttsd2si)(result, value);
   // Overflow is signalled with minint.
   compiler::Label do_call, done;
   // Check for overflow and that it fits into Smi.
-#if !defined(DART_COMPRESSED_POINTERS)
   __ movq(temp, result);
-  __ shlq(temp, compiler::Immediate(1));
+  __ OBJ(shl)(temp, compiler::Immediate(1));
   __ j(OVERFLOW, deopt);
   __ SmiTag(result);
-#else
-  __ movl(temp, result);
-  __ shll(temp, compiler::Immediate(1));
-  __ j(OVERFLOW, deopt);
-  ASSERT(kSmiTagShift == 1 && kSmiTag == 0);
-  __ movsxd(result, temp);
-#endif
 }
 
 LocationSummary* DoubleToDoubleInstr::MakeLocationSummary(Zone* zone,
@@ -6113,7 +6050,7 @@
   ASSERT(result2 == RDX);
   if (RangeUtils::CanBeZero(divisor_range())) {
     // Handle divide by zero in runtime.
-    __ testq(right, right);
+    __ OBJ(test)(right, right);
     __ j(ZERO, deopt);
   }
 #if !defined(DART_COMPRESSED_POINTERS)
@@ -6397,8 +6334,7 @@
   if (index_loc.IsConstant()) {
     Register length = length_loc.reg();
     const Smi& index = Smi::Cast(index_loc.constant());
-    __ CompareImmediate(length,
-                        compiler::Immediate(static_cast<int64_t>(index.ptr())));
+    __ CompareObject(length, index);
     __ j(BELOW_EQUAL, deopt);
   } else if (length_loc.IsConstant()) {
     const Smi& length = Smi::Cast(length_loc.constant());
@@ -6407,11 +6343,10 @@
       __ BranchIfNotSmi(index, deopt);
     }
     if (length.Value() == Smi::kMaxValue) {
-      __ testq(index, index);
+      __ OBJ(test)(index, index);
       __ j(NEGATIVE, deopt);
     } else {
-      __ CompareImmediate(
-          index, compiler::Immediate(static_cast<int64_t>(length.ptr())));
+      __ CompareObject(index, length);
       __ j(ABOVE_EQUAL, deopt);
     }
   } else {
@@ -6420,7 +6355,7 @@
     if (index_cid != kSmiCid) {
       __ BranchIfNotSmi(index, deopt);
     }
-    __ cmpq(index, length);
+    __ OBJ(cmp)(index, length);
     __ j(ABOVE_EQUAL, deopt);
   }
 }
diff --git a/runtime/vm/compiler/backend/inliner_test.cc b/runtime/vm/compiler/backend/inliner_test.cc
index 9a53a7b..26eb9e5 100644
--- a/runtime/vm/compiler/backend/inliner_test.cc
+++ b/runtime/vm/compiler/backend/inliner_test.cc
@@ -222,8 +222,8 @@
   RELEASE_ASSERT(cursor.TryMatch({
       kMoveGlob,
       kMatchAndMoveCreateArray,
-      kMatchAndMoveUnboxInt64,
       {kMoveAny, &unbox1},
+      kMatchAndMoveUnboxInt64,
       {kMoveAny, &unbox2},
       kMatchAndMoveGoto,
 
diff --git a/runtime/vm/compiler/backend/loops_test.cc b/runtime/vm/compiler/backend/loops_test.cc
index 8f1201e..1722f3b 100644
--- a/runtime/vm/compiler/backend/loops_test.cc
+++ b/runtime/vm/compiler/backend/loops_test.cc
@@ -68,9 +68,13 @@
   Invoke(root_library, "main");
 
   std::initializer_list<CompilerPass::Id> passes = {
-      CompilerPass::kComputeSSA,      CompilerPass::kTypePropagation,
-      CompilerPass::kApplyICData,     CompilerPass::kSelectRepresentations,
-      CompilerPass::kTypePropagation, CompilerPass::kCanonicalize,
+      CompilerPass::kComputeSSA,
+      CompilerPass::kTypePropagation,
+      CompilerPass::kApplyICData,
+      CompilerPass::kTypePropagation,
+      CompilerPass::kSelectRepresentations,
+      CompilerPass::kTypePropagation,
+      CompilerPass::kCanonicalize,
   };
   const auto& function = Function::Handle(GetFunction(root_library, "foo"));
   TestPipeline pipeline(function, CompilerPass::kJIT);
@@ -300,7 +304,7 @@
   const char* expected =
       "  [0\n"
       "  WRAP(99, LIN(0 + 1 * i))\n"    // phi (w)
-      "  LIN(0 + 1 * i) 100\n"          // phi
+      "  LIN(0 + 1 * i) 100\n"          // phi (i)
       "  WRAP(102, LIN(3 + 1 * i))\n"   // a
       "  WRAP(94, LIN(-5 + 1 * i))\n"   // b
       "  WRAP(693, LIN(0 + 7 * i))\n"   // c
@@ -383,11 +387,15 @@
   const char* expected =
       "  [0\n"
       "  LIN(9223372036854775806 + 1 * i)\n"  // phi
+#if !defined(TARGET_ARCH_IS_64_BIT)
       "  LIN(9223372036854775806 + 1 * i)\n"  // (un)boxing
       "  LIN(9223372036854775806 + 1 * i)\n"
       "  LIN(9223372036854775806 + 1 * i)\n"
+#endif                                        // !defined(TARGET_ARCH_IS_64_BIT)
       "  LIN(9223372036854775807 + 1 * i)\n"  // add
+#if !defined(TARGET_ARCH_IS_64_BIT)
       "  LIN(9223372036854775807 + 1 * i)\n"  // unbox
+#endif                                        // !defined(TARGET_ARCH_IS_64_BIT)
       "  ]\n";
   EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
 }
@@ -426,11 +434,15 @@
   const char* expected =
       "  [0\n"
       "  LIN(-9223372036854775807 + -1 * i)\n"  // phi
+#if !defined(TARGET_ARCH_IS_64_BIT)
       "  LIN(-9223372036854775807 + -1 * i)\n"  // (un)boxing
       "  LIN(-9223372036854775807 + -1 * i)\n"
       "  LIN(-9223372036854775807 + -1 * i)\n"
+#endif  // !defined(TARGET_ARCH_IS_64_BIT)
       "  LIN(-9223372036854775808 + -1 * i)\n"  // sub
+#if !defined(TARGET_ARCH_IS_64_BIT)
       "  LIN(-9223372036854775808 + -1 * i)\n"  // unbox
+#endif  // !defined(TARGET_ARCH_IS_64_BIT)
       "  ]\n";
   EXPECT_STREQ(expected, ComputeInduction(thread, script_chars));
 }
diff --git a/runtime/vm/compiler/compiler_sources.gni b/runtime/vm/compiler/compiler_sources.gni
index 8cc4b39..9135f50 100644
--- a/runtime/vm/compiler/compiler_sources.gni
+++ b/runtime/vm/compiler/compiler_sources.gni
@@ -160,6 +160,7 @@
   "assembler/disassembler_test.cc",
   "backend/bce_test.cc",
   "backend/constant_propagator_test.cc",
+  "backend/flow_graph_test.cc",
   "backend/il_test.cc",
   "backend/il_test_helper.h",
   "backend/il_test_helper.cc",
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index 022ba2c..c3c6fbc 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -1134,7 +1134,7 @@
     // Check length >= 0 && length <= kMaxNewSpaceElements
     const intptr_t max_len =
         target::ToRawSmi(target::Array::kMaxNewSpaceElements);
-    __ CompareImmediate(R2, max_len);
+    __ CompareImmediate(R2, max_len, kObjectBytes);
     __ b(&slow_case, HI);
 
     const intptr_t cid = kArrayCid;
@@ -1149,7 +1149,7 @@
         target::Array::header_size() +
         target::ObjectAlignment::kObjectAlignment - 1;
     __ LoadImmediate(R3, fixed_size_plus_alignment_padding);
-    __ add(R3, R3, Operand(R2, LSL, 2));  // R2 is Smi.
+    __ add(R3, R3, Operand(R2, LSL, 2), kObjectBytes);  // R2 is Smi.
     ASSERT(kSmiTagShift == 1);
     __ andi(R3, R3,
             Immediate(~(target::ObjectAlignment::kObjectAlignment - 1)));
@@ -2177,25 +2177,19 @@
   __ BranchIfNotSmi(TMP, not_smi_or_overflow);
   switch (kind) {
     case Token::kADD: {
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ adds(R0, R1, Operand(R0));   // Add.
+      __ adds(R0, R1, Operand(R0), kObjectBytes);  // Add.
       __ b(not_smi_or_overflow, VS);  // Branch if overflow.
-#else
-      __ addsw(R0, R1, Operand(R0));  // Add (32-bit).
-      __ b(not_smi_or_overflow, VS);  // Branch if overflow.
-      __ sxtw(R0, R0);                // Sign extend.
-#endif
       break;
     }
     case Token::kLT: {
-      __ CompareRegisters(R0, R1);
+      __ CompareObjectRegisters(R0, R1);
       __ LoadObject(R0, CastHandle<Object>(TrueObject()));
       __ LoadObject(R1, CastHandle<Object>(FalseObject()));
       __ csel(R0, R0, R1, LT);
       break;
     }
     case Token::kEQ: {
-      __ CompareRegisters(R0, R1);
+      __ CompareObjectRegisters(R0, R1);
       __ LoadObject(R0, CastHandle<Object>(TrueObject()));
       __ LoadObject(R1, CastHandle<Object>(FalseObject()));
       __ csel(R0, R0, R1, EQ);
@@ -3136,7 +3130,8 @@
   // Double values bitwise compare.
   __ LoadFieldFromOffset(left, left, target::Double::value_offset());
   __ LoadFieldFromOffset(right, right, target::Double::value_offset());
-  __ b(&reference_compare);
+  __ CompareRegisters(left, right);
+  __ b(&done);
 
   __ Bind(&check_mint);
   __ CompareClassId(left, kMintCid);
@@ -3145,9 +3140,11 @@
   __ b(&done, NE);
   __ LoadFieldFromOffset(left, left, target::Mint::value_offset());
   __ LoadFieldFromOffset(right, right, target::Mint::value_offset());
+  __ CompareRegisters(left, right);
+  __ b(&done);
 
   __ Bind(&reference_compare);
-  __ CompareRegisters(left, right);
+  __ CompareObjectRegisters(left, right);
   __ Bind(&done);
 }
 
@@ -3584,7 +3581,7 @@
   __ SmiUntag(R2);
   /* Check for length >= 0 && length <= max_len. */
   /* R2: untagged array length. */
-  __ CompareImmediate(R2, max_len);
+  __ CompareImmediate(R2, max_len, kObjectBytes);
   __ b(&call_runtime, HI);
   __ LslImmediate(R2, R2, scale_shift);
   const intptr_t fixed_size_plus_alignment_padding =
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index 93bd854c6..db74b1f 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -1067,7 +1067,7 @@
     // Check length >= 0 && length <= kMaxNewSpaceElements
     const Immediate& max_len =
         Immediate(target::ToRawSmi(target::Array::kMaxNewSpaceElements));
-    __ cmpq(RDI, max_len);
+    __ OBJ(cmp)(RDI, max_len);
     __ j(ABOVE, &slow_case);
 
     // Check for allocation tracing.
@@ -1078,7 +1078,7 @@
         target::Array::header_size() +
         target::ObjectAlignment::kObjectAlignment - 1;
     // RDI is a Smi.
-    __ leaq(RDI, Address(RDI, TIMES_4, fixed_size_plus_alignment_padding));
+    __ OBJ(lea)(RDI, Address(RDI, TIMES_4, fixed_size_plus_alignment_padding));
     ASSERT(kSmiTagShift == 1);
     __ andq(RDI, Immediate(-target::ObjectAlignment::kObjectAlignment));
 
@@ -2106,18 +2106,12 @@
   __ j(NOT_ZERO, not_smi_or_overflow);
   switch (kind) {
     case Token::kADD: {
-#if !defined(DART_COMPRESSED_POINTERS)
-      __ addq(RAX, RCX);
+      __ OBJ(add)(RAX, RCX);
       __ j(OVERFLOW, not_smi_or_overflow);
-#else
-      __ addl(RAX, RCX);
-      __ j(OVERFLOW, not_smi_or_overflow);
-      __ movsxd(RAX, RAX);
-#endif
       break;
     }
     case Token::kLT: {
-      __ cmpq(RAX, RCX);
+      __ OBJ(cmp)(RAX, RCX);
       __ setcc(GREATER_EQUAL, ByteRegisterOf(RAX));
       __ movzxb(RAX, RAX);  // RAX := RAX < RCX ? 0 : 1
       __ movq(RAX,
@@ -2127,7 +2121,7 @@
       break;
     }
     case Token::kEQ: {
-      __ cmpq(RAX, RCX);
+      __ OBJ(cmp)(RAX, RCX);
       __ setcc(NOT_EQUAL, ByteRegisterOf(RAX));
       __ movzxb(RAX, RAX);  // RAX := RAX == RCX ? 0 : 1
       __ movq(RAX,
@@ -3102,7 +3096,7 @@
   __ jmp(&done, Assembler::kFarJump);
 
   __ Bind(&reference_compare);
-  __ cmpq(left, right);
+  __ CompareObjectRegisters(left, right);
   __ Bind(&done);
 }
 
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index cdda785..535c62e 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -8552,9 +8552,8 @@
                      Heap::Space space = Heap::kNew) const;
 
   static int64_t GetInt64Value(const IntegerPtr obj) {
-    intptr_t raw_value = static_cast<intptr_t>(obj);
-    if ((raw_value & kSmiTagMask) == kSmiTag) {
-      return (raw_value >> kSmiTagShift);
+    if (obj->IsSmi()) {
+      return RawSmiValue(static_cast<const SmiPtr>(obj));
     } else {
       ASSERT(obj->IsMint());
       return static_cast<const MintPtr>(obj)->untag()->value_;
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index ebd6127..67656c6 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -2878,7 +2878,7 @@
   EXPECT(Smi::Cast(result).Value() == kSmiTestValue);
 }
 
-#if defined(ARCH_IS_64_BIT)
+#if defined(ARCH_IS_64_BIT) && !defined(DART_COMPRESSED_POINTERS)
 // Test for Embedded Smi object in the instructions.
 ISOLATE_UNIT_TEST_CASE(EmbedSmiIn64BitCode) {
   extern void GenerateEmbedSmiInCode(compiler::Assembler * assembler,
@@ -2898,7 +2898,7 @@
       Object::Handle(DartEntry::InvokeFunction(function, Array::empty_array()));
   EXPECT(Smi::Cast(result).Value() == kSmiTestValue);
 }
-#endif  // ARCH_IS_64_BIT
+#endif  // ARCH_IS_64_BIT && !DART_COMPRESSED_POINTERS
 
 ISOLATE_UNIT_TEST_CASE(ExceptionHandlers) {
   const int kNumEntries = 4;
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index b3ee8a3..4f29d72 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -1128,7 +1128,14 @@
 
   // First check if it is a Smi (i.e not a heap object).
   if (!rawobj->IsHeapObject()) {
+#if !defined(DART_COMPRESSED_POINTERS)
     Write<int64_t>(static_cast<intptr_t>(rawobj));
+#else
+    // One might expect this to be unnecessary because the reader will just
+    // ignore the upper bits, but the upper bits affect the variable-length
+    // encoding and can change lower bits in the variable-length reader.
+    Write<int64_t>(static_cast<intptr_t>(rawobj) << 32 >> 32);
+#endif
     return true;
   }
 
diff --git a/runtime/vm/tagged_pointer.h b/runtime/vm/tagged_pointer.h
index 63b446f..6302427 100644
--- a/runtime/vm/tagged_pointer.h
+++ b/runtime/vm/tagged_pointer.h
@@ -349,7 +349,12 @@
 #undef DEFINE_TAGGED_POINTER
 
 inline intptr_t RawSmiValue(const SmiPtr raw_value) {
+#if !defined(DART_COMPRESSED_POINTERS)
   const intptr_t value = static_cast<intptr_t>(raw_value);
+#else
+  const intptr_t value = static_cast<intptr_t>(static_cast<int32_t>(
+      static_cast<uint32_t>(static_cast<uintptr_t>(raw_value))));
+#endif
   ASSERT((value & kSmiTagMask) == kSmiTag);
   return (value >> kSmiTagShift);
 }
diff --git a/tests/language/const_functions/const_functions_variable_assignments_test.dart b/tests/language/const_functions/const_functions_variable_assignments_test.dart
new file mode 100644
index 0000000..f2c47cb
--- /dev/null
+++ b/tests/language/const_functions/const_functions_variable_assignments_test.dart
@@ -0,0 +1,48 @@
+// 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.
+
+// Tests variable assignments for const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = varAssignmentTest(1);
+//           ^^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int varAssignmentTest(int a) {
+  int x = 4;
+  {
+    x = 3;
+  }
+  return x;
+}
+
+int function() {
+  int varAssignmentTest2() {
+    int x = 2;
+    x += 1;
+    return x;
+  }
+
+  const var2 = varAssignmentTest2();
+  //           ^^^^^^^^^^^^^^^^^^^^
+  // [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+  return var2;
+}
+
+const var3 = varAssignmentTest3(1);
+//           ^^^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int varAssignmentTest3(int a) {
+  int x = 4;
+  x = a + 1;
+  return x;
+}
+
+void main() {
+  Expect.equals(var1, 3);
+  Expect.equals(function(), 3);
+  Expect.equals(var3, 2);
+}
diff --git a/tools/VERSION b/tools/VERSION
index b6cf424..5b87bd5 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 130
+PRERELEASE 131
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/lib/src/firestore.dart b/tools/bots/lib/src/firestore.dart
index 166173c..80b2315 100644
--- a/tools/bots/lib/src/firestore.dart
+++ b/tools/bots/lib/src/firestore.dart
@@ -21,8 +21,11 @@
 /// tools/bots.
 class FirestoreDatabase {
   final http.Client _client = http.Client();
-  final String _authToken;
-  final String _project;
+  final Map<String, String> _headers;
+  final Uri _documentsUrl;
+  final Uri _queryUrl;
+  final Uri _beginTransactionUrl;
+  final Uri _commitUrl;
 
   /// The current transaction ID in base64 (or `null`)
   String _currentTransaction;
@@ -34,27 +37,30 @@
         .replaceAll("+", "%2B");
   }
 
-  FirestoreDatabase(this._project, this._authToken);
+  FirestoreDatabase._(this._headers, this._documentsUrl, this._queryUrl,
+      this._beginTransactionUrl, this._commitUrl);
 
-  static const apiUrl = 'https://firestore.googleapis.com/v1beta1';
-
-  String get projectUrl => '$apiUrl/projects/$_project';
-
-  String get documentsUrl => '$projectUrl/databases/(default)/documents';
-
-  String get queryUrl => '$documentsUrl:runQuery';
-
-  Map<String, String> get _headers {
-    return {
-      'Authorization': 'Bearer $_authToken',
+  factory FirestoreDatabase(String project, String authToken) {
+    var databaseUrl = _apiUrl.resolve('projects/$project/databases/(default)/');
+    var documentsUrl = databaseUrl.resolve('documents/');
+    var queryUrl = databaseUrl.resolveUri(Uri(path: 'documents:runQuery'));
+    var beginTransactionUrl =
+        databaseUrl.resolveUri(Uri(path: 'documents:beginTransaction'));
+    var commitUrl = databaseUrl.resolveUri(Uri(path: 'documents:commit'));
+    var headers = {
+      'Authorization': 'Bearer $authToken',
       'Accept': 'application/json',
       'Content-Type': 'application/json'
     };
+    return FirestoreDatabase._(
+        headers, documentsUrl, queryUrl, beginTransactionUrl, commitUrl);
   }
 
+  static final _apiUrl = Uri.https('firestore.googleapis.com', 'v1beta1/');
+
   Future<List /*!*/ > runQuery(Query query) async {
     var body = jsonEncode(query.data);
-    var response = await _client.post(queryUrl, headers: _headers, body: body);
+    var response = await _client.post(_queryUrl, headers: _headers, body: body);
     if (response.statusCode == HttpStatus.ok) {
       return jsonDecode(response.body);
     } else {
@@ -63,10 +69,11 @@
   }
 
   Future<Map> getDocument(String collectionName, String documentName) async {
-    var url = '$documentsUrl/$collectionName/$documentName';
-    if (_currentTransaction != null) {
-      url = '$url?transaction=${_escapedCurrentTransaction}';
-    }
+    var url = _documentsUrl.resolveUri(Uri(
+        path: '$collectionName/$documentName',
+        query: _currentTransaction == null
+            ? null
+            : 'transaction=${_escapedCurrentTransaction}'));
     var response = await _client.get(url, headers: _headers);
     if (response.statusCode == HttpStatus.ok) {
       var document = jsonDecode(response.body);
@@ -80,7 +87,8 @@
   }
 
   Future<Object> updateField(Map document, String field) async {
-    var url = '$apiUrl/${document["name"]}?updateMask.fieldPaths=$field';
+    var url =
+        _apiUrl.resolve('${document["name"]}?updateMask.fieldPaths=$field');
     var response =
         await _client.patch(url, headers: _headers, body: jsonEncode(document));
     if (response.statusCode == HttpStatus.ok) {
@@ -94,9 +102,9 @@
     if (_currentTransaction != null) {
       throw Exception('Error: nested transactions');
     }
-    var url = '$documentsUrl:beginTransaction';
     var body = '{"options": {}}';
-    var response = await _client.post(url, headers: _headers, body: body);
+    var response =
+        await _client.post(_beginTransactionUrl, headers: _headers, body: body);
     if (response.statusCode == HttpStatus.ok) {
       var result = jsonDecode(response.body);
       _currentTransaction = result['transaction'] as String;
@@ -116,8 +124,8 @@
       "writes": writes.map((write) => write.data).toList(),
       "transaction": "$_currentTransaction"
     });
-    var url = '$documentsUrl:commit';
-    var response = await _client.post(url, headers: _headers, body: body);
+    var response =
+        await _client.post(_commitUrl, headers: _headers, body: body);
     _currentTransaction = null;
     if (response.statusCode == HttpStatus.conflict) {
       // This HTTP status code corresponds to the ABORTED error code, see