[vm/aot/tfa] Infer int type, devirtualize dynamic calls

This change improves type flow analysis to infer int types, in addition
to concrete classes (int type is abstract and has 2 concrete subclasses,
_Smi and _Mint).

Also, this CL enables devirtualization of dynamic calls (previously, only
calls with known interface target were devirtualized).

DartMicroBench.PrimeNumber:
x64: +221%
armv8: +461%
armv7h: +47.98%

No measurable impact on Flutter gallery build time in release mode.

Issue: https://github.com/flutter/flutter/issues/19677

Change-Id: I34db195f52f2a434218ee11eee7f308d4661738b
Reviewed-on: https://dart-review.googlesource.com/69520
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
diff --git a/pkg/vm/lib/metadata/inferred_type.dart b/pkg/vm/lib/metadata/inferred_type.dart
index 17daf11..91200ea 100644
--- a/pkg/vm/lib/metadata/inferred_type.dart
+++ b/pkg/vm/lib/metadata/inferred_type.dart
@@ -9,18 +9,25 @@
 /// Metadata for annotating nodes with an inferred type information.
 class InferredType {
   final Reference _concreteClassReference;
-  final bool nullable;
+  final int _flags;
 
-  InferredType(Class concreteClass, bool nullable)
-      : this._byReference(getClassReference(concreteClass), nullable);
+  static const int flagNullable = 1 << 0;
+  static const int flagInt = 1 << 1;
 
-  InferredType._byReference(this._concreteClassReference, this.nullable);
+  InferredType(Class concreteClass, bool nullable, bool isInt)
+      : this._byReference(getClassReference(concreteClass),
+            (nullable ? flagNullable : 0) | (isInt ? flagInt : 0));
+
+  InferredType._byReference(this._concreteClassReference, this._flags);
 
   Class get concreteClass => _concreteClassReference?.asClass;
 
+  bool get nullable => (_flags & flagNullable) != 0;
+  bool get isInt => (_flags & flagInt) != 0;
+
   @override
   String toString() =>
-      "${concreteClass != null ? concreteClass : '!'}${nullable ? '?' : ''}";
+      "${concreteClass != null ? concreteClass : (isInt ? 'int' : '!')}${nullable ? '?' : ''}";
 }
 
 /// Repository for [InferredType].
@@ -35,14 +42,14 @@
   void writeToBinary(InferredType metadata, Node node, BinarySink sink) {
     sink.writeCanonicalNameReference(
         getCanonicalNameOfClass(metadata.concreteClass));
-    sink.writeByte(metadata.nullable ? 1 : 0);
+    sink.writeByte(metadata._flags);
   }
 
   @override
   InferredType readFromBinary(Node node, BinarySource source) {
     final concreteClassReference =
         source.readCanonicalNameReference()?.getReference();
-    final nullable = (source.readByte() != 0);
-    return new InferredType._byReference(concreteClassReference, nullable);
+    final flags = source.readByte();
+    return new InferredType._byReference(concreteClassReference, flags);
   }
 }
diff --git a/pkg/vm/lib/transformations/devirtualization.dart b/pkg/vm/lib/transformations/devirtualization.dart
index b83c1cf..13c4908 100644
--- a/pkg/vm/lib/transformations/devirtualization.dart
+++ b/pkg/vm/lib/transformations/devirtualization.dart
@@ -72,7 +72,7 @@
       directCall.checkReceiverForNull &&
       _objectMemberNames.contains(directCall.target.name);
 
-  DirectCallMetadata getDirectCall(TreeNode node, Member target,
+  DirectCallMetadata getDirectCall(TreeNode node, Member interfaceTarget,
       {bool setter = false});
 
   makeDirectCall(TreeNode node, Member target, DirectCallMetadata directCall) {
@@ -96,17 +96,20 @@
   visitMethodInvocation(MethodInvocation node) {
     super.visitMethodInvocation(node);
 
-    Member target = node.interfaceTarget;
-    if ((target != null) && isMethod(target)) {
-      DirectCallMetadata directCall = getDirectCall(node, target);
-      // TODO(dartbug.com/30480): Convert _isLegalTargetForMethodInvocation()
-      // check into an assertion once front-end implements override checks.
-      if ((directCall != null) &&
-          isMethod(directCall.target) &&
-          isLegalTargetForMethodInvocation(directCall.target, node.arguments) &&
-          !hasExtraTargetForNull(directCall)) {
-        makeDirectCall(node, target, directCall);
-      }
+    final Member target = node.interfaceTarget;
+    if (target != null && !isMethod(target)) {
+      return;
+    }
+
+    final DirectCallMetadata directCall = getDirectCall(node, target);
+
+    // TODO(alexmarkov): Convert _isLegalTargetForMethodInvocation()
+    // check into an assertion once front-end implements all override checks.
+    if ((directCall != null) &&
+        isMethod(directCall.target) &&
+        isLegalTargetForMethodInvocation(directCall.target, node.arguments) &&
+        !hasExtraTargetForNull(directCall)) {
+      makeDirectCall(node, target, directCall);
     }
   }
 
@@ -114,14 +117,17 @@
   visitPropertyGet(PropertyGet node) {
     super.visitPropertyGet(node);
 
-    Member target = node.interfaceTarget;
-    if ((target != null) && isFieldOrGetter(target)) {
-      DirectCallMetadata directCall = getDirectCall(node, target);
-      if ((directCall != null) &&
-          isFieldOrGetter(directCall.target) &&
-          !hasExtraTargetForNull(directCall)) {
-        makeDirectCall(node, target, directCall);
-      }
+    final Member target = node.interfaceTarget;
+    if (target != null && !isFieldOrGetter(target)) {
+      return;
+    }
+
+    final DirectCallMetadata directCall = getDirectCall(node, target);
+
+    if ((directCall != null) &&
+        isFieldOrGetter(directCall.target) &&
+        !hasExtraTargetForNull(directCall)) {
+      makeDirectCall(node, target, directCall);
     }
   }
 
@@ -129,12 +135,11 @@
   visitPropertySet(PropertySet node) {
     super.visitPropertySet(node);
 
-    Member target = node.interfaceTarget;
-    if (target != null) {
-      DirectCallMetadata directCall = getDirectCall(node, target, setter: true);
-      if (directCall != null) {
-        makeDirectCall(node, target, directCall);
-      }
+    final Member target = node.interfaceTarget;
+    final DirectCallMetadata directCall =
+        getDirectCall(node, target, setter: true);
+    if (directCall != null) {
+      makeDirectCall(node, target, directCall);
     }
   }
 }
@@ -148,10 +153,13 @@
       : super(coreTypes, component, hierarchy);
 
   @override
-  DirectCallMetadata getDirectCall(TreeNode node, Member target,
+  DirectCallMetadata getDirectCall(TreeNode node, Member interfaceTarget,
       {bool setter = false}) {
+    if (interfaceTarget == null) {
+      return null;
+    }
     Member singleTarget = _hierarchySubtype
-        .getSingleTargetForInterfaceInvocation(target, setter: setter);
+        .getSingleTargetForInterfaceInvocation(interfaceTarget, setter: setter);
     if (singleTarget == null) {
       return null;
     }
diff --git a/pkg/vm/lib/transformations/type_flow/analysis.dart b/pkg/vm/lib/transformations/type_flow/analysis.dart
index 82210b7..3ba77a7 100644
--- a/pkg/vm/lib/transformations/type_flow/analysis.dart
+++ b/pkg/vm/lib/transformations/type_flow/analysis.dart
@@ -450,6 +450,13 @@
         }
         _getReceiverTypeBuilder(targets, kNoSuchMethodMarker)
             .addConcreteType(receiver);
+      } else if (selector is DynamicSelector) {
+        if (kPrintTrace) {
+          tracePrint(
+              "Dynamic selector - adding noSuchMethod for receiver $receiver");
+        }
+        _getReceiverTypeBuilder(targets, kNoSuchMethodMarker)
+            .addConcreteType(receiver);
       } else {
         if (kPrintTrace) {
           tracePrint("Target is not found for receiver $receiver");
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index b17d1ad..0ed585c 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -1067,16 +1067,17 @@
   }
 
   @override
-  visitSwitchStatement(SwitchStatement node) {
+  TypeExpr visitSwitchStatement(SwitchStatement node) {
     _visit(node.expression);
     for (var switchCase in node.cases) {
       switchCase.expressions.forEach(_visit);
       _visit(switchCase.body);
     }
+    return null;
   }
 
   @override
-  visitTryCatch(TryCatch node) {
+  TypeExpr visitTryCatch(TryCatch node) {
     _visit(node.body);
     for (var catchClause in node.catches) {
       if (catchClause.exception != null) {
@@ -1087,49 +1088,56 @@
       }
       _visit(catchClause.body);
     }
+    return null;
   }
 
   @override
-  visitTryFinally(TryFinally node) {
+  TypeExpr visitTryFinally(TryFinally node) {
     _visit(node.body);
     _visit(node.finalizer);
+    return null;
   }
 
   @override
-  visitVariableDeclaration(VariableDeclaration node) {
+  TypeExpr visitVariableDeclaration(VariableDeclaration node) {
     final v = _declareVariable(node, addInitType: true);
     if (node.initializer == null) {
       v.values.add(_nullType);
     }
+    return null;
   }
 
   @override
-  visitWhileStatement(WhileStatement node) {
+  TypeExpr visitWhileStatement(WhileStatement node) {
     _addUse(_visit(node.condition));
     _visit(node.body);
+    return null;
   }
 
   @override
-  visitYieldStatement(YieldStatement node) {
+  TypeExpr visitYieldStatement(YieldStatement node) {
     _visit(node.expression);
+    return null;
   }
 
   @override
-  visitFieldInitializer(FieldInitializer node) {
+  TypeExpr visitFieldInitializer(FieldInitializer node) {
     final value = _visit(node.value);
     final args = new Args<TypeExpr>([_receiver, value]);
     _makeCall(node,
         new DirectSelector(node.field, callKind: CallKind.PropertySet), args);
+    return null;
   }
 
   @override
-  visitRedirectingInitializer(RedirectingInitializer node) {
+  TypeExpr visitRedirectingInitializer(RedirectingInitializer node) {
     final args = _visitArguments(_receiver, node.arguments);
     _makeCall(node, new DirectSelector(node.target), args);
+    return null;
   }
 
   @override
-  visitSuperInitializer(SuperInitializer node) {
+  TypeExpr visitSuperInitializer(SuperInitializer node) {
     final args = _visitArguments(_receiver, node.arguments);
 
     Constructor target = null;
@@ -1146,22 +1154,27 @@
     }
     assertx(target != null);
     _makeCall(node, new DirectSelector(target), args);
+    return null;
   }
 
   @override
-  visitLocalInitializer(LocalInitializer node) {
+  TypeExpr visitLocalInitializer(LocalInitializer node) {
     visitVariableDeclaration(node.variable);
+    return null;
   }
 
   @override
-  visitAssertInitializer(AssertInitializer node) {
+  TypeExpr visitAssertInitializer(AssertInitializer node) {
     if (!kRemoveAsserts) {
       _visit(node.statement);
     }
+    return null;
   }
 
   @override
-  visitInvalidInitializer(InvalidInitializer node) {}
+  TypeExpr visitInvalidInitializer(InvalidInitializer node) {
+    return null;
+  }
 
   @override
   TypeExpr visitConstantExpression(ConstantExpression node) {
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index eb2f7d1..098e79d 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -96,7 +96,7 @@
             _typeFlowAnalysis.environment.hierarchy);
 
   @override
-  DirectCallMetadata getDirectCall(TreeNode node, Member target,
+  DirectCallMetadata getDirectCall(TreeNode node, Member interfaceTarget,
       {bool setter = false}) {
     final callSite = _typeFlowAnalysis.callSite(node);
     if (callSite != null) {
@@ -115,10 +115,12 @@
   final TypeFlowAnalysis _typeFlowAnalysis;
   final InferredTypeMetadataRepository _inferredTypeMetadata;
   final UnreachableNodeMetadataRepository _unreachableNodeMetadata;
+  final DartType _intType;
 
   AnnotateKernel(Component component, this._typeFlowAnalysis)
       : _inferredTypeMetadata = new InferredTypeMetadataRepository(),
-        _unreachableNodeMetadata = new UnreachableNodeMetadataRepository() {
+        _unreachableNodeMetadata = new UnreachableNodeMetadataRepository(),
+        _intType = _typeFlowAnalysis.environment.intType {
     component.addMetadataRepository(_inferredTypeMetadata);
     component.addMetadataRepository(_unreachableNodeMetadata);
   }
@@ -127,23 +129,25 @@
     assertx(type != null);
 
     Class concreteClass;
+    bool isInt = false;
 
     final nullable = type is NullableType;
     if (nullable) {
-      final baseType = (type as NullableType).baseType;
-
-      if (baseType == const EmptyType()) {
-        concreteClass = _typeFlowAnalysis.environment.coreTypes.nullClass;
-      } else {
-        concreteClass =
-            baseType.getConcreteClass(_typeFlowAnalysis.hierarchyCache);
-      }
-    } else {
-      concreteClass = type.getConcreteClass(_typeFlowAnalysis.hierarchyCache);
+      type = (type as NullableType).baseType;
     }
 
-    if ((concreteClass != null) || !nullable) {
-      return new InferredType(concreteClass, nullable);
+    if (nullable && type == const EmptyType()) {
+      concreteClass = _typeFlowAnalysis.environment.coreTypes.nullClass;
+    } else {
+      concreteClass = type.getConcreteClass(_typeFlowAnalysis.hierarchyCache);
+
+      if (concreteClass == null) {
+        isInt = type.isSubtypeOf(_typeFlowAnalysis.hierarchyCache, _intType);
+      }
+    }
+
+    if ((concreteClass != null) || !nullable || isInt) {
+      return new InferredType(concreteClass, nullable, isInt);
     }
 
     return null;
diff --git a/pkg/vm/lib/transformations/type_flow/types.dart b/pkg/vm/lib/transformations/type_flow/types.dart
index 2100c02..c2da60f 100644
--- a/pkg/vm/lib/transformations/type_flow/types.dart
+++ b/pkg/vm/lib/transformations/type_flow/types.dart
@@ -95,6 +95,8 @@
 
   Class getConcreteClass(TypeHierarchy typeHierarchy) => null;
 
+  bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) => false;
+
   @override
   Type getComputedType(List<Type> types) => this;
 
@@ -168,6 +170,10 @@
   String toString() => "${baseType}?";
 
   @override
+  bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) =>
+      baseType.isSubtypeOf(typeHierarchy, dartType);
+
+  @override
   int get order => TypeOrder.Nullable.index;
 
   @override
@@ -279,6 +285,10 @@
   String toString() => "_T ${types}";
 
   @override
+  bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) =>
+      types.every((ConcreteType t) => t.isSubtypeOf(typeHierarchy, dartType));
+
+  @override
   int get order => TypeOrder.Set.index;
 
   static List<ConcreteType> _unionLists(
@@ -397,6 +407,10 @@
       .getConcreteClass(typeHierarchy);
 
   @override
+  bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) =>
+      typeHierarchy.isSubtype(this.dartType, dartType);
+
+  @override
   int get hashCode => (dartType.hashCode + 37) & kHashMask;
 
   @override
@@ -505,6 +519,10 @@
       (dartType as InterfaceType).classNode;
 
   @override
+  bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) =>
+      typeHierarchy.isSubtype(this.dartType, dartType);
+
+  @override
   int get hashCode => (classId.hashCode ^ 0x1234) & kHashMask;
 
   @override
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/annotation.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/annotation.dart.expect
index 925603f..fde6a80 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/annotation.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/annotation.dart.expect
@@ -37,7 +37,7 @@
 }
 static method foo([@vm.inferred-type.metadata=dart.core::Null?] (core::List<core::int>) → void a) → core::int {
   @self::VarAnnotation::•() core::int x = 2;
-  return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] x.{core::num::+}(2);
+  return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] [@vm.inferred-type.metadata=int?] x.{core::num::+}(2);
 }
 @self::ParametrizedAnnotation::•<core::Null>(null)
 static method main(core::List<core::String> args) → dynamic {
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/bench_is_prime.dart b/pkg/vm/testcases/transformations/type_flow/transformer/bench_is_prime.dart
new file mode 100644
index 0000000..f7a9da8
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/bench_is_prime.dart
@@ -0,0 +1,35 @@
+bool isPrime(var n) {
+  if (n < 2) return false;
+  for (var i = 2; i * i <= n; i++) {
+    if (n % i == 0) return false;
+  }
+  return true;
+}
+
+int nThPrimeNumber(int n) {
+  int counter = 0;
+  for (var i = 1;; i++) {
+    if (isPrime(i)) counter++;
+    if (counter == n) {
+      return i;
+    }
+  }
+}
+
+void run() {
+  int e = 611953;
+  int p = nThPrimeNumber(50000);
+  if (p != e) {
+    throw Exception("Unexpected result: $p != $e");
+  }
+}
+
+main(List<String> args) {
+  Stopwatch timer = new Stopwatch()..start();
+  for (int i = 0; i < 100; ++i) {
+    run();
+  }
+  timer.stop();
+
+  print("Elapsed ${timer.elapsedMilliseconds}ms");
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/bench_is_prime.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/bench_is_prime.dart.expect
new file mode 100644
index 0000000..46769b4
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/bench_is_prime.dart.expect
@@ -0,0 +1,38 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+
+static method isPrime([@vm.inferred-type.metadata=int?] dynamic n) → core::bool {
+  if([@vm.direct-call.metadata=dart.core::_IntegerImplementation::<??] [@vm.inferred-type.metadata=dart.core::bool] n.<(2) as{TypeError} core::bool)
+    return false;
+  for (core::int i = 2; [@vm.direct-call.metadata=dart.core::_IntegerImplementation::<=??] [@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::_IntegerImplementation::*??] [@vm.inferred-type.metadata=int?] i.{core::num::*}(i).{core::num::<=}(n as{TypeError} core::num); i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+??] [@vm.inferred-type.metadata=int?] i.{core::num::+}(1)) {
+    if([@vm.inferred-type.metadata=dart.core::bool] [@vm.direct-call.metadata=dart.core::_IntegerImplementation::%??] [@vm.inferred-type.metadata=int?] n.%(i).{core::Object::==}(0))
+      return false;
+  }
+  return true;
+}
+static method nThPrimeNumber([@vm.inferred-type.metadata=int] core::int n) → core::int {
+  core::int counter = 0;
+  for (core::int i = 1; ; i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+??] [@vm.inferred-type.metadata=int?] i.{core::num::+}(1)) {
+    if([@vm.inferred-type.metadata=dart.core::bool] self::isPrime(i))
+      counter = [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+??] [@vm.inferred-type.metadata=int?] counter.{core::num::+}(1);
+    if([@vm.inferred-type.metadata=dart.core::bool] counter.{core::num::==}(n)) {
+      return i;
+    }
+  }
+}
+static method run() → void {
+  core::int e = 611953;
+  core::int p = [@vm.inferred-type.metadata=int?] self::nThPrimeNumber(50000);
+  if(![@vm.inferred-type.metadata=dart.core::bool] p.{core::num::==}(e)) {
+    throw core::Exception::•("Unexpected result: ${p} != ${e}");
+  }
+}
+static method main(core::List<core::String> args) → dynamic {
+  core::Stopwatch timer = let final core::Stopwatch #t1 = new core::Stopwatch::•() in let final dynamic #t2 = [@vm.direct-call.metadata=dart.core::Stopwatch::start] #t1.{core::Stopwatch::start}() in #t1;
+  for (core::int i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation::<??] [@vm.inferred-type.metadata=dart.core::bool] i.{core::num::<}(100); i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+??] [@vm.inferred-type.metadata=int?] i.{core::num::+}(1)) {
+    self::run();
+  }
+  [@vm.direct-call.metadata=dart.core::Stopwatch::stop] timer.{core::Stopwatch::stop}();
+  core::print("Elapsed ${[@vm.direct-call.metadata=dart.core::Stopwatch::elapsedMilliseconds] timer.{core::Stopwatch::elapsedMilliseconds}}ms");
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect
index 3b702f1..e158e50 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect
@@ -4,20 +4,20 @@
 import "dart:typed_data" as typ;
 
 class _Vector extends core::Object {
-[@vm.inferred-type.metadata=!]  final field core::int _offset;
-[@vm.inferred-type.metadata=!]  final field core::int _length;
+[@vm.inferred-type.metadata=int]  final field core::int _offset;
+[@vm.inferred-type.metadata=int]  final field core::int _length;
 [@vm.inferred-type.metadata=dart.typed_data::_Float64List]  final field core::List<core::double> _elements;
-  constructor •([@vm.inferred-type.metadata=!] core::int size) → void
+  constructor •([@vm.inferred-type.metadata=int] core::int size) → void
     : self::_Vector::_offset = 0, self::_Vector::_length = size, self::_Vector::_elements = [@vm.inferred-type.metadata=dart.typed_data::_Float64List] typ::Float64List::•(size), super core::Object::•()
     ;
   operator [](core::int i) → core::double
-    return [@vm.direct-call.metadata=dart.typed_data::_Float64List::[]] [@vm.inferred-type.metadata=dart.core::_Double] [@vm.direct-call.metadata=#lib::_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements}.{core::List::[]}([@vm.direct-call.metadata=dart.core::_IntegerImplementation::+??] i.{core::num::+}([@vm.direct-call.metadata=#lib::_Vector::_offset] [@vm.inferred-type.metadata=!] this.{self::_Vector::_offset}));
+    return [@vm.direct-call.metadata=dart.typed_data::_Float64List::[]] [@vm.inferred-type.metadata=dart.core::_Double] [@vm.direct-call.metadata=#lib::_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements}.{core::List::[]}([@vm.direct-call.metadata=dart.core::_IntegerImplementation::+??] [@vm.inferred-type.metadata=int?] i.{core::num::+}([@vm.direct-call.metadata=#lib::_Vector::_offset] [@vm.inferred-type.metadata=int] this.{self::_Vector::_offset}));
   operator []=([@vm.inferred-type.metadata=!] core::int i, core::double value) → void {
-    let dynamic #t1 = [@vm.direct-call.metadata=#lib::_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements} in let dynamic #t2 = i in let dynamic #t3 = [@vm.direct-call.metadata=#lib::_Vector::_offset] [@vm.inferred-type.metadata=!] this.{self::_Vector::_offset} in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
+    let dynamic #t1 = [@vm.direct-call.metadata=#lib::_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements} in let dynamic #t2 = i in let dynamic #t3 = [@vm.direct-call.metadata=#lib::_Vector::_offset] [@vm.inferred-type.metadata=int] this.{self::_Vector::_offset} in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
   }
   operator *([@vm.inferred-type.metadata=#lib::_Vector?] self::_Vector a) → core::double {
     core::double result = 0.0;
-    for (core::int i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation::<??] [@vm.inferred-type.metadata=dart.core::bool] i.{core::num::<}([@vm.direct-call.metadata=#lib::_Vector::_length] [@vm.inferred-type.metadata=!] this.{self::_Vector::_length}); i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+??] i.{core::num::+}(1))
+    for (core::int i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation::<??] [@vm.inferred-type.metadata=dart.core::bool] i.{core::num::<}([@vm.direct-call.metadata=#lib::_Vector::_length] [@vm.inferred-type.metadata=int] this.{self::_Vector::_length}); i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+??] [@vm.inferred-type.metadata=int?] i.{core::num::+}(1))
       result = [@vm.direct-call.metadata=dart.core::_Double::+??] [@vm.inferred-type.metadata=dart.core::_Double] result.{core::double::+}([@vm.direct-call.metadata=dart.core::_Double::*] [@vm.inferred-type.metadata=dart.core::_Double] [@vm.direct-call.metadata=#lib::_Vector::[]] [@vm.inferred-type.metadata=dart.core::_Double] this.{self::_Vector::[]}(i).{core::double::*}([@vm.direct-call.metadata=#lib::_Vector::[]??] [@vm.inferred-type.metadata=dart.core::_Double] a.{self::_Vector::[]}(i)));
     return result;
   }
@@ -26,7 +26,7 @@
 [@vm.inferred-type.metadata=dart.core::_Double?]static field core::double x = 0.0;
 static method main(core::List<core::String> args) → dynamic {
   core::Stopwatch timer = let final core::Stopwatch #t4 = new core::Stopwatch::•() in let final dynamic #t5 = [@vm.direct-call.metadata=dart.core::Stopwatch::start] #t4.{core::Stopwatch::start}() in #t4;
-  for (core::int i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation::<??] [@vm.inferred-type.metadata=dart.core::bool] i.{core::num::<}(100000000); i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+??] i.{core::num::+}(1)) {
+  for (core::int i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation::<??] [@vm.inferred-type.metadata=dart.core::bool] i.{core::num::<}(100000000); i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+??] [@vm.inferred-type.metadata=int?] i.{core::num::+}(1)) {
     self::x = [@vm.direct-call.metadata=dart.core::_Double::+??] [@vm.inferred-type.metadata=dart.core::_Double] [@vm.inferred-type.metadata=dart.core::_Double?] self::x.{core::double::+}([@vm.direct-call.metadata=#lib::_Vector::*??] [@vm.inferred-type.metadata=dart.core::_Double] [@vm.inferred-type.metadata=#lib::_Vector?] self::v.{self::_Vector::*}([@vm.inferred-type.metadata=#lib::_Vector?] self::v));
   }
   [@vm.direct-call.metadata=dart.core::Stopwatch::stop] timer.{core::Stopwatch::stop}();
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_while_processing.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_while_processing.dart.expect
index 2d95c84..240802d 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_while_processing.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_while_processing.dart.expect
@@ -28,7 +28,7 @@
     return new self::Point::•([@vm.direct-call.metadata=#lib::Point::x] [@vm.inferred-type.metadata=!] this.{self::Point::x});
 }
 static method getX([@vm.inferred-type.metadata=#lib::Point] dynamic point) → dynamic {
-  point.x;
+  [@vm.direct-call.metadata=#lib::Point::x] point.x;
 }
 static method main() → dynamic {
   self::Point a = new self::Point::•(new self::T1::•());
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
index 01c1009..86a3d0f 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
@@ -51,7 +51,7 @@
     return [@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withoutType("get:bar", const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
   no-such-method-forwarder method foo() → dynamic
     return [@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withoutType("foo", const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
-  no-such-method-forwarder method bazz([@vm.inferred-type.metadata=!] dynamic a1, [@vm.inferred-type.metadata=!] dynamic a2, [@vm.inferred-type.metadata=!] dynamic a3, [[@vm.inferred-type.metadata=!] dynamic a4 = null, [@vm.inferred-type.metadata=dart.core::Null?] dynamic a5 = null]) → dynamic
+  no-such-method-forwarder method bazz([@vm.inferred-type.metadata=int] dynamic a1, [@vm.inferred-type.metadata=int] dynamic a2, [@vm.inferred-type.metadata=int] dynamic a3, [[@vm.inferred-type.metadata=int] dynamic a4 = null, [@vm.inferred-type.metadata=dart.core::Null?] dynamic a5 = null]) → dynamic
     return [@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withoutType("bazz", const <core::Type>[], core::List::unmodifiable<dynamic>(<dynamic>[a1, a2, a3, a4, a5]), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
 }
 abstract class C extends core::Object {
@@ -70,7 +70,7 @@
     return [@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withoutType("get:bar", const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
   no-such-method-forwarder method foo() → dynamic
     return [@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withoutType("foo", const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
-  no-such-method-forwarder method bazz([@vm.inferred-type.metadata=!] dynamic a1, [@vm.inferred-type.metadata=!] dynamic a2, [@vm.inferred-type.metadata=!] dynamic a3, [[@vm.inferred-type.metadata=!] dynamic a4 = null, [@vm.inferred-type.metadata=dart.core::Null?] dynamic a5 = null]) → dynamic
+  no-such-method-forwarder method bazz([@vm.inferred-type.metadata=int] dynamic a1, [@vm.inferred-type.metadata=int] dynamic a2, [@vm.inferred-type.metadata=int] dynamic a3, [[@vm.inferred-type.metadata=int] dynamic a4 = null, [@vm.inferred-type.metadata=dart.core::Null?] dynamic a5 = null]) → dynamic
     return [@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withoutType("bazz", const <core::Type>[], core::List::unmodifiable<dynamic>(<dynamic>[a1, a2, a3, a4, a5]), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
 }
 class E extends core::Object implements self::A {
@@ -103,7 +103,7 @@
   synthetic constructor •() → void
     : super core::Object::•()
     ;
-  method foo({[@vm.inferred-type.metadata=!] dynamic left = null, [@vm.inferred-type.metadata=!] dynamic right = null}) → dynamic
+  method foo({[@vm.inferred-type.metadata=int] dynamic left = null, [@vm.inferred-type.metadata=int] dynamic right = null}) → dynamic
     return new self::T6::•();
   method noSuchMethod(core::Invocation invocation) → dynamic {
     return new self::T7::•();
@@ -130,6 +130,6 @@
   dynamic gg = new self::G::•();
   core::print([@vm.inferred-type.metadata=#lib::T5] gg.noSuchMethod(null, null));
   dynamic hh = new self::H::•();
-  core::print([@vm.inferred-type.metadata=#lib::T6] hh.foo(right: 2, left: 1));
+  core::print([@vm.direct-call.metadata=#lib::H::foo] [@vm.inferred-type.metadata=#lib::T6] hh.foo(right: 2, left: 1));
   core::print([@vm.inferred-type.metadata=#lib::T7] hh.foo(left: 1, top: 2));
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
index 18cbd9d4..1ad7392 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
@@ -16,7 +16,7 @@
   synthetic constructor •() → void
     : super core::Object::•()
     ;
-  method call([dynamic a1 = null, dynamic a2 = null, dynamic a3 = null, dynamic a4 = null, [@vm.inferred-type.metadata=#lib::T1?] dynamic a5 = null]) → void {
+  method call([dynamic a1 = null, dynamic a2 = null, dynamic a3 = null, [@vm.inferred-type.metadata=int?] dynamic a4 = null, [@vm.inferred-type.metadata=#lib::T1?] dynamic a5 = null]) → void {
     [@vm.direct-call.metadata=#lib::A1::foo] this.{self::A1::foo} = a5 as{TypeError} self::T1;
   }
 }
@@ -39,7 +39,7 @@
   synthetic constructor •() → void
     : super core::Object::•()
     ;
-  method call([dynamic a1 = null, dynamic a2 = null, dynamic a3 = null, dynamic a4 = null, dynamic a5 = null, [@vm.inferred-type.metadata=#lib::T2?] dynamic a6 = null]) → void {
+  method call([dynamic a1 = null, dynamic a2 = null, dynamic a3 = null, [@vm.inferred-type.metadata=int?] dynamic a4 = null, [@vm.inferred-type.metadata=int?] dynamic a5 = null, [@vm.inferred-type.metadata=#lib::T2?] dynamic a6 = null]) → void {
     [@vm.direct-call.metadata=#lib::A2::foo] this.{self::A2::foo} = a6;
   }
 }
@@ -56,7 +56,7 @@
     : super self::B2Base::•()
     ;
   method doSuperCall() → void {
-    [@vm.inferred-type.metadata=#lib::A2] super.{self::B2Base::aa2}.call(1, 2, 3, 4, 5, new self::T2::•());
+    [@vm.direct-call.metadata=#lib::A2::call] [@vm.inferred-type.metadata=#lib::A2] super.{self::B2Base::aa2}.call(1, 2, 3, 4, 5, new self::T2::•());
   }
 }
 class T3 extends core::Object {
@@ -72,7 +72,7 @@
   synthetic constructor •() → void
     : super core::Object::•()
     ;
-  method call([dynamic a1 = null, dynamic a2 = null, dynamic a3 = null, dynamic a4 = null, dynamic a5 = null, dynamic a6 = null, [@vm.inferred-type.metadata=#lib::T3?] dynamic a7 = null]) → void {
+  method call([dynamic a1 = null, dynamic a2 = null, dynamic a3 = null, [@vm.inferred-type.metadata=int?] dynamic a4 = null, [@vm.inferred-type.metadata=int?] dynamic a5 = null, [@vm.inferred-type.metadata=int?] dynamic a6 = null, [@vm.inferred-type.metadata=#lib::T3?] dynamic a7 = null]) → void {
     [@vm.direct-call.metadata=#lib::A3::foo] this.{self::A3::foo} = a7;
   }
 }
@@ -95,7 +95,7 @@
   synthetic constructor •() → void
     : super core::Object::•()
     ;
-  method call([dynamic a1 = null, dynamic a2 = null, dynamic a3 = null, dynamic a4 = null, dynamic a5 = null, dynamic a6 = null, dynamic a7 = null, [@vm.inferred-type.metadata=#lib::T4?] dynamic a8 = null]) → void {
+  method call([dynamic a1 = null, dynamic a2 = null, dynamic a3 = null, [@vm.inferred-type.metadata=int?] dynamic a4 = null, [@vm.inferred-type.metadata=int?] dynamic a5 = null, [@vm.inferred-type.metadata=int?] dynamic a6 = null, [@vm.inferred-type.metadata=int?] dynamic a7 = null, [@vm.inferred-type.metadata=#lib::T4?] dynamic a8 = null]) → void {
     [@vm.direct-call.metadata=#lib::A4::foo] this.{self::A4::foo} = a8;
   }
 }
@@ -123,7 +123,7 @@
   self::B2 bb = new self::B2::•();
   [@vm.direct-call.metadata=#lib::B2::doSuperCall] bb.{self::B2::doSuperCall}();
   self::ok = false;
-  [@vm.inferred-type.metadata=#lib::T2?] [@vm.direct-call.metadata=#lib::B2Base::aa2] [@vm.inferred-type.metadata=#lib::A2] bb.{self::B2Base::aa2}.foo.doTest2();
+  [@vm.direct-call.metadata=#lib::T2::doTest2??] [@vm.direct-call.metadata=#lib::A2::foo] [@vm.inferred-type.metadata=#lib::T2?] [@vm.direct-call.metadata=#lib::B2Base::aa2] [@vm.inferred-type.metadata=#lib::A2] bb.{self::B2Base::aa2}.foo.doTest2();
   exp::Expect::isTrue([@vm.inferred-type.metadata=dart.core::bool?] self::ok);
 }
 static method getDynamic3() → dynamic
@@ -131,7 +131,7 @@
 static method test3() → void {
   self::getDynamic3().aa3(1, 2, 3, 4, 5, 6, new self::T3::•());
   self::ok = false;
-  [@vm.inferred-type.metadata=#lib::T3?] [@vm.inferred-type.metadata=#lib::A3] [@vm.inferred-type.metadata=#lib::B3?] self::bb3.aa3.foo.doTest3();
+  [@vm.direct-call.metadata=#lib::T3::doTest3??] [@vm.direct-call.metadata=#lib::A3::foo] [@vm.inferred-type.metadata=#lib::T3?] [@vm.direct-call.metadata=#lib::B3::aa3??] [@vm.inferred-type.metadata=#lib::A3] [@vm.inferred-type.metadata=#lib::B3?] self::bb3.aa3.foo.doTest3();
   exp::Expect::isTrue([@vm.inferred-type.metadata=dart.core::bool?] self::ok);
 }
 static method getDynamic4() → dynamic
@@ -139,7 +139,7 @@
 static method test4() → void {
   self::getDynamic4().aa4(1, 2, 3, 4, 5, 6, 7, new self::T4::•());
   self::ok = false;
-  [@vm.inferred-type.metadata=#lib::T4?] [@vm.inferred-type.metadata=#lib::A4] self::getDynamic4().aa4.foo.doTest4();
+  [@vm.direct-call.metadata=#lib::T4::doTest4??] [@vm.direct-call.metadata=#lib::A4::foo] [@vm.inferred-type.metadata=#lib::T4?] [@vm.inferred-type.metadata=#lib::A4] self::getDynamic4().aa4.foo.doTest4();
   exp::Expect::isTrue([@vm.inferred-type.metadata=dart.core::bool?] self::ok);
 }
 static method main() → void {
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect
index 002fca8..8492698 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect
@@ -12,7 +12,7 @@
     : super self::A::•()
     ;
   method foo() → core::int
-    return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] 1.{core::num::+}([@vm.inferred-type.metadata=#lib::B] self::knownResult().foo() as{TypeError} core::num) as{TypeError} core::int;
+    return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] 1.{core::num::+}([@vm.direct-call.metadata=#lib::B::foo] [@vm.inferred-type.metadata=#lib::B] self::knownResult().foo() as{TypeError} core::num) as{TypeError} core::int;
 }
 class TearOffDynamicMethod extends core::Object {
   field dynamic bazz;
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_interface_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_interface_method.dart.expect
index 00d3f0f..18c3870 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_interface_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_interface_method.dart.expect
@@ -13,7 +13,7 @@
     : super self::A::•()
     ;
   method foo() → core::int
-    return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] 1.{core::num::+}([@vm.inferred-type.metadata=#lib::B] self::knownResult().foo() as{TypeError} core::num) as{TypeError} core::int;
+    return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] [@vm.inferred-type.metadata=int?] 1.{core::num::+}([@vm.direct-call.metadata=#lib::B::foo] [@vm.inferred-type.metadata=int?] [@vm.inferred-type.metadata=#lib::B] self::knownResult().foo() as{TypeError} core::num) as{TypeError} core::int;
 }
 class TearOffInterfaceMethod extends core::Object {
   field dynamic bazz;
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
index 72d0517..689ae8c 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
@@ -13,14 +13,14 @@
     : super self::A::•()
     ;
   method foo() → core::int
-    return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] 1.{core::num::+}([@vm.inferred-type.metadata=#lib::B] self::knownResult().foo() as{TypeError} core::num) as{TypeError} core::int;
+    return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] [@vm.inferred-type.metadata=int?] 1.{core::num::+}([@vm.direct-call.metadata=#lib::B::foo] [@vm.inferred-type.metadata=int?] [@vm.inferred-type.metadata=#lib::B] self::knownResult().foo() as{TypeError} core::num) as{TypeError} core::int;
 }
 abstract class Base extends core::Object {
   synthetic constructor •() → void
     : super core::Object::•()
     ;
   method foo() → core::int
-    return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] 3.{core::num::+}([@vm.inferred-type.metadata=#lib::B] self::knownResult().foo() as{TypeError} core::num) as{TypeError} core::int;
+    return [@vm.direct-call.metadata=dart.core::_IntegerImplementation::+] [@vm.inferred-type.metadata=int?] 3.{core::num::+}([@vm.direct-call.metadata=#lib::B::foo] [@vm.inferred-type.metadata=int?] [@vm.inferred-type.metadata=#lib::B] self::knownResult().foo() as{TypeError} core::num) as{TypeError} core::int;
   method doCall(dynamic x) → core::int
     return x.call() as{TypeError} core::int;
 }
@@ -29,7 +29,7 @@
     : super self::Base::•()
     ;
   method bar() → core::int
-    return [@vm.direct-call.metadata=#lib::Base::doCall] this.{self::Base::doCall}(super.{self::Base::foo});
+    return [@vm.direct-call.metadata=#lib::Base::doCall] [@vm.inferred-type.metadata=int?] this.{self::Base::doCall}(super.{self::Base::foo});
 }
 [@vm.inferred-type.metadata=#lib::B?]static field self::A aa = new self::B::•();
 static method knownResult() → dynamic
diff --git a/runtime/vm/compiler/backend/type_propagator.cc b/runtime/vm/compiler/backend/type_propagator.cc
index 7b6458c..c1d4f2b 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -957,9 +957,10 @@
       if (block_->IsGraphEntry()) {
         inferred_type = param->parameter_type();
       }
-      // Best bet: use inferred type if it has a concrete class.
-      if ((inferred_type != NULL) &&
-          (inferred_type->ToNullableCid() != kDynamicCid)) {
+      // Best bet: use inferred type if it is a concrete class or int.
+      if ((inferred_type != nullptr) &&
+          ((inferred_type->ToNullableCid() != kDynamicCid) ||
+           inferred_type->IsNullableInt())) {
         TraceStrongModeType(this, inferred_type);
         return *inferred_type;
       }
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index d51b1c9..bda808c 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -359,8 +359,7 @@
       checked_argument_count, ic_data_array_, GetNextDeoptId(),
       interface_target);
   if ((result_type != NULL) && !result_type->IsTrivial()) {
-    call->SetResultType(Z, CompileType::CreateNullable(result_type->nullable,
-                                                       result_type->cid));
+    call->SetResultType(Z, result_type->ToCompileType(Z));
   }
   Push(call);
   return Fragment(call);
@@ -553,8 +552,7 @@
     }
   }
   if ((result_type != NULL) && !result_type->IsTrivial()) {
-    call->SetResultType(Z, CompileType::CreateNullable(result_type->nullable,
-                                                       result_type->cid));
+    call->SetResultType(Z, result_type->ToCompileType(Z));
   }
 }
 
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index 586eedb..02047bd 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -1607,17 +1607,18 @@
     intptr_t node_offset) {
   const intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset);
   if (md_offset < 0) {
-    return InferredTypeMetadata(kDynamicCid, true);
+    return InferredTypeMetadata(kDynamicCid,
+                                InferredTypeMetadata::kFlagNullable);
   }
 
   AlternativeReadingScope alt(&helper_->reader_, &H.metadata_payloads(),
                               md_offset);
 
   const NameIndex kernel_name = helper_->ReadCanonicalNameReference();
-  const bool nullable = helper_->ReadBool();
+  const uint8_t flags = helper_->ReadByte();
 
   if (H.IsRoot(kernel_name)) {
-    return InferredTypeMetadata(kDynamicCid, nullable);
+    return InferredTypeMetadata(kDynamicCid, flags);
   }
 
   const Class& klass =
@@ -1631,7 +1632,7 @@
     cid = kDynamicCid;
   }
 
-  return InferredTypeMetadata(cid, nullable);
+  return InferredTypeMetadata(cid, flags);
 }
 
 ProcedureAttributesMetadataHelper::ProcedureAttributesMetadataHelper(
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h
index 9a7fbf5..ea2f79b 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.h
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h
@@ -5,6 +5,7 @@
 #ifndef RUNTIME_VM_COMPILER_FRONTEND_KERNEL_TRANSLATION_HELPER_H_
 #define RUNTIME_VM_COMPILER_FRONTEND_KERNEL_TRANSLATION_HELPER_H_
 
+#include "vm/compiler/backend/il.h"  // For CompileType.
 #include "vm/kernel.h"
 #include "vm/kernel_binary.h"
 #include "vm/object.h"
@@ -831,13 +832,31 @@
 };
 
 struct InferredTypeMetadata {
-  InferredTypeMetadata(intptr_t cid_, bool nullable_)
-      : cid(cid_), nullable(nullable_) {}
+  enum Flag {
+    kFlagNullable = 1 << 0,
+    kFlagInt = 1 << 1,
+  };
+
+  InferredTypeMetadata(intptr_t cid_, uint8_t flags_)
+      : cid(cid_), flags(flags_) {}
 
   const intptr_t cid;
-  const bool nullable;
+  const uint8_t flags;
 
-  bool IsTrivial() const { return (cid == kDynamicCid) && nullable; }
+  bool IsTrivial() const {
+    return (cid == kDynamicCid) && (flags == kFlagNullable);
+  }
+  bool IsNullable() const { return (flags & kFlagNullable) != 0; }
+  bool IsInt() const { return (flags & kFlagInt) != 0; }
+
+  CompileType ToCompileType(Zone* zone) const {
+    if (IsInt()) {
+      return CompileType::FromAbstractType(
+          Type::ZoneHandle(zone, Type::IntType()), IsNullable());
+    } else {
+      return CompileType::CreateNullable(IsNullable(), cid);
+    }
+  }
 };
 
 // Helper class which provides access to inferred type metadata.
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 9c50544..d78f149 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -1482,8 +1482,7 @@
     const InferredTypeMetadata* param_type_md /* = NULL */) {
   CompileType* param_type = NULL;
   if ((param_type_md != NULL) && !param_type_md->IsTrivial()) {
-    param_type = new (Z) CompileType(CompileType::CreateNullable(
-        param_type_md->nullable, param_type_md->cid));
+    param_type = new (Z) CompileType(param_type_md->ToCompileType(Z));
   }
   return new (Z)
       LocalVariable(declaration_pos, token_pos, name, type, param_type);