Revive AbstractValueDomain

- to decouple inference results from TypeMask.

Change-Id: Ibe2ec234e84262c5bff169ec7068ca1419706029
Reviewed-on: https://dart-review.googlesource.com/53942
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
index 44c7969..184b42e 100644
--- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
+++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
@@ -60,7 +60,7 @@
   ClosedWorld get closedWorld;
   ClosedWorldRefiner get closedWorldRefiner;
   DiagnosticReporter get reporter;
-  CommonMasks get commonMasks => closedWorld.commonMasks;
+  CommonMasks get commonMasks => closedWorld.abstractValueDomain;
   CommonElements get commonElements => closedWorld.commonElements;
 
   // TODO(johnniwinther): This should be part of [ClosedWorld] or
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
index d0d98eb..b59bf9f 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
@@ -61,7 +61,7 @@
 
   String get name => 'Graph inferrer';
 
-  CommonMasks get commonMasks => closedWorld.commonMasks;
+  CommonMasks get commonMasks => closedWorld.abstractValueDomain;
 
   TypeMask get _dynamicType => commonMasks.dynamicType;
 
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
index a5ea018..d17c19d 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
@@ -2005,7 +2005,7 @@
   } else if (annotation.isVoid) {
     return type;
   } else if (annotation.isTypedef || annotation.isFunctionType) {
-    otherType = closedWorld.commonMasks.functionType;
+    otherType = closedWorld.abstractValueDomain.functionType;
   } else if (annotation.isFutureOr) {
     // TODO(johnniwinther): Narrow FutureOr types.
     return type;
diff --git a/pkg/compiler/lib/src/inferrer/type_system.dart b/pkg/compiler/lib/src/inferrer/type_system.dart
index 90c3101..3d3c9c8 100644
--- a/pkg/compiler/lib/src/inferrer/type_system.dart
+++ b/pkg/compiler/lib/src/inferrer/type_system.dart
@@ -99,7 +99,7 @@
     nonNullEmptyType = getConcreteTypeFor(commonMasks.emptyType);
   }
 
-  CommonMasks get commonMasks => closedWorld.commonMasks;
+  CommonMasks get commonMasks => closedWorld.abstractValueDomain;
 
   /// Used to group [TypeInformation] nodes by the element that triggered their
   /// creation.
diff --git a/pkg/compiler/lib/src/kernel/kernel_backend_strategy.dart b/pkg/compiler/lib/src/kernel/kernel_backend_strategy.dart
index b575680..3c3f6bf 100644
--- a/pkg/compiler/lib/src/kernel/kernel_backend_strategy.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_backend_strategy.dart
@@ -149,7 +149,7 @@
   TypeMask typeOfListLiteral(
       MemberEntity owner, ir.ListLiteral listLiteral, ClosedWorld closedWorld) {
     return _resultOf(owner).typeOfListLiteral(listLiteral) ??
-        closedWorld.commonMasks.dynamicType;
+        closedWorld.abstractValueDomain.dynamicType;
   }
 
   TypeMask typeOfIterator(ir.ForInStatement node) {
@@ -183,7 +183,7 @@
         mask.containsOnly(
             closedWorld.commonElements.jsUnmodifiableArrayClass) ||
         mask.containsOnlyString(closedWorld) ||
-        closedWorld.commonMasks.isTypedArray(mask)) {
+        closedWorld.abstractValueDomain.isTypedArray(mask)) {
       return true;
     }
     return false;
diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart
index 8a75570..9224019 100644
--- a/pkg/compiler/lib/src/ssa/builder.dart
+++ b/pkg/compiler/lib/src/ssa/builder.dart
@@ -480,7 +480,7 @@
       // Don't inline operator== methods if the parameter can be null.
       if (function.name == '==') {
         if (function.enclosingClass != commonElements.objectClass &&
-            providedArguments[1].canBeNull()) {
+            providedArguments[1].canBeNull(abstractValueDomain)) {
           return false;
         }
       }
@@ -756,8 +756,8 @@
       }
     }
     if (const bool.fromEnvironment('unreachable-throw')) {
-      var emptyParameters =
-          parameters.values.where((p) => p.instructionType.isEmpty);
+      var emptyParameters = parameters.values
+          .where((p) => abstractValueDomain.isEmpty(p.instructionType));
       if (emptyParameters.length > 0) {
         addComment('${emptyParameters} inferred as [empty]');
         pushInvokeStatic(
@@ -2802,7 +2802,7 @@
     if (trustedMask != null) {
       // We only allow the type argument to narrow `dynamic`, which probably
       // comes from an unspecified return type in the NativeBehavior.
-      if (code.instructionType.containsAll(closedWorld)) {
+      if (abstractValueDomain.containsAll(code.instructionType)) {
         // Overwrite the type with the narrower type.
         code.instructionType = trustedMask;
       } else if (trustedMask.containsMask(code.instructionType, closedWorld)) {
@@ -3471,7 +3471,7 @@
       } else if (isGrowableListConstructorCall) {
         TypeMask inferred = _inferredTypeOfNewList(send);
         return inferred.containsAll(closedWorld)
-            ? commonMasks.extendableArrayType
+            ? commonMasks.growableListType
             : inferred;
       } else if (Elements.isConstructorOfTypedArraySubclass(
           originalElement, closedWorld)) {
@@ -3579,7 +3579,7 @@
 
     TypeMask elementType = computeType(constructor);
     if (isFixedListConstructorCall) {
-      if (!inputs[0].isNumber(closedWorld)) {
+      if (!inputs[0].isNumber(abstractValueDomain)) {
         HTypeConversion conversion = new HTypeConversion(
             null,
             HTypeConversion.ARGUMENT_TYPE_CHECK,
@@ -3598,7 +3598,7 @@
       // TODO(sra): Array allocation should be an instruction so that canThrow
       // can depend on a length type discovered in optimization.
       bool canThrow = true;
-      if (inputs[0].isInteger(closedWorld) && inputs[0] is HConstant) {
+      if (inputs[0].isInteger(abstractValueDomain) && inputs[0] is HConstant) {
         dynamic constant = inputs[0];
         IntConstantValue intConstant = constant.constant;
         int value = intConstant.intValue;
@@ -4359,7 +4359,7 @@
       type = TypeMaskFactory.inferredReturnTypeForElement(
           method, globalInferenceResults);
     } else {
-      type = closedWorld.commonMasks.dynamicType;
+      type = closedWorld.abstractValueDomain.dynamicType;
     }
     HInstruction instruction = new HInvokeSuper(
         element,
@@ -5894,7 +5894,8 @@
 
       // We lift this common call pattern into a helper function to save space
       // in the output.
-      if (typeInputs.every((HInstruction input) => input.isNull())) {
+      if (typeInputs
+          .every((HInstruction input) => input.isNull(abstractValueDomain))) {
         if (listInputs.isEmpty) {
           createFunction = commonElements.mapLiteralUntypedEmptyMaker;
         } else {
@@ -6782,7 +6783,7 @@
     //      conversions.
     //   2. The value can be primitive, because the library stringifier has
     //      fast-path code for most primitives.
-    if (expression.canBePrimitive(builder.closedWorld)) {
+    if (expression.canBePrimitive(builder.abstractValueDomain)) {
       append(stringify(node, expression));
       return;
     }
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 3d8966f..859e6e7 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -1591,7 +1591,7 @@
           const <DartType>[],
           _sourceInformationBuilder.buildForInMoveNext(node));
       HInstruction future = pop();
-      push(new HAwait(future, closedWorld.commonMasks.dynamicType));
+      push(new HAwait(future, abstractValueDomain.dynamicType));
       return popBoolified();
     }
 
@@ -1628,7 +1628,7 @@
     void finalizerFunction() {
       _pushDynamicInvocation(node, null, Selectors.cancel, [streamIterator],
           const <DartType>[], _sourceInformationBuilder.buildGeneric(node));
-      add(new HAwait(pop(), closedWorld.commonMasks.dynamicType));
+      add(new HAwait(pop(), abstractValueDomain.dynamicType));
     }
 
     tryBuilder
@@ -2586,7 +2586,8 @@
 
       // We lift this common call pattern into a helper function to save space
       // in the output.
-      if (typeInputs.every((HInstruction input) => input.isNull())) {
+      if (typeInputs
+          .every((HInstruction input) => input.isNull(abstractValueDomain))) {
         if (constructorArgs.isEmpty) {
           constructor = _commonElements.mapLiteralUntypedEmptyMaker;
         } else {
@@ -3176,7 +3177,7 @@
               "Unexpected arguments. "
               "Expected 1-2 argument, actual: $arguments."));
       HInstruction lengthInput = arguments.first;
-      if (!lengthInput.isNumber(closedWorld)) {
+      if (!lengthInput.isNumber(abstractValueDomain)) {
         HTypeConversion conversion = new HTypeConversion(
             null,
             HTypeConversion.ARGUMENT_TYPE_CHECK,
@@ -3199,7 +3200,7 @@
       // TODO(sra): Array allocation should be an instruction so that canThrow
       // can depend on a length type discovered in optimization.
       bool canThrow = true;
-      if (lengthInput.isUInt32(closedWorld)) {
+      if (lengthInput.isUInt32(abstractValueDomain)) {
         canThrow = false;
       }
 
@@ -3747,7 +3748,7 @@
     if (trustedMask != null) {
       // We only allow the type argument to narrow `dynamic`, which probably
       // comes from an unspecified return type in the NativeBehavior.
-      if (code.instructionType.containsAll(closedWorld)) {
+      if (abstractValueDomain.containsAll(code.instructionType)) {
         // Overwrite the type with the narrower type.
         code.instructionType = trustedMask;
       } else if (trustedMask.containsMask(code.instructionType, closedWorld)) {
@@ -4176,7 +4177,7 @@
     if (target is FunctionEntity) {
       typeMask = _typeInferenceMap.getReturnTypeOf(target);
     } else {
-      typeMask = closedWorld.commonMasks.dynamicType;
+      typeMask = abstractValueDomain.dynamicType;
     }
     HInstruction instruction = new HInvokeSuper(
         target,
@@ -4559,7 +4560,7 @@
     node.operand.accept(this);
     HInstruction awaited = pop();
     // TODO(herhut): Improve this type.
-    push(new HAwait(awaited, closedWorld.commonMasks.dynamicType)
+    push(new HAwait(awaited, abstractValueDomain.dynamicType)
       ..sourceInformation = _sourceInformationBuilder.buildAwait(node));
   }
 
@@ -4699,7 +4700,7 @@
       // Don't inline operator== methods if the parameter can be null.
       if (function.name == '==') {
         if (function.enclosingClass != commonElements.objectClass &&
-            providedArguments[1].canBeNull()) {
+            providedArguments[1].canBeNull(abstractValueDomain)) {
           return false;
         }
       }
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 2e4c056..fc0c71b 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -25,6 +25,7 @@
 import '../js_emitter/code_emitter_task.dart';
 import '../native/native.dart' as native;
 import '../options.dart';
+import '../types/abstract_value_domain.dart';
 import '../types/types.dart';
 import '../universe/call_structure.dart' show CallStructure;
 import '../universe/selector.dart' show Selector;
@@ -237,6 +238,9 @@
 
   InterceptorData get _interceptorData => _closedWorld.interceptorData;
 
+  AbstractValueDomain get _abstractValueDomain =>
+      _closedWorld.abstractValueDomain;
+
   bool isGenerateAtUseSite(HInstruction instruction) {
     return generateAtUseSite.contains(instruction);
   }
@@ -306,7 +310,7 @@
   }
 
   bool requiresUintConversion(HInstruction instruction) {
-    if (instruction.isUInt31(_closedWorld)) return false;
+    if (instruction.isUInt31(_abstractValueDomain)) return false;
     if (bitWidth(instruction) <= 31) return false;
     // If the result of a bit-operation is only used by other bit
     // operations, we do not have to convert to an unsigned integer.
@@ -353,7 +357,8 @@
         .visitGraph(graph);
     new SsaTypeKnownRemover().visitGraph(graph);
     new SsaTrustedCheckRemover(_options).visitGraph(graph);
-    new SsaInstructionMerger(generateAtUseSite, _superMemberData)
+    new SsaInstructionMerger(_closedWorld.abstractValueDomain,
+            generateAtUseSite, _superMemberData)
         .visitGraph(graph);
     new SsaConditionMerger(generateAtUseSite, controlFlowOperators)
         .visitGraph(graph);
@@ -1467,11 +1472,11 @@
   visitShiftRight(HShiftRight node) => visitBitInvokeBinary(node, '>>>');
 
   visitTruncatingDivide(HTruncatingDivide node) {
-    assert(node.isUInt31(_closedWorld));
+    assert(node.isUInt31(_abstractValueDomain));
     // TODO(karlklose): Enable this assertion again when type propagation is
     // fixed. Issue 23555.
 //    assert(node.left.isUInt32(compiler));
-    assert(node.right.isPositiveInteger(_closedWorld));
+    assert(node.right.isPositiveInteger(_abstractValueDomain));
     use(node.left);
     js.Expression jsLeft = pop();
     use(node.right);
@@ -1806,7 +1811,7 @@
       // invoke dynamic knows more than the receiver.
       ClassEntity enclosing = node.element.enclosingClass;
       if (_closedWorld.isInstantiated(enclosing)) {
-        return _closedWorld.commonMasks.createNonNullExact(enclosing);
+        return _closedWorld.abstractValueDomain.createNonNullExact(enclosing);
       } else {
         // The element is mixed in so a non-null subtype mask is the most
         // precise we have.
@@ -1816,7 +1821,7 @@
                 node,
                 "Element ${node.element} from $enclosing expected "
                 "to be mixed in."));
-        return _closedWorld.commonMasks.createNonNullSubtype(enclosing);
+        return _closedWorld.abstractValueDomain.createNonNullSubtype(enclosing);
       }
     }
     // If [JSInvocationMirror._invokeOn] is enabled, and this call
@@ -2207,14 +2212,15 @@
 
       HInstruction left = relational.left;
       HInstruction right = relational.right;
-      if (left.isStringOrNull(_closedWorld) &&
-          right.isStringOrNull(_closedWorld)) {
+      if (left.isStringOrNull(_abstractValueDomain) &&
+          right.isStringOrNull(_abstractValueDomain)) {
         return true;
       }
 
       // This optimization doesn't work for NaN, so we only do it if the
       // type is known to be an integer.
-      return left.isInteger(_closedWorld) && right.isInteger(_closedWorld);
+      return left.isInteger(_abstractValueDomain) &&
+          right.isInteger(_abstractValueDomain);
     }
 
     bool handledBySpecialCase = false;
@@ -2350,13 +2356,13 @@
       js.Expression over;
       if (node.staticChecks != HBoundsCheck.ALWAYS_ABOVE_ZERO) {
         use(node.index);
-        if (node.index.isInteger(_closedWorld)) {
+        if (node.index.isInteger(_abstractValueDomain)) {
           under = js.js("# < 0", pop());
         } else {
           js.Expression jsIndex = pop();
           under = js.js("# >>> 0 !== #", [jsIndex, jsIndex]);
         }
-      } else if (!node.index.isInteger(_closedWorld)) {
+      } else if (!node.index.isInteger(_abstractValueDomain)) {
         checkInt(node.index, '!==');
         under = pop();
       }
@@ -2486,9 +2492,10 @@
 
   void visitStringify(HStringify node) {
     HInstruction input = node.inputs.first;
-    if (input.isString(_closedWorld)) {
+    if (input.isString(_abstractValueDomain)) {
       use(input);
-    } else if (input.isInteger(_closedWorld) || input.isBoolean(_closedWorld)) {
+    } else if (input.isInteger(_abstractValueDomain) ||
+        input.isBoolean(_abstractValueDomain)) {
       // JavaScript's + operator with a string for the left operand will convert
       // the right operand to a string, and the conversion result is correct.
       use(input);
@@ -2879,9 +2886,9 @@
       } else if (type.isFunctionType) {
         checkType(input, interceptor, type, sourceInformation,
             negative: negative);
-      } else if ((input.canBePrimitive(_closedWorld) &&
-              !input.canBePrimitiveArray(_closedWorld)) ||
-          input.canBeNull()) {
+      } else if ((input.canBePrimitive(_abstractValueDomain) &&
+              !input.canBePrimitiveArray(_abstractValueDomain)) ||
+          input.canBeNull(_abstractValueDomain)) {
         checkObject(input, relation, node.sourceInformation);
         js.Expression objectTest = pop();
         checkType(input, interceptor, type, sourceInformation,
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index 85be9f0..d7194b8 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -7,6 +7,7 @@
 import '../js_backend/js_backend.dart';
 import '../js_backend/interceptor_data.dart';
 import '../options.dart';
+import '../types/abstract_value_domain.dart';
 import '../types/types.dart';
 import '../universe/selector.dart' show Selector;
 import '../world.dart' show ClosedWorld;
@@ -23,6 +24,9 @@
 
   SsaInstructionSelection(this._closedWorld, this._interceptorData);
 
+  AbstractValueDomain get _abstractValueDomain =>
+      _closedWorld.abstractValueDomain;
+
   void visitGraph(HGraph graph) {
     this.graph = graph;
     visitDominatorTree(graph);
@@ -68,7 +72,7 @@
       HInstruction interceptor = node.interceptor;
       if (interceptor != null) {
         return new HIsViaInterceptor(node.typeExpression, interceptor,
-            _closedWorld.commonMasks.boolType);
+            _closedWorld.abstractValueDomain.boolType);
       }
     }
     return node;
@@ -87,7 +91,7 @@
     if (leftType.isNullable && rightType.isNullable) {
       if (left.isConstantNull() ||
           right.isConstantNull() ||
-          (left.isPrimitive(_closedWorld) && leftType == rightType)) {
+          (left.isPrimitive(_abstractValueDomain) && leftType == rightType)) {
         return '==';
       }
       return null;
@@ -244,7 +248,7 @@
     HInstruction bitop(String assignOp) {
       // HBitAnd, HBitOr etc. are more difficult because HBitAnd(a.x, y)
       // sometimes needs to be forced to unsigned: a.x = (a.x & y) >>> 0.
-      if (op.isUInt31(_closedWorld)) return simpleBinary(assignOp);
+      if (op.isUInt31(_abstractValueDomain)) return simpleBinary(assignOp);
       return noMatchingRead();
     }
 
@@ -335,6 +339,7 @@
  *   t2 = add(4, 3);
  */
 class SsaInstructionMerger extends HBaseVisitor {
+  final AbstractValueDomain _abstractValueDomain;
   final SuperMemberData _superMemberData;
   /**
    * List of [HInstruction] that the instruction merger expects in
@@ -354,7 +359,8 @@
     generateAtUseSite.add(instruction);
   }
 
-  SsaInstructionMerger(this.generateAtUseSite, this._superMemberData);
+  SsaInstructionMerger(
+      this._abstractValueDomain, this.generateAtUseSite, this._superMemberData);
 
   void visitGraph(HGraph graph) {
     visitDominatorTree(graph);
@@ -404,7 +410,7 @@
   // construction always precede a use.
   bool isEffectivelyPure(HInstruction instruction) {
     if (instruction is HLocalGet) return !isAssignedLocal(instruction.local);
-    return instruction.isPure();
+    return instruction.isPure(_abstractValueDomain);
   }
 
   bool isAssignedLocal(HLocalValue local) {
diff --git a/pkg/compiler/lib/src/ssa/graph_builder.dart b/pkg/compiler/lib/src/ssa/graph_builder.dart
index 9abf34d..e13dc3f 100644
--- a/pkg/compiler/lib/src/ssa/graph_builder.dart
+++ b/pkg/compiler/lib/src/ssa/graph_builder.dart
@@ -25,6 +25,7 @@
 import '../js_emitter/code_emitter_task.dart';
 import '../options.dart';
 import '../resolution/tree_elements.dart';
+import '../types/abstract_value_domain.dart';
 import '../types/types.dart';
 import '../world.dart' show ClosedWorld;
 import 'jump_handler.dart';
@@ -59,7 +60,10 @@
 
   ClosedWorld get closedWorld;
 
-  CommonMasks get commonMasks => closedWorld.commonMasks;
+  AbstractValueDomain get abstractValueDomain =>
+      closedWorld.abstractValueDomain;
+
+  CommonMasks get commonMasks => closedWorld.abstractValueDomain;
 
   DiagnosticReporter get reporter => backend.reporter;
 
@@ -135,7 +139,7 @@
   /// Pushes a boolean checking [expression] against null.
   pushCheckNull(HInstruction expression) {
     push(new HIdentity(expression, graph.addConstantNull(closedWorld), null,
-        closedWorld.commonMasks.boolType));
+        closedWorld.abstractValueDomain.boolType));
   }
 
   void dup() {
@@ -246,7 +250,7 @@
   MemberEntity get sourceElement;
 
   HLiteralList buildLiteralList(List<HInstruction> inputs) {
-    return new HLiteralList(inputs, commonMasks.extendableArrayType);
+    return new HLiteralList(inputs, commonMasks.growableListType);
   }
 
   HInstruction callSetRuntimeTypeInfoWithTypeArguments(
@@ -262,7 +266,7 @@
         TypeInfoExpressionKind.INSTANCE,
         closedWorld.elementEnvironment.getThisType(type.element),
         rtiInputs,
-        closedWorld.commonMasks.dynamicType);
+        closedWorld.abstractValueDomain.dynamicType);
     add(typeInfo);
     return callSetRuntimeTypeInfo(typeInfo, newObject, sourceInformation);
   }
diff --git a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
index cb2948a..11b98a0 100644
--- a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
+++ b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
@@ -7,6 +7,7 @@
 import '../constants/values.dart';
 import '../elements/entities.dart';
 import '../js_backend/interceptor_data.dart';
+import '../types/abstract_value_domain.dart';
 import '../types/types.dart';
 import '../universe/selector.dart' show Selector;
 import '../world.dart' show ClosedWorld;
@@ -48,6 +49,9 @@
 
   InterceptorData get interceptorData => closedWorld.interceptorData;
 
+  AbstractValueDomain get _abstractValueDomain =>
+      closedWorld.abstractValueDomain;
+
   void visitGraph(HGraph graph) {
     this.graph = graph;
     visitDominatorTree(graph);
@@ -94,11 +98,11 @@
 
   bool canUseSelfForInterceptor(
       HInstruction receiver, Set<ClassEntity> interceptedClasses) {
-    if (receiver.canBePrimitive(closedWorld)) {
+    if (receiver.canBePrimitive(_abstractValueDomain)) {
       // Primitives always need interceptors.
       return false;
     }
-    if (receiver.canBeNull() &&
+    if (receiver.canBeNull(_abstractValueDomain) &&
         interceptedClasses.contains(_commonElements.jsNullClass)) {
       // Need the JSNull interceptor.
       return false;
@@ -312,14 +316,15 @@
     // `NoSuchMethodError`s, and if the receiver was not null we would have a
     // constant interceptor `C`.  Then we can use `(receiver && C)` for the
     // interceptor.
-    if (receiver.canBeNull()) {
+    if (receiver.canBeNull(_abstractValueDomain)) {
       if (!interceptedClasses.contains(_commonElements.jsNullClass)) {
         // Can use `(receiver && C)` only if receiver is either null or truthy.
-        if (!(receiver.canBePrimitiveNumber(closedWorld) ||
-            receiver.canBePrimitiveBoolean(closedWorld) ||
-            receiver.canBePrimitiveString(closedWorld))) {
+        if (!(receiver.canBePrimitiveNumber(_abstractValueDomain) ||
+            receiver.canBePrimitiveBoolean(_abstractValueDomain) ||
+            receiver.canBePrimitiveString(_abstractValueDomain))) {
           ClassEntity interceptorClass = tryComputeConstantInterceptorFromType(
-              receiver.instructionType.nonNullable(), interceptedClasses);
+              _abstractValueDomain.excludeNull(receiver.instructionType),
+              interceptedClasses);
           if (interceptorClass != null) {
             HInstruction constantInstruction = graph.addConstant(
                 new InterceptorConstantValue(interceptorClass), closedWorld);
@@ -365,6 +370,7 @@
         List<HInstruction> inputs = new List<HInstruction>.from(user.inputs);
         inputs[0] = nullConstant;
         HOneShotInterceptor oneShotInterceptor = new HOneShotInterceptor(
+            closedWorld.abstractValueDomain,
             user.selector,
             user.mask,
             inputs,
diff --git a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
index f02b6c6..b37002d 100644
--- a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
+++ b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
@@ -124,8 +124,10 @@
       ClosedWorld closedWorld) {
     HInstruction receiver = instruction.inputs[1];
     HInstruction index = instruction.inputs[2];
-    if (!receiver.isMutableIndexable(closedWorld)) return null;
-    if (!index.isInteger(closedWorld) && options.enableTypeAssertions) {
+    if (!receiver.isMutableIndexable(closedWorld.abstractValueDomain))
+      return null;
+    if (!index.isInteger(closedWorld.abstractValueDomain) &&
+        options.enableTypeAssertions) {
       // We want the right checked mode error.
       return null;
     }
@@ -153,9 +155,9 @@
     if (instruction.element != null) {
       ClassEntity cls = instruction.element.enclosingClass;
       if (cls == commonElements.typedArrayOfIntClass) {
-        return value.isInteger(closedWorld);
+        return value.isInteger(closedWorld.abstractValueDomain);
       } else if (cls == commonElements.typedArrayOfDoubleClass) {
-        return value.isNumber(closedWorld);
+        return value.isNumber(closedWorld.abstractValueDomain);
       }
     }
 
@@ -181,8 +183,9 @@
       CompilerOptions options,
       CommonElements commonElements,
       ClosedWorld closedWorld) {
-    if (!instruction.inputs[1].isIndexablePrimitive(closedWorld)) return null;
-    if (!instruction.inputs[2].isInteger(closedWorld) &&
+    if (!instruction.inputs[1]
+        .isIndexablePrimitive(closedWorld.abstractValueDomain)) return null;
+    if (!instruction.inputs[2].isInteger(closedWorld.abstractValueDomain) &&
         options.enableTypeAssertions) {
       // We want the right checked mode error.
       return null;
@@ -210,8 +213,9 @@
       ClosedWorld closedWorld) {
     // All bitwise operations on primitive types either produce an
     // integer or throw an error.
-    if (instruction.inputs[1].isPrimitiveOrNull(closedWorld)) {
-      return closedWorld.commonMasks.uint32Type;
+    if (instruction.inputs[1]
+        .isPrimitiveOrNull(closedWorld.abstractValueDomain)) {
+      return closedWorld.abstractValueDomain.uint32Type;
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
@@ -225,7 +229,7 @@
       CommonElements commonElements,
       ClosedWorld closedWorld) {
     HInstruction input = instruction.inputs[1];
-    if (input.isNumber(closedWorld)) {
+    if (input.isNumber(closedWorld.abstractValueDomain)) {
       return new HBitNot(
           input,
           instruction.selector,
@@ -249,16 +253,16 @@
       CompilerOptions options,
       ClosedWorld closedWorld) {
     HInstruction operand = instruction.inputs[1];
-    if (operand.isNumberOrNull(closedWorld)) {
+    if (operand.isNumberOrNull(closedWorld.abstractValueDomain)) {
       // We have integer subclasses that represent ranges, so widen any int
       // subclass to full integer.
-      if (operand.isIntegerOrNull(closedWorld)) {
-        return closedWorld.commonMasks.intType;
+      if (operand.isIntegerOrNull(closedWorld.abstractValueDomain)) {
+        return closedWorld.abstractValueDomain.intType;
       }
-      if (operand.isDoubleOrNull(closedWorld)) {
-        return closedWorld.commonMasks.doubleType;
+      if (operand.isDoubleOrNull(closedWorld.abstractValueDomain)) {
+        return closedWorld.abstractValueDomain.doubleType;
       }
-      return closedWorld.commonMasks.numType;
+      return closedWorld.abstractValueDomain.numType;
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
@@ -272,7 +276,7 @@
       CommonElements commonElements,
       ClosedWorld closedWorld) {
     HInstruction input = instruction.inputs[1];
-    if (input.isNumber(closedWorld)) {
+    if (input.isNumber(closedWorld.abstractValueDomain)) {
       return new HNegate(
           input,
           instruction.selector,
@@ -296,8 +300,8 @@
       CompilerOptions options,
       ClosedWorld closedWorld) {
     HInstruction input = instruction.inputs[1];
-    if (input.isNumberOrNull(closedWorld)) {
-      return input.instructionType.nonNullable();
+    if (input.isNumberOrNull(closedWorld.abstractValueDomain)) {
+      return closedWorld.abstractValueDomain.excludeNull(input.instructionType);
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
@@ -311,7 +315,7 @@
       CommonElements commonElements,
       ClosedWorld closedWorld) {
     HInstruction input = instruction.inputs[1];
-    if (input.isNumber(closedWorld)) {
+    if (input.isNumber(closedWorld.abstractValueDomain)) {
       return new HAbs(
           input,
           instruction.selector,
@@ -332,24 +336,24 @@
       ClosedWorld closedWorld) {
     HInstruction left = instruction.inputs[1];
     HInstruction right = instruction.inputs[2];
-    if (left.isIntegerOrNull(closedWorld) &&
-        right.isIntegerOrNull(closedWorld)) {
-      return closedWorld.commonMasks.intType;
+    if (left.isIntegerOrNull(closedWorld.abstractValueDomain) &&
+        right.isIntegerOrNull(closedWorld.abstractValueDomain)) {
+      return closedWorld.abstractValueDomain.intType;
     }
-    if (left.isNumberOrNull(closedWorld)) {
-      if (left.isDoubleOrNull(closedWorld) ||
-          right.isDoubleOrNull(closedWorld)) {
-        return closedWorld.commonMasks.doubleType;
+    if (left.isNumberOrNull(closedWorld.abstractValueDomain)) {
+      if (left.isDoubleOrNull(closedWorld.abstractValueDomain) ||
+          right.isDoubleOrNull(closedWorld.abstractValueDomain)) {
+        return closedWorld.abstractValueDomain.doubleType;
       }
-      return closedWorld.commonMasks.numType;
+      return closedWorld.abstractValueDomain.numType;
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
   }
 
   bool isBuiltin(HInvokeDynamic instruction, ClosedWorld closedWorld) {
-    return instruction.inputs[1].isNumber(closedWorld) &&
-        instruction.inputs[2].isNumber(closedWorld);
+    return instruction.inputs[1].isNumber(closedWorld.abstractValueDomain) &&
+        instruction.inputs[2].isNumber(closedWorld.abstractValueDomain);
   }
 
   HInstruction tryConvertToBuiltin(
@@ -375,14 +379,15 @@
       HInstruction instruction, ClosedWorld closedWorld) {
     HInstruction left = instruction.inputs[1];
     HInstruction right = instruction.inputs[2];
-    return left.isPositiveIntegerOrNull(closedWorld) &&
-        right.isPositiveIntegerOrNull(closedWorld);
+    return left.isPositiveIntegerOrNull(closedWorld.abstractValueDomain) &&
+        right.isPositiveIntegerOrNull(closedWorld.abstractValueDomain);
   }
 
   bool inputsAreUInt31(HInstruction instruction, ClosedWorld closedWorld) {
     HInstruction left = instruction.inputs[1];
     HInstruction right = instruction.inputs[2];
-    return left.isUInt31(closedWorld) && right.isUInt31(closedWorld);
+    return left.isUInt31(closedWorld.abstractValueDomain) &&
+        right.isUInt31(closedWorld.abstractValueDomain);
   }
 
   HInstruction newBuiltinVariant(
@@ -401,10 +406,10 @@
       CompilerOptions options,
       ClosedWorld closedWorld) {
     if (inputsAreUInt31(instruction, closedWorld)) {
-      return closedWorld.commonMasks.uint32Type;
+      return closedWorld.abstractValueDomain.uint32Type;
     }
     if (inputsArePositiveIntegers(instruction, closedWorld)) {
-      return closedWorld.commonMasks.positiveIntType;
+      return closedWorld.abstractValueDomain.positiveIntType;
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
@@ -440,8 +445,8 @@
       CompilerOptions options,
       ClosedWorld closedWorld) {
     HInstruction left = instruction.inputs[1];
-    if (left.isNumberOrNull(closedWorld)) {
-      return closedWorld.commonMasks.doubleType;
+    if (left.isNumberOrNull(closedWorld.abstractValueDomain)) {
+      return closedWorld.abstractValueDomain.doubleType;
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
@@ -453,7 +458,7 @@
       CompilerOptions options,
       ClosedWorld closedWorld) {
     return new HDivide(instruction.inputs[1], instruction.inputs[2],
-        instruction.selector, closedWorld.commonMasks.doubleType);
+        instruction.selector, closedWorld.abstractValueDomain.doubleType);
   }
 }
 
@@ -466,7 +471,7 @@
       CompilerOptions options,
       ClosedWorld closedWorld) {
     if (inputsArePositiveIntegers(instruction, closedWorld)) {
-      return closedWorld.commonMasks.positiveIntType;
+      return closedWorld.abstractValueDomain.positiveIntType;
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
@@ -552,7 +557,7 @@
       CompilerOptions options,
       ClosedWorld closedWorld) {
     if (inputsArePositiveIntegers(instruction, closedWorld)) {
-      return closedWorld.commonMasks.positiveIntType;
+      return closedWorld.abstractValueDomain.positiveIntType;
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
@@ -588,7 +593,7 @@
       CompilerOptions options,
       ClosedWorld closedWorld) {
     if (inputsArePositiveIntegers(instruction, closedWorld)) {
-      return closedWorld.commonMasks.positiveIntType;
+      return closedWorld.abstractValueDomain.positiveIntType;
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
@@ -640,10 +645,10 @@
       CompilerOptions options,
       ClosedWorld closedWorld) {
     if (hasUint31Result(instruction, closedWorld)) {
-      return closedWorld.commonMasks.uint31Type;
+      return closedWorld.abstractValueDomain.uint31Type;
     }
     if (inputsArePositiveIntegers(instruction, closedWorld)) {
-      return closedWorld.commonMasks.positiveIntType;
+      return closedWorld.abstractValueDomain.positiveIntType;
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
@@ -668,11 +673,12 @@
   bool hasUint31Result(HInstruction instruction, ClosedWorld closedWorld) {
     HInstruction left = instruction.inputs[1];
     HInstruction right = instruction.inputs[2];
-    if (right.isPositiveInteger(closedWorld)) {
-      if (left.isUInt31(closedWorld) && isNotZero(right)) {
+    if (right.isPositiveInteger(closedWorld.abstractValueDomain)) {
+      if (left.isUInt31(closedWorld.abstractValueDomain) && isNotZero(right)) {
         return true;
       }
-      if (left.isUInt32(closedWorld) && isTwoOrGreater(right)) {
+      if (left.isUInt32(closedWorld.abstractValueDomain) &&
+          isTwoOrGreater(right)) {
         return true;
       }
     }
@@ -688,7 +694,8 @@
       ClosedWorld closedWorld) {
     HInstruction right = instruction.inputs[2];
     if (isBuiltin(instruction, closedWorld)) {
-      if (right.isPositiveInteger(closedWorld) && isNotZero(right)) {
+      if (right.isPositiveInteger(closedWorld.abstractValueDomain) &&
+          isNotZero(right)) {
         if (hasUint31Result(instruction, closedWorld)) {
           return newBuiltinVariant(instruction, results, options, closedWorld);
         }
@@ -726,8 +733,8 @@
     // All bitwise operations on primitive types either produce an
     // integer or throw an error.
     HInstruction left = instruction.inputs[1];
-    if (left.isPrimitiveOrNull(closedWorld)) {
-      return closedWorld.commonMasks.uint32Type;
+    if (left.isPrimitiveOrNull(closedWorld.abstractValueDomain)) {
+      return closedWorld.abstractValueDomain.uint32Type;
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
@@ -756,7 +763,7 @@
   bool isPositive(HInstruction instruction, ClosedWorld closedWorld) {
     // TODO: We should use the value range analysis. Currently, ranges
     // are discarded just after the analysis.
-    return instruction.isPositiveInteger(closedWorld);
+    return instruction.isPositiveInteger(closedWorld.abstractValueDomain);
   }
 }
 
@@ -776,7 +783,7 @@
       ClosedWorld closedWorld) {
     HInstruction left = instruction.inputs[1];
     HInstruction right = instruction.inputs[2];
-    if (left.isNumber(closedWorld)) {
+    if (left.isNumber(closedWorld.abstractValueDomain)) {
       if (argumentLessThan32(right)) {
         return newBuiltinVariant(instruction, results, options, closedWorld);
       }
@@ -814,7 +821,8 @@
       CompilerOptions options,
       ClosedWorld closedWorld) {
     HInstruction left = instruction.inputs[1];
-    if (left.isUInt32(closedWorld)) return left.instructionType;
+    if (left.isUInt32(closedWorld.abstractValueDomain))
+      return left.instructionType;
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
   }
@@ -828,7 +836,7 @@
       ClosedWorld closedWorld) {
     HInstruction left = instruction.inputs[1];
     HInstruction right = instruction.inputs[2];
-    if (left.isNumber(closedWorld)) {
+    if (left.isNumber(closedWorld.abstractValueDomain)) {
       if (argumentLessThan32(right) && isPositive(left, closedWorld)) {
         return newBuiltinVariant(instruction, results, options, closedWorld);
       }
@@ -839,7 +847,8 @@
       if (isPositive(right, closedWorld) && isPositive(left, closedWorld)) {
         instruction.selector = renameToOptimizedSelector(
             '_shrBothPositive', instruction.selector, commonElements);
-      } else if (isPositive(left, closedWorld) && right.isNumber(closedWorld)) {
+      } else if (isPositive(left, closedWorld) &&
+          right.isNumber(closedWorld.abstractValueDomain)) {
         instruction.selector = renameToOptimizedSelector(
             '_shrReceiverPositive', instruction.selector, commonElements);
       } else if (isPositive(right, closedWorld)) {
@@ -881,8 +890,9 @@
       ClosedWorld closedWorld) {
     HInstruction left = instruction.inputs[1];
     HInstruction right = instruction.inputs[2];
-    if (left.isUInt31(closedWorld) && right.isUInt31(closedWorld)) {
-      return closedWorld.commonMasks.uint31Type;
+    if (left.isUInt31(closedWorld.abstractValueDomain) &&
+        right.isUInt31(closedWorld.abstractValueDomain)) {
+      return closedWorld.abstractValueDomain.uint31Type;
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
@@ -915,9 +925,10 @@
       ClosedWorld closedWorld) {
     HInstruction left = instruction.inputs[1];
     HInstruction right = instruction.inputs[2];
-    if (left.isPrimitiveOrNull(closedWorld) &&
-        (left.isUInt31(closedWorld) || right.isUInt31(closedWorld))) {
-      return closedWorld.commonMasks.uint31Type;
+    if (left.isPrimitiveOrNull(closedWorld.abstractValueDomain) &&
+        (left.isUInt31(closedWorld.abstractValueDomain) ||
+            right.isUInt31(closedWorld.abstractValueDomain))) {
+      return closedWorld.abstractValueDomain.uint31Type;
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
@@ -950,8 +961,9 @@
       ClosedWorld closedWorld) {
     HInstruction left = instruction.inputs[1];
     HInstruction right = instruction.inputs[2];
-    if (left.isUInt31(closedWorld) && right.isUInt31(closedWorld)) {
-      return closedWorld.commonMasks.uint31Type;
+    if (left.isUInt31(closedWorld.abstractValueDomain) &&
+        right.isUInt31(closedWorld.abstractValueDomain)) {
+      return closedWorld.abstractValueDomain.uint31Type;
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
@@ -978,8 +990,9 @@
       GlobalTypeInferenceResults results,
       CompilerOptions options,
       ClosedWorld closedWorld) {
-    if (instruction.inputs[1].isPrimitiveOrNull(closedWorld)) {
-      return closedWorld.commonMasks.boolType;
+    if (instruction.inputs[1]
+        .isPrimitiveOrNull(closedWorld.abstractValueDomain)) {
+      return closedWorld.abstractValueDomain.boolType;
     }
     return super
         .computeTypeFromInputTypes(instruction, results, options, closedWorld);
@@ -994,7 +1007,8 @@
       ClosedWorld closedWorld) {
     HInstruction left = instruction.inputs[1];
     HInstruction right = instruction.inputs[2];
-    if (left.isNumber(closedWorld) && right.isNumber(closedWorld)) {
+    if (left.isNumber(closedWorld.abstractValueDomain) &&
+        right.isNumber(closedWorld.abstractValueDomain)) {
       return newBuiltinVariant(instruction, closedWorld);
     }
     return null;
@@ -1017,7 +1031,8 @@
     HInstruction left = instruction.inputs[1];
     HInstruction right = instruction.inputs[2];
     TypeMask instructionType = left.instructionType;
-    if (right.isConstantNull() || left.isPrimitiveOrNull(closedWorld)) {
+    if (right.isConstantNull() ||
+        left.isPrimitiveOrNull(closedWorld.abstractValueDomain)) {
       return newBuiltinVariant(instruction, closedWorld);
     }
     if (closedWorld.includesClosureCall(
@@ -1043,7 +1058,7 @@
   HInstruction newBuiltinVariant(
       HInvokeDynamic instruction, ClosedWorld closedWorld) {
     return new HIdentity(instruction.inputs[1], instruction.inputs[2],
-        instruction.selector, closedWorld.commonMasks.boolType);
+        instruction.selector, closedWorld.abstractValueDomain.boolType);
   }
 }
 
@@ -1057,7 +1072,7 @@
   HInstruction newBuiltinVariant(
       HInvokeDynamic instruction, ClosedWorld closedWorld) {
     return new HLess(instruction.inputs[1], instruction.inputs[2],
-        instruction.selector, closedWorld.commonMasks.boolType);
+        instruction.selector, closedWorld.abstractValueDomain.boolType);
   }
 }
 
@@ -1071,7 +1086,7 @@
   HInstruction newBuiltinVariant(
       HInvokeDynamic instruction, ClosedWorld closedWorld) {
     return new HGreater(instruction.inputs[1], instruction.inputs[2],
-        instruction.selector, closedWorld.commonMasks.boolType);
+        instruction.selector, closedWorld.abstractValueDomain.boolType);
   }
 }
 
@@ -1085,7 +1100,7 @@
   HInstruction newBuiltinVariant(
       HInvokeDynamic instruction, ClosedWorld closedWorld) {
     return new HGreaterEqual(instruction.inputs[1], instruction.inputs[2],
-        instruction.selector, closedWorld.commonMasks.boolType);
+        instruction.selector, closedWorld.abstractValueDomain.boolType);
   }
 }
 
@@ -1099,7 +1114,7 @@
   HInstruction newBuiltinVariant(
       HInvokeDynamic instruction, ClosedWorld closedWorld) {
     return new HLessEqual(instruction.inputs[1], instruction.inputs[2],
-        instruction.selector, closedWorld.commonMasks.boolType);
+        instruction.selector, closedWorld.abstractValueDomain.boolType);
   }
 }
 
@@ -1120,12 +1135,13 @@
     // TODO(sra): Implement a builtin HCodeUnitAt instruction and the same index
     // bounds checking optimizations as for HIndex.
     HInstruction receiver = instruction.getDartReceiver(closedWorld);
-    if (receiver.isStringOrNull(closedWorld)) {
+    if (receiver.isStringOrNull(closedWorld.abstractValueDomain)) {
       // Even if there is no builtin equivalent instruction, we know
       // String.codeUnitAt does not have any side effect (other than throwing),
       // and that it can be GVN'ed.
       clearAllSideEffects(instruction);
-      if (instruction.inputs.last.isPositiveInteger(closedWorld)) {
+      if (instruction.inputs.last
+          .isPositiveInteger(closedWorld.abstractValueDomain)) {
         instruction.selector = renameToOptimizedSelector(
             '_codeUnitAt', instruction.selector, commonElements);
       }
@@ -1147,13 +1163,15 @@
     HInstruction receiver = instruction.getDartReceiver(closedWorld);
     // `compareTo` has no side-effect (other than throwing) and can be GVN'ed
     // for some known types.
-    if (receiver.isStringOrNull(closedWorld) ||
-        receiver.isNumberOrNull(closedWorld)) {
+    if (receiver.isStringOrNull(closedWorld.abstractValueDomain) ||
+        receiver.isNumberOrNull(closedWorld.abstractValueDomain)) {
       // Replace `a.compareTo(a)` with `0`, but only if receiver and argument
       // are such that no exceptions can be thrown.
       HInstruction argument = instruction.inputs.last;
-      if ((receiver.isNumber(closedWorld) && argument.isNumber(closedWorld)) ||
-          (receiver.isString(closedWorld) && argument.isString(closedWorld))) {
+      if ((receiver.isNumber(closedWorld.abstractValueDomain) &&
+              argument.isNumber(closedWorld.abstractValueDomain)) ||
+          (receiver.isString(closedWorld.abstractValueDomain) &&
+              argument.isString(closedWorld.abstractValueDomain))) {
         if (identical(receiver.nonCheck(), argument.nonCheck())) {
           return graph.addConstantInt(0, closedWorld);
         }
@@ -1175,7 +1193,7 @@
       CommonElements commonElements,
       ClosedWorld closedWorld) {
     HInstruction receiver = instruction.getDartReceiver(closedWorld);
-    if (receiver.isStringOrNull(closedWorld)) {
+    if (receiver.isStringOrNull(closedWorld.abstractValueDomain)) {
       // String.xxx does not have any side effect (other than throwing), and it
       // can be GVN'ed.
       clearAllSideEffects(instruction);
@@ -1204,8 +1222,8 @@
       ClosedWorld closedWorld) {
     HInstruction receiver = instruction.getDartReceiver(closedWorld);
     HInstruction pattern = instruction.inputs[2];
-    if (receiver.isStringOrNull(closedWorld) &&
-        pattern.isStringOrNull(closedWorld)) {
+    if (receiver.isStringOrNull(closedWorld.abstractValueDomain) &&
+        pattern.isStringOrNull(closedWorld.abstractValueDomain)) {
       // String.contains(String s) does not have any side effect (other than
       // throwing), and it can be GVN'ed.
       clearAllSideEffects(instruction);
@@ -1229,7 +1247,7 @@
       CommonElements commonElements,
       ClosedWorld closedWorld) {
     HInstruction receiver = instruction.getDartReceiver(closedWorld);
-    if (receiver.isNumberOrNull(closedWorld)) {
+    if (receiver.isNumberOrNull(closedWorld.abstractValueDomain)) {
       // Even if there is no builtin equivalent instruction, we know the
       // instruction does not have any side effect, and that it can be GVN'ed.
       clearAllSideEffects(instruction);
diff --git a/pkg/compiler/lib/src/ssa/kernel_string_builder.dart b/pkg/compiler/lib/src/ssa/kernel_string_builder.dart
index 090d77b..92e4b4d 100644
--- a/pkg/compiler/lib/src/ssa/kernel_string_builder.dart
+++ b/pkg/compiler/lib/src/ssa/kernel_string_builder.dart
@@ -33,7 +33,7 @@
     //      conversions.
     //   2. The value can be primitive, because the library stringifier has
     //      fast-path code for most primitives.
-    if (expression.canBePrimitive(builder.closedWorld)) {
+    if (expression.canBePrimitive(builder.abstractValueDomain)) {
       append(stringify(expression));
       return;
     }
diff --git a/pkg/compiler/lib/src/ssa/locals_handler.dart b/pkg/compiler/lib/src/ssa/locals_handler.dart
index 6626095..4922a85 100644
--- a/pkg/compiler/lib/src/ssa/locals_handler.dart
+++ b/pkg/compiler/lib/src/ssa/locals_handler.dart
@@ -68,7 +68,7 @@
 
   ClosedWorld get closedWorld => builder.closedWorld;
 
-  CommonMasks get commonMasks => closedWorld.commonMasks;
+  CommonMasks get commonMasks => closedWorld.abstractValueDomain;
 
   GlobalTypeInferenceResults get _globalInferenceResults =>
       builder.globalInferenceResults;
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index fbe8edd..984a270 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -4,7 +4,6 @@
 
 import '../closure.dart';
 import '../common.dart';
-import '../common_elements.dart' show CommonElements;
 import '../compiler.dart' show Compiler;
 import '../constants/constant_system.dart';
 import '../constants/values.dart';
@@ -17,6 +16,7 @@
 import '../js_backend/js_backend.dart';
 import '../native/native.dart' as native;
 import '../types/constants.dart' show computeTypeMask;
+import '../types/abstract_value_domain.dart';
 import '../types/types.dart';
 import '../universe/selector.dart' show Selector;
 import '../universe/side_effects.dart' show SideEffects;
@@ -975,225 +975,118 @@
    * effect, nor any dependency. They can be moved anywhere in the
    * graph.
    */
-  bool isPure() {
+  bool isPure(AbstractValueDomain domain) {
     return !sideEffects.hasSideEffects() &&
         !sideEffects.dependsOnSomething() &&
-        !canThrow();
+        !canThrow(domain);
   }
 
   /// An instruction is an 'allocation' is it is the sole alias for an object.
   /// This applies to instructions that allocate new objects and can be extended
   /// to methods that return other allocations without escaping them.
-  bool get isAllocation => false;
+  bool isAllocation(AbstractValueDomain domain) => false;
 
   /// Overridden by [HCheck] to return the actual non-[HCheck]
   /// instruction it checks against.
   HInstruction nonCheck() => this;
 
   /// Can this node throw an exception?
-  bool canThrow() => false;
+  bool canThrow(AbstractValueDomain domain) => false;
 
   /// Does this node potentially affect control flow.
   bool isControlFlow() => false;
 
-  bool isExact() => instructionType.isExact || isNull();
+  bool isExact(AbstractValueDomain domain) => domain.isExact(instructionType);
 
-  bool isValue() => instructionType.isValue;
+  bool isValue(AbstractValueDomain domain) => domain.isValue(instructionType);
 
-  bool canBeNull() => instructionType.isNullable;
+  bool canBeNull(AbstractValueDomain domain) =>
+      domain.canBeNull(instructionType);
 
-  bool isNull() => instructionType.isNull;
+  bool isNull(AbstractValueDomain domain) => domain.isNull(instructionType);
 
-  bool isConflicting() => instructionType.isEmpty;
+  bool isConflicting(AbstractValueDomain domain) =>
+      domain.isEmpty(instructionType);
 
-  /// Returns `true` if [typeMask] contains [cls].
-  static bool containsType(
-      TypeMask typeMask, ClassEntity cls, ClosedWorld closedWorld) {
-    return closedWorld.isInstantiated(cls) &&
-        typeMask.contains(cls, closedWorld);
-  }
+  bool canBePrimitive(AbstractValueDomain domain) =>
+      domain.canBePrimitive(instructionType);
 
-  /// Returns `true` if [typeMask] contains only [cls].
-  static bool containsOnlyType(
-      TypeMask typeMask, ClassEntity cls, ClosedWorld closedWorld) {
-    return closedWorld.isInstantiated(cls) && typeMask.containsOnly(cls);
-  }
+  bool canBePrimitiveNumber(AbstractValueDomain domain) =>
+      domain.canBePrimitiveNumber(instructionType);
 
-  /// Returns `true` if [typeMask] is an instance of [cls].
-  static bool isInstanceOf(
-      TypeMask typeMask, ClassEntity cls, ClosedWorld closedWorld) {
-    return closedWorld.isImplemented(cls) &&
-        typeMask.satisfies(cls, closedWorld);
-  }
+  bool canBePrimitiveBoolean(AbstractValueDomain domain) =>
+      domain.canBePrimitiveBoolean(instructionType);
 
-  bool canBePrimitive(ClosedWorld closedWorld) {
-    return canBePrimitiveNumber(closedWorld) ||
-        canBePrimitiveArray(closedWorld) ||
-        canBePrimitiveBoolean(closedWorld) ||
-        canBePrimitiveString(closedWorld) ||
-        isNull();
-  }
+  bool canBePrimitiveArray(AbstractValueDomain domain) =>
+      domain.canBePrimitiveArray(instructionType);
 
-  bool canBePrimitiveNumber(ClosedWorld closedWorld) {
-    CommonElements commonElements = closedWorld.commonElements;
-    // TODO(sra): It should be possible to test only jsDoubleClass and
-    // jsUInt31Class, since all others are superclasses of these two.
-    return containsType(
-            instructionType, commonElements.jsNumberClass, closedWorld) ||
-        containsType(instructionType, commonElements.jsIntClass, closedWorld) ||
-        containsType(
-            instructionType, commonElements.jsPositiveIntClass, closedWorld) ||
-        containsType(
-            instructionType, commonElements.jsUInt32Class, closedWorld) ||
-        containsType(
-            instructionType, commonElements.jsUInt31Class, closedWorld) ||
-        containsType(
-            instructionType, commonElements.jsDoubleClass, closedWorld);
-  }
+  bool isIndexablePrimitive(AbstractValueDomain domain) =>
+      domain.isIndexablePrimitive(instructionType);
 
-  bool canBePrimitiveBoolean(ClosedWorld closedWorld) {
-    return containsType(
-        instructionType, closedWorld.commonElements.jsBoolClass, closedWorld);
-  }
+  bool isFixedArray(AbstractValueDomain domain) =>
+      domain.isFixedArray(instructionType);
 
-  bool canBePrimitiveArray(ClosedWorld closedWorld) {
-    CommonElements commonElements = closedWorld.commonElements;
-    return containsType(
-            instructionType, commonElements.jsArrayClass, closedWorld) ||
-        containsType(
-            instructionType, commonElements.jsFixedArrayClass, closedWorld) ||
-        containsType(instructionType, commonElements.jsExtendableArrayClass,
-            closedWorld) ||
-        containsType(instructionType, commonElements.jsUnmodifiableArrayClass,
-            closedWorld);
-  }
+  bool isExtendableArray(AbstractValueDomain domain) =>
+      domain.isExtendableArray(instructionType);
 
-  bool isIndexablePrimitive(ClosedWorld closedWorld) {
-    return instructionType.containsOnlyString(closedWorld) ||
-        isInstanceOf(instructionType,
-            closedWorld.commonElements.jsIndexableClass, closedWorld);
-  }
+  bool isMutableArray(AbstractValueDomain domain) =>
+      domain.isMutableArray(instructionType);
 
-  bool isFixedArray(ClosedWorld closedWorld) {
-    CommonElements commonElements = closedWorld.commonElements;
-    // TODO(sra): Recognize the union of these types as well.
-    return containsOnlyType(
-            instructionType, commonElements.jsFixedArrayClass, closedWorld) ||
-        containsOnlyType(instructionType,
-            commonElements.jsUnmodifiableArrayClass, closedWorld);
-  }
+  bool isMutableIndexable(AbstractValueDomain domain) =>
+      domain.isMutableIndexable(instructionType);
 
-  bool isExtendableArray(ClosedWorld closedWorld) {
-    return containsOnlyType(instructionType,
-        closedWorld.commonElements.jsExtendableArrayClass, closedWorld);
-  }
+  bool isArray(AbstractValueDomain domain) => domain.isArray(instructionType);
 
-  bool isMutableArray(ClosedWorld closedWorld) {
-    return isInstanceOf(instructionType,
-        closedWorld.commonElements.jsMutableArrayClass, closedWorld);
-  }
+  bool canBePrimitiveString(AbstractValueDomain domain) =>
+      domain.canBePrimitiveString(instructionType);
 
-  bool isReadableArray(ClosedWorld closedWorld) {
-    return isInstanceOf(
-        instructionType, closedWorld.commonElements.jsArrayClass, closedWorld);
-  }
+  bool isInteger(AbstractValueDomain domain) =>
+      domain.isInteger(instructionType);
 
-  bool isMutableIndexable(ClosedWorld closedWorld) {
-    return isInstanceOf(instructionType,
-        closedWorld.commonElements.jsMutableIndexableClass, closedWorld);
-  }
+  bool isUInt32(AbstractValueDomain domain) => domain.isUInt32(instructionType);
 
-  bool isArray(ClosedWorld closedWorld) => isReadableArray(closedWorld);
+  bool isUInt31(AbstractValueDomain domain) => domain.isUInt31(instructionType);
 
-  bool canBePrimitiveString(ClosedWorld closedWorld) {
-    return containsType(
-        instructionType, closedWorld.commonElements.jsStringClass, closedWorld);
-  }
+  bool isPositiveInteger(AbstractValueDomain domain) =>
+      domain.isPositiveInteger(instructionType);
 
-  bool isInteger(ClosedWorld closedWorld) {
-    return instructionType.containsOnlyInt(closedWorld) &&
-        !instructionType.isNullable;
-  }
+  bool isPositiveIntegerOrNull(AbstractValueDomain domain) =>
+      domain.isPositiveIntegerOrNull(instructionType);
 
-  bool isUInt32(ClosedWorld closedWorld) {
-    return !instructionType.isNullable &&
-        isInstanceOf(instructionType, closedWorld.commonElements.jsUInt32Class,
-            closedWorld);
-  }
+  bool isIntegerOrNull(AbstractValueDomain domain) =>
+      domain.isIntegerOrNull(instructionType);
 
-  bool isUInt31(ClosedWorld closedWorld) {
-    return !instructionType.isNullable &&
-        isInstanceOf(instructionType, closedWorld.commonElements.jsUInt31Class,
-            closedWorld);
-  }
+  bool isNumber(AbstractValueDomain domain) => domain.isNumber(instructionType);
 
-  bool isPositiveInteger(ClosedWorld closedWorld) {
-    return !instructionType.isNullable &&
-        isInstanceOf(instructionType,
-            closedWorld.commonElements.jsPositiveIntClass, closedWorld);
-  }
+  bool isNumberOrNull(AbstractValueDomain domain) =>
+      domain.isNumberOrNull(instructionType);
 
-  bool isPositiveIntegerOrNull(ClosedWorld closedWorld) {
-    return isInstanceOf(instructionType,
-        closedWorld.commonElements.jsPositiveIntClass, closedWorld);
-  }
+  bool isDouble(AbstractValueDomain domain) => domain.isDouble(instructionType);
 
-  bool isIntegerOrNull(ClosedWorld closedWorld) {
-    return instructionType.containsOnlyInt(closedWorld);
-  }
+  bool isDoubleOrNull(AbstractValueDomain domain) =>
+      domain.isDoubleOrNull(instructionType);
 
-  bool isNumber(ClosedWorld closedWorld) {
-    return instructionType.containsOnlyNum(closedWorld) &&
-        !instructionType.isNullable;
-  }
+  bool isBoolean(AbstractValueDomain domain) =>
+      domain.isBoolean(instructionType);
 
-  bool isNumberOrNull(ClosedWorld closedWorld) {
-    return instructionType.containsOnlyNum(closedWorld);
-  }
+  bool isBooleanOrNull(AbstractValueDomain domain) =>
+      domain.isBooleanOrNull(instructionType);
 
-  bool isDouble(ClosedWorld closedWorld) {
-    return instructionType.containsOnlyDouble(closedWorld) &&
-        !instructionType.isNullable;
-  }
+  bool isString(AbstractValueDomain domain) => domain.isString(instructionType);
 
-  bool isDoubleOrNull(ClosedWorld closedWorld) {
-    return instructionType.containsOnlyDouble(closedWorld);
-  }
+  bool isStringOrNull(AbstractValueDomain domain) =>
+      domain.isStringOrNull(instructionType);
 
-  bool isBoolean(ClosedWorld closedWorld) {
-    return instructionType.containsOnlyBool(closedWorld) &&
-        !instructionType.isNullable;
-  }
+  bool isPrimitive(AbstractValueDomain domain) =>
+      domain.isPrimitive(instructionType);
 
-  bool isBooleanOrNull(ClosedWorld closedWorld) {
-    return instructionType.containsOnlyBool(closedWorld);
-  }
-
-  bool isString(ClosedWorld closedWorld) {
-    return instructionType.containsOnlyString(closedWorld) &&
-        !instructionType.isNullable;
-  }
-
-  bool isStringOrNull(ClosedWorld closedWorld) {
-    return instructionType.containsOnlyString(closedWorld);
-  }
-
-  bool isPrimitive(ClosedWorld closedWorld) {
-    return (isPrimitiveOrNull(closedWorld) && !instructionType.isNullable) ||
-        isNull();
-  }
-
-  bool isPrimitiveOrNull(ClosedWorld closedWorld) {
-    return isIndexablePrimitive(closedWorld) ||
-        isNumberOrNull(closedWorld) ||
-        isBooleanOrNull(closedWorld) ||
-        isNull();
-  }
+  bool isPrimitiveOrNull(AbstractValueDomain domain) =>
+      domain.isPrimitiveOrNull(instructionType);
 
   /**
    * Type of the instruction.
    */
-  TypeMask instructionType;
+  AbstractValue instructionType;
 
   Selector get selector => null;
   HInstruction getDartReceiver(ClosedWorld closedWorld) => null;
@@ -1367,13 +1260,13 @@
     if (type == closedWorld.commonElements.objectType) return this;
     if (type.isFunctionType || type.isMalformed || type.isFutureOr) {
       return new HTypeConversion(type, kind,
-          closedWorld.commonMasks.dynamicType, this, sourceInformation);
+          closedWorld.abstractValueDomain.dynamicType, this, sourceInformation);
     }
     assert(type.isInterfaceType);
     if (kind == HTypeConversion.BOOLEAN_CONVERSION_CHECK) {
       // Boolean conversion checks work on non-nullable booleans.
-      return new HTypeConversion(type, kind, closedWorld.commonMasks.boolType,
-          this, sourceInformation);
+      return new HTypeConversion(type, kind,
+          closedWorld.abstractValueDomain.boolType, this, sourceInformation);
     } else if (kind == HTypeConversion.CHECKED_MODE_CHECK && !type.treatAsRaw) {
       throw 'creating compound check to $type (this = ${this})';
     } else {
@@ -1586,7 +1479,7 @@
   }
   HInstruction get checkedInput => inputs[0];
   bool isJsStatement() => true;
-  bool canThrow() => true;
+  bool canThrow(AbstractValueDomain domain) => true;
 
   HInstruction nonCheck() => checkedInput.nonCheck();
 }
@@ -1657,7 +1550,7 @@
     this.sourceInformation = sourceInformation;
   }
 
-  bool get isAllocation => true;
+  bool isAllocation(AbstractValueDomain domain) => true;
 
   HInstruction get rtiInput {
     assert(hasRtiInput);
@@ -1673,7 +1566,7 @@
 class HCreateBox extends HInstruction {
   HCreateBox(TypeMask type) : super(<HInstruction>[], type);
 
-  bool get isAllocation => true;
+  bool isAllocation(AbstractValueDomain domain) => true;
 
   accept(HVisitor visitor) => visitor.visitCreateBox(this);
 
@@ -1681,7 +1574,7 @@
 }
 
 abstract class HInvoke extends HInstruction {
-  bool isAllocation = false;
+  bool _isAllocation = false;
 
   /// [isInterceptedCall] is true if this invocation uses the interceptor
   /// calling convention where the first input is the methods and the second
@@ -1692,7 +1585,11 @@
     sideEffects.setDependsOnSomething();
   }
   static const int ARGUMENTS_OFFSET = 1;
-  bool canThrow() => true;
+  bool canThrow(AbstractValueDomain domain) => true;
+  bool isAllocation(AbstractValueDomain domain) => _isAllocation;
+  void setAllocation(bool value) {
+    _isAllocation = value;
+  }
 }
 
 abstract class HInvokeDynamic extends HInvoke {
@@ -1799,7 +1696,8 @@
   List<DartType> get typeArguments => const <DartType>[];
 
   // There might be an interceptor input, so `inputs.last` is the dart receiver.
-  bool canThrow() => isTearOff ? inputs.last.canBeNull() : super.canThrow();
+  bool canThrow(AbstractValueDomain domain) =>
+      isTearOff ? inputs.last.canBeNull(domain) : super.canThrow(domain);
 
   String toString() => 'invoke dynamic getter: selector=$selector, mask=$mask';
 }
@@ -1832,7 +1730,7 @@
 
   final bool targetCanThrow;
 
-  bool canThrow() => targetCanThrow;
+  bool canThrow(AbstractValueDomain domain) => targetCanThrow;
 
   /// If this instruction is a call to a constructor, [instantiatedTypes]
   /// contains the type(s) used in the (Dart) `New` expression(s). The
@@ -1952,7 +1850,7 @@
     return false;
   }
 
-  bool canThrow() => receiver.canBeNull();
+  bool canThrow(AbstractValueDomain domain) => receiver.canBeNull(domain);
 
   HInstruction getDartReceiver(ClosedWorld closedWorld) => receiver;
   bool onlyThrowsNSM() => true;
@@ -1975,7 +1873,7 @@
     sideEffects.setChangesInstanceProperty();
   }
 
-  bool canThrow() => receiver.canBeNull();
+  bool canThrow(AbstractValueDomain domain) => receiver.canBeNull(domain);
 
   HInstruction getDartReceiver(ClosedWorld closedWorld) => receiver;
   bool onlyThrowsNSM() => true;
@@ -2002,7 +1900,7 @@
 
   HInstruction get receiver => inputs.single;
 
-  bool canThrow() => receiver.canBeNull();
+  bool canThrow(AbstractValueDomain domain) => receiver.canBeNull(domain);
 
   HInstruction getDartReceiver(ClosedWorld closedWorld) => receiver;
   bool onlyThrowsNSM() => true;
@@ -2055,7 +1953,7 @@
   bool get isPostOp => opKind == POST_OP;
   bool get isAssignOp => opKind == ASSIGN_OP;
 
-  bool canThrow() => receiver.canBeNull();
+  bool canThrow(AbstractValueDomain domain) => receiver.canBeNull(domain);
 
   HInstruction getDartReceiver(ClosedWorld closedWorld) => receiver;
   bool onlyThrowsNSM() => true;
@@ -2108,7 +2006,7 @@
   bool get isStatement => false;
   native.NativeBehavior get nativeBehavior => null;
 
-  bool canThrow() {
+  bool canThrow(AbstractValueDomain domain) {
     return sideEffects.hasSideEffects() || sideEffects.dependsOnSomething();
   }
 }
@@ -2156,9 +2054,9 @@
   accept(HVisitor visitor) => visitor.visitForeignCode(this);
 
   bool isJsStatement() => isStatement;
-  bool canThrow() {
+  bool canThrow(AbstractValueDomain domain) {
     if (inputs.length > 0) {
-      return inputs.first.canBeNull()
+      return inputs.first.canBeNull(domain)
           ? throwBehavior.canThrow
           : throwBehavior.onNonNull.canThrow;
     }
@@ -2167,8 +2065,10 @@
 
   bool onlyThrowsNSM() => throwBehavior.isOnlyNullNSMGuard;
 
-  bool get isAllocation =>
-      nativeBehavior != null && nativeBehavior.isAllocation && !canBeNull();
+  bool isAllocation(AbstractValueDomain domain) =>
+      nativeBehavior != null &&
+      nativeBehavior.isAllocation &&
+      !canBeNull(domain);
 
   int typeCode() => HInstruction.FOREIGN_CODE_TYPECODE;
   bool typeEquals(other) => other is HForeignCode;
@@ -2742,7 +2642,7 @@
   }
   toString() => 'throw expression';
   accept(HVisitor visitor) => visitor.visitThrowExpression(this);
-  bool canThrow() => true;
+  bool canThrow(AbstractValueDomain domain) => true;
 }
 
 class HAwait extends HInstruction {
@@ -2751,7 +2651,7 @@
   toString() => 'await';
   accept(HVisitor visitor) => visitor.visitAwait(this);
   // An await will throw if its argument is not a real future.
-  bool canThrow() => true;
+  bool canThrow(AbstractValueDomain domain) => true;
   SideEffects sideEffects = new SideEffects();
 }
 
@@ -2763,7 +2663,7 @@
   bool hasStar;
   toString() => 'yield';
   accept(HVisitor visitor) => visitor.visitYield(this);
-  bool canThrow() => false;
+  bool canThrow(AbstractValueDomain domain) => false;
   SideEffects sideEffects = new SideEffects();
 }
 
@@ -2858,6 +2758,7 @@
   List<DartType> typeArguments;
   Set<ClassEntity> interceptedClasses;
   HOneShotInterceptor(
+      AbstractValueDomain domain,
       Selector selector,
       TypeMask mask,
       List<HInstruction> inputs,
@@ -2866,7 +2767,7 @@
       this.interceptedClasses)
       : super(selector, mask, null, inputs, true, type) {
     assert(inputs[0] is HConstant);
-    assert(inputs[0].isNull());
+    assert(inputs[0].isNull(domain));
     assert(selector.callStructure.typeArgumentCount == typeArguments.length);
   }
   bool isCallOnInterceptor(ClosedWorld closedWorld) => true;
@@ -2894,7 +2795,7 @@
   int typeCode() => 30;
   // TODO(4931): can we do better here?
   bool isCodeMotionInvariant() => false;
-  bool canThrow() => true;
+  bool canThrow(AbstractValueDomain domain) => true;
 }
 
 class HStaticStore extends HInstruction {
@@ -2919,7 +2820,7 @@
   toString() => 'literal list';
   accept(HVisitor visitor) => visitor.visitLiteralList(this);
 
-  bool get isAllocation => true;
+  bool isAllocation(AbstractValueDomain domain) => true;
 }
 
 /**
@@ -2949,7 +2850,7 @@
 
   HInstruction getDartReceiver(ClosedWorld closedWorld) => receiver;
   bool onlyThrowsNSM() => true;
-  bool canThrow() => receiver.canBeNull();
+  bool canThrow(AbstractValueDomain domain) => receiver.canBeNull(domain);
 
   int typeCode() => HInstruction.INDEX_TYPECODE;
   bool typeEquals(HInstruction other) => other is HIndex;
@@ -2983,7 +2884,7 @@
 
   HInstruction getDartReceiver(ClosedWorld closedWorld) => receiver;
   bool onlyThrowsNSM() => true;
-  bool canThrow() => receiver.canBeNull();
+  bool canThrow(AbstractValueDomain domain) => receiver.canBeNull(domain);
 }
 
 class HIs extends HInstruction {
@@ -3222,7 +3123,7 @@
 
   bool isJsStatement() => false;
   bool isControlFlow() => false;
-  bool canThrow() => false;
+  bool canThrow(AbstractValueDomain domain) => false;
 
   bool get isPinned => inputs.length == 1;
 
@@ -3568,7 +3469,7 @@
 
   accept(HVisitor visitor) => visitor.visitTypeInfoReadRaw(this);
 
-  bool canThrow() => false;
+  bool canThrow(AbstractValueDomain domain) => false;
 
   int typeCode() => HInstruction.TYPE_INFO_READ_RAW_TYPECODE;
   bool typeEquals(HInstruction other) => other is HTypeInfoReadRaw;
@@ -3609,7 +3510,7 @@
 
   accept(HVisitor visitor) => visitor.visitTypeInfoReadVariable(this);
 
-  bool canThrow() => false;
+  bool canThrow(AbstractValueDomain domain) => false;
 
   int typeCode() => HInstruction.TYPE_INFO_READ_VARIABLE_TYPECODE;
   bool typeEquals(HInstruction other) => other is HTypeInfoReadVariable;
@@ -3684,7 +3585,7 @@
 
   accept(HVisitor visitor) => visitor.visitTypeInfoExpression(this);
 
-  bool canThrow() => false;
+  bool canThrow(AbstractValueDomain domain) => false;
 
   int typeCode() => HInstruction.TYPE_INFO_EXPRESSION_TYPECODE;
   bool typeEquals(HInstruction other) => other is HTypeInfoExpression;
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index dd8bea5..f3174f2 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -17,6 +17,7 @@
 import '../js_backend/runtime_types.dart';
 import '../native/native.dart' as native;
 import '../options.dart';
+import '../types/abstract_value_domain.dart';
 import '../types/types.dart';
 import '../universe/selector.dart' show Selector;
 import '../universe/side_effects.dart' show SideEffects;
@@ -88,12 +89,12 @@
         // Run a dead code eliminator before LICM because dead
         // interceptors are often in the way of LICM'able instructions.
         new SsaDeadCodeEliminator(closedWorld, this),
-        new SsaGlobalValueNumberer(),
+        new SsaGlobalValueNumberer(closedWorld.abstractValueDomain),
         // After GVN, some instructions might need their type to be
         // updated because they now have different inputs.
         new SsaTypePropagator(
             _results, _options, closedWorld.commonElements, closedWorld),
-        codeMotion = new SsaCodeMotion(),
+        codeMotion = new SsaCodeMotion(closedWorld.abstractValueDomain),
         loadElimination = new SsaLoadElimination(_compiler, closedWorld),
         new SsaRedundantPhiEliminator(),
         new SsaDeadPhiEliminator(),
@@ -126,8 +127,8 @@
         phases = <OptimizationPhase>[
           new SsaTypePropagator(
               _results, _options, closedWorld.commonElements, closedWorld),
-          new SsaGlobalValueNumberer(),
-          new SsaCodeMotion(),
+          new SsaGlobalValueNumberer(closedWorld.abstractValueDomain),
+          new SsaCodeMotion(closedWorld.abstractValueDomain),
           new SsaValueRangeAnalyzer(closedWorld, this),
           new SsaInstructionSimplifier(
               _results, _options, _rtiSubstitutions, closedWorld, registry),
@@ -163,7 +164,7 @@
   if (mask.containsOnly(closedWorld.commonElements.jsFixedArrayClass) ||
       mask.containsOnly(closedWorld.commonElements.jsUnmodifiableArrayClass) ||
       mask.containsOnlyString(closedWorld) ||
-      closedWorld.commonMasks.isTypedArray(mask)) {
+      closedWorld.abstractValueDomain.isTypedArray(mask)) {
     return true;
   }
   return false;
@@ -195,6 +196,9 @@
 
   ConstantSystem get constantSystem => _closedWorld.constantSystem;
 
+  AbstractValueDomain get _abstractValueDomain =>
+      _closedWorld.abstractValueDomain;
+
   NativeData get _nativeData => _closedWorld.nativeData;
 
   void visitGraph(HGraph visitee) {
@@ -215,12 +219,12 @@
         // might be that an operation thought to return double, can be
         // simplified to an int. For example:
         // `2.5 * 10`.
-        if (!(replacement.isNumberOrNull(_closedWorld) &&
-            instruction.isNumberOrNull(_closedWorld))) {
+        if (!(replacement.isNumberOrNull(_abstractValueDomain) &&
+            instruction.isNumberOrNull(_abstractValueDomain))) {
           // If we can replace [instruction] with [replacement], then
           // [replacement]'s type can be narrowed.
-          TypeMask newType = replacement.instructionType
-              .intersection(instruction.instructionType, _closedWorld);
+          TypeMask newType = _abstractValueDomain.intersection(
+              replacement.instructionType, instruction.instructionType);
           replacement.instructionType = newType;
         }
 
@@ -249,7 +253,8 @@
   }
 
   ConstantValue getConstantFromType(HInstruction node) {
-    if (node.isValue() && !node.canBeNull()) {
+    if (node.isValue(_abstractValueDomain) &&
+        !node.canBeNull(_abstractValueDomain)) {
       ValueTypeMask valueMask = node.instructionType;
       if (valueMask.value.isBool) {
         return valueMask.value;
@@ -299,13 +304,13 @@
     List<HInstruction> inputs = node.inputs;
     assert(inputs.length == 1);
     HInstruction input = inputs[0];
-    if (input.isBoolean(_closedWorld)) return input;
+    if (input.isBoolean(_abstractValueDomain)) return input;
 
     // If the code is unreachable, remove the HBoolify.  This can happen when
     // there is a throw expression in a short-circuit conditional.  Removing the
     // unreachable HBoolify makes it easier to reconstruct the short-circuit
     // operation.
-    if (input.instructionType.isEmpty) return input;
+    if (_abstractValueDomain.isEmpty(input.instructionType)) return input;
 
     // All values that cannot be 'true' are boolified to false.
     TypeMask mask = input.instructionType;
@@ -346,7 +351,7 @@
 
   HInstruction tryOptimizeLengthInterceptedGetter(HInvokeDynamic node) {
     HInstruction actualReceiver = node.inputs[1];
-    if (actualReceiver.isIndexablePrimitive(_closedWorld)) {
+    if (actualReceiver.isIndexablePrimitive(_abstractValueDomain)) {
       if (actualReceiver.isConstantString()) {
         HConstant constantInput = actualReceiver;
         StringConstantValue constant = constantInput.constant;
@@ -359,14 +364,14 @@
       bool isFixed =
           isFixedLength(actualReceiver.instructionType, _closedWorld);
       TypeMask actualType = node.instructionType;
-      TypeMask resultType = _closedWorld.commonMasks.positiveIntType;
+      TypeMask resultType = _abstractValueDomain.positiveIntType;
       // If we already have computed a more specific type, keep that type.
-      if (HInstruction.isInstanceOf(
-          actualType, commonElements.jsUInt31Class, _closedWorld)) {
-        resultType = _closedWorld.commonMasks.uint31Type;
-      } else if (HInstruction.isInstanceOf(
-          actualType, commonElements.jsUInt32Class, _closedWorld)) {
-        resultType = _closedWorld.commonMasks.uint32Type;
+      if (_abstractValueDomain.isInstanceOf(
+          actualType, commonElements.jsUInt31Class)) {
+        resultType = _abstractValueDomain.uint31Type;
+      } else if (_abstractValueDomain.isInstanceOf(
+          actualType, commonElements.jsUInt32Class)) {
+        resultType = _abstractValueDomain.uint32Type;
       }
       HGetLength result =
           new HGetLength(actualReceiver, resultType, isAssignable: !isFixed);
@@ -410,7 +415,7 @@
 
     if (selector.isCall || selector.isOperator) {
       FunctionEntity target;
-      if (input.isExtendableArray(_closedWorld)) {
+      if (input.isExtendableArray(_abstractValueDomain)) {
         if (applies(commonElements.jsArrayRemoveLast)) {
           target = commonElements.jsArrayRemoveLast;
         } else if (applies(commonElements.jsArrayAdd)) {
@@ -420,7 +425,7 @@
             target = commonElements.jsArrayAdd;
           }
         }
-      } else if (input.isStringOrNull(_closedWorld)) {
+      } else if (input.isStringOrNull(_abstractValueDomain)) {
         if (commonElements.appliesToJsStringSplit(
             selector, mask, _closedWorld)) {
           return handleStringSplit(node);
@@ -429,11 +434,12 @@
           // make sure the receiver and the argument are not null.
           // TODO(sra): Do this via [node.specializer].
           HInstruction argument = node.inputs[2];
-          if (argument.isString(_closedWorld) && !input.canBeNull()) {
+          if (argument.isString(_abstractValueDomain) &&
+              !input.canBeNull(_abstractValueDomain)) {
             return new HStringConcat(input, argument, node.instructionType);
           }
         } else if (applies(commonElements.jsStringToString) &&
-            !input.canBeNull()) {
+            !input.canBeNull(_abstractValueDomain)) {
           return input;
         }
       }
@@ -469,7 +475,7 @@
 
   HInstruction handleStringSplit(HInvokeDynamic node) {
     HInstruction argument = node.inputs[2];
-    if (!argument.isString(_closedWorld)) return node;
+    if (!argument.isString(_abstractValueDomain)) return node;
 
     // Replace `s.split$1(pattern)` with
     //
@@ -479,7 +485,7 @@
     //     t4 = setRuntimeTypeInfo(t1, t3);
     //
 
-    TypeMask resultMask = _closedWorld.commonMasks.growableListType;
+    TypeMask resultMask = _abstractValueDomain.growableListType;
 
     HInvokeDynamicMethod splitInstruction = new HInvokeDynamicMethod(
         node.selector,
@@ -490,7 +496,7 @@
         node.sourceInformation,
         isIntercepted: false)
       ..element = commonElements.jsStringSplit
-      ..isAllocation = true;
+      ..setAllocation(true);
 
     if (!_closedWorld.rtiNeed
         .classNeedsTypeArguments(commonElements.jsArrayClass)) {
@@ -503,7 +509,7 @@
         TypeInfoExpressionKind.COMPLETE,
         _closedWorld.elementEnvironment.getThisType(commonElements.stringClass),
         <HInstruction>[],
-        _closedWorld.commonMasks.dynamicType);
+        _abstractValueDomain.dynamicType);
     node.block.addBefore(node, stringTypeInfo);
 
     HInstruction typeInfo = new HTypeInfoExpression(
@@ -511,7 +517,7 @@
         _closedWorld.elementEnvironment
             .getThisType(commonElements.jsArrayClass),
         <HInstruction>[stringTypeInfo],
-        _closedWorld.commonMasks.dynamicType);
+        _abstractValueDomain.dynamicType);
     node.block.addBefore(node, typeInfo);
 
     HInvokeStatic tagInstruction = new HInvokeStatic(
@@ -522,7 +528,7 @@
     // 'Linear typing' trick: [tagInstruction] is the only use of the
     // [splitInstruction], so it becomes the sole alias.
     // TODO(sra): Build this knowledge into alias analysis.
-    tagInstruction.isAllocation = true;
+    tagInstruction.setAllocation(true);
 
     return tagInstruction;
   }
@@ -660,7 +666,7 @@
 
   HInstruction visitBoundsCheck(HBoundsCheck node) {
     HInstruction index = node.index;
-    if (index.isInteger(_closedWorld)) return node;
+    if (index.isInteger(_abstractValueDomain)) return node;
     if (index.isConstant()) {
       HConstant constantInstruction = index;
       assert(!constantInstruction.constant.isInt);
@@ -688,7 +694,8 @@
     HInstruction right = node.right;
     // We can only perform this rewriting on Integer, as it is not
     // valid for -0.0.
-    if (left.isInteger(_closedWorld) && right.isInteger(_closedWorld)) {
+    if (left.isInteger(_abstractValueDomain) &&
+        right.isInteger(_abstractValueDomain)) {
       if (left is HConstant && left.constant.isZero) return right;
       if (right is HConstant && right.constant.isZero) return left;
     }
@@ -698,7 +705,8 @@
   HInstruction visitMultiply(HMultiply node) {
     HInstruction left = node.left;
     HInstruction right = node.right;
-    if (left.isNumber(_closedWorld) && right.isNumber(_closedWorld)) {
+    if (left.isNumber(_abstractValueDomain) &&
+        right.isNumber(_abstractValueDomain)) {
       if (left is HConstant && left.constant.isOne) return right;
       if (right is HConstant && right.constant.isOne) return left;
     }
@@ -744,14 +752,15 @@
 
     // Intersection of int and double return conflicting, so
     // we don't optimize on numbers to preserve the runtime semantics.
-    if (!(left.isNumberOrNull(_closedWorld) &&
-        right.isNumberOrNull(_closedWorld))) {
+    if (!(left.isNumberOrNull(_abstractValueDomain) &&
+        right.isNumberOrNull(_abstractValueDomain))) {
       if (leftType.isDisjoint(rightType, _closedWorld)) {
         return makeFalse();
       }
     }
 
-    if (left.isNull() && right.isNull()) {
+    if (left.isNull(_abstractValueDomain) &&
+        right.isNull(_abstractValueDomain)) {
       return makeTrue();
     }
 
@@ -759,15 +768,15 @@
       if (constant.constant.isTrue) {
         return input;
       } else {
-        return new HNot(input, _closedWorld.commonMasks.boolType);
+        return new HNot(input, _abstractValueDomain.boolType);
       }
     }
 
-    if (left.isConstantBoolean() && right.isBoolean(_closedWorld)) {
+    if (left.isConstantBoolean() && right.isBoolean(_abstractValueDomain)) {
       return compareConstant(left, right);
     }
 
-    if (right.isConstantBoolean() && left.isBoolean(_closedWorld)) {
+    if (right.isConstantBoolean() && left.isBoolean(_abstractValueDomain)) {
       return compareConstant(right, left);
     }
 
@@ -776,8 +785,8 @@
       // dart2js runtime has not always been consistent with the Dart
       // specification (section 16.0.1), which makes distinctions on NaNs and
       // -0.0 that are hard to implement efficiently.
-      if (left.isIntegerOrNull(_closedWorld)) return makeTrue();
-      if (!left.canBePrimitiveNumber(_closedWorld)) return makeTrue();
+      if (left.isIntegerOrNull(_abstractValueDomain)) return makeTrue();
+      if (!left.canBePrimitiveNumber(_abstractValueDomain)) return makeTrue();
     }
 
     return null;
@@ -859,7 +868,7 @@
     InterfaceType interfaceType = type;
     ClassEntity element = interfaceType.element;
     HInstruction expression = node.expression;
-    if (expression.isInteger(_closedWorld)) {
+    if (expression.isInteger(_abstractValueDomain)) {
       if (element == commonElements.intClass ||
           element == commonElements.numClass ||
           commonElements.isNumberOrStringSupertype(element)) {
@@ -871,7 +880,7 @@
       } else {
         return _graph.addConstantBool(false, _closedWorld);
       }
-    } else if (expression.isDouble(_closedWorld)) {
+    } else if (expression.isDouble(_abstractValueDomain)) {
       if (element == commonElements.doubleClass ||
           element == commonElements.numClass ||
           commonElements.isNumberOrStringSupertype(element)) {
@@ -884,14 +893,14 @@
       } else {
         return _graph.addConstantBool(false, _closedWorld);
       }
-    } else if (expression.isNumber(_closedWorld)) {
+    } else if (expression.isNumber(_abstractValueDomain)) {
       if (element == commonElements.numClass) {
         return _graph.addConstantBool(true, _closedWorld);
       } else {
         // We cannot just return false, because the expression may be of
         // type int or double.
       }
-    } else if (expression.canBePrimitiveNumber(_closedWorld) &&
+    } else if (expression.canBePrimitiveNumber(_abstractValueDomain) &&
         element == commonElements.intClass) {
       // We let the JS semantics decide for that check.
       return node;
@@ -928,14 +937,14 @@
       if (type.isFutureOr) {
         HInstruction input = node.checkedInput;
         // `null` always passes type conversion.
-        if (input.isNull()) return input;
+        if (input.isNull(_abstractValueDomain)) return input;
         // TODO(johnniwinther): Optimize FutureOr type conversions.
         return node;
       }
       if (!type.treatAsRaw) {
         HInstruction input = node.checkedInput;
         // `null` always passes type conversion.
-        if (input.isNull()) return input;
+        if (input.isNull(_abstractValueDomain)) return input;
         // TODO(sra): We can statically check [input] if it is a constructor.
         // TODO(sra): We can statically check [input] if it is load from a field
         // of the same ground type, or load from a field of a parameterized type
@@ -945,7 +954,7 @@
       if (type.isFunctionType) {
         HInstruction input = node.checkedInput;
         // `null` always passes type conversion.
-        if (input.isNull()) return input;
+        if (input.isNull(_abstractValueDomain)) return input;
         // TODO(johnniwinther): Optimize function type conversions.
         // TODO(sra): We can statically check [input] if it is a closure getter.
         return node;
@@ -999,7 +1008,7 @@
       // should not be necessary but it currently makes it easier for
       // other optimizations to reason about a fixed length constructor
       // that we know takes an int.
-      if (receiver.inputs[0].isInteger(_closedWorld)) {
+      if (receiver.inputs[0].isInteger(_abstractValueDomain)) {
         return receiver.inputs[0];
       }
     } else if (receiver.isConstantList() || receiver.isConstantString()) {
@@ -1151,8 +1160,8 @@
 
     if (element == commonElements.identicalFunction) {
       if (node.inputs.length == 2) {
-        return new HIdentity(node.inputs[0], node.inputs[1], null,
-            _closedWorld.commonMasks.boolType)
+        return new HIdentity(
+            node.inputs[0], node.inputs[1], null, _abstractValueDomain.boolType)
           ..sourceInformation = node.sourceInformation;
       }
     } else if (element == commonElements.setRuntimeTypeInfo) {
@@ -1170,17 +1179,17 @@
     } else if (commonElements.isCheckInt(element)) {
       if (node.inputs.length == 1) {
         HInstruction argument = node.inputs[0];
-        if (argument.isInteger(_closedWorld)) return argument;
+        if (argument.isInteger(_abstractValueDomain)) return argument;
       }
     } else if (commonElements.isCheckNum(element)) {
       if (node.inputs.length == 1) {
         HInstruction argument = node.inputs[0];
-        if (argument.isNumber(_closedWorld)) return argument;
+        if (argument.isNumber(_abstractValueDomain)) return argument;
       }
     } else if (commonElements.isCheckString(element)) {
       if (node.inputs.length == 1) {
         HInstruction argument = node.inputs[0];
-        if (argument.isString(_closedWorld)) return argument;
+        if (argument.isString(_abstractValueDomain)) return argument;
       }
     }
     return node;
@@ -1190,7 +1199,7 @@
     // If type information is not needed, use the raw Array.
     HInstruction source = node.inputs[0];
     if (source.usedBy.length != 1) return node;
-    if (!source.isArray(_closedWorld)) return node;
+    if (!source.isArray(_abstractValueDomain)) return node;
     for (HInstruction user in node.usedBy) {
       if (user is HGetLength) continue;
       if (user is HIndex) continue;
@@ -1263,13 +1272,12 @@
             .createString(leftString.stringValue + rightString.stringValue),
         _closedWorld);
     if (prefix == null) return folded;
-    return new HStringConcat(
-        prefix, folded, _closedWorld.commonMasks.stringType);
+    return new HStringConcat(prefix, folded, _abstractValueDomain.stringType);
   }
 
   HInstruction visitStringify(HStringify node) {
     HInstruction input = node.inputs[0];
-    if (input.isString(_closedWorld)) return input;
+    if (input.isString(_abstractValueDomain)) return input;
 
     HInstruction asString(String string) =>
         _graph.addConstant(constantSystem.createString(string), _closedWorld);
@@ -1305,8 +1313,8 @@
       // it directly. Keep the stringifier for primitives (since they have fast
       // path code in the stringifier) and for classes requiring interceptors
       // (since SsaInstructionSimplifier runs after SsaSimplifyInterceptors).
-      if (input.canBePrimitive(_closedWorld)) return null;
-      if (input.canBeNull()) return null;
+      if (input.canBePrimitive(_abstractValueDomain)) return null;
+      if (input.canBeNull(_abstractValueDomain)) return null;
       Selector selector = Selectors.toString_;
       TypeMask toStringType = TypeMaskFactory.inferredTypeForSelector(
           selector, input.instructionType, _globalInferenceResults);
@@ -1394,7 +1402,7 @@
       }
 
       if (source == null) return null;
-      return new HTypeInfoReadRaw(source, _closedWorld.commonMasks.dynamicType);
+      return new HTypeInfoReadRaw(source, _abstractValueDomain.dynamicType);
     }
 
     // TODO(sra): Consider fusing type expression trees with no type variables,
@@ -1418,7 +1426,7 @@
             TypeInfoExpressionKind.COMPLETE,
             typeArgument,
             const <HInstruction>[],
-            _closedWorld.commonMasks.dynamicType);
+            _abstractValueDomain.dynamicType);
         return replacement;
       }
       return node;
@@ -1450,7 +1458,7 @@
           TypeInfoExpressionKind.COMPLETE,
           type,
           arguments,
-          _closedWorld.commonMasks.dynamicType);
+          _abstractValueDomain.dynamicType);
       return replacement;
     }
 
@@ -1543,6 +1551,9 @@
 
   SsaCheckInserter(this.trustPrimitives, this.closedWorld, this.boundsChecked);
 
+  AbstractValueDomain get _abstractValueDomain =>
+      closedWorld.abstractValueDomain;
+
   void visitGraph(HGraph graph) {
     this.graph = graph;
 
@@ -1566,13 +1577,13 @@
   HBoundsCheck insertBoundsCheck(
       HInstruction indexNode, HInstruction array, HInstruction indexArgument) {
     HGetLength length = new HGetLength(
-        array, closedWorld.commonMasks.positiveIntType,
+        array, closedWorld.abstractValueDomain.positiveIntType,
         isAssignable: !isFixedLength(array.instructionType, closedWorld));
     indexNode.block.addBefore(indexNode, length);
 
-    TypeMask type = indexArgument.isPositiveInteger(closedWorld)
+    TypeMask type = indexArgument.isPositiveInteger(_abstractValueDomain)
         ? indexArgument.instructionType
-        : closedWorld.commonMasks.positiveIntType;
+        : closedWorld.abstractValueDomain.positiveIntType;
     HBoundsCheck check = new HBoundsCheck(indexArgument, length, array, type)
       ..sourceInformation = indexNode.sourceInformation;
     indexNode.block.addBefore(indexNode, check);
@@ -1583,7 +1594,7 @@
     // the index eg. if it is a constant.  The range information from the
     // BoundsCheck instruction is attached to the input directly by
     // visitBoundsCheck in the SsaValueRangeAnalyzer.
-    if (!indexArgument.isInteger(closedWorld)) {
+    if (!indexArgument.isInteger(_abstractValueDomain)) {
       indexArgument.replaceAllUsersDominatedBy(indexNode, check);
     }
     boundsChecked.add(indexNode);
@@ -1630,6 +1641,9 @@
 
   SsaDeadCodeEliminator(this.closedWorld, this.optimizer);
 
+  AbstractValueDomain get _abstractValueDomain =>
+      closedWorld.abstractValueDomain;
+
   HInstruction zapInstructionCache;
   HInstruction get zapInstruction {
     if (zapInstructionCache == null) {
@@ -1683,14 +1697,15 @@
     HInstruction current = instruction.next;
     do {
       if ((current.getDartReceiver(closedWorld) == receiver) &&
-          current.canThrow()) {
+          current.canThrow(_abstractValueDomain)) {
         return true;
       }
       if (current is HForeignCode &&
           templateThrowsNSMonNull(current, receiver)) {
         return true;
       }
-      if (current.canThrow() || current.sideEffects.hasSideEffects()) {
+      if (current.canThrow(_abstractValueDomain) ||
+          current.sideEffects.hasSideEffects()) {
         return false;
       }
       HInstruction next = current.next;
@@ -1734,8 +1749,8 @@
       return false;
     }
 
-    return instruction.isAllocation &&
-        instruction.isPure() &&
+    return instruction.isAllocation(_abstractValueDomain) &&
+        instruction.isPure(_abstractValueDomain) &&
         trivialDeadStoreReceivers.putIfAbsent(
             instruction, () => instruction.usedBy.every(isDeadUse));
   }
@@ -1749,12 +1764,12 @@
     if (!instruction.usedBy.isEmpty) return false;
     if (isTrivialDeadStore(instruction)) return true;
     if (instruction.sideEffects.hasSideEffects()) return false;
-    if (instruction.canThrow() &&
+    if (instruction.canThrow(_abstractValueDomain) &&
         instruction.onlyThrowsNSM() &&
         hasFollowingThrowingNSM(instruction)) {
       return true;
     }
-    return !instruction.canThrow() &&
+    return !instruction.canThrow(_abstractValueDomain) &&
         instruction is! HParameterValue &&
         instruction is! HLocalSet;
   }
@@ -1966,6 +1981,9 @@
 
   SsaLiveBlockAnalyzer(this.graph, this.closedWorld, this.optimizer);
 
+  AbstractValueDomain get _abstractValueDomain =>
+      closedWorld.abstractValueDomain;
+
   Map<HInstruction, Range> get ranges => optimizer.ranges;
 
   bool isDeadBlock(HBasicBlock block) => !live.contains(block);
@@ -2003,7 +2021,7 @@
   }
 
   void visitSwitch(HSwitch node) {
-    if (node.expression.isInteger(closedWorld)) {
+    if (node.expression.isInteger(_abstractValueDomain)) {
       Range switchRange = ranges[node.expression];
       if (switchRange != null &&
           switchRange.lower is IntValue &&
@@ -2149,13 +2167,14 @@
 }
 
 class SsaGlobalValueNumberer implements OptimizationPhase {
+  final AbstractValueDomain _abstractValueDomain;
   final String name = "SsaGlobalValueNumberer";
   final Set<int> visited;
 
   List<int> blockChangesFlags;
   List<int> loopChangesFlags;
 
-  SsaGlobalValueNumberer() : visited = new Set<int>();
+  SsaGlobalValueNumberer(this._abstractValueDomain) : visited = new Set<int>();
 
   void visitGraph(HGraph graph) {
     computeChangesFlags(graph);
@@ -2210,7 +2229,8 @@
       HInstruction next = instruction.next;
       if (instruction.useGvn() &&
           instruction.isMovable &&
-          (!instruction.canThrow() || firstInstructionInLoop) &&
+          (!instruction.canThrow(_abstractValueDomain) ||
+              firstInstructionInLoop) &&
           !instruction.sideEffects.dependsOn(dependsFlags)) {
         bool loopInvariantInputs = true;
         List<HInstruction> inputs = instruction.inputs;
@@ -2369,11 +2389,15 @@
 // these computed ValueSet. It moves all instructions of the
 // intersection into its own list of instructions.
 class SsaCodeMotion extends HBaseVisitor implements OptimizationPhase {
+  final AbstractValueDomain _abstractValueDomain;
+
   final String name = "SsaCodeMotion";
 
   bool movedCode = false;
   List<ValueSet> values;
 
+  SsaCodeMotion(this._abstractValueDomain);
+
   void visitGraph(HGraph graph) {
     values = new List<ValueSet>(graph.blocks.length);
     for (int i = 0; i < graph.blocks.length; i++) {
@@ -2434,7 +2458,7 @@
       // TODO(sra): We could move throwing instructions provided we keep the
       // exceptions in the same order.  This requires they are in the same order
       // in all successors, which is not tracked by the ValueSet.
-      if (current.canThrow()) continue;
+      if (current.canThrow(_abstractValueDomain)) continue;
       if (current.sideEffects.dependsOn(dependsFlags)) continue;
 
       bool canBeMoved = true;
@@ -2465,6 +2489,9 @@
 
   SsaTypeConversionInserter(this.closedWorld);
 
+  AbstractValueDomain get _abstractValueDomain =>
+      closedWorld.abstractValueDomain;
+
   void visitGraph(HGraph graph) {
     visitDominatorTree(graph);
   }
@@ -2537,7 +2564,7 @@
       return;
     }
 
-    if (!input.instructionType.isNullable) return;
+    if (!_abstractValueDomain.canBeNull(input.instructionType)) return;
 
     List<HBasicBlock> trueTargets = <HBasicBlock>[];
     List<HBasicBlock> falseTargets = <HBasicBlock>[];
@@ -2546,7 +2573,8 @@
 
     if (trueTargets.isEmpty && falseTargets.isEmpty) return;
 
-    TypeMask nonNullType = input.instructionType.nonNullable();
+    TypeMask nonNullType =
+        _abstractValueDomain.excludeNull(input.instructionType);
 
     for (HBasicBlock block in falseTargets) {
       insertTypePropagationForDominatedUsers(block, input, nonNullType);
@@ -2607,6 +2635,9 @@
 
   SsaLoadElimination(this.compiler, this.closedWorld);
 
+  AbstractValueDomain get _abstractValueDomain =>
+      closedWorld.abstractValueDomain;
+
   void visitGraph(HGraph graph) {
     memories = new List<MemorySet>(graph.blocks.length);
     List<HBasicBlock> blocks = graph.blocks;
@@ -2764,7 +2795,7 @@
   }
 
   void visitInstruction(HInstruction instruction) {
-    if (instruction.isAllocation) {
+    if (instruction.isAllocation(_abstractValueDomain)) {
       memorySet.registerAllocation(instruction);
     }
     memorySet.killAffectedBy(instruction);
@@ -2877,6 +2908,9 @@
 
   MemorySet(this.closedWorld);
 
+  AbstractValueDomain get _abstractValueDomain =>
+      closedWorld.abstractValueDomain;
+
   /// Returns whether [first] and [second] always alias to the same object.
   bool mustAlias(HInstruction first, HInstruction second) {
     return first == second;
@@ -2890,8 +2924,8 @@
     if (nonEscapingReceivers.contains(second)) return false;
     // Typed arrays of different types might have a shared buffer.
     if (couldBeTypedArray(first) && couldBeTypedArray(second)) return true;
-    return !first.instructionType
-        .isDisjoint(second.instructionType, closedWorld);
+    return !_abstractValueDomain.areDisjoint(
+        first.instructionType, second.instructionType);
   }
 
   bool isFinal(Object element) {
@@ -2905,7 +2939,8 @@
   }
 
   bool couldBeTypedArray(HInstruction receiver) {
-    return closedWorld.commonMasks.couldBeTypedArray(receiver.instructionType);
+    return closedWorld.abstractValueDomain
+        .couldBeTypedArray(receiver.instructionType);
   }
 
   /// Returns whether [receiver] escapes the current function.
@@ -3090,8 +3125,8 @@
         }
       }
     }
-    TypeMask phiType =
-        second.instructionType.union(first.instructionType, closedWorld);
+    TypeMask phiType = _abstractValueDomain.union(
+        second.instructionType, first.instructionType);
     if (first is HPhi && first.block == block) {
       HPhi phi = first;
       phi.addInput(second);
diff --git a/pkg/compiler/lib/src/ssa/ssa_tracer.dart b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
index 02bdb6b..f7956fb 100644
--- a/pkg/compiler/lib/src/ssa/ssa_tracer.dart
+++ b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
@@ -7,6 +7,7 @@
 import '../../compiler_new.dart' show OutputSink;
 import '../diagnostics/invariant.dart' show DEBUG_MODE;
 import '../js_backend/namer.dart' show Namer;
+import '../types/abstract_value_domain.dart';
 import '../tracer.dart';
 import '../world.dart' show ClosedWorld;
 import 'nodes.dart';
@@ -118,35 +119,38 @@
 
   HInstructionStringifier(this.currentBlock, this.closedWorld, this.namer);
 
+  AbstractValueDomain get _abstractValueDomain =>
+      closedWorld.abstractValueDomain;
+
   visit(HInstruction node) => '${node.accept(this)} ${node.instructionType}';
 
   String temporaryId(HInstruction instruction) {
     String prefix;
-    if (instruction.isNull()) {
+    if (instruction.isNull(_abstractValueDomain)) {
       prefix = 'u';
-    } else if (instruction.isConflicting()) {
+    } else if (instruction.isConflicting(_abstractValueDomain)) {
       prefix = 'c';
-    } else if (instruction.isExtendableArray(closedWorld)) {
+    } else if (instruction.isExtendableArray(_abstractValueDomain)) {
       prefix = 'e';
-    } else if (instruction.isFixedArray(closedWorld)) {
+    } else if (instruction.isFixedArray(_abstractValueDomain)) {
       prefix = 'f';
-    } else if (instruction.isMutableArray(closedWorld)) {
+    } else if (instruction.isMutableArray(_abstractValueDomain)) {
       prefix = 'm';
-    } else if (instruction.isReadableArray(closedWorld)) {
+    } else if (instruction.isArray(_abstractValueDomain)) {
       prefix = 'a';
-    } else if (instruction.isString(closedWorld)) {
+    } else if (instruction.isString(_abstractValueDomain)) {
       prefix = 's';
-    } else if (instruction.isIndexablePrimitive(closedWorld)) {
+    } else if (instruction.isIndexablePrimitive(_abstractValueDomain)) {
       prefix = 'r';
-    } else if (instruction.isBoolean(closedWorld)) {
+    } else if (instruction.isBoolean(_abstractValueDomain)) {
       prefix = 'b';
-    } else if (instruction.isInteger(closedWorld)) {
+    } else if (instruction.isInteger(_abstractValueDomain)) {
       prefix = 'i';
-    } else if (instruction.isDouble(closedWorld)) {
+    } else if (instruction.isDouble(_abstractValueDomain)) {
       prefix = 'd';
-    } else if (instruction.isNumber(closedWorld)) {
+    } else if (instruction.isNumber(_abstractValueDomain)) {
       prefix = 'n';
-    } else if (instruction.instructionType.containsAll(closedWorld)) {
+    } else if (_abstractValueDomain.containsAll(instruction.instructionType)) {
       prefix = 'v';
     } else {
       prefix = 'U';
diff --git a/pkg/compiler/lib/src/ssa/types.dart b/pkg/compiler/lib/src/ssa/types.dart
index d973ef6..d93488d 100644
--- a/pkg/compiler/lib/src/ssa/types.dart
+++ b/pkg/compiler/lib/src/ssa/types.dart
@@ -13,30 +13,30 @@
   static TypeMask inferredReturnTypeForElement(
       FunctionEntity element, GlobalTypeInferenceResults results) {
     return results.resultOfMember(element).returnType ??
-        results.closedWorld.commonMasks.dynamicType;
+        results.closedWorld.abstractValueDomain.dynamicType;
   }
 
   static TypeMask inferredTypeForMember(
       MemberEntity element, GlobalTypeInferenceResults results) {
     return results.resultOfMember(element).type ??
-        results.closedWorld.commonMasks.dynamicType;
+        results.closedWorld.abstractValueDomain.dynamicType;
   }
 
   static TypeMask inferredTypeForParameter(
       Local element, GlobalTypeInferenceResults results) {
     return results.resultOfParameter(element).type ??
-        results.closedWorld.commonMasks.dynamicType;
+        results.closedWorld.abstractValueDomain.dynamicType;
   }
 
   static TypeMask inferredTypeForSelector(
       Selector selector, TypeMask mask, GlobalTypeInferenceResults results) {
     return results.typeOfSelector(selector, mask) ??
-        results.closedWorld.commonMasks.dynamicType;
+        results.closedWorld.abstractValueDomain.dynamicType;
   }
 
   static TypeMask fromNativeBehavior(
       native.NativeBehavior nativeBehavior, ClosedWorld closedWorld) {
-    CommonMasks commonMasks = closedWorld.commonMasks;
+    CommonMasks commonMasks = closedWorld.abstractValueDomain;
     var typesReturned = nativeBehavior.typesReturned;
     if (typesReturned.isEmpty) return commonMasks.dynamicType;
 
diff --git a/pkg/compiler/lib/src/ssa/types_propagation.dart b/pkg/compiler/lib/src/ssa/types_propagation.dart
index 1d4a182..5b84020 100644
--- a/pkg/compiler/lib/src/ssa/types_propagation.dart
+++ b/pkg/compiler/lib/src/ssa/types_propagation.dart
@@ -5,6 +5,7 @@
 import '../common_elements.dart' show CommonElements;
 import '../elements/entities.dart';
 import '../options.dart';
+import '../types/abstract_value_domain.dart';
 import '../types/types.dart';
 import '../universe/selector.dart' show Selector;
 import '../world.dart' show ClosedWorld;
@@ -42,6 +43,9 @@
   SsaTypePropagator(
       this.results, this.options, this.commonElements, this.closedWorld);
 
+  AbstractValueDomain get abstractValueDomain =>
+      closedWorld.abstractValueDomain;
+
   TypeMask computeType(HInstruction instruction) {
     return instruction.accept(this);
   }
@@ -125,21 +129,22 @@
   TypeMask visitBinaryArithmetic(HBinaryArithmetic instruction) {
     HInstruction left = instruction.left;
     HInstruction right = instruction.right;
-    if (left.isInteger(closedWorld) && right.isInteger(closedWorld)) {
-      return closedWorld.commonMasks.intType;
+    if (left.isInteger(closedWorld.abstractValueDomain) &&
+        right.isInteger(closedWorld.abstractValueDomain)) {
+      return closedWorld.abstractValueDomain.intType;
     }
-    if (left.isDouble(closedWorld)) {
-      return closedWorld.commonMasks.doubleType;
+    if (left.isDouble(closedWorld.abstractValueDomain)) {
+      return closedWorld.abstractValueDomain.doubleType;
     }
-    return closedWorld.commonMasks.numType;
+    return closedWorld.abstractValueDomain.numType;
   }
 
   TypeMask checkPositiveInteger(HBinaryArithmetic instruction) {
     HInstruction left = instruction.left;
     HInstruction right = instruction.right;
-    if (left.isPositiveInteger(closedWorld) &&
-        right.isPositiveInteger(closedWorld)) {
-      return closedWorld.commonMasks.positiveIntType;
+    if (left.isPositiveInteger(closedWorld.abstractValueDomain) &&
+        right.isPositiveInteger(closedWorld.abstractValueDomain)) {
+      return closedWorld.abstractValueDomain.positiveIntType;
     }
     return visitBinaryArithmetic(instruction);
   }
@@ -171,8 +176,8 @@
     HInstruction operand = instruction.operand;
     // We have integer subclasses that represent ranges, so widen any int
     // subclass to full integer.
-    if (operand.isInteger(closedWorld)) {
-      return closedWorld.commonMasks.intType;
+    if (operand.isInteger(closedWorld.abstractValueDomain)) {
+      return closedWorld.abstractValueDomain.intType;
     }
     return instruction.operand.instructionType;
   }
@@ -188,7 +193,7 @@
   }
 
   TypeMask visitPhi(HPhi phi) {
-    TypeMask candidateType = closedWorld.commonMasks.emptyType;
+    TypeMask candidateType = closedWorld.abstractValueDomain.emptyType;
     for (int i = 0, length = phi.inputs.length; i < length; i++) {
       TypeMask inputType = phi.inputs[i].instructionType;
       candidateType = candidateType.union(inputType, closedWorld);
@@ -206,11 +211,11 @@
       // We only do an int check if the input is integer or null.
       if (checkedType.containsOnlyNum(closedWorld) &&
           !checkedType.containsOnlyDouble(closedWorld) &&
-          input.isIntegerOrNull(closedWorld)) {
-        instruction.checkedType = closedWorld.commonMasks.intType;
+          input.isIntegerOrNull(closedWorld.abstractValueDomain)) {
+        instruction.checkedType = closedWorld.abstractValueDomain.intType;
       } else if (checkedType.containsOnlyInt(closedWorld) &&
-          !input.isIntegerOrNull(closedWorld)) {
-        instruction.checkedType = closedWorld.commonMasks.numType;
+          !input.isIntegerOrNull(closedWorld.abstractValueDomain)) {
+        instruction.checkedType = closedWorld.abstractValueDomain.numType;
       }
     }
 
@@ -224,9 +229,10 @@
       if (inputType.containsOnlyInt(closedWorld) &&
           checkedType.containsOnlyDouble(closedWorld)) {
         if (inputType.isNullable && checkedType.isNullable) {
-          outputType = closedWorld.commonMasks.doubleType.nullable();
+          outputType =
+              abstractValueDomain.includeNull(abstractValueDomain.doubleType);
         } else {
-          outputType = closedWorld.commonMasks.doubleType;
+          outputType = abstractValueDomain.doubleType;
         }
       }
     }
@@ -279,7 +285,7 @@
       // If the instruction's type is integer or null, the codegen
       // will emit a null check, which is enough to know if it will
       // hit a noSuchMethod.
-      return instruction.isIntegerOrNull(closedWorld);
+      return instruction.isIntegerOrNull(closedWorld.abstractValueDomain);
     }
     return true;
   }
@@ -290,12 +296,12 @@
   bool checkReceiver(HInvokeDynamic instruction) {
     assert(instruction.isInterceptedCall);
     HInstruction receiver = instruction.inputs[1];
-    if (receiver.isNumber(closedWorld)) return false;
-    if (receiver.isNumberOrNull(closedWorld)) {
+    if (receiver.isNumber(closedWorld.abstractValueDomain)) return false;
+    if (receiver.isNumberOrNull(closedWorld.abstractValueDomain)) {
       convertInput(
           instruction,
           receiver,
-          receiver.instructionType.nonNullable(),
+          closedWorld.abstractValueDomain.excludeNull(receiver.instructionType),
           HTypeConversion.RECEIVER_TYPE_CHECK);
       return true;
     } else if (instruction.element == null) {
@@ -334,11 +340,11 @@
     HInstruction right = instruction.inputs[2];
 
     Selector selector = instruction.selector;
-    if (selector.isOperator && left.isNumber(closedWorld)) {
-      if (right.isNumber(closedWorld)) return false;
-      TypeMask type = right.isIntegerOrNull(closedWorld)
-          ? right.instructionType.nonNullable()
-          : closedWorld.commonMasks.numType;
+    if (selector.isOperator && left.isNumber(closedWorld.abstractValueDomain)) {
+      if (right.isNumber(closedWorld.abstractValueDomain)) return false;
+      TypeMask type = right.isIntegerOrNull(closedWorld.abstractValueDomain)
+          ? closedWorld.abstractValueDomain.excludeNull(right.instructionType)
+          : closedWorld.abstractValueDomain.numType;
       // TODO(ngeoffray): Some number operations don't have a builtin
       // variant and will do the check in their method anyway. We
       // still add a check because it allows to GVN these operations,
diff --git a/pkg/compiler/lib/src/ssa/value_range_analyzer.dart b/pkg/compiler/lib/src/ssa/value_range_analyzer.dart
index 1be4244f..97b75ff 100644
--- a/pkg/compiler/lib/src/ssa/value_range_analyzer.dart
+++ b/pkg/compiler/lib/src/ssa/value_range_analyzer.dart
@@ -633,7 +633,7 @@
   void visitBasicBlock(HBasicBlock block) {
     void visit(HInstruction instruction) {
       Range range = instruction.accept(this);
-      if (instruction.isInteger(closedWorld)) {
+      if (instruction.isInteger(closedWorld.abstractValueDomain)) {
         assert(range != null);
         ranges[instruction] = range;
       }
@@ -644,10 +644,10 @@
   }
 
   Range visitInstruction(HInstruction instruction) {
-    if (instruction.isPositiveInteger(closedWorld)) {
+    if (instruction.isPositiveInteger(closedWorld.abstractValueDomain)) {
       return info.newNormalizedRange(
           info.intZero, info.newPositiveValue(instruction));
-    } else if (instruction.isInteger(closedWorld)) {
+    } else if (instruction.isInteger(closedWorld.abstractValueDomain)) {
       InstructionValue value = info.newInstructionValue(instruction);
       return info.newNormalizedRange(value, value);
     } else {
@@ -656,12 +656,13 @@
   }
 
   Range visitPhi(HPhi phi) {
-    if (!phi.isInteger(closedWorld)) return info.newUnboundRange();
+    if (!phi.isInteger(closedWorld.abstractValueDomain))
+      return info.newUnboundRange();
     // Some phases may replace instructions that change the inputs of
     // this phi. Only the [SsaTypesPropagation] phase will update the
     // phi type. Play it safe by assuming the [SsaTypesPropagation]
     // phase is not necessarily run before the [ValueRangeAnalyzer].
-    if (phi.inputs.any((i) => !i.isInteger(closedWorld))) {
+    if (phi.inputs.any((i) => !i.isInteger(closedWorld.abstractValueDomain))) {
       return info.newUnboundRange();
     }
     if (phi.block.isLoopHeader()) {
@@ -679,7 +680,8 @@
   }
 
   Range visitConstant(HConstant hConstant) {
-    if (!hConstant.isInteger(closedWorld)) return info.newUnboundRange();
+    if (!hConstant.isInteger(closedWorld.abstractValueDomain))
+      return info.newUnboundRange();
     ConstantValue constant = hConstant.constant;
     NumConstantValue constantNum;
     if (constant is DeferredConstantValue) {
@@ -721,7 +723,7 @@
     Range lengthRange = ranges[check.length];
     if (indexRange == null) {
       indexRange = info.newUnboundRange();
-      assert(!check.index.isInteger(closedWorld));
+      assert(!check.index.isInteger(closedWorld.abstractValueDomain));
     }
     if (lengthRange == null) {
       // We might have lost the length range due to a type conversion that
@@ -729,7 +731,7 @@
       // get to this point anyway, so no need to try and refine ranges.
       return indexRange;
     }
-    assert(check.length.isInteger(closedWorld));
+    assert(check.length.isInteger(closedWorld.abstractValueDomain));
 
     // Check if the index is strictly below the upper bound of the length
     // range.
@@ -782,8 +784,10 @@
   Range visitRelational(HRelational relational) {
     HInstruction right = relational.right;
     HInstruction left = relational.left;
-    if (!left.isInteger(closedWorld)) return info.newUnboundRange();
-    if (!right.isInteger(closedWorld)) return info.newUnboundRange();
+    if (!left.isInteger(closedWorld.abstractValueDomain))
+      return info.newUnboundRange();
+    if (!right.isInteger(closedWorld.abstractValueDomain))
+      return info.newUnboundRange();
     BinaryOperation operation = relational.operation(constantSystem);
     Range rightRange = ranges[relational.right];
     Range leftRange = ranges[relational.left];
@@ -818,7 +822,8 @@
     if (divisor != null) {
       // For Integer values we can be precise in the upper bound, so special
       // case those.
-      if (left.isInteger(closedWorld) && right.isInteger(closedWorld)) {
+      if (left.isInteger(closedWorld.abstractValueDomain) &&
+          right.isInteger(closedWorld.abstractValueDomain)) {
         if (divisor.isPositive) {
           return info.newNormalizedRange(
               info.intZero, divisor.upper - info.intOne);
@@ -826,7 +831,8 @@
           return info.newNormalizedRange(
               info.intZero, info.newNegateValue(divisor.lower) - info.intOne);
         }
-      } else if (left.isNumber(closedWorld) && right.isNumber(closedWorld)) {
+      } else if (left.isNumber(closedWorld.abstractValueDomain) &&
+          right.isNumber(closedWorld.abstractValueDomain)) {
         if (divisor.isPositive) {
           return info.newNormalizedRange(info.intZero, divisor.upper);
         } else if (divisor.isNegative) {
@@ -844,16 +850,18 @@
     Range dividend = ranges[left];
     // If both operands are >=0, the result is >= 0 and bounded by the divisor.
     if ((dividend != null && dividend.isPositive) ||
-        left.isPositiveInteger(closedWorld)) {
+        left.isPositiveInteger(closedWorld.abstractValueDomain)) {
       Range divisor = ranges[right];
       if (divisor != null) {
         if (divisor.isPositive) {
           // For Integer values we can be precise in the upper bound.
-          if (left.isInteger(closedWorld) && right.isInteger(closedWorld)) {
+          if (left.isInteger(closedWorld.abstractValueDomain) &&
+              right.isInteger(closedWorld.abstractValueDomain)) {
             return info.newNormalizedRange(
                 info.intZero, divisor.upper - info.intOne);
           }
-          if (left.isNumber(closedWorld) && right.isNumber(closedWorld)) {
+          if (left.isNumber(closedWorld.abstractValueDomain) &&
+              right.isNumber(closedWorld.abstractValueDomain)) {
             return info.newNormalizedRange(info.intZero, divisor.upper);
           }
         }
@@ -869,7 +877,9 @@
   }
 
   Range handleBinaryOperation(HBinaryArithmetic instruction) {
-    if (!instruction.isInteger(closedWorld)) return info.newUnboundRange();
+    if (!instruction.isInteger(closedWorld.abstractValueDomain)) {
+      return info.newUnboundRange();
+    }
     return instruction
         .operation(constantSystem)
         .apply(ranges[instruction.left], ranges[instruction.right]);
@@ -884,10 +894,13 @@
   }
 
   Range visitBitAnd(HBitAnd node) {
-    if (!node.isInteger(closedWorld)) return info.newUnboundRange();
+    if (!node.isInteger(closedWorld.abstractValueDomain)) {
+      return info.newUnboundRange();
+    }
     HInstruction right = node.right;
     HInstruction left = node.left;
-    if (left.isInteger(closedWorld) && right.isInteger(closedWorld)) {
+    if (left.isInteger(closedWorld.abstractValueDomain) &&
+        right.isInteger(closedWorld.abstractValueDomain)) {
       return ranges[left] & ranges[right];
     }
 
@@ -901,9 +914,9 @@
       return info.newUnboundRange();
     }
 
-    if (left.isInteger(closedWorld)) {
+    if (left.isInteger(closedWorld.abstractValueDomain)) {
       return tryComputeRange(left);
-    } else if (right.isInteger(closedWorld)) {
+    } else if (right.isInteger(closedWorld.abstractValueDomain)) {
       return tryComputeRange(right);
     }
     return info.newUnboundRange();
@@ -918,8 +931,8 @@
 
   HInstruction createRangeConversion(
       HInstruction cursor, HInstruction instruction) {
-    HRangeConversion newInstruction =
-        new HRangeConversion(instruction, closedWorld.commonMasks.intType);
+    HRangeConversion newInstruction = new HRangeConversion(
+        instruction, closedWorld.abstractValueDomain.intType);
     conversions.add(newInstruction);
     cursor.block.addBefore(cursor, newInstruction);
     // Update the users of the instruction dominated by [cursor] to
@@ -982,8 +995,12 @@
     if (condition is HIdentity) return info.newUnboundRange();
     HInstruction right = condition.right;
     HInstruction left = condition.left;
-    if (!left.isInteger(closedWorld)) return info.newUnboundRange();
-    if (!right.isInteger(closedWorld)) return info.newUnboundRange();
+    if (!left.isInteger(closedWorld.abstractValueDomain)) {
+      return info.newUnboundRange();
+    }
+    if (!right.isInteger(closedWorld.abstractValueDomain)) {
+      return info.newUnboundRange();
+    }
 
     Range rightRange = ranges[right];
     Range leftRange = ranges[left];
@@ -1073,7 +1090,7 @@
   }
 
   Range visit(HInstruction instruction) {
-    if (!instruction.isInteger(closedWorld)) return null;
+    if (!instruction.isInteger(closedWorld.abstractValueDomain)) return null;
     if (ranges[instruction] != null) return ranges[instruction];
     return instruction.accept(this);
   }
diff --git a/pkg/compiler/lib/src/types/abstract_value_domain.dart b/pkg/compiler/lib/src/types/abstract_value_domain.dart
index 287cd2e..0bf4247 100644
--- a/pkg/compiler/lib/src/types/abstract_value_domain.dart
+++ b/pkg/compiler/lib/src/types/abstract_value_domain.dart
@@ -4,166 +4,237 @@
 
 library dart2js.abstract_value_domain;
 
-import '../constants/values.dart';
-import '../elements/resolution_types.dart';
-import '../elements/elements.dart';
-import '../universe/selector.dart' show Selector;
-
-enum AbstractBool { True, False, Maybe, Nothing }
+import '../elements/entities.dart';
 
 /// A value in an abstraction of runtime values.
 abstract class AbstractValue {}
 
-/// A system that implements an abstraction over runtime values and provides
-/// access to interprocedural analysis results.
-// TODO(johnniwinther): Consider extracting the inference result access from
-// this interface.
+/// A system that implements an abstraction over runtime values.
 abstract class AbstractValueDomain {
+  /// The [AbstractValue] that represents an unknown runtime value.
   AbstractValue get dynamicType;
+
+  /// The [AbstractValue] that represents a non-null subtype of `Type` at
+  /// runtime.
   AbstractValue get typeType;
+
+  /// The [AbstractValue] that represents a non-null subtype of `Function` at
+  /// runtime.
   AbstractValue get functionType;
+
+  /// The [AbstractValue] that represents a non-null subtype of `bool` at
+  /// runtime.
   AbstractValue get boolType;
+
+  /// The [AbstractValue] that represents a non-null subtype of `int` at
+  /// runtime.
   AbstractValue get intType;
+
+  /// The [AbstractValue] that represents a non-null subtype of `double` at
+  /// runtime.
   AbstractValue get doubleType;
+
+  /// The [AbstractValue] that represents a non-null subtype of `num` at
+  /// runtime.
   AbstractValue get numType;
+
+  /// The [AbstractValue] that represents a non-null subtype of `String` at
+  /// runtime.
   AbstractValue get stringType;
+
+  /// The [AbstractValue] that represents a non-null subtype of `List` at
+  /// runtime.
   AbstractValue get listType;
+
+  /// The [AbstractValue] that represents a non-null subtype of `Map` at
+  /// runtime.
   AbstractValue get mapType;
+
+  /// The [AbstractValue] that represents a non-null value at runtime.
   AbstractValue get nonNullType;
+
+  /// The [AbstractValue] that represents the `null` at runtime.
   AbstractValue get nullType;
-  AbstractValue get extendableArrayType;
+
+  /// The [AbstractValue] that represents a non-null growable JavaScript array
+  /// at runtime.
+  AbstractValue get growableListType;
+
+  /// The [AbstractValue] that represents a non-null fixed size JavaScript array
+  /// at runtime.
   AbstractValue get fixedArrayType;
-  AbstractValue get arrayType;
+
+  /// The [AbstractValue] that represents a non-null 31-bit unsigned integer at
+  /// runtime.
   AbstractValue get uint31Type;
+
+  /// The [AbstractValue] that represents a non-null 32-bit unsigned integer at
+  /// runtime.
   AbstractValue get uint32Type;
-  AbstractValue get uintType;
 
-  AbstractValue get numStringBoolType;
+  /// The [AbstractValue] that represents a non-null unsigned integer at
+  /// runtime.
+  AbstractValue get positiveIntType;
 
-  AbstractValue get fixedLengthType;
+  /// The [AbstractValue] that represents a non-null constant list literal at
+  /// runtime.
+  AbstractValue get constListType;
 
-  AbstractValue get interceptorType;
+  /// The [AbstractValue] that represents a non-null constant map literal at
+  /// runtime.
+  AbstractValue get constMapType;
 
-  AbstractValue get interceptedTypes;
+  /// The [AbstractValue] that represents the empty set of runtime values.
+  AbstractValue get emptyType;
 
-  /// If true, [function] ignores its explicit receiver argument and will use
-  /// its `this` value instead.
-  bool methodIgnoresReceiverArgument(FunctionElement function);
+  /// Creates an [AbstractValue] for non-null exact instance of [cls].
+  AbstractValue createNonNullExact(ClassEntity cls);
 
-  /// If true, the explicit receiver argument can be ignored when invoking
-  /// [selector] on a value of [type].
-  bool targetIgnoresReceiverArgument(AbstractValue type, Selector selector);
+  /// Creates an [AbstractValue] for non-null instance that implements [cls].
+  AbstractValue createNonNullSubtype(ClassEntity cls);
 
-  Element locateSingleElement(AbstractValue mask, Selector selector);
+  /// Returns `true` if [value] is a native typed array or `null` at runtime.
+  bool isTypedArray(covariant AbstractValue value);
 
-  ClassElement singleClass(AbstractValue mask);
+  /// Returns `true` if [value] could be a native typed array at runtime.
+  bool couldBeTypedArray(covariant AbstractValue value);
 
-  bool needsNoSuchMethodHandling(AbstractValue mask, Selector selector);
+  /// Returns the version of the abstract [value] that excludes `null`.
+  AbstractValue excludeNull(covariant AbstractValue value);
 
-  AbstractValue getReceiverType(MethodElement method);
+  /// Returns the version of the abstract [value] that includes `null`.
+  AbstractValue includeNull(covariant AbstractValue value);
 
-  AbstractValue getParameterType(ParameterElement parameter);
+  /// Returns `true` if [value] contains instances of [cls] at runtime.
+  bool containsType(covariant AbstractValue value, ClassEntity cls);
 
-  AbstractValue getReturnType(FunctionElement function);
+  /// Returns `true` if [value] only contains subtypes of [cls] or `null` at
+  /// runtime.
+  bool containsOnlyType(covariant AbstractValue value, ClassEntity cls);
 
-  AbstractValue getInvokeReturnType(Selector selector, AbstractValue mask);
+  /// Returns `true` if [value] is an instance of [cls] or `null` at runtime.
+  bool isInstanceOf(covariant AbstractValue value, ClassEntity cls);
 
-  AbstractValue getFieldType(FieldElement field);
+  /// Returns `true` if [value] is empty set of runtime values.
+  bool isEmpty(covariant AbstractValue value);
 
-  AbstractValue join(AbstractValue a, AbstractValue b);
+  /// Returns `true` if [value] is an exact class or `null` at runtime.
+  bool isExact(covariant AbstractValue value);
 
-  AbstractValue intersection(AbstractValue a, AbstractValue b);
+  /// Returns `true` if [value] a known primitive JavaScript value at runtime.
+  bool isValue(covariant AbstractValue value);
 
-  AbstractValue getTypeOf(ConstantValue constant);
+  /// Returns `true` if [value] can be `null` at runtime.
+  bool canBeNull(covariant AbstractValue value);
 
-  /// Returns the constant value if the [AbstractValue] represents a single
-  /// constant value. Returns `null` if [value] is not a constant.
-  ConstantValue getConstantOf(AbstractValue value);
+  /// Returns `true` if [value] is `null` at runtime.
+  bool isNull(covariant AbstractValue value);
 
-  AbstractValue nonNullExact(ClassElement element);
+  /// Returns `true` if [value] could be a JavaScript bool, number, string,
+  /// array or `null` at runtime.
+  bool canBePrimitive(covariant AbstractValue value);
 
-  AbstractValue nonNullSubclass(ClassElement element);
+  /// Returns `true` if [value] could be a JavaScript number at runtime.
+  bool canBePrimitiveNumber(covariant AbstractValue value);
 
-  AbstractValue nonNullSubtype(ClassElement element);
+  /// Returns `true` if [value] could be a JavaScript bool at runtime.
+  bool canBePrimitiveBoolean(covariant AbstractValue value);
 
-  bool isDefinitelyBool(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] could be a JavaScript array at runtime.
+  bool canBePrimitiveArray(covariant AbstractValue value);
 
-  bool isDefinitelyNum(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] is a JavaScript string, array, native HTML list
+  /// or `null` at runtime.
+  bool isIndexablePrimitive(covariant AbstractValue value);
 
-  bool isDefinitelyString(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] is a fixed-size or constant JavaScript array or
+  /// `null` at
+  /// runtime.
+  bool isFixedArray(covariant AbstractValue value);
 
-  bool isDefinitelyNumStringBool(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] is a growable JavaScript array or `null` at
+  /// runtime.
+  bool isExtendableArray(covariant AbstractValue value);
 
-  bool isDefinitelyNotNumStringBool(AbstractValue t);
+  /// Returns `true` if [value] is a mutable JavaScript array or `null` at
+  /// runtime.
+  bool isMutableArray(covariant AbstractValue value);
 
-  /// True if all values of [t] are either integers or not numbers at all.
-  ///
-  /// This does not imply that the value is an integer, since most other values
-  /// such as null are also not a non-integer double.
-  bool isDefinitelyNotNonIntegerDouble(AbstractValue t);
+  /// Returns `true` if [value] is a mutable JavaScript array, native HTML list
+  /// or `null` at runtime.
+  bool isMutableIndexable(covariant AbstractValue value);
 
-  bool isDefinitelyNonNegativeInt(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] is a JavaScript array or `null` at runtime.
+  bool isArray(covariant AbstractValue value);
 
-  bool isDefinitelyInt(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] could be a JavaScript string at runtime.
+  bool canBePrimitiveString(covariant AbstractValue value);
 
-  bool isDefinitelyUint31(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] is a non-null integer value at runtime.
+  bool isInteger(covariant AbstractValue value);
 
-  bool isDefinitelyUint32(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] is a non-null 32 bit unsigned integer value at
+  /// runtime.
+  bool isUInt32(covariant AbstractValue value);
 
-  bool isDefinitelyUint(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] is a non-null 31 bit unsigned integer value at
+  /// runtime.
+  bool isUInt31(covariant AbstractValue value);
 
-  bool isDefinitelyArray(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] is a non-null unsigned integer value at runtime.
+  bool isPositiveInteger(covariant AbstractValue value);
 
-  bool isDefinitelyMutableArray(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] is an unsigned integer value or `null` at
+  /// runtime.
+  bool isPositiveIntegerOrNull(covariant AbstractValue value);
 
-  bool isDefinitelyFixedArray(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] is an integer value or `null` at runtime.
+  bool isIntegerOrNull(covariant AbstractValue value);
 
-  bool isDefinitelyExtendableArray(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] is a non-null JavaScript number at runtime.
+  bool isNumber(covariant AbstractValue value);
 
-  bool isDefinitelyIndexable(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] is a JavaScript number or `null` at runtime.
+  bool isNumberOrNull(covariant AbstractValue value);
 
-  bool isDefinitelyMutableIndexable(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] is a non-integer number at runtime.
+  bool isDouble(covariant AbstractValue value);
 
-  bool isDefinitelyFixedLengthIndexable(AbstractValue t,
-      {bool allowNull: false});
+  /// Returns `true` if [value] is a non-integer number or `null` at runtime.
+  bool isDoubleOrNull(covariant AbstractValue value);
 
-  bool isDefinitelyIntercepted(AbstractValue t, {bool allowNull});
+  /// Returns `true` if [value] is a JavaScript bool at runtime.
+  bool isBoolean(covariant AbstractValue value);
 
-  bool isDefinitelySelfInterceptor(AbstractValue t, {bool allowNull: false});
+  /// Returns `true` if [value] is a JavaScript bool or `null` at runtime.
+  bool isBooleanOrNull(covariant AbstractValue value);
 
-  /// Given a class from the interceptor hierarchy, returns an [AbstractValue]
-  /// matching all values with that interceptor (or a subtype thereof).
-  AbstractValue getInterceptorSubtypes(ClassElement class_);
+  /// Returns `true` if [value] is a JavaScript string at runtime.
+  bool isString(covariant AbstractValue value);
 
-  bool areDisjoint(AbstractValue leftType, AbstractValue rightType);
+  /// Returns `true` if [value] is a JavaScript string or `null` at runtime.
+  bool isStringOrNull(covariant AbstractValue value);
 
-  bool isMorePreciseOrEqual(AbstractValue t1, AbstractValue t2);
+  /// Returns `true` if [value] a non-null JavaScript primitive or `null`?
+  // TODO(johnniwinther): This should probably not return true on `null`,
+  // investigate.
+  bool isPrimitive(covariant AbstractValue value);
 
-  AbstractBool isSubtypeOf(AbstractValue value, ResolutionDartType type,
-      {bool allowNull});
+  /// Returns `true` if [value] a JavaScript primitive, possible `null`.
+  bool isPrimitiveOrNull(covariant AbstractValue value);
 
-  /// Returns whether [value] is one of the falsy values: false, 0, -0, NaN,
-  /// the empty string, or null.
-  AbstractBool boolify(AbstractValue value);
+  /// Returns [AbstractValue] for the runtime values contained in either [a] or
+  /// [b].
+  AbstractValue union(covariant AbstractValue a, covariant AbstractValue b);
 
-  AbstractBool strictBoolify(AbstractValue type);
+  /// Returns [AbstractValue] for the runtime values that [a] and [b] have in
+  /// common.
+  AbstractValue intersection(
+      covariant AbstractValue a, covariant AbstractValue b);
 
-  /// Create a type mask containing at least all subtypes of [type].
-  AbstractValue subtypesOf(ResolutionDartType type);
+  /// Returns `true` if [a] and [b] have no runtime values in common.
+  bool areDisjoint(covariant AbstractValue a, covariant AbstractValue b);
 
-  /// Returns a subset of [receiver] containing at least the types
-  /// that can respond to [selector] without throwing.
-  AbstractValue receiverTypeFor(Selector selector, AbstractValue receiver);
-
-  /// The result of an index operation on [value], or the dynamic type if
-  /// unknown.
-  AbstractValue elementTypeOfIndexable(AbstractValue value);
-
-  /// The length property of [value], or `null` if unknown.
-  int getContainerLength(AbstractValue value);
-
-  /// Returns the type of the entry of [container] at a given index.
-  /// Returns `null` if unknown.
-  AbstractValue indexWithConstant(
-      AbstractValue container, ConstantValue indexValue);
+  /// Returns `true` if [a] contains all non-null runtime values.
+  bool containsAll(covariant AbstractValue a);
 }
diff --git a/pkg/compiler/lib/src/types/constants.dart b/pkg/compiler/lib/src/types/constants.dart
index 5d2b2af..58a51d7 100644
--- a/pkg/compiler/lib/src/types/constants.dart
+++ b/pkg/compiler/lib/src/types/constants.dart
@@ -23,7 +23,7 @@
   TypeMask visitConstructed(
       ConstructedConstantValue constant, ClosedWorld closedWorld) {
     if (closedWorld.interceptorData.isInterceptedClass(constant.type.element)) {
-      return closedWorld.commonMasks.nonNullType;
+      return closedWorld.abstractValueDomain.nonNullType;
     }
     return new TypeMask.nonNullExact(constant.type.element, closedWorld);
   }
@@ -45,13 +45,13 @@
     // We have to recognize double constants that are 'is int'.
     if (closedWorld.constantSystem.isInt(constant)) {
       if (constant.isMinusZero) {
-        return closedWorld.commonMasks.uint31Type;
+        return closedWorld.abstractValueDomain.uint31Type;
       } else {
         assert(constant.isPositiveInfinity || constant.isNegativeInfinity);
-        return closedWorld.commonMasks.intType;
+        return closedWorld.abstractValueDomain.intType;
       }
     }
-    return closedWorld.commonMasks.doubleType;
+    return closedWorld.abstractValueDomain.doubleType;
   }
 
   @override
@@ -63,9 +63,9 @@
       case SyntheticConstantKind.EMPTY_VALUE:
         return constant.payload;
       case SyntheticConstantKind.TYPEVARIABLE_REFERENCE:
-        return closedWorld.commonMasks.intType;
+        return closedWorld.abstractValueDomain.intType;
       case SyntheticConstantKind.NAME:
-        return closedWorld.commonMasks.stringType;
+        return closedWorld.abstractValueDomain.stringType;
       default:
         throw failedAt(CURRENT_ELEMENT_SPANNABLE,
             "Unexpected DummyConstantKind: ${constant.toStructuredText()}.");
@@ -74,63 +74,64 @@
 
   @override
   TypeMask visitBool(BoolConstantValue constant, ClosedWorld closedWorld) {
-    return closedWorld.commonMasks.boolType;
+    return closedWorld.abstractValueDomain.boolType;
   }
 
   @override
   TypeMask visitFunction(
       FunctionConstantValue constant, ClosedWorld closedWorld) {
-    return closedWorld.commonMasks.functionType;
+    return closedWorld.abstractValueDomain.functionType;
   }
 
   @override
   TypeMask visitInstantiation(
       InstantiationConstantValue constant, ClosedWorld closedWorld) {
-    return closedWorld.commonMasks.functionType;
+    return closedWorld.abstractValueDomain.functionType;
   }
 
   @override
   TypeMask visitInt(IntConstantValue constant, ClosedWorld closedWorld) {
-    if (constant.isUInt31()) return closedWorld.commonMasks.uint31Type;
-    if (constant.isUInt32()) return closedWorld.commonMasks.uint32Type;
-    if (constant.isPositive()) return closedWorld.commonMasks.positiveIntType;
-    return closedWorld.commonMasks.intType;
+    if (constant.isUInt31()) return closedWorld.abstractValueDomain.uint31Type;
+    if (constant.isUInt32()) return closedWorld.abstractValueDomain.uint32Type;
+    if (constant.isPositive())
+      return closedWorld.abstractValueDomain.positiveIntType;
+    return closedWorld.abstractValueDomain.intType;
   }
 
   @override
   TypeMask visitInterceptor(
       InterceptorConstantValue constant, ClosedWorld closedWorld) {
-    return closedWorld.commonMasks.nonNullType;
+    return closedWorld.abstractValueDomain.nonNullType;
   }
 
   @override
   TypeMask visitList(ListConstantValue constant, ClosedWorld closedWorld) {
-    return closedWorld.commonMasks.constListType;
+    return closedWorld.abstractValueDomain.constListType;
   }
 
   @override
   TypeMask visitMap(MapConstantValue constant, ClosedWorld closedWorld) {
-    return closedWorld.commonMasks.constMapType;
+    return closedWorld.abstractValueDomain.constMapType;
   }
 
   @override
   TypeMask visitNull(NullConstantValue constant, ClosedWorld closedWorld) {
-    return closedWorld.commonMasks.nullType;
+    return closedWorld.abstractValueDomain.nullType;
   }
 
   @override
   TypeMask visitNonConstant(
       NonConstantValue constant, ClosedWorld closedWorld) {
-    return closedWorld.commonMasks.nullType;
+    return closedWorld.abstractValueDomain.nullType;
   }
 
   @override
   TypeMask visitString(StringConstantValue constant, ClosedWorld closedWorld) {
-    return closedWorld.commonMasks.stringType;
+    return closedWorld.abstractValueDomain.stringType;
   }
 
   @override
   TypeMask visitType(TypeConstantValue constant, ClosedWorld closedWorld) {
-    return closedWorld.commonMasks.typeType;
+    return closedWorld.abstractValueDomain.typeType;
   }
 }
diff --git a/pkg/compiler/lib/src/types/masks.dart b/pkg/compiler/lib/src/types/masks.dart
index 4960ebc..2f8448f 100644
--- a/pkg/compiler/lib/src/types/masks.dart
+++ b/pkg/compiler/lib/src/types/masks.dart
@@ -17,7 +17,7 @@
         SelectorConstraintsStrategy;
 import '../util/util.dart';
 import '../world.dart' show ClassQuery, ClosedWorld;
-import 'abstract_value_domain.dart' show AbstractValue;
+import 'abstract_value_domain.dart';
 
 part 'container_type_mask.dart';
 part 'dictionary_type_mask.dart';
@@ -28,14 +28,14 @@
 part 'union_type_mask.dart';
 part 'value_type_mask.dart';
 
-class CommonMasks {
+class CommonMasks implements AbstractValueDomain {
   // TODO(sigmund): once we split out the backend common elements, depend
   // directly on those instead.
-  final ClosedWorld closedWorld;
+  final ClosedWorld _closedWorld;
 
-  CommonMasks(this.closedWorld);
+  CommonMasks(this._closedWorld);
 
-  CommonElements get commonElements => closedWorld.commonElements;
+  CommonElements get commonElements => _closedWorld.commonElements;
 
   TypeMask _dynamicType;
   TypeMask _nonNullType;
@@ -63,139 +63,306 @@
   TypeMask _readableArrayType;
   TypeMask _mutableArrayType;
   TypeMask _fixedArrayType;
-  TypeMask _extendableArrayType;
   TypeMask _unmodifiableArrayType;
   TypeMask _interceptorType;
 
   TypeMask get dynamicType => _dynamicType ??= new TypeMask.subclass(
-      closedWorld.commonElements.objectClass, closedWorld);
+      _closedWorld.commonElements.objectClass, _closedWorld);
 
   TypeMask get nonNullType => _nonNullType ??= new TypeMask.nonNullSubclass(
-      closedWorld.commonElements.objectClass, closedWorld);
+      _closedWorld.commonElements.objectClass, _closedWorld);
 
   TypeMask get intType => _intType ??=
-      new TypeMask.nonNullSubclass(commonElements.jsIntClass, closedWorld);
+      new TypeMask.nonNullSubclass(commonElements.jsIntClass, _closedWorld);
 
   TypeMask get uint32Type => _uint32Type ??=
-      new TypeMask.nonNullSubclass(commonElements.jsUInt32Class, closedWorld);
+      new TypeMask.nonNullSubclass(commonElements.jsUInt32Class, _closedWorld);
 
   TypeMask get uint31Type => _uint31Type ??=
-      new TypeMask.nonNullExact(commonElements.jsUInt31Class, closedWorld);
+      new TypeMask.nonNullExact(commonElements.jsUInt31Class, _closedWorld);
 
   TypeMask get positiveIntType =>
       _positiveIntType ??= new TypeMask.nonNullSubclass(
-          commonElements.jsPositiveIntClass, closedWorld);
+          commonElements.jsPositiveIntClass, _closedWorld);
 
   TypeMask get doubleType => _doubleType ??=
-      new TypeMask.nonNullExact(commonElements.jsDoubleClass, closedWorld);
+      new TypeMask.nonNullExact(commonElements.jsDoubleClass, _closedWorld);
 
   TypeMask get numType => _numType ??=
-      new TypeMask.nonNullSubclass(commonElements.jsNumberClass, closedWorld);
+      new TypeMask.nonNullSubclass(commonElements.jsNumberClass, _closedWorld);
 
   TypeMask get boolType => _boolType ??=
-      new TypeMask.nonNullExact(commonElements.jsBoolClass, closedWorld);
+      new TypeMask.nonNullExact(commonElements.jsBoolClass, _closedWorld);
 
   TypeMask get functionType => _functionType ??=
-      new TypeMask.nonNullSubtype(commonElements.functionClass, closedWorld);
+      new TypeMask.nonNullSubtype(commonElements.functionClass, _closedWorld);
 
   TypeMask get listType => _listType ??=
-      new TypeMask.nonNullExact(commonElements.jsArrayClass, closedWorld);
+      new TypeMask.nonNullExact(commonElements.jsArrayClass, _closedWorld);
 
   TypeMask get constListType => _constListType ??= new TypeMask.nonNullExact(
-      commonElements.jsUnmodifiableArrayClass, closedWorld);
+      commonElements.jsUnmodifiableArrayClass, _closedWorld);
 
   TypeMask get fixedListType => _fixedListType ??=
-      new TypeMask.nonNullExact(commonElements.jsFixedArrayClass, closedWorld);
+      new TypeMask.nonNullExact(commonElements.jsFixedArrayClass, _closedWorld);
 
   TypeMask get growableListType =>
       _growableListType ??= new TypeMask.nonNullExact(
-          commonElements.jsExtendableArrayClass, closedWorld);
+          commonElements.jsExtendableArrayClass, _closedWorld);
 
   TypeMask get mapType => _mapType ??=
-      new TypeMask.nonNullSubtype(commonElements.mapLiteralClass, closedWorld);
+      new TypeMask.nonNullSubtype(commonElements.mapLiteralClass, _closedWorld);
 
   TypeMask get constMapType => _constMapType ??= new TypeMask.nonNullSubtype(
-      commonElements.constMapLiteralClass, closedWorld);
+      commonElements.constMapLiteralClass, _closedWorld);
 
   TypeMask get stringType => _stringType ??=
-      new TypeMask.nonNullExact(commonElements.jsStringClass, closedWorld);
+      new TypeMask.nonNullExact(commonElements.jsStringClass, _closedWorld);
 
   TypeMask get typeType => _typeType ??=
-      new TypeMask.nonNullExact(commonElements.typeLiteralClass, closedWorld);
+      new TypeMask.nonNullExact(commonElements.typeLiteralClass, _closedWorld);
 
   TypeMask get syncStarIterableType => _syncStarIterableType ??=
-      new TypeMask.nonNullExact(commonElements.syncStarIterable, closedWorld);
+      new TypeMask.nonNullExact(commonElements.syncStarIterable, _closedWorld);
 
   TypeMask get asyncFutureType =>
       _asyncFutureType ??= new TypeMask.nonNullExact(
-          commonElements.futureImplementation, closedWorld);
+          commonElements.futureImplementation, _closedWorld);
 
   TypeMask get asyncStarStreamType => _asyncStarStreamType ??=
-      new TypeMask.nonNullExact(commonElements.controllerStream, closedWorld);
+      new TypeMask.nonNullExact(commonElements.controllerStream, _closedWorld);
 
   // TODO(johnniwinther): Assert that the null type has been resolved.
   TypeMask get nullType => _nullType ??= const TypeMask.empty();
 
   TypeMask get emptyType => const TypeMask.nonNullEmpty();
 
-  TypeMask get indexablePrimitiveType => _indexablePrimitiveType ??=
-      new TypeMask.nonNullSubtype(commonElements.jsIndexableClass, closedWorld);
+  TypeMask get indexablePrimitiveType =>
+      _indexablePrimitiveType ??= new TypeMask.nonNullSubtype(
+          commonElements.jsIndexableClass, _closedWorld);
 
   TypeMask get readableArrayType => _readableArrayType ??=
-      new TypeMask.nonNullSubclass(commonElements.jsArrayClass, closedWorld);
+      new TypeMask.nonNullSubclass(commonElements.jsArrayClass, _closedWorld);
 
   TypeMask get mutableArrayType =>
       _mutableArrayType ??= new TypeMask.nonNullSubclass(
-          commonElements.jsMutableArrayClass, closedWorld);
+          commonElements.jsMutableArrayClass, _closedWorld);
 
   TypeMask get fixedArrayType => _fixedArrayType ??=
-      new TypeMask.nonNullExact(commonElements.jsFixedArrayClass, closedWorld);
-
-  TypeMask get extendableArrayType =>
-      _extendableArrayType ??= new TypeMask.nonNullExact(
-          commonElements.jsExtendableArrayClass, closedWorld);
+      new TypeMask.nonNullExact(commonElements.jsFixedArrayClass, _closedWorld);
 
   TypeMask get unmodifiableArrayType =>
       _unmodifiableArrayType ??= new TypeMask.nonNullExact(
-          commonElements.jsUnmodifiableArrayClass, closedWorld);
+          commonElements.jsUnmodifiableArrayClass, _closedWorld);
 
   TypeMask get interceptorType =>
       _interceptorType ??= new TypeMask.nonNullSubclass(
-          commonElements.jsInterceptorClass, closedWorld);
+          commonElements.jsInterceptorClass, _closedWorld);
 
   bool isTypedArray(TypeMask mask) {
-    // Just checking for [:TypedData:] is not sufficient, as it is an
-    // abstract class any user-defined class can implement. So we also
-    // check for the interface [JavaScriptIndexingBehavior].
-    ClassEntity typedDataClass = closedWorld.commonElements.typedDataClass;
+    // Just checking for `TypedData` is not sufficient, as it is an abstract
+    // class any user-defined class can implement. So we also check for the
+    // interface `JavaScriptIndexingBehavior`.
+    ClassEntity typedDataClass = _closedWorld.commonElements.typedDataClass;
     return typedDataClass != null &&
-        closedWorld.isInstantiated(typedDataClass) &&
-        mask.satisfies(typedDataClass, closedWorld) &&
-        mask.satisfies(closedWorld.commonElements.jsIndexingBehaviorInterface,
-            closedWorld);
+        _closedWorld.isInstantiated(typedDataClass) &&
+        mask.satisfies(typedDataClass, _closedWorld) &&
+        mask.satisfies(_closedWorld.commonElements.jsIndexingBehaviorInterface,
+            _closedWorld);
   }
 
   bool couldBeTypedArray(TypeMask mask) {
     bool intersects(TypeMask type1, TypeMask type2) =>
-        !type1.intersection(type2, closedWorld).isEmpty;
+        !type1.intersection(type2, _closedWorld).isEmpty;
     // TODO(herhut): Maybe cache the TypeMask for typedDataClass and
     //               jsIndexingBehaviourInterface.
-    ClassEntity typedDataClass = closedWorld.commonElements.typedDataClass;
+    ClassEntity typedDataClass = _closedWorld.commonElements.typedDataClass;
     return typedDataClass != null &&
-        closedWorld.isInstantiated(typedDataClass) &&
-        intersects(mask, new TypeMask.subtype(typedDataClass, closedWorld)) &&
+        _closedWorld.isInstantiated(typedDataClass) &&
+        intersects(mask, new TypeMask.subtype(typedDataClass, _closedWorld)) &&
         intersects(
             mask,
             new TypeMask.subtype(
-                closedWorld.commonElements.jsIndexingBehaviorInterface,
-                closedWorld));
+                _closedWorld.commonElements.jsIndexingBehaviorInterface,
+                _closedWorld));
   }
 
   TypeMask createNonNullExact(ClassEntity cls) {
-    return new TypeMask.nonNullExact(cls, closedWorld);
+    return new TypeMask.nonNullExact(cls, _closedWorld);
   }
 
   TypeMask createNonNullSubtype(ClassEntity cls) {
-    return new TypeMask.nonNullSubtype(cls, closedWorld);
+    return new TypeMask.nonNullSubtype(cls, _closedWorld);
   }
+
+  TypeMask excludeNull(TypeMask mask) => mask.nonNullable();
+
+  @override
+  TypeMask includeNull(TypeMask mask) => mask.nullable();
+
+  bool containsType(TypeMask typeMask, ClassEntity cls) {
+    return _closedWorld.isInstantiated(cls) &&
+        typeMask.contains(cls, _closedWorld);
+  }
+
+  bool containsOnlyType(TypeMask typeMask, ClassEntity cls) {
+    return _closedWorld.isInstantiated(cls) && typeMask.containsOnly(cls);
+  }
+
+  bool isInstanceOf(TypeMask typeMask, ClassEntity cls) {
+    return _closedWorld.isImplemented(cls) &&
+        typeMask.satisfies(cls, _closedWorld);
+  }
+
+  bool isEmpty(TypeMask value) => value.isEmpty;
+
+  bool isExact(TypeMask value) => value.isExact || isNull(value);
+
+  bool isValue(TypeMask value) => value.isValue;
+
+  bool canBeNull(TypeMask value) => value.isNullable;
+
+  bool isNull(TypeMask value) => value.isNull;
+
+  bool canBePrimitive(TypeMask value) {
+    return canBePrimitiveNumber(value) ||
+        canBePrimitiveArray(value) ||
+        canBePrimitiveBoolean(value) ||
+        canBePrimitiveString(value) ||
+        isNull(value);
+  }
+
+  bool canBePrimitiveNumber(TypeMask value) {
+    // TODO(sra): It should be possible to test only jsDoubleClass and
+    // jsUInt31Class, since all others are superclasses of these two.
+    return containsType(value, commonElements.jsNumberClass) ||
+        containsType(value, commonElements.jsIntClass) ||
+        containsType(value, commonElements.jsPositiveIntClass) ||
+        containsType(value, commonElements.jsUInt32Class) ||
+        containsType(value, commonElements.jsUInt31Class) ||
+        containsType(value, commonElements.jsDoubleClass);
+  }
+
+  bool canBePrimitiveBoolean(TypeMask value) {
+    return containsType(value, commonElements.jsBoolClass);
+  }
+
+  bool canBePrimitiveArray(TypeMask value) {
+    return containsType(value, commonElements.jsArrayClass) ||
+        containsType(value, commonElements.jsFixedArrayClass) ||
+        containsType(value, commonElements.jsExtendableArrayClass) ||
+        containsType(value, commonElements.jsUnmodifiableArrayClass);
+  }
+
+  bool isIndexablePrimitive(TypeMask value) {
+    return value.containsOnlyString(_closedWorld) ||
+        isInstanceOf(value, commonElements.jsIndexableClass);
+  }
+
+  bool isFixedArray(TypeMask value) {
+    // TODO(sra): Recognize the union of these types as well.
+    return containsOnlyType(value, commonElements.jsFixedArrayClass) ||
+        containsOnlyType(value, commonElements.jsUnmodifiableArrayClass);
+  }
+
+  bool isExtendableArray(TypeMask value) {
+    return containsOnlyType(value, commonElements.jsExtendableArrayClass);
+  }
+
+  bool isMutableArray(TypeMask value) {
+    return isInstanceOf(value, commonElements.jsMutableArrayClass);
+  }
+
+  bool isReadableArray(TypeMask value) {
+    return isInstanceOf(value, commonElements.jsArrayClass);
+  }
+
+  bool isMutableIndexable(TypeMask value) {
+    return isInstanceOf(value, commonElements.jsMutableIndexableClass);
+  }
+
+  bool isArray(TypeMask value) => isReadableArray(value);
+
+  bool canBePrimitiveString(TypeMask value) {
+    return containsType(value, commonElements.jsStringClass);
+  }
+
+  bool isInteger(TypeMask value) {
+    return value.containsOnlyInt(_closedWorld) && !value.isNullable;
+  }
+
+  bool isUInt32(TypeMask value) {
+    return !value.isNullable &&
+        isInstanceOf(value, commonElements.jsUInt32Class);
+  }
+
+  bool isUInt31(TypeMask value) {
+    return !value.isNullable &&
+        isInstanceOf(value, commonElements.jsUInt31Class);
+  }
+
+  bool isPositiveInteger(TypeMask value) {
+    return !value.isNullable &&
+        isInstanceOf(value, commonElements.jsPositiveIntClass);
+  }
+
+  bool isPositiveIntegerOrNull(TypeMask value) {
+    return isInstanceOf(value, commonElements.jsPositiveIntClass);
+  }
+
+  bool isIntegerOrNull(TypeMask value) {
+    return value.containsOnlyInt(_closedWorld);
+  }
+
+  bool isNumber(TypeMask value) {
+    return value.containsOnlyNum(_closedWorld) && !value.isNullable;
+  }
+
+  bool isNumberOrNull(TypeMask value) {
+    return value.containsOnlyNum(_closedWorld);
+  }
+
+  bool isDouble(TypeMask value) {
+    return value.containsOnlyDouble(_closedWorld) && !value.isNullable;
+  }
+
+  bool isDoubleOrNull(TypeMask value) {
+    return value.containsOnlyDouble(_closedWorld);
+  }
+
+  bool isBoolean(TypeMask value) {
+    return value.containsOnlyBool(_closedWorld) && !value.isNullable;
+  }
+
+  bool isBooleanOrNull(TypeMask value) {
+    return value.containsOnlyBool(_closedWorld);
+  }
+
+  bool isString(TypeMask value) {
+    return value.containsOnlyString(_closedWorld) && !value.isNullable;
+  }
+
+  bool isStringOrNull(TypeMask value) {
+    return value.containsOnlyString(_closedWorld);
+  }
+
+  bool isPrimitive(TypeMask value) {
+    return (isPrimitiveOrNull(value) && !value.isNullable) || isNull(value);
+  }
+
+  bool isPrimitiveOrNull(TypeMask value) {
+    return isIndexablePrimitive(value) ||
+        isNumberOrNull(value) ||
+        isBooleanOrNull(value) ||
+        isNull(value);
+  }
+
+  TypeMask union(TypeMask a, TypeMask b) => a.union(b, _closedWorld);
+
+  TypeMask intersection(TypeMask a, TypeMask b) =>
+      a.intersection(b, _closedWorld);
+
+  bool areDisjoint(TypeMask a, TypeMask b) => a.isDisjoint(b, _closedWorld);
+
+  bool containsAll(TypeMask a) => a.containsAll(_closedWorld);
 }
diff --git a/pkg/compiler/lib/src/types/type_mask.dart b/pkg/compiler/lib/src/types/type_mask.dart
index bf79fb9..50bc096 100644
--- a/pkg/compiler/lib/src/types/type_mask.dart
+++ b/pkg/compiler/lib/src/types/type_mask.dart
@@ -334,9 +334,7 @@
    */
   bool contains(ClassEntity cls, ClosedWorld closedWorld);
 
-  /**
-   * Returns whether or not this type mask contains all types.
-   */
+  /// Returns whether or not this type mask contains all types.
   bool containsAll(ClosedWorld closedWorld);
 
   /// Returns the [ClassEntity] if this type represents a single class,
diff --git a/pkg/compiler/lib/src/types/types.dart b/pkg/compiler/lib/src/types/types.dart
index d5554c6..963a622 100644
--- a/pkg/compiler/lib/src/types/types.dart
+++ b/pkg/compiler/lib/src/types/types.dart
@@ -293,7 +293,7 @@
         parameter, () => createParameterResult(_inferrer, parameter));
   }
 
-  TypeMask get dynamicType => closedWorld.commonMasks.dynamicType;
+  TypeMask get dynamicType => closedWorld.abstractValueDomain.dynamicType;
 
   /// Returns the type of a [selector] when applied to a receiver with the given
   /// type [mask].
diff --git a/pkg/compiler/lib/src/world.dart b/pkg/compiler/lib/src/world.dart
index f2d8b34..c781eb0 100644
--- a/pkg/compiler/lib/src/world.dart
+++ b/pkg/compiler/lib/src/world.dart
@@ -28,6 +28,7 @@
     show RuntimeTypesNeed, RuntimeTypesNeedBuilder;
 import 'ordered_typeset.dart';
 import 'options.dart';
+import 'types/abstract_value_domain.dart';
 import 'types/masks.dart' show CommonMasks, FlatTypeMask, TypeMask;
 import 'universe/class_set.dart';
 import 'universe/function_set.dart' show FunctionSet;
@@ -60,7 +61,8 @@
 
   CommonElements get commonElements;
 
-  CommonMasks get commonMasks;
+  /// Returns the [AbstractValueDomain] used in the global type inference.
+  AbstractValueDomain get abstractValueDomain;
 
   ConstantSystem get constantSystem;
 
@@ -526,7 +528,7 @@
   final List<Map<ClassEntity, TypeMask>> _canonicalizedTypeMasks =
       new List<Map<ClassEntity, TypeMask>>.filled(8, null);
 
-  CommonMasks get commonMasks {
+  CommonMasks get abstractValueDomain {
     return _commonMasks;
   }
 
@@ -1069,13 +1071,13 @@
   bool includesClosureCall(Selector selector, TypeMask mask) {
     return selector.name == Identifiers.call &&
         (mask == null ||
-            mask.containsMask(commonMasks.functionType, closedWorld));
+            mask.containsMask(abstractValueDomain.functionType, closedWorld));
   }
 
   TypeMask computeReceiverType(Selector selector, TypeMask mask) {
     _ensureFunctionSet();
     if (includesClosureCall(selector, mask)) {
-      return commonMasks.dynamicType;
+      return abstractValueDomain.dynamicType;
     }
     return _allFunctions.receiverType(selector, mask, this);
   }
@@ -1101,7 +1103,7 @@
     if (includesClosureCall(selector, mask)) {
       return null;
     }
-    mask ??= commonMasks.dynamicType;
+    mask ??= abstractValueDomain.dynamicType;
     return mask.locateSingleMember(selector, this);
   }
 
@@ -1111,7 +1113,7 @@
       canReachAll = backendUsage.isInvokeOnUsed &&
           mask.needsNoSuchMethodHandling(selector, this);
     }
-    return canReachAll ? commonMasks.dynamicType : mask;
+    return canReachAll ? abstractValueDomain.dynamicType : mask;
   }
 
   bool fieldNeverChanges(MemberEntity element) {
diff --git a/tests/compiler/dart2js/codegen/expect_annotations_test.dart b/tests/compiler/dart2js/codegen/expect_annotations_test.dart
index 584af2d..bff2af5 100644
--- a/tests/compiler/dart2js/codegen/expect_annotations_test.dart
+++ b/tests/compiler/dart2js/codegen/expect_annotations_test.dart
@@ -113,12 +113,12 @@
           method, expectedParameterType, expectedReturnType, inferrer);
     } else if (expectAssumeDynamic) {
       testTypeMatch(
-          method, closedWorld.commonMasks.dynamicType, null, inferrer);
+          method, closedWorld.abstractValueDomain.dynamicType, null, inferrer);
     }
   }
 
-  TypeMask jsStringType = closedWorld.commonMasks.stringType;
-  TypeMask jsIntType = closedWorld.commonMasks.intType;
+  TypeMask jsStringType = closedWorld.abstractValueDomain.stringType;
+  TypeMask jsIntType = closedWorld.abstractValueDomain.intType;
   TypeMask coreStringType =
       new TypeMask.subtype(closedWorld.commonElements.stringClass, closedWorld);
 
diff --git a/tests/compiler/dart2js/codegen/type_inference8_test.dart b/tests/compiler/dart2js/codegen/type_inference8_test.dart
index 1222676..e1be1bb 100644
--- a/tests/compiler/dart2js/codegen/type_inference8_test.dart
+++ b/tests/compiler/dart2js/codegen/type_inference8_test.dart
@@ -38,7 +38,7 @@
       options: ['--use-old-frontend', '--disable-inlining']);
   var compiler = result.compiler;
   var typesInferrer = compiler.globalInference.typesInferrerInternal;
-  var commonMasks = typesInferrer.closedWorld.commonMasks;
+  var commonMasks = typesInferrer.closedWorld.abstractValueDomain;
   MemberElement element = findElement(compiler, "foo");
   var mask = typesInferrer.getReturnTypeOfMember(element);
   var falseType =
@@ -81,7 +81,7 @@
       options: ['--use-old-frontend', '--disable-inlining']);
   var compiler = result.compiler;
   var typesInferrer = compiler.globalInference.typesInferrerInternal;
-  var commonMasks = typesInferrer.closedWorld.commonMasks;
+  var commonMasks = typesInferrer.closedWorld.abstractValueDomain;
   MemberElement element = findElement(compiler, "foo");
   var mask = typesInferrer.getReturnTypeOfMember(element);
   // Can't infer value for foo's return type, it could be either true or false
diff --git a/tests/compiler/dart2js/inference/list_tracer_test.dart b/tests/compiler/dart2js/inference/list_tracer_test.dart
index 3df1824..4e3b241 100644
--- a/tests/compiler/dart2js/inference/list_tracer_test.dart
+++ b/tests/compiler/dart2js/inference/list_tracer_test.dart
@@ -213,7 +213,7 @@
   var compiler = result.compiler;
   var typesInferrer = compiler.globalInference.typesInferrerInternal;
   var closedWorld = typesInferrer.closedWorld;
-  var commonMasks = closedWorld.commonMasks;
+  var commonMasks = closedWorld.abstractValueDomain;
 
   checkType(String name, type) {
     var element = findMember(closedWorld, name);
diff --git a/tests/compiler/dart2js/inference/load_deferred_library_test.dart b/tests/compiler/dart2js/inference/load_deferred_library_test.dart
index 2d9e0c9..78a0ea5 100644
--- a/tests/compiler/dart2js/inference/load_deferred_library_test.dart
+++ b/tests/compiler/dart2js/inference/load_deferred_library_test.dart
@@ -10,6 +10,7 @@
 import 'package:compiler/src/elements/entities.dart';
 import 'package:compiler/src/js_model/js_strategy.dart';
 import 'package:compiler/src/kernel/element_map.dart';
+import 'package:compiler/src/types/abstract_value_domain.dart';
 import 'package:compiler/src/types/types.dart';
 import 'package:compiler/src/world.dart';
 import 'package:expect/expect.dart';
@@ -45,6 +46,7 @@
   Expect.isTrue(result.isSuccess);
   Compiler compiler = result.compiler;
   ClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
+  AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
   ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
   LibraryEntity helperLibrary =
       elementEnvironment.lookupLibrary(Uris.dart__js_helper);
@@ -64,8 +66,10 @@
       .type;
 
   if (trust) {
-    Expect.equals(closedWorld.commonMasks.stringType.nullable(), typeMask);
+    Expect.equals(
+        abstractValueDomain.includeNull(abstractValueDomain.stringType),
+        typeMask);
   } else {
-    Expect.equals(closedWorld.commonMasks.dynamicType, typeMask);
+    Expect.equals(abstractValueDomain.dynamicType, typeMask);
   }
 }
diff --git a/tests/compiler/dart2js/inference/map_tracer_test.dart b/tests/compiler/dart2js/inference/map_tracer_test.dart
index aaac0c0..c450698 100644
--- a/tests/compiler/dart2js/inference/map_tracer_test.dart
+++ b/tests/compiler/dart2js/inference/map_tracer_test.dart
@@ -233,7 +233,7 @@
   TypeGraphInferrer typesInferrer =
       compiler.globalInference.typesInferrerInternal;
   ClosedWorld closedWorld = typesInferrer.closedWorld;
-  CommonMasks commonMasks = closedWorld.commonMasks;
+  CommonMasks commonMasks = closedWorld.abstractValueDomain;
   TypeMask emptyType = new TypeMask.nonNullEmpty();
   MemberEntity aKey = findMember(closedWorld, 'aKey');
   TypeMask aKeyType = typesInferrer.getTypeOfMember(aKey);