Version 2.7.1

* Cherry-pick a1b895a900dc519559f04951ae110e900aeb499c to stable
* Cherry-pick 7373347c7ce72e423ca42c3515caa3c66a46b024 to stable
* Cherry-pick aa56cd39a3 to stable
* Cherry-pick 1103d9fdb5 to stable
* Cherry-pick b050a59d3b to stable
* Cherry-pick a57a97703a to stable
* Cherry-pick 75fc15c7e1 to stable
* Cherry-pick b233061a90 to stable
* Cherry-pick 4bc9c8b52c to stable
* Cherry-pick 529913fd39 to stable
* Cherry-pick a92a9f1ba2 to stable
* Cherry-pick d202ee3b72ac2ce38d5588e5b384efed0070f7ca to stable
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd63681..d316f79 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 2.7.1 - 2020-01-32
+
+This is a patch release that improves dart2js compile-time (issue [40217][]).
+
+[40217]: https://github.com/dart-lang/sdk/issues/40217
+
+**Breaking Change**:
+The Dart SDK for macOS is now only available for x64 (issue [39810][]).
+
+[39810]: https://github.com/dart-lang/sdk/issues/39810
+
 ## 2.7.0 - 2019-12-11
 
 ### Language
diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
index 48e3b92..9a003bd 100644
--- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart
+++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
@@ -339,9 +339,8 @@
     ArgumentsTypes arguments = analyzeArguments(node.arguments);
     Selector selector = new Selector(SelectorKind.CALL, constructor.memberName,
         _elementMap.getCallStructure(node.arguments));
-    AbstractValue mask = _memberData.typeOfSend(node);
     handleConstructorInvoke(
-        node, node.arguments, selector, mask, constructor, arguments);
+        node, node.arguments, selector, constructor, arguments);
 
     _inferrer.analyze(constructor);
     if (_inferrer.checkIfExposesThis(constructor)) {
@@ -356,9 +355,8 @@
     ArgumentsTypes arguments = analyzeArguments(node.arguments);
     Selector selector = new Selector(SelectorKind.CALL, constructor.memberName,
         _elementMap.getCallStructure(node.arguments));
-    AbstractValue mask = _memberData.typeOfSend(node);
     handleConstructorInvoke(
-        node, node.arguments, selector, mask, constructor, arguments);
+        node, node.arguments, selector, constructor, arguments);
 
     _inferrer.analyze(constructor);
     if (_inferrer.checkIfExposesThis(constructor)) {
@@ -391,7 +389,8 @@
 
     if (_closedWorld.nativeData.isNativeMember(_analyzedMember)) {
       // Native methods do not have a body, and we currently just say
-      // they return dynamic.
+      // they return dynamic and may contain all side-effects.
+      _sideEffectsBuilder.setAllSideEffectsAndDependsOnSomething();
       return _types.dynamicType;
     }
 
@@ -850,10 +849,32 @@
     return new ArgumentsTypes(positional, named);
   }
 
+  AbstractValue _typeOfReceiver(ir.TreeNode node, ir.TreeNode receiver) {
+    KernelGlobalTypeInferenceElementData data = _memberData;
+    AbstractValue mask = data.typeOfReceiver(node);
+    if (mask != null) return mask;
+    // TODO(sigmund): ensure that this is only called once per node.
+    DartType staticType = _getStaticType(receiver);
+    // TODO(sigmund): this needs to be adjusted when we enable non-nullable
+    // types to handle legacy and nullable wrappers.
+    if (staticType is InterfaceType) {
+      ClassEntity cls = staticType.element;
+      if (receiver is ir.ThisExpression && !_closedWorld.isUsedAsMixin(cls)) {
+        mask = _closedWorld.abstractValueDomain.createNonNullSubclass(cls);
+      } else {
+        mask = _closedWorld.abstractValueDomain.createNullableSubtype(cls);
+      }
+      data.setReceiverTypeMask(node, mask);
+      return mask;
+    }
+    // TODO(sigmund): consider also extracting the bound of type parameters.
+    return null;
+  }
+
   @override
   TypeInformation visitMethodInvocation(ir.MethodInvocation node) {
     Selector selector = _elementMap.getSelector(node);
-    AbstractValue mask = _memberData.typeOfSend(node);
+    AbstractValue mask = _typeOfReceiver(node, node.receiver);
 
     ir.TreeNode receiver = node.receiver;
     if (receiver is ir.VariableGet &&
@@ -867,7 +888,7 @@
       }
 
       TypeInformation type =
-          handleStaticInvoke(node, selector, mask, info.callMethod, arguments);
+          handleStaticInvoke(node, selector, info.callMethod, arguments);
       FunctionType functionType =
           _elementMap.elementEnvironment.getFunctionType(info.callMethod);
       if (functionType.returnType.containsFreeTypeVariables) {
@@ -1028,8 +1049,8 @@
           _closedWorld.commonElements.streamIteratorConstructor;
 
       /// Synthesize a call to the [StreamIterator] constructor.
-      iteratorType = handleStaticInvoke(node, null, null, constructor,
-          new ArgumentsTypes([expressionType], null));
+      iteratorType = handleStaticInvoke(
+          node, null, constructor, new ArgumentsTypes([expressionType], null));
     } else {
       TypeInformation expressionType = visit(node.iterable);
       Selector iteratorSelector = Selectors.iterator;
@@ -1118,9 +1139,8 @@
     ConstructorEntity constructor = _elementMap.getConstructor(node.target);
     ArgumentsTypes arguments = analyzeArguments(node.arguments);
     Selector selector = _elementMap.getSelector(node);
-    AbstractValue mask = _memberData.typeOfSend(node);
     return handleConstructorInvoke(
-        node, node.arguments, selector, mask, constructor, arguments);
+        node, node.arguments, selector, constructor, arguments);
   }
 
   /// Try to find the length given to a fixed array constructor call.
@@ -1176,11 +1196,10 @@
       ir.Node node,
       ir.Arguments arguments,
       Selector selector,
-      AbstractValue mask,
       ConstructorEntity constructor,
       ArgumentsTypes argumentsTypes) {
     TypeInformation returnType =
-        handleStaticInvoke(node, selector, mask, constructor, argumentsTypes);
+        handleStaticInvoke(node, selector, constructor, argumentsTypes);
     if (_elementMap.commonElements.isUnnamedListConstructor(constructor)) {
       // We have `new List(...)`.
       if (arguments.positional.isEmpty && arguments.named.isEmpty) {
@@ -1227,17 +1246,16 @@
   }
 
   TypeInformation handleStaticInvoke(ir.Node node, Selector selector,
-      AbstractValue mask, MemberEntity element, ArgumentsTypes arguments) {
-    return _inferrer.registerCalledMember(node, selector, mask, _analyzedMember,
+      MemberEntity element, ArgumentsTypes arguments) {
+    return _inferrer.registerCalledMember(node, selector, _analyzedMember,
         element, arguments, _sideEffectsBuilder, inLoop);
   }
 
   TypeInformation handleClosureCall(ir.Node node, Selector selector,
-      AbstractValue mask, MemberEntity member, ArgumentsTypes arguments) {
+      MemberEntity member, ArgumentsTypes arguments) {
     return _inferrer.registerCalledClosure(
         node,
         selector,
-        mask,
         _inferrer.typeOfMember(member),
         _analyzedMember,
         arguments,
@@ -1245,14 +1263,10 @@
         inLoop: inLoop);
   }
 
-  TypeInformation handleForeignInvoke(
-      ir.StaticInvocation node,
-      FunctionEntity function,
-      ArgumentsTypes arguments,
-      Selector selector,
-      AbstractValue mask) {
+  TypeInformation handleForeignInvoke(ir.StaticInvocation node,
+      FunctionEntity function, ArgumentsTypes arguments, Selector selector) {
     String name = function.name;
-    handleStaticInvoke(node, selector, mask, function, arguments);
+    handleStaticInvoke(node, selector, function, arguments);
     if (name == Identifiers.JS) {
       NativeBehavior nativeBehavior =
           _elementMap.getNativeBehaviorForJsCall(node);
@@ -1281,16 +1295,15 @@
     MemberEntity member = _elementMap.getMember(node.target);
     ArgumentsTypes arguments = analyzeArguments(node.arguments);
     Selector selector = _elementMap.getSelector(node);
-    AbstractValue mask = _memberData.typeOfSend(node);
     if (_closedWorld.commonElements.isForeign(member)) {
-      return handleForeignInvoke(node, member, arguments, selector, mask);
+      return handleForeignInvoke(node, member, arguments, selector);
     } else if (member.isConstructor) {
       return handleConstructorInvoke(
-          node, node.arguments, selector, mask, member, arguments);
+          node, node.arguments, selector, member, arguments);
     } else {
       assert(member.isFunction, "Unexpected static invocation target: $member");
       TypeInformation type =
-          handleStaticInvoke(node, selector, mask, member, arguments);
+          handleStaticInvoke(node, selector, member, arguments);
       FunctionType functionType =
           _elementMap.elementEnvironment.getFunctionType(member);
       if (functionType.returnType.containsFreeTypeVariables) {
@@ -1310,16 +1323,14 @@
 
   @override
   TypeInformation visitStaticGet(ir.StaticGet node) {
-    AbstractValue mask = _memberData.typeOfSend(node);
-    assert(mask == null);
-    return createStaticGetTypeInformation(node, node.target, mask: mask);
+    return createStaticGetTypeInformation(node, node.target);
   }
 
-  TypeInformation createStaticGetTypeInformation(ir.Node node, ir.Member target,
-      {AbstractValue mask}) {
+  TypeInformation createStaticGetTypeInformation(
+      ir.Node node, ir.Member target) {
     MemberEntity member = _elementMap.getMember(target);
     return handleStaticInvoke(
-        node, new Selector.getter(member.memberName), mask, member, null);
+        node, new Selector.getter(member.memberName), member, null);
   }
 
   @override
@@ -1329,17 +1340,16 @@
       _state.markThisAsExposed();
     }
     MemberEntity member = _elementMap.getMember(node.target);
-    AbstractValue mask = _memberData.typeOfSend(node);
-    handleStaticInvoke(node, new Selector.setter(member.memberName), mask,
-        member, new ArgumentsTypes([rhsType], null));
+    handleStaticInvoke(node, new Selector.setter(member.memberName), member,
+        new ArgumentsTypes([rhsType], null));
     return rhsType;
   }
 
-  TypeInformation handlePropertyGet(
-      ir.TreeNode node, TypeInformation receiverType, ir.Member interfaceTarget,
+  TypeInformation handlePropertyGet(ir.TreeNode node, ir.TreeNode receiver,
+      TypeInformation receiverType, ir.Member interfaceTarget,
       {bool isThis}) {
     Selector selector = _elementMap.getSelector(node);
-    AbstractValue mask = _memberData.typeOfSend(node);
+    AbstractValue mask = _typeOfReceiver(node, receiver);
     if (isThis) {
       _checkIfExposesThis(
           selector, _types.newTypedSelector(receiverType, mask));
@@ -1362,21 +1372,23 @@
   @override
   TypeInformation visitPropertyGet(ir.PropertyGet node) {
     TypeInformation receiverType = visit(node.receiver);
-    return handlePropertyGet(node, receiverType, node.interfaceTarget,
+    return handlePropertyGet(
+        node, node.receiver, receiverType, node.interfaceTarget,
         isThis: node.receiver is ir.ThisExpression);
   }
 
   @override
   TypeInformation visitDirectPropertyGet(ir.DirectPropertyGet node) {
     TypeInformation receiverType = thisType;
-    return handlePropertyGet(node, receiverType, node.target, isThis: true);
+    return handlePropertyGet(node, node.receiver, receiverType, node.target,
+        isThis: true);
   }
 
   @override
   TypeInformation visitPropertySet(ir.PropertySet node) {
     TypeInformation receiverType = visit(node.receiver);
     Selector selector = _elementMap.getSelector(node);
-    AbstractValue mask = _memberData.typeOfSend(node);
+    AbstractValue mask = _typeOfReceiver(node, node.receiver);
 
     TypeInformation rhsType = visit(node.value);
     if (node.value is ir.ThisExpression) {
@@ -1729,13 +1741,13 @@
     return _types.nonNullEmpty();
   }
 
-  TypeInformation handleSuperNoSuchMethod(ir.Node node, Selector selector,
-      AbstractValue mask, ArgumentsTypes arguments) {
+  TypeInformation handleSuperNoSuchMethod(
+      ir.Node node, Selector selector, ArgumentsTypes arguments) {
     // Ensure we create a node, to make explicit the call to the
     // `noSuchMethod` handler.
     FunctionEntity noSuchMethod =
         _elementMap.getSuperNoSuchMethod(_analyzedMember.enclosingClass);
-    return handleStaticInvoke(node, selector, mask, noSuchMethod, arguments);
+    return handleStaticInvoke(node, selector, noSuchMethod, arguments);
   }
 
   @override
@@ -1747,10 +1759,8 @@
     MemberEntity member =
         _elementMap.getSuperMember(_analyzedMember, node.name);
     assert(member != null, "No member found for super property get: $node");
-    AbstractValue mask = _memberData.typeOfSend(node);
     Selector selector = new Selector.getter(_elementMap.getName(node.name));
-    TypeInformation type =
-        handleStaticInvoke(node, selector, mask, member, null);
+    TypeInformation type = handleStaticInvoke(node, selector, member, null);
     if (member.isGetter) {
       FunctionType functionType =
           _elementMap.elementEnvironment.getFunctionType(member);
@@ -1780,10 +1790,9 @@
     MemberEntity member =
         _elementMap.getSuperMember(_analyzedMember, node.name, setter: true);
     assert(member != null, "No member found for super property set: $node");
-    AbstractValue mask = _memberData.typeOfSend(node);
     Selector selector = new Selector.setter(_elementMap.getName(node.name));
     ArgumentsTypes arguments = new ArgumentsTypes([rhsType], null);
-    handleStaticInvoke(node, selector, mask, member, arguments);
+    handleStaticInvoke(node, selector, member, arguments);
     return rhsType;
   }
 
@@ -1797,17 +1806,16 @@
         _elementMap.getSuperMember(_analyzedMember, node.name);
     ArgumentsTypes arguments = analyzeArguments(node.arguments);
     Selector selector = _elementMap.getSelector(node);
-    AbstractValue mask = _memberData.typeOfSend(node);
     if (member == null) {
       // TODO(johnniwinther): This shouldn't be necessary.
-      return handleSuperNoSuchMethod(node, selector, mask, arguments);
+      return handleSuperNoSuchMethod(node, selector, arguments);
     } else {
       assert(member.isFunction, "Unexpected super invocation target: $member");
       if (isIncompatibleInvoke(member, arguments)) {
-        return handleSuperNoSuchMethod(node, selector, mask, arguments);
+        return handleSuperNoSuchMethod(node, selector, arguments);
       } else {
         TypeInformation type =
-            handleStaticInvoke(node, selector, mask, member, arguments);
+            handleStaticInvoke(node, selector, member, arguments);
         FunctionType functionType =
             _elementMap.elementEnvironment.getFunctionType(member);
         if (functionType.returnType.containsFreeTypeVariables) {
diff --git a/pkg/compiler/lib/src/inferrer/closure_tracer.dart b/pkg/compiler/lib/src/inferrer/closure_tracer.dart
index 3ee6a53..0a01c9b 100644
--- a/pkg/compiler/lib/src/inferrer/closure_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/closure_tracer.dart
@@ -7,7 +7,6 @@
 import '../common/names.dart' show Identifiers, Names;
 import '../elements/entities.dart';
 import '../universe/selector.dart' show Selector;
-import 'abstract_value_domain.dart';
 import 'debug.dart' as debug;
 import 'inferrer_engine.dart';
 import 'node_tracer.dart';
@@ -56,14 +55,13 @@
 
   void _analyzeCall(CallSiteTypeInformation info) {
     Selector selector = info.selector;
-    AbstractValue mask = info.mask;
     tracedElements.forEach((FunctionEntity functionElement) {
       if (!selector.callStructure
           .signatureApplies(functionElement.parameterStructure)) {
         return;
       }
-      inferrer.updateParameterAssignments(
-          info, functionElement, info.arguments, selector, mask,
+      inferrer.updateParameterInputs(
+          info, functionElement, info.arguments, selector,
           remove: false, addToQueue: false);
     });
   }
diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
index 98c43b2..a510d20 100644
--- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
+++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
@@ -43,7 +43,7 @@
 /// An inferencing engine that computes a call graph of [TypeInformation] nodes
 /// by visiting the AST of the application, and then does the inferencing on the
 /// graph.
-abstract class InferrerEngine {
+class InferrerEngine {
   /// A set of selector names that [List] implements, that we know return their
   /// element type.
   final Set<Selector> returnsListElementTypeSet =
@@ -58,11 +58,45 @@
     new Selector.call(const PublicName('removeLast'), CallStructure.NO_ARGS)
   ]);
 
-  CompilerOptions get options;
-
   /// The [JClosedWorld] on which inference reasoning is based.
-  JClosedWorld get closedWorld;
-  DiagnosticReporter get reporter;
+  final JsClosedWorld closedWorld;
+
+  final TypeSystem types;
+  final Map<ir.TreeNode, TypeInformation> concreteTypes = {};
+  final InferredDataBuilder inferredDataBuilder;
+
+  final FunctionEntity mainElement;
+
+  final Map<Local, TypeInformation> _defaultTypeOfParameter =
+      new Map<Local, TypeInformation>();
+
+  final WorkQueue _workQueue = new WorkQueue();
+
+  final Set<MemberEntity> _analyzedElements = new Set<MemberEntity>();
+
+  /// The maximum number of times we allow a node in the graph to
+  /// change types. If a node reaches that limit, we give up
+  /// inferencing on it and give it the dynamic type.
+  final int _MAX_CHANGE_COUNT = 6;
+
+  int _overallRefineCount = 0;
+  int _addedInGraph = 0;
+
+  final CompilerOptions _options;
+  final Progress _progress;
+  final DiagnosticReporter _reporter;
+  final CompilerOutput _compilerOutput;
+
+  final Set<ConstructorEntity> _generativeConstructorsExposingThis =
+      new Set<ConstructorEntity>();
+
+  /// Data computed internally within elements, like the type-mask of a send a
+  /// list allocation, or a for-in loop.
+  final Map<MemberEntity, GlobalTypeInferenceElementData> _memberData =
+      new Map<MemberEntity, GlobalTypeInferenceElementData>();
+
+  ElementEnvironment get _elementEnvironment => closedWorld.elementEnvironment;
+
   AbstractValueDomain get abstractValueDomain =>
       closedWorld.abstractValueDomain;
   CommonElements get commonElements => closedWorld.commonElements;
@@ -71,255 +105,19 @@
   // [ClosureWorldRefiner].
   NoSuchMethodData get noSuchMethodData => closedWorld.noSuchMethodData;
 
-  TypeSystem get types;
-  Map<ir.TreeNode, TypeInformation> get concreteTypes;
-  InferredDataBuilder get inferredDataBuilder;
-
-  FunctionEntity get mainElement;
-
-  void runOverAllElements();
-
-  void analyze(MemberEntity member);
-  void analyzeListAndEnqueue(ListTypeInformation info);
-  void analyzeSetAndEnqueue(SetTypeInformation info);
-  void analyzeMapAndEnqueue(MapTypeInformation info);
-
-  /// Notifies to the inferrer that [analyzedElement] can have return type
-  /// [newType]. [currentType] is the type the inference has currently found.
-  ///
-  /// Returns the new type for [analyzedElement].
-  TypeInformation addReturnTypeForMethod(
-      FunctionEntity element, TypeInformation unused, TypeInformation newType);
-
-  /// Applies [f] to all elements in the universe that match [selector] and
-  /// [mask]. If [f] returns false, aborts the iteration.
-  void forEachElementMatching(
-      Selector selector, AbstractValue mask, bool f(MemberEntity element));
-
-  /// Returns the [TypeInformation] node for the default value of a parameter.
-  /// If this is queried before it is set by [setDefaultTypeOfParameter], a
-  /// [PlaceholderTypeInformation] is returned, which will later be replaced
-  /// by the actual node when [setDefaultTypeOfParameter] is called.
-  ///
-  /// Invariant: After graph construction, no [PlaceholderTypeInformation] nodes
-  /// should be present and a default type for each parameter should exist.
-  TypeInformation getDefaultTypeOfParameter(Local parameter);
-
-  /// This helper breaks abstractions but is currently required to work around
-  /// the wrong modeling of default values of optional parameters of
-  /// synthetic constructors.
-  ///
-  /// TODO(johnniwinther): Remove once default values of synthetic parameters
-  /// are fixed.
-  bool hasAlreadyComputedTypeOfParameterDefault(Local parameter);
-
-  /// Sets the type of a parameter's default value to [type]. If the global
-  /// mapping in [defaultTypeOfParameter] already contains a type, it must be
-  /// a [PlaceholderTypeInformation], which will be replaced. All its uses are
-  /// updated.
-  void setDefaultTypeOfParameter(Local parameter, TypeInformation type,
-      {bool isInstanceMember});
-
-  Iterable<MemberEntity> getCallersOfForTesting(MemberEntity element);
-
-  // TODO(johnniwinther): Make this private again.
-  GlobalTypeInferenceElementData dataOfMember(MemberEntity element);
-
-  bool checkIfExposesThis(ConstructorEntity element);
-
-  void recordExposesThis(ConstructorEntity element, bool exposesThis);
-
-  /// Records that the return type [element] is of type [type].
-  void recordReturnType(FunctionEntity element, TypeInformation type);
-
-  /// Records that [element] is of type [type].
-  void recordTypeOfField(FieldEntity element, TypeInformation type);
-
-  /// Registers a call to await with an expression of type [argumentType] as
-  /// argument.
-  TypeInformation registerAwait(ir.Node node, TypeInformation argument);
-
-  /// Registers a call to yield with an expression of type [argumentType] as
-  /// argument.
-  TypeInformation registerYield(ir.Node node, TypeInformation argument);
-
-  /// Registers that [caller] calls [closure] with [arguments].
-  ///
-  /// [sideEffectsBuilder] will be updated to incorporate the potential callees'
-  /// side effects.
-  ///
-  /// [inLoop] tells whether the call happens in a loop.
-  TypeInformation registerCalledClosure(
-      ir.Node node,
-      Selector selector,
-      AbstractValue mask,
-      TypeInformation closure,
-      MemberEntity caller,
-      ArgumentsTypes arguments,
-      SideEffectsBuilder sideEffectsBuilder,
-      {bool inLoop});
-
-  /// Registers that [caller] calls [callee] at location [node], with
-  /// [selector], and [arguments]. Note that [selector] is null for forwarding
-  /// constructors.
-  ///
-  /// [sideEffectsBuilder] will be updated to incorporate [callee]'s side
-  /// effects.
-  ///
-  /// [inLoop] tells whether the call happens in a loop.
-  TypeInformation registerCalledMember(
-      Object node,
-      Selector selector,
-      AbstractValue mask,
-      MemberEntity caller,
-      MemberEntity callee,
-      ArgumentsTypes arguments,
-      SideEffectsBuilder sideEffectsBuilder,
-      bool inLoop);
-
-  /// Registers that [caller] calls [selector] with [receiverType] as receiver,
-  /// and [arguments].
-  ///
-  /// [sideEffectsBuilder] will be updated to incorporate the potential callees'
-  /// side effects.
-  ///
-  /// [inLoop] tells whether the call happens in a loop.
-  TypeInformation registerCalledSelector(
-      CallType callType,
-      ir.Node node,
-      Selector selector,
-      AbstractValue mask,
-      TypeInformation receiverType,
-      MemberEntity caller,
-      ArgumentsTypes arguments,
-      SideEffectsBuilder sideEffectsBuilder,
-      {bool inLoop,
-      bool isConditional});
-
-  /// Update the assignments to parameters in the graph. [remove] tells whether
-  /// assignments must be added or removed. If [init] is false, parameters are
-  /// added to the work queue.
-  void updateParameterAssignments(TypeInformation caller, MemberEntity callee,
-      ArgumentsTypes arguments, Selector selector, AbstractValue mask,
-      {bool remove, bool addToQueue: true});
-
-  void updateSelectorInMember(MemberEntity owner, CallType callType,
-      ir.Node node, Selector selector, AbstractValue mask);
-
-  /// Returns the return type of [element].
-  TypeInformation returnTypeOfMember(MemberEntity element);
-
-  /// Returns the type of [element] when being called with [selector].
-  TypeInformation typeOfMemberWithSelector(
-      MemberEntity element, Selector selector);
-
-  /// Returns the type of [element].
-  TypeInformation typeOfMember(MemberEntity element);
-
-  /// Returns the type of [element].
-  TypeInformation typeOfParameter(Local element);
-
-  /// Returns the type for [nativeBehavior]. See documentation on
-  /// [NativeBehavior].
-  TypeInformation typeOfNativeBehavior(NativeBehavior nativeBehavior);
-
-  /// For a given selector, return a shared dynamic call site that will be used
-  /// to combine the results of multiple dynamic calls in the program via
-  /// [IndirectDynamicCallSiteTypeInformation].
-  ///
-  /// This is used only for scalability reasons: if there are too many targets
-  /// and call sites, we may have a quadratic number of edges in the graph, so
-  /// we add a level of indirection to merge the information and keep the graph
-  /// smaller.
-  DynamicCallSiteTypeInformation typeOfSharedDynamicCall(
-      Selector selector, CallStructure structure);
-
-  bool returnsListElementType(Selector selector, AbstractValue mask);
-
-  bool returnsMapValueType(Selector selector, AbstractValue mask);
-
-  void close();
-
-  void clear();
-
-  /// Returns true if global optimizations such as type inferencing can apply to
-  /// the field [element].
-  ///
-  /// One category of elements that do not apply is runtime helpers that the
-  /// backend calls, but the optimizations don't see those calls.
-  bool canFieldBeUsedForGlobalOptimizations(FieldEntity element);
-
-  /// Returns true if global optimizations such as type inferencing can apply to
-  /// the parameter [element].
-  ///
-  /// One category of elements that do not apply is runtime helpers that the
-  /// backend calls, but the optimizations don't see those calls.
-  bool canFunctionParametersBeUsedForGlobalOptimizations(
-      FunctionEntity function);
-
-  /// Returns `true` if inference of parameter types is disabled for [member].
-  bool assumeDynamic(MemberEntity member);
-}
-
-class InferrerEngineImpl extends InferrerEngine {
-  final Map<Local, TypeInformation> defaultTypeOfParameter =
-      new Map<Local, TypeInformation>();
-  final WorkQueue workQueue = new WorkQueue();
-  @override
-  final FunctionEntity mainElement;
-  final Set<MemberEntity> analyzedElements = new Set<MemberEntity>();
-
-  /// The maximum number of times we allow a node in the graph to
-  /// change types. If a node reaches that limit, we give up
-  /// inferencing on it and give it the dynamic type.
-  final int MAX_CHANGE_COUNT = 6;
-
-  int overallRefineCount = 0;
-  int addedInGraph = 0;
-
-  @override
-  final CompilerOptions options;
-  final Progress progress;
-  @override
-  final DiagnosticReporter reporter;
-  final CompilerOutput _compilerOutput;
-
-  @override
-  final JsClosedWorld closedWorld;
-  @override
-  final InferredDataBuilder inferredDataBuilder;
-
-  @override
-  final TypeSystem types;
-  @override
-  final Map<ir.TreeNode, TypeInformation> concreteTypes =
-      new Map<ir.TreeNode, TypeInformation>();
-
-  final Set<ConstructorEntity> generativeConstructorsExposingThis =
-      new Set<ConstructorEntity>();
-
-  /// Data computed internally within elements, like the type-mask of a send a
-  /// list allocation, or a for-in loop.
-  final Map<MemberEntity, GlobalTypeInferenceElementData> _memberData =
-      new Map<MemberEntity, GlobalTypeInferenceElementData>();
-
-  final NoSuchMethodRegistry noSuchMethodRegistry;
-
-  InferrerEngineImpl(
-      this.options,
-      this.progress,
-      this.reporter,
+  InferrerEngine(
+      this._options,
+      this._progress,
+      this._reporter,
       this._compilerOutput,
       this.closedWorld,
-      this.noSuchMethodRegistry,
       this.mainElement,
       this.inferredDataBuilder)
       : this.types = new TypeSystem(
             closedWorld, new KernelTypeSystemStrategy(closedWorld));
 
-  ElementEnvironment get _elementEnvironment => closedWorld.elementEnvironment;
-
-  @override
+  /// Applies [f] to all elements in the universe that match [selector] and
+  /// [mask]. If [f] returns false, aborts the iteration.
   void forEachElementMatching(
       Selector selector, AbstractValue mask, bool f(MemberEntity element)) {
     Iterable<MemberEntity> elements = closedWorld.locateMembers(selector, mask);
@@ -329,13 +127,12 @@
   }
 
   // TODO(johnniwinther): Make this private again.
-  @override
   GlobalTypeInferenceElementData dataOfMember(MemberEntity element) =>
       _memberData[element] ??= new KernelGlobalTypeInferenceElementData();
 
   /// Update [sideEffects] with the side effects of [callee] being
   /// called with [selector].
-  void updateSideEffects(SideEffectsBuilder sideEffectsBuilder,
+  void _updateSideEffects(SideEffectsBuilder sideEffectsBuilder,
       Selector selector, MemberEntity callee) {
     if (callee.isField) {
       if (callee.isInstanceMember) {
@@ -363,7 +160,8 @@
     }
   }
 
-  @override
+  /// Returns the type for [nativeBehavior]. See documentation on
+  /// [NativeBehavior].
   TypeInformation typeOfNativeBehavior(NativeBehavior nativeBehavior) {
     if (nativeBehavior == null) return types.dynamicType;
     List typesReturned = nativeBehavior.typesReturned;
@@ -404,14 +202,13 @@
     return returnType;
   }
 
-  @override
   void updateSelectorInMember(MemberEntity owner, CallType callType,
       ir.Node node, Selector selector, AbstractValue mask) {
     KernelGlobalTypeInferenceElementData data = dataOfMember(owner);
     assert(validCallType(callType, node, selector));
     switch (callType) {
       case CallType.access:
-        data.setTypeMask(node, mask);
+        data.setReceiverTypeMask(node, mask);
         break;
       case CallType.indirectAccess:
         // indirect access is not diretly recorded in the result data.
@@ -429,31 +226,26 @@
     }
   }
 
-  @override
   bool checkIfExposesThis(ConstructorEntity element) {
-    return generativeConstructorsExposingThis.contains(element);
+    return _generativeConstructorsExposingThis.contains(element);
   }
 
-  @override
   void recordExposesThis(ConstructorEntity element, bool exposesThis) {
     if (exposesThis) {
-      generativeConstructorsExposingThis.add(element);
+      _generativeConstructorsExposingThis.add(element);
     }
   }
 
-  @override
   bool returnsListElementType(Selector selector, AbstractValue mask) {
     return mask != null &&
         abstractValueDomain.isContainer(mask) &&
         returnsListElementTypeSet.contains(selector);
   }
 
-  @override
   bool returnsMapValueType(Selector selector, AbstractValue mask) {
     return mask != null && abstractValueDomain.isMap(mask) && selector.isIndex;
   }
 
-  @override
   void analyzeListAndEnqueue(ListTypeInformation info) {
     if (info.analyzed) return;
     info.analyzed = true;
@@ -468,13 +260,12 @@
         info.originalType, abstractValueDomain.fixedListType)) {
       info.checksGrowable = tracer.callsGrowableMethod;
     }
-    tracer.assignments.forEach(info.elementType.addAssignment);
+    tracer.inputs.forEach(info.elementType.addInput);
     // Enqueue the list for later refinement
-    workQueue.add(info);
-    workQueue.add(info.elementType);
+    _workQueue.add(info);
+    _workQueue.add(info.elementType);
   }
 
-  @override
   void analyzeSetAndEnqueue(SetTypeInformation info) {
     if (info.analyzed) return;
     info.analyzed = true;
@@ -486,13 +277,12 @@
     info.bailedOut = false;
     info.elementType.inferred = true;
 
-    tracer.assignments.forEach(info.elementType.addAssignment);
+    tracer.inputs.forEach(info.elementType.addInput);
     // Enqueue the set for later refinement.
-    workQueue.add(info);
-    workQueue.add(info.elementType);
+    _workQueue.add(info);
+    _workQueue.add(info.elementType);
   }
 
-  @override
   void analyzeMapAndEnqueue(MapTypeInformation info) {
     if (info.analyzed) return;
     info.analyzed = true;
@@ -502,31 +292,30 @@
     if (!succeeded) return;
 
     info.bailedOut = false;
-    for (int i = 0; i < tracer.keyAssignments.length; ++i) {
-      TypeInformation newType = info.addEntryAssignment(abstractValueDomain,
-          tracer.keyAssignments[i], tracer.valueAssignments[i]);
-      if (newType != null) workQueue.add(newType);
+    for (int i = 0; i < tracer.keyInputs.length; ++i) {
+      TypeInformation newType = info.addEntryInput(
+          abstractValueDomain, tracer.keyInputs[i], tracer.valueInputs[i]);
+      if (newType != null) _workQueue.add(newType);
     }
-    for (TypeInformation map in tracer.mapAssignments) {
-      workQueue.addAll(info.addMapAssignment(abstractValueDomain, map));
+    for (TypeInformation map in tracer.mapInputs) {
+      _workQueue.addAll(info.addMapInput(abstractValueDomain, map));
     }
 
     info.markAsInferred();
-    workQueue.add(info.keyType);
-    workQueue.add(info.valueType);
-    workQueue.addAll(info.typeInfoMap.values);
-    workQueue.add(info);
+    _workQueue.add(info.keyType);
+    _workQueue.add(info.valueType);
+    _workQueue.addAll(info.typeInfoMap.values);
+    _workQueue.add(info);
   }
 
-  @override
   void runOverAllElements() {
-    analyzeAllElements();
+    _analyzeAllElements();
     TypeGraphDump dump =
         debug.PRINT_GRAPH ? new TypeGraphDump(_compilerOutput, this) : null;
 
     dump?.beforeAnalysis();
-    buildWorkQueue();
-    refine();
+    _buildWorkQueue();
+    _refine();
 
     // Try to infer element types of lists and compute their escape information.
     types.allocatedLists.values.forEach((TypeInformation info) {
@@ -560,7 +349,7 @@
             types.strategy.forEachParameter(element, (Local parameter) {
               types
                   .getInferredTypeOfParameter(parameter)
-                  .giveUp(this, clearAssignments: false);
+                  .giveUp(this, clearInputs: false);
             });
           });
           bailedOutOn.addAll(elements);
@@ -573,7 +362,7 @@
             ParameterTypeInformation info =
                 types.getInferredTypeOfParameter(parameter);
             info.maybeResume();
-            workQueue.add(info);
+            _workQueue.add(info);
           });
           if (tracer.tracedType.mightBePassedToFunctionApply) {
             inferredDataBuilder.registerMightBePassedToApply(element);
@@ -598,7 +387,7 @@
           assert(calledElement is ConstructorEntity &&
               calledElement.isGenerativeConstructor);
           ClassEntity cls = calledElement.enclosingClass;
-          FunctionEntity callMethod = lookupCallMethod(cls);
+          FunctionEntity callMethod = _lookupCallMethod(cls);
           assert(callMethod != null, failedAt(cls));
           Iterable<FunctionEntity> elements = [callMethod];
           trace(elements, new ClosureTracerVisitor(elements, info, this));
@@ -625,17 +414,17 @@
     // as nodes that use elements fetched from these lists/maps. The
     // workset for a new run of the analysis will be these nodes.
     Set<TypeInformation> seenTypes = new Set<TypeInformation>();
-    while (!workQueue.isEmpty) {
-      TypeInformation info = workQueue.remove();
+    while (!_workQueue.isEmpty) {
+      TypeInformation info = _workQueue.remove();
       if (seenTypes.contains(info)) continue;
       // If the node cannot be reset, we do not need to update its users either.
       if (!info.reset(this)) continue;
       seenTypes.add(info);
-      workQueue.addAll(info.users);
+      _workQueue.addAll(info.users);
     }
 
-    workQueue.addAll(seenTypes);
-    refine();
+    _workQueue.addAll(seenTypes);
+    _refine();
 
     if (debug.PRINT_SUMMARY) {
       types.allocatedLists.values.forEach((_info) {
@@ -681,42 +470,42 @@
           }
         } else if (info is StaticCallSiteTypeInformation) {
           ClassEntity cls = info.calledElement.enclosingClass;
-          FunctionEntity callMethod = lookupCallMethod(cls);
+          FunctionEntity callMethod = _lookupCallMethod(cls);
           print('${types.getInferredSignatureOfMethod(callMethod)} for ${cls}');
         } else {
           print('${info.type} for some unknown kind of closure');
         }
       });
-      analyzedElements.forEach((MemberEntity elem) {
+      _analyzedElements.forEach((MemberEntity elem) {
         TypeInformation type = types.getInferredTypeOfMember(elem);
-        print('${elem} :: ${type} from ${type.assignments} ');
+        print('${elem} :: ${type} from ${type.inputs} ');
       });
     }
     dump?.afterAnalysis();
 
-    reporter.log('Inferred $overallRefineCount types.');
+    _reporter.log('Inferred $_overallRefineCount types.');
 
-    processLoopInformation();
+    _processLoopInformation();
   }
 
   /// Call [analyze] for all live members.
-  void analyzeAllElements() {
+  void _analyzeAllElements() {
     Iterable<MemberEntity> processedMembers = closedWorld.processedMembers
         .where((MemberEntity member) => !member.isAbstract);
 
-    progress.startPhase();
+    _progress.startPhase();
     processedMembers.forEach((MemberEntity member) {
-      progress.showProgress(
-          'Added ', addedInGraph, ' elements in inferencing graph.');
+      _progress.showProgress(
+          'Added ', _addedInGraph, ' elements in inferencing graph.');
       // This also forces the creation of the [ElementTypeInformation] to ensure
       // it is in the graph.
       types.withMember(member, () => analyze(member));
     });
-    reporter.log('Added $addedInGraph elements in inferencing graph.');
+    _reporter.log('Added $_addedInGraph elements in inferencing graph.');
   }
 
   /// Returns the body node for [member].
-  ir.Node computeMemberBody(MemberEntity member) {
+  ir.Node _computeMemberBody(MemberEntity member) {
     MemberDefinition definition =
         closedWorld.elementMap.getMemberDefinition(member);
     switch (definition.kind) {
@@ -752,7 +541,7 @@
 
   /// Returns the `call` method on [cls] or the `noSuchMethod` if [cls] doesn't
   /// implement `call`.
-  FunctionEntity lookupCallMethod(ClassEntity cls) {
+  FunctionEntity _lookupCallMethod(ClassEntity cls) {
     FunctionEntity function =
         _elementEnvironment.lookupClassMember(cls, Identifiers.call);
     if (function == null || function.isAbstract) {
@@ -762,18 +551,17 @@
     return function;
   }
 
-  @override
   void analyze(MemberEntity element) {
-    if (analyzedElements.contains(element)) return;
-    analyzedElements.add(element);
+    if (_analyzedElements.contains(element)) return;
+    _analyzedElements.add(element);
 
-    ir.Node body = computeMemberBody(element);
+    ir.Node body = _computeMemberBody(element);
 
     TypeInformation type;
-    reporter.withCurrentElement(element, () {
-      type = computeMemberTypeInformation(element, body);
+    _reporter.withCurrentElement(element, () {
+      type = _computeMemberTypeInformation(element, body);
     });
-    addedInGraph++;
+    _addedInGraph++;
 
     if (element.isField) {
       FieldEntity field = element;
@@ -784,7 +572,7 @@
           if (type is! ListTypeInformation && type is! MapTypeInformation) {
             // For non-container types, the constant handler does
             // constant folding that could give more precise results.
-            ConstantValue value = getFieldConstant(field);
+            ConstantValue value = _getFieldConstant(field);
             if (value != null) {
               if (value.isFunction) {
                 FunctionConstantValue functionConstant = value;
@@ -818,7 +606,7 @@
       if ((element.isStatic || element.isTopLevel) &&
           body != null &&
           !element.isConst) {
-        if (isFieldInitializerPotentiallyNull(element, body)) {
+        if (_isFieldInitializerPotentiallyNull(element, body)) {
           recordTypeOfField(field, types.nullType);
         }
       }
@@ -829,10 +617,10 @@
   }
 
   /// Visits [body] to compute the [TypeInformation] node for [member].
-  TypeInformation computeMemberTypeInformation(
+  TypeInformation _computeMemberTypeInformation(
       MemberEntity member, ir.Node body) {
     KernelTypeGraphBuilder visitor = new KernelTypeGraphBuilder(
-        options,
+        _options,
         closedWorld,
         this,
         member,
@@ -844,7 +632,7 @@
 
   /// Returns `true` if the [initializer] of the non-const static or top-level
   /// [field] is potentially `null`.
-  bool isFieldInitializerPotentiallyNull(
+  bool _isFieldInitializerPotentiallyNull(
       FieldEntity field, ir.Node initializer) {
     // TODO(13429): We could do better here by using the
     // constant handler to figure out if it's a lazy field or not.
@@ -866,18 +654,18 @@
 
   /// Returns the [ConstantValue] for the initial value of [field], or
   /// `null` if the initializer is not a constant value.
-  ConstantValue getFieldConstant(FieldEntity field) {
+  ConstantValue _getFieldConstant(FieldEntity field) {
     return closedWorld.fieldAnalysis.getFieldData(field).initialValue;
   }
 
   /// Returns `true` if [cls] has a 'call' method.
-  bool hasCallType(ClassEntity cls) {
+  bool _hasCallType(ClassEntity cls) {
     return closedWorld.elementMap.types
             .getCallType(closedWorld.elementEnvironment.getThisType(cls)) !=
         null;
   }
 
-  void processLoopInformation() {
+  void _processLoopInformation() {
     types.allocatedCalls.forEach((CallSiteTypeInformation info) {
       if (!info.inLoop) return;
       // We can't compute the callees of closures, no new information to add.
@@ -887,7 +675,8 @@
       if (info is StaticCallSiteTypeInformation) {
         MemberEntity member = info.calledElement;
         inferredDataBuilder.addFunctionCalledInLoop(member);
-      } else if (info.mask != null &&
+      } else if (info is DynamicCallSiteTypeInformation &&
+          info.mask != null &&
           abstractValueDomain.containsAll(info.mask).isDefinitelyFalse) {
         // For instance methods, we only register a selector called in a
         // loop if it is a typed selector, to avoid marking too many
@@ -900,20 +689,20 @@
     });
   }
 
-  void refine() {
-    progress.startPhase();
-    while (!workQueue.isEmpty) {
-      progress.showProgress('Inferred ', overallRefineCount, ' types.');
-      TypeInformation info = workQueue.remove();
+  void _refine() {
+    _progress.startPhase();
+    while (!_workQueue.isEmpty) {
+      _progress.showProgress('Inferred ', _overallRefineCount, ' types.');
+      TypeInformation info = _workQueue.remove();
       AbstractValue oldType = info.type;
       AbstractValue newType = info.refine(this);
       // Check that refinement has not accidentally changed the type.
       assert(oldType == info.type);
       if (info.abandonInferencing) info.doNotEnqueue = true;
       if ((info.type = newType) != oldType) {
-        overallRefineCount++;
+        _overallRefineCount++;
         info.refineCount++;
-        if (info.refineCount > MAX_CHANGE_COUNT) {
+        if (info.refineCount > _MAX_CHANGE_COUNT) {
           if (debug.ANOMALY_WARN) {
             print("ANOMALY WARNING: max refinement reached for $info");
           }
@@ -921,7 +710,7 @@
           info.type = info.refine(this);
           info.doNotEnqueue = true;
         }
-        workQueue.addAll(info.users);
+        _workQueue.addAll(info.users);
         if (info.hasStableType(this)) {
           info.stabilize(this);
         }
@@ -929,27 +718,29 @@
     }
   }
 
-  void buildWorkQueue() {
-    workQueue.addAll(types.orderedTypeInformations);
-    workQueue.addAll(types.allocatedTypes);
-    workQueue.addAll(types.allocatedClosures);
-    workQueue.addAll(types.allocatedCalls);
+  void _buildWorkQueue() {
+    _workQueue.addAll(types.orderedTypeInformations);
+    _workQueue.addAll(types.allocatedTypes);
+    _workQueue.addAll(types.allocatedClosures);
+    _workQueue.addAll(types.allocatedCalls);
   }
 
-  @override
-  void updateParameterAssignments(TypeInformation caller, MemberEntity callee,
-      ArgumentsTypes arguments, Selector selector, AbstractValue mask,
+  /// Update the inputs to parameters in the graph. [remove] tells whether
+  /// inputs must be added or removed. If [init] is false, parameters are
+  /// added to the work queue.
+  void updateParameterInputs(TypeInformation caller, MemberEntity callee,
+      ArgumentsTypes arguments, Selector selector,
       {bool remove, bool addToQueue: true}) {
     if (callee.name == Identifiers.noSuchMethod_) return;
     if (callee.isField) {
       if (selector.isSetter) {
         ElementTypeInformation info = types.getInferredTypeOfMember(callee);
         if (remove) {
-          info.removeAssignment(arguments.positional[0]);
+          info.removeInput(arguments.positional[0]);
         } else {
-          info.addAssignment(arguments.positional[0]);
+          info.addInput(arguments.positional[0]);
         }
-        if (addToQueue) workQueue.add(info);
+        if (addToQueue) _workQueue.add(info);
       }
     } else if (callee.isGetter) {
       return;
@@ -972,7 +763,7 @@
           ParameterTypeInformation info =
               types.getInferredTypeOfParameter(parameter);
           info.tagAsTearOffClosureParameter(this);
-          if (addToQueue) workQueue.add(info);
+          if (addToQueue) _workQueue.add(info);
         });
       }
     } else {
@@ -991,34 +782,37 @@
         if (type == null) type = getDefaultTypeOfParameter(parameter);
         TypeInformation info = types.getInferredTypeOfParameter(parameter);
         if (remove) {
-          info.removeAssignment(type);
+          info.removeInput(type);
         } else {
-          info.addAssignment(type);
+          info.addInput(type);
         }
         parameterIndex++;
-        if (addToQueue) workQueue.add(info);
+        if (addToQueue) _workQueue.add(info);
       });
     }
   }
 
-  @override
+  /// Sets the type of a parameter's default value to [type]. If the global
+  /// mapping in `_defaultTypeOfParameter` already contains a type, it must be
+  /// a [PlaceholderTypeInformation], which will be replaced. All its uses are
+  /// updated.
   void setDefaultTypeOfParameter(Local parameter, TypeInformation type,
       {bool isInstanceMember}) {
     assert(
         type != null, failedAt(parameter, "No default type for $parameter."));
-    TypeInformation existing = defaultTypeOfParameter[parameter];
-    defaultTypeOfParameter[parameter] = type;
+    TypeInformation existing = _defaultTypeOfParameter[parameter];
+    _defaultTypeOfParameter[parameter] = type;
     TypeInformation info = types.getInferredTypeOfParameter(parameter);
     if (existing != null && existing is PlaceholderTypeInformation) {
       // Replace references to [existing] to use [type] instead.
       if (isInstanceMember) {
-        ParameterAssignments assignments = info.assignments;
-        assignments.replace(existing, type);
+        ParameterInputs inputs = info.inputs;
+        inputs.replace(existing, type);
       } else {
-        List<TypeInformation> assignments = info.assignments;
-        for (int i = 0; i < assignments.length; i++) {
-          if (assignments[i] == existing) {
-            assignments[i] = type;
+        List<TypeInformation> inputs = info.inputs;
+        for (int i = 0; i < inputs.length; i++) {
+          if (inputs[i] == existing) {
+            inputs[i] = type;
           }
         }
       }
@@ -1029,56 +823,59 @@
     }
   }
 
-  @override
+  /// Returns the [TypeInformation] node for the default value of a parameter.
+  /// If this is queried before it is set by [setDefaultTypeOfParameter], a
+  /// [PlaceholderTypeInformation] is returned, which will later be replaced
+  /// by the actual node when [setDefaultTypeOfParameter] is called.
+  ///
+  /// Invariant: After graph construction, no [PlaceholderTypeInformation] nodes
+  /// should be present and a default type for each parameter should exist.
   TypeInformation getDefaultTypeOfParameter(Local parameter) {
-    return defaultTypeOfParameter.putIfAbsent(parameter, () {
+    return _defaultTypeOfParameter.putIfAbsent(parameter, () {
       return new PlaceholderTypeInformation(
           abstractValueDomain, types.currentMember);
     });
   }
 
-  @override
-  bool hasAlreadyComputedTypeOfParameterDefault(Local parameter) {
-    TypeInformation seen = defaultTypeOfParameter[parameter];
-    return (seen != null && seen is! PlaceholderTypeInformation);
-  }
-
-  @override
+  /// Returns the type of [element].
   TypeInformation typeOfParameter(Local element) {
     return types.getInferredTypeOfParameter(element);
   }
 
-  @override
+  /// Returns the type of [element].
   TypeInformation typeOfMember(MemberEntity element) {
     if (element is FunctionEntity) return types.functionType;
     return types.getInferredTypeOfMember(element);
   }
 
-  @override
+  /// Returns the return type of [element].
   TypeInformation returnTypeOfMember(MemberEntity element) {
     if (element is! FunctionEntity) return types.dynamicType;
     return types.getInferredTypeOfMember(element);
   }
 
-  @override
+  /// Records that [element] is of type [type].
   void recordTypeOfField(FieldEntity element, TypeInformation type) {
-    types.getInferredTypeOfMember(element).addAssignment(type);
+    types.getInferredTypeOfMember(element).addInput(type);
   }
 
-  @override
+  /// Records that the return type [element] is of type [type].
   void recordReturnType(FunctionEntity element, TypeInformation type) {
     TypeInformation info = types.getInferredTypeOfMember(element);
     if (element.name == '==') {
       // Even if x.== doesn't return a bool, 'x == null' evaluates to 'false'.
-      info.addAssignment(types.boolType);
+      info.addInput(types.boolType);
     }
     // TODO(ngeoffray): Clean up. We do these checks because
     // [SimpleTypesInferrer] deals with two different inferrers.
     if (type == null) return;
-    if (info.assignments.isEmpty) info.addAssignment(type);
+    if (info.inputs.isEmpty) info.addInput(type);
   }
 
-  @override
+  /// Notifies to the inferrer that [analyzedElement] can have return type
+  /// [newType]. [currentType] is the type the inference has currently found.
+  ///
+  /// Returns the new type for [analyzedElement].
   TypeInformation addReturnTypeForMethod(
       FunctionEntity element, TypeInformation unused, TypeInformation newType) {
     TypeInformation type = types.getInferredTypeOfMember(element);
@@ -1087,15 +884,21 @@
     if (element is ConstructorEntity && element.isGenerativeConstructor) {
       return type;
     }
-    type.addAssignment(newType);
+    type.addInput(newType);
     return type;
   }
 
-  @override
+  /// Registers that [caller] calls [callee] at location [node], with
+  /// [selector], and [arguments]. Note that [selector] is null for forwarding
+  /// constructors.
+  ///
+  /// [sideEffectsBuilder] will be updated to incorporate [callee]'s side
+  /// effects.
+  ///
+  /// [inLoop] tells whether the call happens in a loop.
   TypeInformation registerCalledMember(
       Object node,
       Selector selector,
-      AbstractValue mask,
       MemberEntity caller,
       MemberEntity callee,
       ArgumentsTypes arguments,
@@ -1108,7 +911,6 @@
         caller,
         callee,
         selector,
-        mask,
         arguments,
         inLoop);
     // If this class has a 'call' method then we have essentially created a
@@ -1121,17 +923,23 @@
         callee is ConstructorEntity &&
         callee.isGenerativeConstructor) {
       ClassEntity cls = callee.enclosingClass;
-      if (hasCallType(cls)) {
+      if (_hasCallType(cls)) {
         types.allocatedClosures.add(info);
       }
     }
     info.addToGraph(this);
     types.allocatedCalls.add(info);
-    updateSideEffects(sideEffectsBuilder, selector, callee);
+    _updateSideEffects(sideEffectsBuilder, selector, callee);
     return info;
   }
 
-  @override
+  /// Registers that [caller] calls [selector] with [receiverType] as receiver,
+  /// and [arguments].
+  ///
+  /// [sideEffectsBuilder] will be updated to incorporate the potential callees'
+  /// side effects.
+  ///
+  /// [inLoop] tells whether the call happens in a loop.
   TypeInformation registerCalledSelector(
       CallType callType,
       ir.Node node,
@@ -1144,80 +952,65 @@
       {bool inLoop,
       bool isConditional}) {
     if (selector.isClosureCall) {
-      return registerCalledClosure(node, selector, mask, receiverType, caller,
-          arguments, sideEffectsBuilder,
+      return registerCalledClosure(
+          node, selector, receiverType, caller, arguments, sideEffectsBuilder,
           inLoop: inLoop);
     }
 
     if (closedWorld.includesClosureCall(selector, mask)) {
       sideEffectsBuilder.setAllSideEffectsAndDependsOnSomething();
     }
+
     closedWorld.locateMembers(selector, mask).forEach((callee) {
-      updateSideEffects(sideEffectsBuilder, selector, callee);
+      _updateSideEffects(sideEffectsBuilder, selector, callee);
     });
 
-    CallSiteTypeInformation info;
-
-    // We force using indirection for `==` because it is a very common dynamic
-    // call site on many apps.
-    // TODO(sigmund): it would be even better if we could automatically detect
-    // when dynamic calls are growing too big and add the indirection at that
-    // point.
-    if (selector.name == '==') {
-      info = new IndirectDynamicCallSiteTypeInformation(
-          abstractValueDomain,
-          types.currentMember,
-          node,
-          typeOfSharedDynamicCall(selector, CallStructure.ONE_ARG),
-          caller,
-          selector,
-          mask,
-          receiverType,
-          arguments,
-          inLoop,
-          isConditional);
-    } else {
-      info = new DynamicCallSiteTypeInformation(
-          abstractValueDomain,
-          types.currentMember,
-          callType,
-          node,
-          caller,
-          selector,
-          mask,
-          receiverType,
-          arguments,
-          inLoop,
-          isConditional);
-    }
+    CallSiteTypeInformation info = new DynamicCallSiteTypeInformation(
+        abstractValueDomain,
+        types.currentMember,
+        callType,
+        node,
+        caller,
+        selector,
+        mask,
+        receiverType,
+        arguments,
+        inLoop,
+        isConditional);
     info.addToGraph(this);
     types.allocatedCalls.add(info);
     return info;
   }
 
-  @override
+  /// Registers a call to await with an expression of type [argumentType] as
+  /// argument.
   TypeInformation registerAwait(ir.Node node, TypeInformation argument) {
     AwaitTypeInformation info = new AwaitTypeInformation(
         abstractValueDomain, types.currentMember, node);
-    info.addAssignment(argument);
+    info.addInput(argument);
     types.allocatedTypes.add(info);
     return info;
   }
 
-  @override
+  /// Registers a call to yield with an expression of type [argumentType] as
+  /// argument.
   TypeInformation registerYield(ir.Node node, TypeInformation argument) {
     YieldTypeInformation info = new YieldTypeInformation(
         abstractValueDomain, types.currentMember, node);
-    info.addAssignment(argument);
+    info.addInput(argument);
     types.allocatedTypes.add(info);
     return info;
   }
 
-  @override
+  /// Registers that [caller] calls [closure] with [arguments].
+  ///
+  /// [sideEffectsBuilder] will be updated to incorporate the potential callees'
+  /// side effects.
+  ///
+  /// [inLoop] tells whether the call happens in a loop.
   TypeInformation registerCalledClosure(
       ir.Node node,
       Selector selector,
-      AbstractValue mask,
       TypeInformation closure,
       MemberEntity caller,
       ArgumentsTypes arguments,
@@ -1230,7 +1023,6 @@
         node,
         caller,
         selector,
-        mask,
         closure,
         arguments,
         inLoop);
@@ -1239,7 +1031,6 @@
     return info;
   }
 
-  @override
   void close() {
     for (MemberTypeInformation typeInformation
         in types.memberTypeInformations.values) {
@@ -1247,7 +1038,6 @@
     }
   }
 
-  @override
   void clear() {
     if (retainDataForTesting) return;
 
@@ -1258,7 +1048,7 @@
     types.allocatedCalls.forEach(cleanup);
     types.allocatedCalls.clear();
 
-    defaultTypeOfParameter.clear();
+    _defaultTypeOfParameter.clear();
 
     types.parameterTypeInformations.values.forEach(cleanup);
     types.memberTypeInformations.values.forEach(cleanup);
@@ -1271,8 +1061,8 @@
     types.allocatedClosures.forEach(cleanup);
     types.allocatedClosures.clear();
 
-    analyzedElements.clear();
-    generativeConstructorsExposingThis.clear();
+    _analyzedElements.clear();
+    _generativeConstructorsExposingThis.clear();
 
     types.allocatedMaps.values.forEach(cleanup);
     types.allocatedSets.values.forEach(cleanup);
@@ -1281,13 +1071,12 @@
     _memberData.clear();
   }
 
-  @override
   Iterable<MemberEntity> getCallersOfForTesting(MemberEntity element) {
     MemberTypeInformation info = types.getInferredTypeOfMember(element);
     return info.callersForTesting;
   }
 
-  @override
+  /// Returns the type of [element] when being called with [selector].
   TypeInformation typeOfMemberWithSelector(
       MemberEntity element, Selector selector) {
     if (element.name == Identifiers.noSuchMethod_ &&
@@ -1321,8 +1110,17 @@
   /// cache holds that shared dynamic call node for a given selector.
   Map<Selector, DynamicCallSiteTypeInformation> _sharedCalls = {};
 
-  @override
-  DynamicCallSiteTypeInformation typeOfSharedDynamicCall(
+  /// For a given selector, return a shared dynamic call site that will be used
+  /// to combine the results of multiple dynamic calls in the program via
+  /// [IndirectDynamicCallSiteTypeInformation].
+  ///
+  /// This is used only for scalability reasons: if there are too many targets
+  /// and call sites, we may have a quadratic number of edges in the graph, so
+  /// we add a level of indirection to merge the information and keep the graph
+  /// smaller.
+  // TODO(sigmund): start using or delete indirection logic.
+  // ignore: unused_element
+  DynamicCallSiteTypeInformation _typeOfSharedDynamicCall(
       Selector selector, CallStructure structure) {
     DynamicCallSiteTypeInformation info = _sharedCalls[selector];
     if (info != null) return info;
@@ -1359,7 +1157,11 @@
     return info;
   }
 
-  @override
+  /// Returns true if global optimizations such as type inferencing can apply to
+  /// the field [element].
+  ///
+  /// One category of elements that do not apply is runtime helpers that the
+  /// backend calls, but the optimizations don't see those calls.
   bool canFieldBeUsedForGlobalOptimizations(FieldEntity element) {
     if (closedWorld.backendUsage.isFieldUsedByBackend(element)) {
       return false;
@@ -1370,13 +1172,17 @@
     return true;
   }
 
-  @override
+  /// Returns true if global optimizations such as type inferencing can apply to
+  /// the parameter [element].
+  ///
+  /// One category of elements that do not apply is runtime helpers that the
+  /// backend calls, but the optimizations don't see those calls.
   bool canFunctionParametersBeUsedForGlobalOptimizations(
       FunctionEntity function) {
     return !closedWorld.backendUsage.isFunctionUsedByBackend(function);
   }
 
-  @override
+  /// Returns `true` if inference of parameter types is disabled for [member].
   bool assumeDynamic(MemberEntity member) {
     return closedWorld.annotationsData.hasAssumeDynamic(member);
   }
@@ -1451,7 +1257,7 @@
           parameter,
           type,
           member,
-          new ParameterAssignments());
+          new ParameterInputs());
     } else {
       return new ParameterTypeInformation.static(
           abstractValueDomain, memberTypeInformation, parameter, type, member);
@@ -1496,8 +1302,7 @@
   /// objects in a debugging data stream.
   static const String tag = 'global-type-inference-element-data';
 
-  // TODO(johnniwinther): Rename this together with [typeOfSend].
-  Map<ir.TreeNode, AbstractValue> _sendMap;
+  Map<ir.TreeNode, AbstractValue> _receiverMap;
 
   Map<ir.ForInStatement, AbstractValue> _iteratorMap;
   Map<ir.ForInStatement, AbstractValue> _currentMap;
@@ -1505,8 +1310,8 @@
 
   KernelGlobalTypeInferenceElementData();
 
-  KernelGlobalTypeInferenceElementData.internal(
-      this._sendMap, this._iteratorMap, this._currentMap, this._moveNextMap);
+  KernelGlobalTypeInferenceElementData.internal(this._receiverMap,
+      this._iteratorMap, this._currentMap, this._moveNextMap);
 
   /// Deserializes a [GlobalTypeInferenceElementData] object from [source].
   factory KernelGlobalTypeInferenceElementData.readFromDataSource(
@@ -1542,7 +1347,7 @@
     sink.inMemberContext(context, () {
       sink.begin(tag);
       sink.writeTreeNodeMapInContext(
-          _sendMap,
+          _receiverMap,
           (AbstractValue value) =>
               abstractValueDomain.writeAbstractValueToDataSink(sink, value),
           allowNull: true);
@@ -1567,10 +1372,10 @@
 
   @override
   GlobalTypeInferenceElementData compress() {
-    if (_sendMap != null) {
-      _sendMap.removeWhere(_mapsToNull);
-      if (_sendMap.isEmpty) {
-        _sendMap = null;
+    if (_receiverMap != null) {
+      _receiverMap.removeWhere(_mapsToNull);
+      if (_receiverMap.isEmpty) {
+        _receiverMap = null;
       }
     }
     if (_iteratorMap != null) {
@@ -1591,7 +1396,7 @@
         _moveNextMap = null;
       }
     }
-    if (_sendMap == null &&
+    if (_receiverMap == null &&
         _iteratorMap == null &&
         _currentMap == null &&
         _moveNextMap == null) {
@@ -1601,9 +1406,9 @@
   }
 
   @override
-  AbstractValue typeOfSend(ir.TreeNode node) {
-    if (_sendMap == null) return null;
-    return _sendMap[node];
+  AbstractValue typeOfReceiver(ir.TreeNode node) {
+    if (_receiverMap == null) return null;
+    return _receiverMap[node];
   }
 
   void setCurrentTypeMask(ir.ForInStatement node, AbstractValue mask) {
@@ -1639,15 +1444,9 @@
     return _iteratorMap[node];
   }
 
-  void setTypeMask(ir.TreeNode node, AbstractValue mask) {
-    _sendMap ??= <ir.TreeNode, AbstractValue>{};
-    _sendMap[node] = mask;
-  }
-
-  @override
-  AbstractValue typeOfGetter(ir.TreeNode node) {
-    if (_sendMap == null) return null;
-    return _sendMap[node];
+  void setReceiverTypeMask(ir.TreeNode node, AbstractValue mask) {
+    _receiverMap ??= <ir.TreeNode, AbstractValue>{};
+    _receiverMap[node] = mask;
   }
 }
 
diff --git a/pkg/compiler/lib/src/inferrer/list_tracer.dart b/pkg/compiler/lib/src/inferrer/list_tracer.dart
index aaa69e4..db86752 100644
--- a/pkg/compiler/lib/src/inferrer/list_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/list_tracer.dart
@@ -130,13 +130,13 @@
 
 class ListTracerVisitor extends TracerVisitor {
   // The [Set] of found assignments to the list.
-  Set<TypeInformation> assignments = new Setlet<TypeInformation>();
+  Set<TypeInformation> inputs = new Setlet<TypeInformation>();
   bool callsGrowableMethod = false;
 
   ListTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer);
 
   /// Returns [true] if the analysis completed successfully, [false] if it
-  /// bailed out. In the former case, [assignments] holds a list of
+  /// bailed out. In the former case, [inputs] holds a list of
   /// [TypeInformation] nodes that flow into the element type of this list.
   bool run() {
     analyze();
@@ -149,7 +149,7 @@
       return true;
     } else {
       callsGrowableMethod = true;
-      assignments = null;
+      inputs = null;
       return false;
     }
   }
@@ -180,18 +180,18 @@
           int positionalLength = info.arguments.positional.length;
           if (selectorName == 'add') {
             if (positionalLength == 1) {
-              assignments.add(info.arguments.positional[0]);
+              inputs.add(info.arguments.positional[0]);
             }
           } else if (selectorName == 'insert') {
             if (positionalLength == 2) {
-              assignments.add(info.arguments.positional[1]);
+              inputs.add(info.arguments.positional[1]);
             }
           } else {
             bailout('Used in a not-ok selector');
             return;
           }
         } else if (selector.isIndexSet) {
-          assignments.add(info.arguments.positional[1]);
+          inputs.add(info.arguments.positional[1]);
         } else if (!selector.isIndex) {
           bailout('Used in a not-ok selector');
           return;
@@ -202,7 +202,7 @@
       }
       if (selectorName == 'length' && selector.isSetter) {
         callsGrowableMethod = true;
-        assignments.add(inferrer.types.nullType);
+        inputs.add(inferrer.types.nullType);
       }
     } else if (selector.isCall &&
         (info.hasClosureCallTargets ||
diff --git a/pkg/compiler/lib/src/inferrer/map_tracer.dart b/pkg/compiler/lib/src/inferrer/map_tracer.dart
index f21fd15..3f29c0c 100644
--- a/pkg/compiler/lib/src/inferrer/map_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/map_tracer.dart
@@ -35,17 +35,17 @@
   // These lists are used to keep track of newly discovered assignments to
   // the map. Note that elements at corresponding indices are expected to
   // belong to the same assignment operation.
-  List<TypeInformation> keyAssignments = <TypeInformation>[];
-  List<TypeInformation> valueAssignments = <TypeInformation>[];
+  List<TypeInformation> keyInputs = <TypeInformation>[];
+  List<TypeInformation> valueInputs = <TypeInformation>[];
   // This list is used to keep track of assignments of entire maps to
   // this map.
-  List<MapTypeInformation> mapAssignments = <MapTypeInformation>[];
+  List<MapTypeInformation> mapInputs = <MapTypeInformation>[];
 
   MapTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer);
 
   /// Returns [true] if the analysis completed successfully, [false]
-  /// if it bailed out. In the former case, [keyAssignments] and
-  /// [valueAssignments] hold a list of [TypeInformation] nodes that
+  /// if it bailed out. In the former case, [keyInputs] and
+  /// [valueInputs] hold a list of [TypeInformation] nodes that
   /// flow into the key and value types of this map.
   bool run() {
     analyze();
@@ -54,7 +54,7 @@
       map.addFlowsIntoTargets(flowsInto);
       return true;
     }
-    keyAssignments = valueAssignments = mapAssignments = null;
+    keyInputs = valueInputs = mapInputs = null;
     return false;
   }
 
@@ -87,7 +87,7 @@
             TypeInformation map = info.arguments.positional[0];
             if (map is MapTypeInformation) {
               inferrer.analyzeMapAndEnqueue(map);
-              mapAssignments.add(map);
+              mapInputs.add(map);
             } else {
               // If we could select a component from a [TypeInformation],
               // like the keytype or valuetype in this case, we could
@@ -103,8 +103,8 @@
             // to go to dynamic.
             // TODO(herhut,16507): Use return type of closure in
             // Map.putIfAbsent.
-            keyAssignments.add(info.arguments.positional[0]);
-            valueAssignments.add(inferrer.types.dynamicType);
+            keyInputs.add(info.arguments.positional[0]);
+            valueInputs.add(inferrer.types.dynamicType);
           } else {
             // It would be nice to handle [Map.keys] and [Map.values], too.
             // However, currently those calls do not trigger the creation
@@ -115,8 +115,8 @@
             return;
           }
         } else if (selector.isIndexSet) {
-          keyAssignments.add(info.arguments.positional[0]);
-          valueAssignments.add(info.arguments.positional[1]);
+          keyInputs.add(info.arguments.positional[0]);
+          valueInputs.add(info.arguments.positional[1]);
         } else if (!selector.isIndex) {
           bailout('Map used in a not-ok selector [$selectorName]');
           return;
diff --git a/pkg/compiler/lib/src/inferrer/set_tracer.dart b/pkg/compiler/lib/src/inferrer/set_tracer.dart
index 72cf043..8a98719 100644
--- a/pkg/compiler/lib/src/inferrer/set_tracer.dart
+++ b/pkg/compiler/lib/src/inferrer/set_tracer.dart
@@ -65,12 +65,12 @@
 ]);
 
 class SetTracerVisitor extends TracerVisitor {
-  List<TypeInformation> assignments = <TypeInformation>[];
+  List<TypeInformation> inputs = <TypeInformation>[];
 
   SetTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer);
 
   /// Returns [true] if the analysis completed successfully, [false] if it
-  /// bailed out. In the former case, [assignments] holds a list of
+  /// bailed out. In the former case, [inputs] holds a list of
   /// [TypeInformation] nodes that flow into the element type of this set.
   bool run() {
     analyze();
@@ -79,7 +79,7 @@
       set.addFlowsIntoTargets(flowsInto);
       return true;
     }
-    assignments = null;
+    inputs = null;
     return false;
   }
 
@@ -108,7 +108,7 @@
         if (selector.isCall) {
           switch (selectorName) {
             case 'add':
-              assignments.add(info.arguments.positional[0]);
+              inputs.add(info.arguments.positional[0]);
               break;
             case 'addAll':
               // TODO(fishythefish): Extract type argument from type
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_dump.dart b/pkg/compiler/lib/src/inferrer/type_graph_dump.dart
index a4ed3e3..c215ca1 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_dump.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_dump.dart
@@ -44,7 +44,7 @@
   /// during the analysis.
   void beforeAnalysis() {
     for (TypeInformation node in inferrer.types.allTypes) {
-      Set<TypeInformation> copy = node.assignments.toSet();
+      Set<TypeInformation> copy = node.inputs.toSet();
       if (!copy.isEmpty) {
         assignmentsBeforeAnalysis[node] = copy;
       }
@@ -54,7 +54,7 @@
   /// Like [beforeAnalysis], takes a copy of the assignment sets.
   void beforeTracing() {
     for (TypeInformation node in inferrer.types.allTypes) {
-      Set<TypeInformation> copy = node.assignments.toSet();
+      Set<TypeInformation> copy = node.inputs.toSet();
       if (!copy.isEmpty) {
         assignmentsBeforeTracing[node] = copy;
       }
@@ -266,7 +266,7 @@
   /// Creates a node for [node] displaying the given [text] in its box.
   ///
   /// [inputs] specify named inputs to the node. If omitted, edges will be
-  /// based on [node.assignments].
+  /// based on [node.inputs].
   void addNode(TypeInformation node, String text,
       {String color: defaultNodeColor, Map<String, TypeInformation> inputs}) {
     seen.add(node);
@@ -290,7 +290,7 @@
       // added, removed, temporary, or unchanged.
       dynamic originalSet = global.assignmentsBeforeAnalysis[node] ?? const [];
       var tracerSet = global.assignmentsBeforeTracing[node] ?? const [];
-      var currentSet = node.assignments.toSet();
+      var currentSet = node.inputs.toSet();
       for (TypeInformation assignment in currentSet) {
         String color =
             originalSet.contains(assignment) ? unchangedEdge : addedEdge;
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
index a1e7c15..2de18dc 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
@@ -70,13 +70,12 @@
   }
 
   InferrerEngine createInferrerEngineFor(FunctionEntity main) {
-    return new InferrerEngineImpl(
+    return InferrerEngine(
         _compiler.options,
         _compiler.progress,
         _compiler.reporter,
         _compiler.outputProvider,
         closedWorld,
-        _compiler.frontendStrategy.noSuchMethodRegistry,
         main,
         _inferredDataBuilder);
   }
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
index af0410d..622b6fb 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
@@ -31,13 +31,13 @@
 /// - Containers (for lists)
 /// - Type of the element in a container
 ///
-/// A node has a set of assignments and users. Assignments are used to
+/// A node has a set of inputs and users. Inputs are used to
 /// compute the type of the node ([TypeInformation.computeType]). Users are
 /// added to the inferrer's work queue when the type of the node
 /// changes.
 abstract class TypeInformation {
   Set<TypeInformation> users;
-  var /* List|ParameterAssignments */ _assignments;
+  var /* List|ParameterInputs */ _inputs;
 
   /// The type the inferrer has found for this [TypeInformation].
   /// Initially empty.
@@ -49,15 +49,14 @@
   /// The element this [TypeInformation] node belongs to.
   MemberEntity get contextMember => context == null ? null : context.member;
 
-  Iterable<TypeInformation> get assignments => _assignments;
+  Iterable<TypeInformation> get inputs => _inputs;
 
   /// We abandon inference in certain cases (complex cyclic flow, native
   /// behaviours, etc.). In some case, we might resume inference in the
-  /// closure tracer, which is handled by checking whether [assignments] has
-  /// been set to [STOP_TRACKING_ASSIGNMENTS_MARKER].
+  /// closure tracer, which is handled by checking whether [inputs] has
+  /// been set to [STOP_TRACKING_INPUTS_MARKER].
   bool abandonInferencing = false;
-  bool get mightResume =>
-      !identical(assignments, STOP_TRACKING_ASSIGNMENTS_MARKER);
+  bool get mightResume => !identical(inputs, STOP_TRACKING_INPUTS_MARKER);
 
   /// Number of times this [TypeInformation] has changed type.
   int refineCount = 0;
@@ -85,19 +84,19 @@
   bool get isConcrete => false;
 
   TypeInformation(this.type, this.context)
-      : _assignments = <TypeInformation>[],
+      : _inputs = <TypeInformation>[],
         users = new Setlet<TypeInformation>();
 
-  TypeInformation.noAssignments(this.type, this.context)
-      : _assignments = const <TypeInformation>[],
+  TypeInformation.noInputs(this.type, this.context)
+      : _inputs = const <TypeInformation>[],
         users = new Setlet<TypeInformation>();
 
   TypeInformation.untracked(this.type)
-      : _assignments = const <TypeInformation>[],
+      : _inputs = const <TypeInformation>[],
         users = const ImmutableEmptySet(),
         context = null;
 
-  TypeInformation.withAssignments(this.type, this.context, this._assignments)
+  TypeInformation.withInputs(this.type, this.context, this._inputs)
       : users = new Setlet<TypeInformation>();
 
   void addUser(TypeInformation user) {
@@ -116,31 +115,31 @@
 
   // The below is not a compile time constant to make it differentiable
   // from other empty lists of [TypeInformation].
-  static final STOP_TRACKING_ASSIGNMENTS_MARKER = new List<TypeInformation>(0);
+  static final STOP_TRACKING_INPUTS_MARKER = new List<TypeInformation>(0);
 
-  bool areAssignmentsTracked() {
-    return assignments != STOP_TRACKING_ASSIGNMENTS_MARKER;
+  bool areInputsTracked() {
+    return inputs != STOP_TRACKING_INPUTS_MARKER;
   }
 
-  void addAssignment(TypeInformation assignment) {
+  void addInput(TypeInformation input) {
     // Cheap one-level cycle detection.
-    if (assignment == this) return;
-    if (areAssignmentsTracked()) {
-      _assignments.add(assignment);
+    if (input == this) return;
+    if (areInputsTracked()) {
+      _inputs.add(input);
     }
     // Even if we abandon inferencing on this [TypeInformation] we
     // need to collect the users, so that phases that track where
     // elements flow in still work.
-    assignment.addUser(this);
+    input.addUser(this);
   }
 
-  void removeAssignment(TypeInformation assignment) {
+  void removeInput(TypeInformation input) {
     if (!abandonInferencing || mightResume) {
-      _assignments.remove(assignment);
+      _inputs.remove(input);
     }
-    // We can have multiple assignments of the same [TypeInformation].
-    if (!assignments.contains(assignment)) {
-      assignment.removeUser(this);
+    // We can have multiple inputs of the same [TypeInformation].
+    if (!inputs.contains(input)) {
+      input.removeUser(this);
     }
   }
 
@@ -158,18 +157,18 @@
     return inferrer.types.dynamicType.type;
   }
 
-  void giveUp(InferrerEngine inferrer, {bool clearAssignments: true}) {
+  void giveUp(InferrerEngine inferrer, {bool clearInputs: true}) {
     abandonInferencing = true;
-    // Do not remove [this] as a user of nodes in [assignments],
+    // Do not remove [this] as a user of nodes in [inputs],
     // because our tracing analysis could be interested in tracing
     // this node.
-    if (clearAssignments) _assignments = STOP_TRACKING_ASSIGNMENTS_MARKER;
+    if (clearInputs) _inputs = STOP_TRACKING_INPUTS_MARKER;
     // Do not remove users because our tracing analysis could be
     // interested in tracing the users of this node.
   }
 
   void clear() {
-    _assignments = STOP_TRACKING_ASSIGNMENTS_MARKER;
+    _inputs = STOP_TRACKING_INPUTS_MARKER;
     users = const ImmutableEmptySet();
   }
 
@@ -192,11 +191,11 @@
   /// Returns whether the type cannot change after it has been
   /// inferred.
   bool hasStableType(InferrerEngine inferrer) {
-    return !mightResume && assignments.every((e) => e.isStable);
+    return !mightResume && inputs.every((e) => e.isStable);
   }
 
   void removeAndClearReferences(InferrerEngine inferrer) {
-    assignments.forEach((info) {
+    inputs.forEach((info) {
       info.removeUser(this);
     });
   }
@@ -205,7 +204,7 @@
     removeAndClearReferences(inferrer);
     // Do not remove users because the tracing analysis could be interested
     // in tracing the users of this node.
-    _assignments = STOP_TRACKING_ASSIGNMENTS_MARKER;
+    _inputs = STOP_TRACKING_INPUTS_MARKER;
     abandonInferencing = true;
     isStable = true;
   }
@@ -219,7 +218,7 @@
   /// Destroys information not needed after type inference.
   void cleanup() {
     users = null;
-    _assignments = null;
+    _inputs = null;
   }
 
   String toStructuredText(String indent) {
@@ -264,52 +263,52 @@
 }
 
 /// Parameters of instance functions behave differently than other
-/// elements because the inferrer may remove assignments. This happens
+/// elements because the inferrer may remove inputs. This happens
 /// when the receiver of a dynamic call site can be refined
 /// to a type where we know more about which instance method is being
 /// called.
-class ParameterAssignments extends IterableBase<TypeInformation> {
-  final Map<TypeInformation, int> assignments = new Map<TypeInformation, int>();
+class ParameterInputs extends IterableBase<TypeInformation> {
+  final Map<TypeInformation, int> _inputs = new Map<TypeInformation, int>();
 
   void remove(TypeInformation info) {
-    int existing = assignments[info];
+    int existing = _inputs[info];
     if (existing == null) return;
     if (existing == 1) {
-      assignments.remove(info);
+      _inputs.remove(info);
     } else {
-      assignments[info] = existing - 1;
+      _inputs[info] = existing - 1;
     }
   }
 
   void add(TypeInformation info) {
-    int existing = assignments[info];
+    int existing = _inputs[info];
     if (existing == null) {
-      assignments[info] = 1;
+      _inputs[info] = 1;
     } else {
-      assignments[info] = existing + 1;
+      _inputs[info] = existing + 1;
     }
   }
 
   void replace(TypeInformation old, TypeInformation replacement) {
-    int existing = assignments[old];
+    int existing = _inputs[old];
     if (existing != null) {
-      int other = assignments[replacement];
+      int other = _inputs[replacement];
       if (other != null) existing += other;
-      assignments[replacement] = existing;
-      assignments.remove(old);
+      _inputs[replacement] = existing;
+      _inputs.remove(old);
     }
   }
 
   @override
-  Iterator<TypeInformation> get iterator => assignments.keys.iterator;
+  Iterator<TypeInformation> get iterator => _inputs.keys.iterator;
   @override
-  Iterable<TypeInformation> where(Function f) => assignments.keys.where(f);
+  Iterable<TypeInformation> where(Function f) => _inputs.keys.where(f);
 
   @override
-  bool contains(Object info) => assignments.containsKey(info);
+  bool contains(Object info) => _inputs.containsKey(info);
 
   @override
-  String toString() => assignments.keys.toList().toString();
+  String toString() => _inputs.keys.toList().toString();
 }
 
 /// A node representing a resolved element of the component. The kind of
@@ -344,12 +343,9 @@
   ElementTypeInformation._internal(
       AbstractValueDomain abstractValueDomain, MemberTypeInformation context)
       : super(abstractValueDomain.emptyType, context);
-  ElementTypeInformation._withAssignments(
-      AbstractValueDomain abstractValueDomain,
-      MemberTypeInformation context,
-      ParameterAssignments assignments)
-      : super.withAssignments(
-            abstractValueDomain.emptyType, context, assignments);
+  ElementTypeInformation._withInputs(AbstractValueDomain abstractValueDomain,
+      MemberTypeInformation context, ParameterInputs inputs)
+      : super.withInputs(abstractValueDomain.emptyType, context, inputs);
 
   String getInferredSignature(TypeSystem types);
 
@@ -485,7 +481,7 @@
     AbstractValue special = handleSpecialCases(inferrer);
     if (special != null) return potentiallyNarrowType(special, inferrer);
     return potentiallyNarrowType(
-        inferrer.types.computeTypeMask(assignments), inferrer);
+        inferrer.types.computeTypeMask(inputs), inferrer);
   }
 
   @override
@@ -552,7 +548,7 @@
 
   @override
   bool hasStableType(InferrerEngine inferrer) {
-    // The number of assignments of non-final fields is
+    // The number of inputs of non-final fields is
     // not stable. Therefore such a field cannot be stable.
     if (!_field.isAssignable) {
       return false;
@@ -728,11 +724,11 @@
       this._parameter,
       this._type,
       this._method,
-      ParameterAssignments assignments)
+      ParameterInputs inputs)
       : _isInstanceMemberParameter = true,
         _isClosureParameter = false,
         _isInitializingFormal = false,
-        super._withAssignments(abstractValueDomain, context, assignments);
+        super._withInputs(abstractValueDomain, context, inputs);
 
   FunctionEntity get method => _method;
 
@@ -770,9 +766,9 @@
     if ((_isTearOffClosureParameter || _isClosureParameter) &&
         disableInferenceForClosures) {
       // Do not infer types for parameters of closures. We do not
-      // clear the assignments in case the closure is successfully
+      // clear the inputs in case the closure is successfully
       // traced.
-      giveUp(inferrer, clearAssignments: false);
+      giveUp(inferrer, clearInputs: false);
       return safeType(inferrer);
     }
     if (_isInstanceMemberParameter &&
@@ -845,7 +841,7 @@
     AbstractValue special = handleSpecialCases(inferrer);
     if (special != null) return special;
     return potentiallyNarrowType(
-        inferrer.types.computeTypeMask(assignments), inferrer);
+        inferrer.types.computeTypeMask(inputs), inferrer);
   }
 
   @override
@@ -855,7 +851,7 @@
 
   @override
   bool hasStableType(InferrerEngine inferrer) {
-    // The number of assignments of parameters of instance methods is
+    // The number of inputs of parameters of instance methods is
     // not stable. Therefore such a parameter cannot be stable.
     if (_isInstanceMemberParameter) {
       return false;
@@ -888,7 +884,7 @@
 
   @override
   AbstractValue computeType(InferrerEngine inferrer) =>
-      inferrer.types.computeTypeMask(assignments);
+      inferrer.types.computeTypeMask(inputs);
 
   @override
   accept(TypeInformationVisitor visitor) {
@@ -930,7 +926,6 @@
   final Object _call;
   final MemberEntity caller;
   final Selector selector;
-  final AbstractValue mask;
   final ArgumentsTypes arguments;
   final bool inLoop;
 
@@ -940,10 +935,9 @@
       this._call,
       this.caller,
       this.selector,
-      this.mask,
       this.arguments,
       this.inLoop)
-      : super.noAssignments(abstractValueDomain.emptyType, context) {
+      : super.noInputs(abstractValueDomain.emptyType, context) {
     assert(_call is ir.Node || (_call == null && selector.name == '=='));
   }
 
@@ -969,10 +963,9 @@
       MemberEntity enclosing,
       this.calledElement,
       Selector selector,
-      AbstractValue mask,
       ArgumentsTypes arguments,
       bool inLoop)
-      : super(abstractValueDomain, context, call, enclosing, selector, mask,
+      : super(abstractValueDomain, context, call, enclosing, selector,
             arguments, inLoop);
 
   MemberTypeInformation _getCalledTypeInfo(InferrerEngine inferrer) {
@@ -987,8 +980,7 @@
     if (arguments != null) {
       arguments.forEach((info) => info.addUser(this));
     }
-    inferrer.updateParameterAssignments(
-        this, calledElement, arguments, selector, mask,
+    inferrer.updateParameterInputs(this, calledElement, arguments, selector,
         remove: false, addToQueue: false);
   }
 
@@ -1056,6 +1048,7 @@
   final DynamicCallSiteTypeInformation dynamicCall;
   final bool isConditional;
   final TypeInformation receiver;
+  final AbstractValue mask;
 
   IndirectDynamicCallSiteTypeInformation(
       AbstractValueDomain abstractValueDomain,
@@ -1064,25 +1057,25 @@
       this.dynamicCall,
       MemberEntity enclosing,
       Selector selector,
-      AbstractValue mask,
+      this.mask,
       this.receiver,
       ArgumentsTypes arguments,
       bool inLoop,
       this.isConditional)
-      : super(abstractValueDomain, context, call, enclosing, selector, mask,
+      : super(abstractValueDomain, context, call, enclosing, selector,
             arguments, inLoop);
 
   @override
   void addToGraph(InferrerEngine inferrer) {
     receiver.addUser(this);
-    dynamicCall.receiver.addAssignment(receiver);
+    dynamicCall.receiver.addInput(receiver);
     List<TypeInformation> positional = arguments.positional;
     for (int i = 0; i < positional.length; i++) {
       positional[i].addUser(this);
-      dynamicCall.arguments.positional[i].addAssignment(positional[i]);
+      dynamicCall.arguments.positional[i].addInput(positional[i]);
     }
     arguments.named.forEach((name, namedInfo) {
-      dynamicCall.arguments.named[name].addAssignment(namedInfo);
+      dynamicCall.arguments.named[name].addInput(namedInfo);
     });
     dynamicCall.addUser(this);
   }
@@ -1113,12 +1106,12 @@
   }
 
   @override
-  void giveUp(InferrerEngine inferrer, {bool clearAssignments: true}) {
+  void giveUp(InferrerEngine inferrer, {bool clearInputs: true}) {
     if (!abandonInferencing) {
       inferrer.updateSelectorInMember(
           caller, CallType.access, _call, selector, mask);
     }
-    super.giveUp(inferrer, clearAssignments: clearAssignments);
+    super.giveUp(inferrer, clearInputs: clearInputs);
   }
 
   @override
@@ -1147,6 +1140,7 @@
 class DynamicCallSiteTypeInformation<T> extends CallSiteTypeInformation {
   final CallType _callType;
   final TypeInformation receiver;
+  final AbstractValue mask;
   final bool isConditional;
   bool _hasClosureCallTargets;
 
@@ -1160,12 +1154,12 @@
       T call,
       MemberEntity enclosing,
       Selector selector,
-      AbstractValue mask,
+      this.mask,
       this.receiver,
       ArgumentsTypes arguments,
       bool inLoop,
       this.isConditional)
-      : super(abstractValueDomain, context, call, enclosing, selector, mask,
+      : super(abstractValueDomain, context, call, enclosing, selector,
             arguments, inLoop) {
     assert(validCallType(_callType, _call, selector));
   }
@@ -1200,8 +1194,7 @@
           inferrer.types.getInferredTypeOfMember(element);
       _addCall(callee);
       callee.addUser(this);
-      inferrer.updateParameterAssignments(
-          this, element, arguments, selector, typeMask,
+      inferrer.updateParameterInputs(this, element, arguments, selector,
           remove: false, addToQueue: false);
     }
   }
@@ -1364,8 +1357,7 @@
             inferrer.types.getInferredTypeOfMember(element);
         _addCall(callee);
         callee.addUser(this);
-        inferrer.updateParameterAssignments(
-            this, element, arguments, selector, typeMask,
+        inferrer.updateParameterInputs(this, element, arguments, selector,
             remove: false, addToQueue: true);
       });
 
@@ -1377,8 +1369,7 @@
             inferrer.types.getInferredTypeOfMember(element);
         _removeCall(callee);
         callee.removeUser(this);
-        inferrer.updateParameterAssignments(
-            this, element, arguments, selector, typeMask,
+        inferrer.updateParameterInputs(this, element, arguments, selector,
             remove: true, addToQueue: true);
       });
     }
@@ -1440,7 +1431,7 @@
   }
 
   @override
-  void giveUp(InferrerEngine inferrer, {bool clearAssignments: true}) {
+  void giveUp(InferrerEngine inferrer, {bool clearInputs: true}) {
     if (!abandonInferencing) {
       inferrer.updateSelectorInMember(caller, _callType, _call, selector, mask);
       Iterable<MemberEntity> oldTargets = _concreteTargets;
@@ -1452,13 +1443,12 @@
           MemberTypeInformation callee =
               inferrer.types.getInferredTypeOfMember(element);
           callee.addCall(caller, _call);
-          inferrer.updateParameterAssignments(
-              this, element, arguments, selector, mask,
+          inferrer.updateParameterInputs(this, element, arguments, selector,
               remove: false, addToQueue: true);
         }
       }
     }
-    super.giveUp(inferrer, clearAssignments: clearAssignments);
+    super.giveUp(inferrer, clearInputs: clearInputs);
   }
 
   @override
@@ -1501,11 +1491,10 @@
       Object call,
       MemberEntity enclosing,
       Selector selector,
-      AbstractValue mask,
       this.closure,
       ArgumentsTypes arguments,
       bool inLoop)
-      : super(abstractValueDomain, context, call, enclosing, selector, mask,
+      : super(abstractValueDomain, context, call, enclosing, selector,
             arguments, inLoop);
 
   @override
@@ -1574,12 +1563,12 @@
   void removeUser(TypeInformation user) {}
 
   @override
-  void addAssignment(TypeInformation assignment) {
+  void addInput(TypeInformation assignment) {
     throw "Not supported";
   }
 
   @override
-  void removeAssignment(TypeInformation assignment) {
+  void removeInput(TypeInformation assignment) {
     throw "Not supported";
   }
 
@@ -1661,19 +1650,19 @@
   NarrowTypeInformation(AbstractValueDomain abstractValueDomain,
       TypeInformation narrowedType, this.typeAnnotation)
       : super(abstractValueDomain.emptyType, narrowedType.context) {
-    addAssignment(narrowedType);
+    addInput(narrowedType);
   }
 
   @override
-  addAssignment(TypeInformation info) {
-    super.addAssignment(info);
-    assert(assignments.length == 1);
+  addInput(TypeInformation info) {
+    super.addInput(info);
+    assert(inputs.length == 1);
   }
 
   @override
   AbstractValue computeType(InferrerEngine inferrer) {
     AbstractValueDomain abstractValueDomain = inferrer.abstractValueDomain;
-    AbstractValue input = assignments.first.type;
+    AbstractValue input = inputs.first.type;
     AbstractValue intersection =
         abstractValueDomain.intersection(input, typeAnnotation);
     if (debug.ANOMALY_WARN) {
@@ -1702,7 +1691,7 @@
 /// An [InferredTypeInformation] is a [TypeInformation] that
 /// defaults to the dynamic type until it is marked as being
 /// inferred, at which point it computes its type based on
-/// its assignments.
+/// its inputs.
 abstract class InferredTypeInformation extends TypeInformation {
   /// Whether the element type in that container has been inferred.
   bool inferred = false;
@@ -1710,13 +1699,13 @@
   InferredTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context, TypeInformation parentType)
       : super(abstractValueDomain.emptyType, context) {
-    if (parentType != null) addAssignment(parentType);
+    if (parentType != null) addInput(parentType);
   }
 
   @override
   AbstractValue computeType(InferrerEngine inferrer) {
     if (!inferred) return safeType(inferrer);
-    return inferrer.types.computeTypeMask(assignments);
+    return inferrer.types.computeTypeMask(inputs);
   }
 
   @override
@@ -1901,7 +1890,7 @@
     valueType.addUser(this);
   }
 
-  TypeInformation addEntryAssignment(AbstractValueDomain abstractValueDomain,
+  TypeInformation addEntryInput(AbstractValueDomain abstractValueDomain,
       TypeInformation key, TypeInformation value,
       [bool nonNull = false]) {
     TypeInformation newInfo = null;
@@ -1912,19 +1901,19 @@
             abstractValueDomain, context, null, nonNull);
         return newInfo;
       });
-      typeInfoMap[keyString].addAssignment(value);
+      typeInfoMap[keyString].addInput(value);
     } else {
       _allKeysAreStrings = false;
       typeInfoMap.clear();
     }
-    keyType.addAssignment(key);
-    valueType.addAssignment(value);
+    keyType.addInput(key);
+    valueType.addInput(value);
     if (newInfo != null) newInfo.addUser(this);
 
     return newInfo;
   }
 
-  List<TypeInformation> addMapAssignment(
+  List<TypeInformation> addMapInput(
       AbstractValueDomain abstractValueDomain, MapTypeInformation other) {
     List<TypeInformation> newInfos = <TypeInformation>[];
     if (_allKeysAreStrings && other.inDictionaryMode) {
@@ -1935,14 +1924,14 @@
           newInfos.add(newInfo);
           return newInfo;
         });
-        typeInfoMap[keyString].addAssignment(value);
+        typeInfoMap[keyString].addInput(value);
       });
     } else {
       _allKeysAreStrings = false;
       typeInfoMap.clear();
     }
-    keyType.addAssignment(other.keyType);
-    valueType.addAssignment(other.valueType);
+    keyType.addInput(other.keyType);
+    valueType.addInput(other.valueType);
 
     return newInfos;
   }
@@ -1953,7 +1942,7 @@
   }
 
   @override
-  addAssignment(TypeInformation other) {
+  addInput(TypeInformation other) {
     throw "not supported";
   }
 
@@ -2103,7 +2092,7 @@
 
   @override
   AbstractValue computeType(InferrerEngine inferrer) {
-    return inferrer.types.computeTypeMask(assignments);
+    return inferrer.types.computeTypeMask(inputs);
   }
 
   @override
@@ -2114,7 +2103,7 @@
       StringBuffer sb, String indent, Set<TypeInformation> seen) {
     if (seen.add(this)) {
       sb.write('${toString()} [');
-      for (TypeInformation assignment in assignments) {
+      for (TypeInformation assignment in inputs) {
         sb.write('\n$indent  ');
         assignment._toStructuredText(sb, '$indent  ', seen);
       }
diff --git a/pkg/compiler/lib/src/inferrer/type_system.dart b/pkg/compiler/lib/src/inferrer/type_system.dart
index 2e05bac..6d5622e 100644
--- a/pkg/compiler/lib/src/inferrer/type_system.dart
+++ b/pkg/compiler/lib/src/inferrer/type_system.dart
@@ -536,7 +536,7 @@
         new MapTypeInformation(currentMember, mask, keyTypeInfo, valueTypeInfo);
 
     for (int i = 0; i < keyTypes.length; ++i) {
-      TypeInformation newType = map.addEntryAssignment(
+      TypeInformation newType = map.addEntryInput(
           _abstractValueDomain, keyTypes[i], valueTypes[i], true);
       if (newType != null) allocatedTypes.add(newType);
     }
@@ -562,8 +562,8 @@
     PhiElementTypeInformation result = new PhiElementTypeInformation(
         _abstractValueDomain, currentMember, null, null,
         isTry: false);
-    result.addAssignment(firstInput);
-    result.addAssignment(secondInput);
+    result.addInput(firstInput);
+    result.addInput(secondInput);
     allocatedTypes.add(result);
     return result;
   }
@@ -574,7 +574,7 @@
         _abstractValueDomain, currentMember, node, variable,
         isTry: isTry);
     allocatedTypes.add(result);
-    result.addAssignment(inputType);
+    result.addInput(inputType);
     return result;
   }
 
@@ -614,14 +614,14 @@
   TypeInformation simplifyPhi(
       ir.Node node, Local variable, PhiElementTypeInformation phiType) {
     assert(phiType.branchNode == node);
-    if (phiType.assignments.length == 1) return phiType.assignments.first;
+    if (phiType.inputs.length == 1) return phiType.inputs.first;
     return phiType;
   }
 
   /// Adds [newType] as an input of [phiType].
   PhiElementTypeInformation addPhiInput(Local variable,
       PhiElementTypeInformation phiType, TypeInformation newType) {
-    phiType.addAssignment(newType);
+    phiType.addInput(newType);
     return phiType;
   }
 
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
index d7717bf..4f94658 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
@@ -756,10 +756,28 @@
 
   @override
   AbstractBool isInterceptor(TypeMask value) {
+    // TODO(39874): Remove cache when [TypeMask.isDisjoint] is faster.
+    var result = _isInterceptorCache[value];
+    if (result == null) {
+      result = _isInterceptorCacheSecondChance[value] ?? _isInterceptor(value);
+      if (_isInterceptorCache.length >= _kIsInterceptorCacheLimit) {
+        _isInterceptorCacheSecondChance = _isInterceptorCache;
+        _isInterceptorCache = {};
+      }
+      _isInterceptorCache[value] = result;
+    }
+    return result;
+  }
+
+  AbstractBool _isInterceptor(TypeMask value) {
     return AbstractBool.maybeOrFalse(
         !interceptorType.isDisjoint(value, _closedWorld));
   }
 
+  static const _kIsInterceptorCacheLimit = 500;
+  Map<TypeMask, AbstractBool> _isInterceptorCache = {};
+  Map<TypeMask, AbstractBool> _isInterceptorCacheSecondChance = {};
+
   @override
   bool isMap(TypeMask value) {
     return value.isMap;
diff --git a/pkg/compiler/lib/src/inferrer/types.dart b/pkg/compiler/lib/src/inferrer/types.dart
index 374f01a..5c5c4f1 100644
--- a/pkg/compiler/lib/src/inferrer/types.dart
+++ b/pkg/compiler/lib/src/inferrer/types.dart
@@ -52,13 +52,9 @@
   /// The inferred return type when this result belongs to a function element.
   AbstractValue get returnType;
 
-  /// Returns the type of a send [node].
-  // TODO(johnniwinther): Rename this.
-  AbstractValue typeOfSend(ir.TreeNode node);
-
-  /// Returns the type of the getter in a complex send-set [node], for example,
-  /// the type of the `a.f` getter in `a.f += b`.
-  AbstractValue typeOfGetter(ir.TreeNode node);
+  /// Returns the receiver type of a node that is a property get, set, or method
+  /// invocation.
+  AbstractValue typeOfReceiver(ir.TreeNode node);
 
   /// Returns the type of the iterator in a [loop].
   AbstractValue typeOfIterator(ir.TreeNode node);
@@ -88,8 +84,7 @@
   GlobalTypeInferenceElementData compress();
 
   // TODO(johnniwinther): Remove this. Maybe split by access/invoke.
-  AbstractValue typeOfSend(ir.TreeNode node);
-  AbstractValue typeOfGetter(ir.TreeNode node);
+  AbstractValue typeOfReceiver(ir.TreeNode node);
 
   AbstractValue typeOfIterator(ir.TreeNode node);
 
@@ -443,9 +438,7 @@
   }
 
   @override
-  AbstractValue typeOfSend(ir.Node node) => _data?.typeOfSend(node);
-  @override
-  AbstractValue typeOfGetter(ir.Node node) => _data?.typeOfGetter(node);
+  AbstractValue typeOfReceiver(ir.Node node) => _data?.typeOfReceiver(node);
   @override
   AbstractValue typeOfIterator(ir.Node node) => _data?.typeOfIterator(node);
   @override
@@ -524,10 +517,7 @@
   AbstractValue typeOfIterator(ir.Node node) => null;
 
   @override
-  AbstractValue typeOfGetter(ir.Node node) => null;
-
-  @override
-  AbstractValue typeOfSend(ir.Node node) => null;
+  AbstractValue typeOfReceiver(ir.Node node) => null;
 
   @override
   bool get isCalledOnce => false;
@@ -568,10 +558,7 @@
   AbstractValue typeOfIterator(ir.Node node) => null;
 
   @override
-  AbstractValue typeOfGetter(ir.Node node) => null;
-
-  @override
-  AbstractValue typeOfSend(ir.Node node) => null;
+  AbstractValue typeOfReceiver(ir.Node node) => null;
 
   @override
   bool get isCalledOnce => false;
@@ -612,10 +599,7 @@
   AbstractValue typeOfIterator(ir.Node node) => null;
 
   @override
-  AbstractValue typeOfGetter(ir.Node node) => null;
-
-  @override
-  AbstractValue typeOfSend(ir.Node node) => null;
+  AbstractValue typeOfReceiver(ir.Node node) => null;
 
   @override
   bool get isCalledOnce => false;
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index 822be8c..c313b19 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -539,23 +539,23 @@
   @override
   AbstractValue receiverTypeOfInvocation(
       ir.MethodInvocation node, AbstractValueDomain abstractValueDomain) {
-    return _targetResults.typeOfSend(node);
+    return _targetResults.typeOfReceiver(node);
   }
 
   @override
   AbstractValue receiverTypeOfGet(ir.PropertyGet node) {
-    return _targetResults.typeOfSend(node);
+    return _targetResults.typeOfReceiver(node);
   }
 
   @override
   AbstractValue receiverTypeOfDirectGet(ir.DirectPropertyGet node) {
-    return _targetResults.typeOfSend(node);
+    return _targetResults.typeOfReceiver(node);
   }
 
   @override
   AbstractValue receiverTypeOfSet(
       ir.PropertySet node, AbstractValueDomain abstractValueDomain) {
-    return _targetResults.typeOfSend(node);
+    return _targetResults.typeOfReceiver(node);
   }
 
   @override
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index edbcb67..32396ae 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -2894,15 +2894,28 @@
 }
 
 class HParameterValue extends HLocalValue {
+  bool _potentiallyUsedAsVariable = true;
+
   HParameterValue(Entity variable, AbstractValue type) : super(variable, type);
 
   // [HParameterValue]s are either the value of the parameter (in fully SSA
   // converted code), or the mutable variable containing the value (in
   // incompletely SSA converted code, e.g. methods containing exceptions).
   bool usedAsVariable() {
-    for (HInstruction user in usedBy) {
-      if (user is HLocalGet) return true;
-      if (user is HLocalSet && user.local == this) return true;
+    if (_potentiallyUsedAsVariable) {
+      // If the HParameterValue is used as a variable, all of the uses should be
+      // HLocalGet or HLocalSet, so this loop exits fast.
+      for (HInstruction user in usedBy) {
+        if (user is HLocalGet) return true;
+        if (user is HLocalSet && user.local == this) return true;
+      }
+      // An 'ssa-conversion' optimization can make the HParameterValue change
+      // from a variable to a value, but there is no transformation that
+      // re-introduces the variable.
+      // TODO(sra): The builder knows that most parameters are not variables to
+      // begin with, so could initialize [_potentiallyUsedAsVariable] to
+      // `false`.
+      _potentiallyUsedAsVariable = false;
     }
     return false;
   }
diff --git a/tests/compiler/dart2js/inference/data/closurization_instance_call.dart b/tests/compiler/dart2js/inference/data/closurization_instance_call.dart
index 4f785bf..d5eabac 100644
--- a/tests/compiler/dart2js/inference/data/closurization_instance_call.dart
+++ b/tests/compiler/dart2js/inference/data/closurization_instance_call.dart
@@ -24,7 +24,7 @@
 /*member: closurizedCallToString:[exact=JSString]*/
 closurizedCallToString() {
   var c = new Class();
-  c.call(); // Make `Class.call` live.
+  c. /*invoke: [null|exact=Class]*/ call(); // Make `Class.call` live.
   var local = c. /*[exact=Class]*/ method;
   local. /*invoke: [subclass=Closure]*/ toString();
   local();
diff --git a/tests/compiler/dart2js/inference/data/closurization_local_call.dart b/tests/compiler/dart2js/inference/data/closurization_local_call.dart
index 65439e1..19c5758 100644
--- a/tests/compiler/dart2js/inference/data/closurization_local_call.dart
+++ b/tests/compiler/dart2js/inference/data/closurization_local_call.dart
@@ -21,7 +21,7 @@
 /*member: closurizedCallToString:[exact=JSString]*/
 closurizedCallToString() {
   var c = new Class();
-  c.call(); // Make `Class.call` live.
+  c. /*invoke: [null|exact=Class]*/ call(); // Make `Class.call` live.
   var local = /*[exact=JSUInt31]*/ () => 42;
   local. /*invoke: [subclass=Closure]*/ toString();
   local();
diff --git a/tests/compiler/dart2js/inference/data/closurization_static_call.dart b/tests/compiler/dart2js/inference/data/closurization_static_call.dart
index a29e594..575f68d 100644
--- a/tests/compiler/dart2js/inference/data/closurization_static_call.dart
+++ b/tests/compiler/dart2js/inference/data/closurization_static_call.dart
@@ -24,7 +24,7 @@
 /*member: closurizedCallToString:[exact=JSString]*/
 closurizedCallToString() {
   var c = new Class();
-  c.call(); // Make `Class.call` live.
+  c. /*invoke: [null|exact=Class]*/ call(); // Make `Class.call` live.
   var local = method;
   local. /*invoke: [subclass=Closure]*/ toString();
   local();
diff --git a/tests/compiler/dart2js/inference/data/do.dart b/tests/compiler/dart2js/inference/data/do.dart
index 549d826..d9dae617 100644
--- a/tests/compiler/dart2js/inference/data/do.dart
+++ b/tests/compiler/dart2js/inference/data/do.dart
@@ -47,7 +47,7 @@
   var o = '';
   do {
     o = o. /*invoke: [exact=JSString]*/ toString();
-  } while (o != null);
+  } while (o /*invoke: [null|exact=JSString]*/ != null);
   return o;
 }
 
@@ -60,7 +60,7 @@
   var o = '';
   do {
     o = o. /*invoke: [exact=JSString]*/ toString();
-  } while (o == null);
+  } while (o /*invoke: [null|exact=JSString]*/ == null);
   return o;
 }
 
diff --git a/tests/compiler/dart2js/inference/data/for.dart b/tests/compiler/dart2js/inference/data/for.dart
index b3829b7..9bc55db 100644
--- a/tests/compiler/dart2js/inference/data/for.dart
+++ b/tests/compiler/dart2js/inference/data/for.dart
@@ -47,7 +47,9 @@
 /*member: forNotNull:[null|exact=JSString]*/
 forNotNull() {
   var local;
-  for (var o = ''; o != null; o = o. /*invoke: [exact=JSString]*/ toString()) {
+  for (var o = '';
+      o /*invoke: [null|exact=JSString]*/ != null;
+      o = o. /*invoke: [exact=JSString]*/ toString()) {
     local = o;
   }
   return local;
@@ -60,7 +62,9 @@
 /*member: forNullFalse:[null]*/
 forNullFalse() {
   var local;
-  for (var o = ''; o == null; o = o. /*invoke: [null]*/ toString()) {
+  for (var o = '';
+      o /*invoke: [null|exact=JSString]*/ == null;
+      o = o. /*invoke: [null]*/ toString()) {
     local = o;
   }
   return local;
diff --git a/tests/compiler/dart2js/inference/data/general.dart b/tests/compiler/dart2js/inference/data/general.dart
index beb067d..f3d814f 100644
--- a/tests/compiler/dart2js/inference/data/general.dart
+++ b/tests/compiler/dart2js/inference/data/general.dart
@@ -675,7 +675,8 @@
   A.generative();
 
   /*member: A.==:[exact=JSBool]*/
-  operator ==(/*[null|subclass=Object]*/ other) => 42 as dynamic;
+  operator ==(/*Union([exact=JSString], [exact=JSUInt31])*/ other) =>
+      42 as dynamic;
 
   /*member: A.myField:[exact=JSUInt31]*/
   get myField => 42;
@@ -892,7 +893,7 @@
   testDoWhile2();
   testDoWhile3();
   testDoWhile4();
-  new A() == null;
+  new A() /*invoke: [null|subclass=A]*/ == null;
   new A()
     .. /*invoke: [exact=A]*/ returnInt1()
     .. /*invoke: [exact=A]*/ returnInt2()
diff --git a/tests/compiler/dart2js/inference/data/narrowing.dart b/tests/compiler/dart2js/inference/data/narrowing.dart
index ba26c79..27c041d 100644
--- a/tests/compiler/dart2js/inference/data/narrowing.dart
+++ b/tests/compiler/dart2js/inference/data/narrowing.dart
@@ -13,7 +13,7 @@
 /*member: nonNull1:[null]*/
 void nonNull1() {
   var x = 1;
-  if (x == null) return;
+  if (x /*invoke: [null|subclass=JSInt]*/ == null) return;
   argIsNonNull1(x);
 }
 
@@ -25,7 +25,9 @@
 /*member: nonNull2:[null]*/
 void nonNull2() {
   var x = 1;
-  if ((x == null) /*invoke: [exact=JSBool]*/ == true) return;
+  if ((x /*invoke: [null|subclass=JSInt]*/ ==
+          null) /*invoke: [exact=JSBool]*/ ==
+      true) return;
   argIsNonNull2(x);
 }
 
@@ -37,7 +39,9 @@
 /*member: nonNull3:[null]*/
 void nonNull3() {
   var x = 1;
-  if ((x == null) /*invoke: [exact=JSBool]*/ != false) return;
+  if ((x /*invoke: [null|subclass=JSInt]*/ ==
+          null) /*invoke: [exact=JSBool]*/ !=
+      false) return;
   argIsNonNull3(x);
 }
 
@@ -52,7 +56,7 @@
 /*member: nonNull4:[null]*/
 void nonNull4() {
   var x = 1;
-  if (discard(x != null)) return;
+  if (discard(x /*invoke: [null|subclass=JSInt]*/ != null)) return;
   argIsNonNull4(x);
 }
 
@@ -64,7 +68,7 @@
 /*member: nonNull5:[null]*/
 void nonNull5() {
   var x = 1;
-  if (x != null ? false : false) return;
+  if (x /*invoke: [null|subclass=JSInt]*/ != null ? false : false) return;
   argIsNonNull5(x);
 }
 
@@ -76,8 +80,8 @@
 /*member: nonNull6:[null]*/
 void nonNull6() {
   var x = 1;
-  if ((/*[exact=JSBool]*/ (/*[exact=JSBool]*/ y) => y && false)(x != null))
-    return;
+  if ((/*[exact=JSBool]*/ (/*[exact=JSBool]*/ y) =>
+      y && false)(x /*invoke: [null|subclass=JSInt]*/ != null)) return;
   argIsNonNull6(x);
 }
 
@@ -90,7 +94,7 @@
 void nonNull7() {
   var f = false;
   var x = 1;
-  if (f ? (throw x != null) : false) return;
+  if (f ? (throw x /*invoke: [null|subclass=JSInt]*/ != null) : false) return;
   argIsNonNull7(x);
 }
 
@@ -103,7 +107,7 @@
 void nonNull8() {
   var f = false;
   var x = 1;
-  if (f ?? (x != null)) return;
+  if (f ?? (x /*invoke: [null|subclass=JSInt]*/ != null)) return;
   argIsNonNull8(x);
 }
 
diff --git a/tests/compiler/dart2js/inference/data/null.dart b/tests/compiler/dart2js/inference/data/null.dart
index 9e53378..05fd76b 100644
--- a/tests/compiler/dart2js/inference/data/null.dart
+++ b/tests/compiler/dart2js/inference/data/null.dart
@@ -24,7 +24,7 @@
 
 /*member: ifThenNullCheck:[exact=JSUInt31]*/
 ifThenNullCheck(int /*[null|exact=JSUInt31]*/ value) {
-  if (value == null) {
+  if (value /*invoke: [null|subclass=JSInt]*/ == null) {
     return 0;
   }
   return value;
@@ -32,7 +32,7 @@
 
 /*member: ifThenElseNullCheck:[exact=JSUInt31]*/
 ifThenElseNullCheck(int /*[null|exact=JSUInt31]*/ value) {
-  if (value == null) {
+  if (value /*invoke: [null|subclass=JSInt]*/ == null) {
     return 0;
   } else {
     return value;
@@ -41,7 +41,7 @@
 
 /*member: ifNotThenNullCheck:[exact=JSUInt31]*/
 ifNotThenNullCheck(int /*[null|exact=JSUInt31]*/ value) {
-  if (value != null) {
+  if (value /*invoke: [null|subclass=JSInt]*/ != null) {
     return value;
   }
   return 0;
@@ -49,7 +49,7 @@
 
 /*member: ifNotThenElseNullCheck:[exact=JSUInt31]*/
 ifNotThenElseNullCheck(int /*[null|exact=JSUInt31]*/ value) {
-  if (value != null) {
+  if (value /*invoke: [null|subclass=JSInt]*/ != null) {
     return value;
   } else {
     return 0;
@@ -59,7 +59,8 @@
 /*member: ifThenNotNullComplexCheck:[exact=JSUInt31]*/
 ifThenNotNullComplexCheck(
     int /*[null|exact=JSUInt31]*/ a, int /*[null|exact=JSUInt31]*/ b) {
-  if (a != null && a /*invoke: [exact=JSUInt31]*/ != b) {
+  if (a /*invoke: [null|subclass=JSInt]*/ != null &&
+      a /*invoke: [exact=JSUInt31]*/ != b) {
     return a;
   }
   return 0;
@@ -68,7 +69,8 @@
 /*member: ifThenElseNotNullComplexCheck:[null|exact=JSUInt31]*/
 ifThenElseNotNullComplexCheck(
     int /*[null|exact=JSUInt31]*/ a, int /*[null|exact=JSUInt31]*/ b) {
-  if (a != null && a /*invoke: [exact=JSUInt31]*/ != b) {
+  if (a /*invoke: [null|subclass=JSInt]*/ != null &&
+      a /*invoke: [exact=JSUInt31]*/ != b) {
     return a;
   }
   return a;
@@ -78,7 +80,7 @@
 ifThenNotNullGradualCheck1(
     int /*[null|exact=JSUInt31]*/ a, int /*[exact=JSUInt31]*/ b) {
   if (a /*invoke: [null|exact=JSUInt31]*/ != b) {
-    if (a != null) {
+    if (a /*invoke: [null|subclass=JSInt]*/ != null) {
       return a;
     }
   }
@@ -88,7 +90,7 @@
 /*member: ifThenNotNullGradualCheck2:[exact=JSUInt31]*/
 ifThenNotNullGradualCheck2(
     int /*[null|exact=JSUInt31]*/ a, int /*[exact=JSUInt31]*/ b) {
-  if (a != null) {
+  if (a /*invoke: [null|subclass=JSInt]*/ != null) {
     if (a /*invoke: [exact=JSUInt31]*/ != b) {
       return a;
     }
diff --git a/tests/compiler/dart2js/inference/data/while.dart b/tests/compiler/dart2js/inference/data/while.dart
index a6f7373..ddafecf 100644
--- a/tests/compiler/dart2js/inference/data/while.dart
+++ b/tests/compiler/dart2js/inference/data/while.dart
@@ -49,7 +49,7 @@
 /*member: whileNotNull:[exact=JSString]*/
 whileNotNull() {
   var o = '';
-  while (o != null) {
+  while (o /*invoke: [null|exact=JSString]*/ != null) {
     o = o. /*invoke: [exact=JSString]*/ toString();
   }
   return o;
@@ -62,7 +62,7 @@
 /*member: whileNullUnreachable:[exact=JSString]*/
 whileNullUnreachable() {
   var o = '';
-  while (o == null) {
+  while (o /*invoke: [null|exact=JSString]*/ == null) {
     o = o. /*invoke: [null]*/ toString();
   }
   return o;
diff --git a/tests/compiler/dart2js/inference/inference_data/called_in_loop.dart b/tests/compiler/dart2js/inference/inference_data/called_in_loop.dart
index d984418..884773e 100644
--- a/tests/compiler/dart2js/inference/inference_data/called_in_loop.dart
+++ b/tests/compiler/dart2js/inference/inference_data/called_in_loop.dart
@@ -84,7 +84,7 @@
   Class.constructorNotCalledInForLoop();
 
   // TODO(johnniwinther): Should we track instance calls in loops?
-  /*member: Class.instanceCalledInForLoop:*/
+  /*member: Class.instanceCalledInForLoop:loop*/
   instanceCalledInForLoop() {}
 
   /*member: Class.instanceNotCalledInForLoop:*/
diff --git a/tests/compiler/dart2js/inference/inference_data_test.dart b/tests/compiler/dart2js/inference/inference_data_test.dart
index 9041fc7..3aeb592 100644
--- a/tests/compiler/dart2js/inference/inference_data_test.dart
+++ b/tests/compiler/dart2js/inference/inference_data_test.dart
@@ -24,6 +24,7 @@
     await checkTests(dataDir, const InferenceDataComputer(),
         args: args,
         testedConfigs: [strongConfig],
+        supportedMarkers: [strongMarker],
         options: [stopAfterTypeInference]);
   });
 }
diff --git a/tests/compiler/dart2js/inference/inference_test_helper.dart b/tests/compiler/dart2js/inference/inference_test_helper.dart
index 06da02c..d0b6caa 100644
--- a/tests/compiler/dart2js/inference/inference_test_helper.dart
+++ b/tests/compiler/dart2js/inference/inference_test_helper.dart
@@ -146,11 +146,11 @@
       ClosureRepresentationInfo info = _closureDataLookup.getClosureInfo(node);
       return getMemberValue(info.callMethod);
     } else if (node is ir.MethodInvocation) {
-      return getTypeMaskValue(result.typeOfSend(node));
+      return getTypeMaskValue(result.typeOfReceiver(node));
     } else if (node is ir.PropertyGet) {
-      return getTypeMaskValue(result.typeOfGetter(node));
+      return getTypeMaskValue(result.typeOfReceiver(node));
     } else if (node is ir.PropertySet) {
-      return getTypeMaskValue(result.typeOfSend(node));
+      return getTypeMaskValue(result.typeOfReceiver(node));
     } else if (node is ir.ForInStatement) {
       if (id.kind == IdKind.iterator) {
         return getTypeMaskValue(result.typeOfIterator(node));
diff --git a/tools/VERSION b/tools/VERSION
index 9c854ec..42b0140 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -32,7 +32,7 @@
 CHANNEL stable
 MAJOR 2
 MINOR 7
-PATCH 0
+PATCH 1
 PRERELEASE 0
 PRERELEASE_PATCH 0
 ABI_VERSION 24
diff --git a/tools/apps/update_homebrew/lib/src/impl.dart b/tools/apps/update_homebrew/lib/src/impl.dart
index 4d4cf86..3c1acab 100644
--- a/tools/apps/update_homebrew/lib/src/impl.dart
+++ b/tools/apps/update_homebrew/lib/src/impl.dart
@@ -12,7 +12,6 @@
   'linux-arm': 'sdk/dartsdk-linux-arm64-release.zip',
 };
 const _ia32Files = {
-  'mac': 'sdk/dartsdk-macos-ia32-release.zip',
   'linux': 'sdk/dartsdk-linux-ia32-release.zip',
   'linux-arm': 'sdk/dartsdk-linux-arm-release.zip',
 };
@@ -93,13 +92,8 @@
 
   version "$stableVersion"
   if OS.mac?
-    if Hardware::CPU.is_64_bit?
-      url "$_urlBase/stable/release/${revisions['stable']}/${_x64Files['mac']}"
-      sha256 "${hashes['stable'][_x64Files['mac']]}"
-    else
-      url "$_urlBase/stable/release/${revisions['stable']}/${_ia32Files['mac']}"
-      sha256 "${hashes['stable'][_ia32Files['mac']]}"
-    end
+    url "$_urlBase/stable/release/${revisions['stable']}/${_x64Files['mac']}"
+    sha256 "${hashes['stable'][_x64Files['mac']]}"
   elsif OS.linux? && Hardware::CPU.intel?
     if Hardware::CPU.is_64_bit?
       url "$_urlBase/stable/release/${revisions['stable']}/${_x64Files['linux']}"
@@ -121,13 +115,8 @@
   devel do
     version "$devVersion"
     if OS.mac?
-      if Hardware::CPU.is_64_bit?
-        url "$_urlBase/dev/release/${revisions['dev']}/${_x64Files['mac']}"
-        sha256 "${hashes['dev'][_x64Files['mac']]}"
-      else
-        url "$_urlBase/dev/release/${revisions['dev']}/${_ia32Files['mac']}"
-        sha256 "${hashes['dev'][_ia32Files['mac']]}"
-      end
+      url "$_urlBase/dev/release/${revisions['dev']}/${_x64Files['mac']}"
+      sha256 "${hashes['dev'][_x64Files['mac']]}"
     elsif OS.linux? && Hardware::CPU.intel?
       if Hardware::CPU.is_64_bit?
         url "$_urlBase/dev/release/${revisions['dev']}/${_x64Files['linux']}"
diff --git a/tools/bots/dart_sdk.py b/tools/bots/dart_sdk.py
index a545a97..2bfc0b9 100755
--- a/tools/bots/dart_sdk.py
+++ b/tools/bots/dart_sdk.py
@@ -25,6 +25,8 @@
 def BuildArchitectures():
     if BUILD_OS == 'linux':
         return ['ia32', 'x64', 'arm', 'arm64']
+    elif BUILD_OS == 'macos':
+        return ['x64']
     else:
         return ['ia32', 'x64']
 
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 8854609..0cc2796 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -2041,7 +2041,7 @@
         {
           "name": "build dart",
           "script": "tools/build.py",
-          "arguments": ["--arch=ia32,x64",
+          "arguments": ["--arch=x64",
                         "--mode=release", "create_sdk"]
         },
         {