Refactor SparseConditionalConstantPropagation to use AbstractValue

Change-Id: I742e8a08e9e57c6e9275dfd477baec4d95f2a69a
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index e0cc79c..259fc15 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteShrinker;
 import com.android.tools.r8.ir.analysis.proto.ProtoShrinker;
 import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
+import com.android.tools.r8.ir.analysis.value.AbstractValueJoiner.AbstractValueConstantPropagationJoiner;
 import com.android.tools.r8.ir.analysis.value.AbstractValueJoiner.AbstractValueFieldJoiner;
 import com.android.tools.r8.ir.analysis.value.AbstractValueJoiner.AbstractValueParameterJoiner;
 import com.android.tools.r8.ir.desugar.TypeRewriter;
@@ -102,6 +103,7 @@
   private KeepInfoCollection keepInfo = null;
 
   private final AbstractValueFactory abstractValueFactory = new AbstractValueFactory();
+  private final AbstractValueConstantPropagationJoiner abstractValueConstantPropagationJoiner;
   private final AbstractValueFieldJoiner abstractValueFieldJoiner;
   private final AbstractValueParameterJoiner abstractValueParameterJoiner;
   private final InstanceFieldInitializationInfoFactory instanceFieldInitializationInfoFactory =
@@ -175,6 +177,7 @@
         timing.time(
             "Compilation context", () -> CompilationContext.createInitialContext(options()));
     this.wholeProgramOptimizations = wholeProgramOptimizations;
+    abstractValueConstantPropagationJoiner = new AbstractValueConstantPropagationJoiner(this);
     if (enableWholeProgramOptimizations()) {
       abstractValueFieldJoiner = new AbstractValueFieldJoiner(withClassHierarchy());
       abstractValueParameterJoiner = new AbstractValueParameterJoiner(withClassHierarchy());
@@ -325,6 +328,10 @@
     return abstractValueFactory;
   }
 
+  public AbstractValueConstantPropagationJoiner getAbstractValueConstantPropagationJoiner() {
+    return abstractValueConstantPropagationJoiner;
+  }
+
   public AbstractValueFieldJoiner getAbstractValueFieldJoiner() {
     return abstractValueFieldJoiner;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/constant/Bottom.java b/src/main/java/com/android/tools/r8/ir/analysis/constant/Bottom.java
deleted file mode 100644
index 2f6e36e..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/constant/Bottom.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.ir.analysis.constant;
-
-public class Bottom extends LatticeElement {
-  private static final Bottom INSTANCE = new Bottom();
-
-  private Bottom() {
-  }
-
-  public static Bottom getInstance() {
-    return INSTANCE;
-  }
-
-  @Override
-  public LatticeElement meet(LatticeElement other) {
-    return this;
-  }
-
-  @Override
-  public boolean isBottom() {
-    return true;
-  }
-
-  @Override
-  public String toString() {
-    return "BOTTOM";
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/constant/ConstLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/constant/ConstLatticeElement.java
deleted file mode 100644
index 982f883..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/constant/ConstLatticeElement.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.ir.analysis.constant;
-
-import com.android.tools.r8.ir.code.ConstNumber;
-
-public class ConstLatticeElement extends LatticeElement {
-  private final ConstNumber value;
-
-  public ConstLatticeElement(ConstNumber value) {
-    this.value = value;
-  }
-
-  @Override
-  public LatticeElement meet(LatticeElement other) {
-    if (other.isTop()) {
-      return this;
-    }
-    if (other.isBottom()) {
-      return other;
-    }
-    if (other.isConst()) {
-      if (value.identicalNonValueNonPositionParts(other.asConst().value)) {
-        return this;
-      }
-    }
-    return Bottom.getInstance();
-  }
-
-  @Override
-  public boolean isConst() {
-    return true;
-  }
-
-  @Override
-  public ConstLatticeElement asConst() {
-    return this;
-  }
-
-  @Override
-  public String toString() {
-    return value.toString();
-  }
-
-  public ConstNumber getConstNumber() {
-    return value;
-  }
-
-  public int getIntValue() {
-    return value.getIntValue();
-  }
-
-  public long getLongValue() {
-    return value.getLongValue();
-  }
-
-  public float getFloatValue() {
-    return value.getFloatValue();
-  }
-
-  public double getDoubleValue() {
-    return value.getDoubleValue();
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/constant/ConstRangeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/constant/ConstRangeLatticeElement.java
deleted file mode 100644
index b6365e1..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/constant/ConstRangeLatticeElement.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.ir.analysis.constant;
-
-import com.android.tools.r8.ir.code.Value;
-
-public class ConstRangeLatticeElement extends LatticeElement {
-  private final Value value;
-
-  public ConstRangeLatticeElement(Value value) {
-    assert value.hasValueRange();
-    this.value = value;
-  }
-
-  @Override
-  public LatticeElement meet(LatticeElement other) {
-    if (other.isTop()) {
-      return this;
-    }
-    if (other.isBottom()) {
-      return other;
-    }
-    if (other.isValueRange()) {
-      ConstRangeLatticeElement otherRange = other.asConstRange();
-      if (getConstRange().getValueRange().equals(otherRange.getConstRange().getValueRange())) {
-        return this;
-      }
-    }
-    return Bottom.getInstance();
-  }
-
-  @Override
-  public boolean isValueRange() {
-    return true;
-  }
-
-  @Override
-  public String toString() {
-    return value.toString();
-  }
-
-  public Value getConstRange() {
-    return value;
-  }
-
-  @Override
-  public ConstRangeLatticeElement asConstRange() {
-    return this;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/constant/LatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/constant/LatticeElement.java
deleted file mode 100644
index 320425f..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/constant/LatticeElement.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.ir.analysis.constant;
-
-abstract public class LatticeElement {
-  abstract public LatticeElement meet(LatticeElement other);
-
-  public boolean isConst() {
-    return false;
-  }
-
-  public boolean isValueRange() {
-    return false;
-  }
-
-  public ConstLatticeElement asConst() {
-    return null;
-  }
-
-  public ConstRangeLatticeElement asConstRange() {
-    return null;
-  }
-
-  public boolean isTop() {
-    return false;
-  }
-
-  public boolean isBottom() {
-    return false;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java b/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
index de4cf6a..95f1b45 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/constant/SparseConditionalConstantPropagation.java
@@ -5,6 +5,9 @@
 
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.AbstractValueJoiner.AbstractValueConstantPropagationJoiner;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.IRCode;
@@ -21,9 +24,11 @@
 import com.android.tools.r8.ir.optimize.AffectedValues;
 import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.WorkList;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
 import java.util.ArrayList;
 import java.util.BitSet;
-import java.util.HashMap;
+import java.util.Comparator;
+import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -34,8 +39,11 @@
  */
 public class SparseConditionalConstantPropagation extends CodeRewriterPass<AppInfo> {
 
+  private final AbstractValueConstantPropagationJoiner joiner;
+
   public SparseConditionalConstantPropagation(AppView<?> appView) {
     super(appView);
+    joiner = appView.getAbstractValueConstantPropagationJoiner();
   }
 
   @Override
@@ -56,7 +64,7 @@
   private class SparseConditionalConstantPropagationOnCode {
 
     private final IRCode code;
-    private final Map<Value, LatticeElement> mapping = new HashMap<>();
+    private final Map<Value, AbstractValue> mapping = new IdentityHashMap<>();
 
     private final WorkList<Value> ssaEdges = WorkList.newIdentityWorkList();
 
@@ -65,6 +73,11 @@
     private final BitSet[] executableFlowEdges;
     private final BitSet visitedBlocks;
 
+    private final Map<IntSwitch, Int2ReferenceSortedMap<BasicBlock>> intSwitchKeyToTargetMapCache =
+        new IdentityHashMap<>();
+    private final Map<StringSwitch, Map<DexString, BasicBlock>> stringSwitchKeyToTargetMapCache =
+        new IdentityHashMap<>();
+
     private SparseConditionalConstantPropagationOnCode(IRCode code) {
       this.code = code;
       int maxBlockNumber = code.getCurrentBlockNumber() + 1;
@@ -108,36 +121,50 @@
       List<BasicBlock> blockToAnalyze = new ArrayList<>();
       BooleanBox hasChanged = new BooleanBox(false);
       mapping.entrySet().stream()
-          .filter(entry -> entry.getValue().isConst())
+          .filter(entry -> isConstNumber(entry.getKey(), entry.getValue()))
+          .sorted(Comparator.comparingInt(entry -> entry.getKey().getNumber()))
           .forEach(
               entry -> {
                 Value value = entry.getKey();
-                ConstNumber evaluatedConst = entry.getValue().asConst().getConstNumber();
-                if (value.definition != evaluatedConst) {
-                  if (value.isPhi()) {
-                    // D8 relies on dead code removal to get rid of the dead phi itself.
-                    if (value.hasAnyUsers()) {
-                      BasicBlock block = value.asPhi().getBlock();
-                      blockToAnalyze.add(block);
-                      // Create a new constant, because it can be an existing constant that flow
-                      // directly
-                      // into the phi.
-                      ConstNumber newConst = ConstNumber.copyOf(code, evaluatedConst);
-                      InstructionListIterator iterator = block.listIterator(code);
-                      Instruction inst = iterator.nextUntil(i -> !i.isMoveException());
-                      newConst.setPosition(inst.getPosition());
-                      if (!inst.isDebugPosition()) {
-                        iterator.previous();
-                      }
-                      iterator.add(newConst);
-                      value.replaceUsers(newConst.outValue(), affectedValues);
-                      hasChanged.set();
-                    }
-                  } else {
-                    BasicBlock block = value.definition.getBlock();
-                    InstructionListIterator iterator = block.listIterator(code);
-                    iterator.nextUntil(i -> i == value.definition);
-                    iterator.replaceCurrentInstruction(evaluatedConst, affectedValues);
+                if (!value.hasAnyUsers()) {
+                  return;
+                }
+                long constValue = entry.getValue().asSingleNumberValue().getValue();
+                if (value.isDefinedByInstructionSatisfying(Instruction::isConstNumber)) {
+                  assert value.getDefinition().asConstNumber().getRawValue() == constValue;
+                  return;
+                }
+                if (value.isPhi()) {
+                  // D8 relies on dead code removal to get rid of the dead phi itself.
+                  BasicBlock block = value.asPhi().getBlock();
+                  blockToAnalyze.add(block);
+                  InstructionListIterator iterator = block.listIterator(code);
+                  Instruction inst = iterator.nextUntil(i -> !i.isMoveException());
+                  if (!inst.isDebugPosition()) {
+                    iterator.previous();
+                  }
+                  // Create a new constant, because it can be an existing constant that flow
+                  // directly into the phi.
+                  ConstNumber newConst =
+                      ConstNumber.builder()
+                          .setFreshOutValue(code, value.getType(), value.getLocalInfo())
+                          .setPosition(inst.getPosition())
+                          .setValue(constValue)
+                          .build();
+                  iterator.add(newConst);
+                  value.replaceUsers(newConst.outValue(), affectedValues);
+                  hasChanged.set();
+                } else {
+                  Instruction definition = value.getDefinition();
+                  BasicBlock block = definition.getBlock();
+                  InstructionListIterator iterator = block.listIterator(code);
+                  iterator.nextUntil(i -> i == definition);
+                  if (!definition.isArgument()
+                      && !definition.instructionMayHaveSideEffects(
+                          appView, code.context(), this::getCachedAbstractValue)) {
+                    ConstNumber replacement =
+                        ConstNumber.builder().setOutValue(value).setValue(constValue).build();
+                    iterator.replaceCurrentInstruction(replacement, affectedValues);
                     hasChanged.set();
                   }
                 }
@@ -154,36 +181,40 @@
       return changed;
     }
 
-    private LatticeElement getLatticeElement(Value value) {
-      return mapping.getOrDefault(value, Top.getInstance());
+    private AbstractValue getCachedAbstractValue(Value value) {
+      return mapping.getOrDefault(value, AbstractValue.bottom());
     }
 
-    private void setLatticeElement(Value value, LatticeElement element) {
-      mapping.put(value, element);
+    private void setAbstractValue(Value value, AbstractValue abstractValue) {
+      mapping.put(value, abstractValue);
+    }
+
+    private boolean isConstNumber(Value value, AbstractValue abstractValue) {
+      return value.getType().isPrimitiveType() && abstractValue.isSingleNumberValue();
     }
 
     private void visitPhi(Phi phi) {
       BasicBlock phiBlock = phi.getBlock();
       int phiBlockNumber = phiBlock.getNumber();
-      LatticeElement element = Top.getInstance();
+      AbstractValue phiValue = AbstractValue.bottom();
       List<BasicBlock> predecessors = phiBlock.getPredecessors();
       int size = predecessors.size();
       for (int i = 0; i < size; i++) {
         BasicBlock predecessor = predecessors.get(i);
         if (isExecutableEdge(predecessor.getNumber(), phiBlockNumber)) {
-          element = element.meet(getLatticeElement(phi.getOperand(i)));
-          // bottom lattice can no longer be changed, thus no need to continue
-          if (element.isBottom()) {
+          phiValue =
+              joiner.join(phiValue, getCachedAbstractValue(phi.getOperand(i)), phi.getType());
+          // Top lattice element can no longer be changed, thus no need to continue.
+          if (phiValue.isUnknown()) {
             break;
           }
         }
       }
-      if (!element.isTop()) {
-        LatticeElement currentPhiElement = getLatticeElement(phi);
-        if (currentPhiElement.meet(element) != currentPhiElement) {
-          ssaEdges.addIfNotSeen(phi);
-          setLatticeElement(phi, element);
-        }
+      AbstractValue previousPhiValue = getCachedAbstractValue(phi);
+      assert joiner.lessThanOrEqualTo(previousPhiValue, phiValue, phi.getType());
+      if (!phiValue.equals(previousPhiValue)) {
+        ssaEdges.addIfNotSeen(phi);
+        setAbstractValue(phi, phiValue);
       }
     }
 
@@ -196,10 +227,12 @@
 
     private void visitInstruction(Instruction instruction) {
       if (instruction.hasOutValue() && !instruction.isDebugLocalUninitialized()) {
-        LatticeElement element = instruction.evaluate(code, this::getLatticeElement);
-        LatticeElement currentLattice = getLatticeElement(instruction.outValue());
-        if (currentLattice.meet(element) != currentLattice) {
-          setLatticeElement(instruction.outValue(), element);
+        AbstractValue value =
+            instruction.getAbstractValue(appView, code.context(), this::getCachedAbstractValue);
+        AbstractValue previousValue = getCachedAbstractValue(instruction.outValue());
+        assert joiner.lessThanOrEqualTo(previousValue, value, instruction.getOutType());
+        if (!value.equals(previousValue)) {
+          setAbstractValue(instruction.outValue(), value);
           ssaEdges.addIfNotSeen(instruction.outValue());
         }
       }
@@ -213,10 +246,11 @@
       int jumpInstBlockNumber = jumpInstBlock.getNumber();
       if (jumpInstruction.isIf()) {
         If theIf = jumpInstruction.asIf();
+        AbstractValue lhsValue = getCachedAbstractValue(theIf.lhs());
         if (theIf.isZeroTest()) {
-          LatticeElement element = getLatticeElement(theIf.inValues().get(0));
-          if (element.isConst()) {
-            BasicBlock target = theIf.targetFromCondition(element.asConst().getConstNumber());
+          if (isConstNumber(theIf.lhs(), lhsValue)) {
+            int intValue = lhsValue.asSingleNumberValue().getIntValue();
+            BasicBlock target = theIf.targetFromCondition(Integer.signum(intValue));
             if (!isExecutableEdge(jumpInstBlockNumber, target.getNumber())) {
               setExecutableEdge(jumpInstBlockNumber, target.getNumber());
               flowEdges.addIfNotSeen(target);
@@ -224,30 +258,27 @@
             return;
           }
         } else {
-          LatticeElement leftElement = getLatticeElement(theIf.inValues().get(0));
-          LatticeElement rightElement = getLatticeElement(theIf.inValues().get(1));
-          if (leftElement.isConst() && rightElement.isConst()) {
-            ConstNumber leftNumber = leftElement.asConst().getConstNumber();
-            ConstNumber rightNumber = rightElement.asConst().getConstNumber();
-            BasicBlock target = theIf.targetFromCondition(leftNumber, rightNumber);
+          AbstractValue rhsValue = getCachedAbstractValue(theIf.rhs());
+          if (isConstNumber(theIf.lhs(), lhsValue) && isConstNumber(theIf.rhs(), rhsValue)) {
+            long leftValue = lhsValue.asSingleNumberValue().getValue();
+            long rightValue = rhsValue.asSingleNumberValue().getValue();
+            BasicBlock target = theIf.targetFromCondition(leftValue, rightValue);
             if (!isExecutableEdge(jumpInstBlockNumber, target.getNumber())) {
               setExecutableEdge(jumpInstBlockNumber, target.getNumber());
               flowEdges.addIfNotSeen(target);
             }
             return;
           }
-          assert !leftElement.isTop();
-          assert !rightElement.isTop();
         }
       } else if (jumpInstruction.isIntSwitch()) {
         IntSwitch switchInst = jumpInstruction.asIntSwitch();
-        LatticeElement switchElement = getLatticeElement(switchInst.value());
-        if (switchElement.isConst()) {
+        AbstractValue value = getCachedAbstractValue(switchInst.value());
+        if (isConstNumber(switchInst.value(), value)) {
+          int intValue = value.asSingleNumberValue().getIntValue();
           BasicBlock target =
-              switchInst.getKeyToTargetMap().get(switchElement.asConst().getIntValue());
-          if (target == null) {
-            target = switchInst.fallthroughBlock();
-          }
+              intSwitchKeyToTargetMapCache
+                  .computeIfAbsent(switchInst, IntSwitch::getKeyToTargetMap)
+                  .getOrDefault(intValue, switchInst.fallthroughBlock());
           assert target != null;
           setExecutableEdge(jumpInstBlockNumber, target.getNumber());
           flowEdges.addIfNotSeen(target);
@@ -255,11 +286,18 @@
         }
       } else if (jumpInstruction.isStringSwitch()) {
         StringSwitch switchInst = jumpInstruction.asStringSwitch();
-        LatticeElement switchElement = getLatticeElement(switchInst.value());
-        if (switchElement.isConst()) {
-          // There is currently no constant propagation for strings, so it must be null.
-          assert switchElement.asConst().getConstNumber().isZero();
-          BasicBlock target = switchInst.fallthroughBlock();
+        AbstractValue value = getCachedAbstractValue(switchInst.value());
+        BasicBlock target = null;
+        if (value.isSingleStringValue()) {
+          DexString stringValue = value.asSingleStringValue().getDexString();
+          target =
+              stringSwitchKeyToTargetMapCache
+                  .computeIfAbsent(switchInst, StringSwitch::getKeyToTargetMap)
+                  .getOrDefault(stringValue, switchInst.fallthroughBlock());
+        } else if (value.isNull()) {
+          target = switchInst.fallthroughBlock();
+        }
+        if (target != null) {
           setExecutableEdge(jumpInstBlockNumber, target.getNumber());
           flowEdges.addIfNotSeen(target);
           return;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/constant/Top.java b/src/main/java/com/android/tools/r8/ir/analysis/constant/Top.java
deleted file mode 100644
index 4e08a73..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/constant/Top.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.ir.analysis.constant;
-
-public class Top extends LatticeElement {
-  private static final Top INSTANCE = new Top();
-
-  private Top() {
-  }
-
-  public static Top getInstance() {
-    return INSTANCE;
-  }
-
-  @Override
-  public LatticeElement meet(LatticeElement other) {
-    return other;
-  }
-
-  @Override
-  public boolean isTop() {
-    return true;
-  }
-
-  @Override
-  public String toString() {
-    return "TOP";
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index 0f47d7e..8d90411 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -304,10 +304,9 @@
             assert !abstractValue.isBottom();
 
             // When approximating the possible values for the $r8$classId fields from horizontal
-            // class
-            // merging, give up if the set of possible values equals the size of the merge group. In
-            // this case, the information is useless.
-            if (abstractValue.isNonConstantNumberValue()) {
+            // class merging, give up if the set of possible values equals the size of the merge
+            // group. In this case, the information is useless.
+            if (abstractValue.isNumberFromSetValue()) {
               assert HorizontalClassMergerUtils.isClassIdField(appView, field);
               NonConstantNumberValue initialAbstractValue =
                   field.getOptimizationInfo().getAbstractValue().asNonConstantNumberValue();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueJoiner.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueJoiner.java
index 0c49fca..7b7d239 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueJoiner.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueJoiner.java
@@ -11,12 +11,14 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerUtils;
+import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
 
 public abstract class AbstractValueJoiner {
 
-  protected final AppView<? extends AppInfoWithClassHierarchy> appView;
+  protected final AppView<?> appView;
 
-  private AbstractValueJoiner(AppView<? extends AppInfoWithClassHierarchy> appView) {
+  private AbstractValueJoiner(AppView<?> appView) {
     this.appView = appView;
   }
 
@@ -28,7 +30,7 @@
       AbstractValue abstractValue,
       AbstractValue otherAbstractValue,
       AbstractValueJoinerConfig config,
-      DexType type) {
+      TypeElement type) {
     if (abstractValue.isBottom() || otherAbstractValue.isUnknown()) {
       return otherAbstractValue;
     }
@@ -37,16 +39,19 @@
         || abstractValue.equals(otherAbstractValue)) {
       return abstractValue;
     }
-    return type.isReferenceType()
-        ? joinReference(abstractValue, otherAbstractValue)
-        : joinPrimitive(abstractValue, otherAbstractValue, config, type);
+    if (type.isReferenceType()) {
+      return joinReference(abstractValue, otherAbstractValue);
+    } else {
+      assert type.isPrimitiveType();
+      return joinPrimitive(abstractValue, otherAbstractValue, config, type.asPrimitiveType());
+    }
   }
 
   private AbstractValue joinPrimitive(
       AbstractValue abstractValue,
       AbstractValue otherAbstractValue,
       AbstractValueJoinerConfig config,
-      DexType type) {
+      PrimitiveTypeElement type) {
     assert !abstractValue.isNullOrAbstractValue();
     assert !otherAbstractValue.isNullOrAbstractValue();
 
@@ -77,8 +82,8 @@
   }
 
   private AbstractValue joinPrimitiveToDefiniteBitsNumberValue(
-      AbstractValue abstractValue, AbstractValue otherAbstractValue, DexType type) {
-    assert type.isIntType();
+      AbstractValue abstractValue, AbstractValue otherAbstractValue, PrimitiveTypeElement type) {
+    assert type.isInt();
     if (!abstractValue.hasDefinitelySetAndUnsetBitsInformation()
         || !otherAbstractValue.hasDefinitelySetAndUnsetBitsInformation()) {
       return unknown();
@@ -135,6 +140,26 @@
     return unknown();
   }
 
+  public static class AbstractValueConstantPropagationJoiner extends AbstractValueJoiner {
+
+    public AbstractValueConstantPropagationJoiner(AppView<?> appView) {
+      super(appView);
+    }
+
+    public AbstractValue join(
+        AbstractValue abstractValue, AbstractValue otherAbstractValue, TypeElement type) {
+      AbstractValueJoinerConfig config = AbstractValueJoinerConfig.getDefaultConfig();
+      AbstractValue result = internalJoin(abstractValue, otherAbstractValue, config, type);
+      assert result.equals(internalJoin(otherAbstractValue, abstractValue, config, type));
+      return result;
+    }
+
+    public boolean lessThanOrEqualTo(
+        AbstractValue abstractValue, AbstractValue otherAbstractValue, TypeElement type) {
+      return join(abstractValue, otherAbstractValue, type).equals(otherAbstractValue);
+    }
+  }
+
   public static class AbstractValueFieldJoiner extends AbstractValueJoiner {
 
     public AbstractValueFieldJoiner(AppView<? extends AppInfoWithClassHierarchy> appView) {
@@ -144,10 +169,9 @@
     public AbstractValue join(
         AbstractValue abstractValue, AbstractValue otherAbstractValue, ProgramField field) {
       AbstractValueJoinerConfig config = getConfig(field);
-      AbstractValue result =
-          internalJoin(abstractValue, otherAbstractValue, config, field.getType());
-      assert result.equals(
-          internalJoin(otherAbstractValue, abstractValue, config, field.getType()));
+      TypeElement type = field.getType().toTypeElement(appView);
+      AbstractValue result = internalJoin(abstractValue, otherAbstractValue, config, type);
+      assert result.equals(internalJoin(otherAbstractValue, abstractValue, config, type));
       return result;
     }
 
@@ -170,8 +194,9 @@
       // TODO(b/196017578): Use a config that allows the definite bits abstraction for parameters
       //  used in bitwise operations.
       AbstractValueJoinerConfig config = AbstractValueJoinerConfig.getDefaultConfig();
-      AbstractValue result = internalJoin(abstractValue, otherAbstractValue, config, type);
-      assert result.equals(internalJoin(otherAbstractValue, abstractValue, config, type));
+      TypeElement typeElement = type.toTypeElement(appView);
+      AbstractValue result = internalJoin(abstractValue, otherAbstractValue, config, typeElement);
+      assert result.equals(internalJoin(otherAbstractValue, abstractValue, config, typeElement));
       return result;
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/ConstantOrNonConstantNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/ConstantOrNonConstantNumberValue.java
index 4c6cf04..1afea03 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/ConstantOrNonConstantNumberValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/ConstantOrNonConstantNumberValue.java
@@ -8,12 +8,18 @@
 
 public interface ConstantOrNonConstantNumberValue {
 
-  boolean containsInt(int value);
+  boolean maybeContainsInt(int value);
+
+  long getMinInclusive();
 
   OptionalBool isSubsetOf(int[] values);
 
   ConstantOrNonConstantNumberValue asConstantOrNonConstantNumberValue();
 
+  boolean isDefiniteBitsNumberValue();
+
+  DefiniteBitsNumberValue asDefiniteBitsNumberValue();
+
   boolean isSingleNumberValue();
 
   SingleNumberValue asSingleNumberValue();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/DefiniteBitsNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/DefiniteBitsNumberValue.java
index b081050..2ba6158 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/DefiniteBitsNumberValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/DefiniteBitsNumberValue.java
@@ -22,8 +22,16 @@
   }
 
   @Override
-  public boolean containsInt(int value) {
-    return false;
+  public boolean maybeContainsInt(int value) {
+    // If a definitely set bit is unset in value, then no.
+    if ((definitelySetBits & ~value) != 0) {
+      return false;
+    }
+    // If a definitely unset bit is set in value, then no.
+    if ((definitelyUnsetBits & value) != 0) {
+      return false;
+    }
+    return true;
   }
 
   @Override
@@ -31,6 +39,15 @@
     return Long.MAX_VALUE;
   }
 
+  public int getDefinitelySetIntBits() {
+    return definitelySetBits;
+  }
+
+  @Override
+  public long getMinInclusive() {
+    return Integer.MIN_VALUE;
+  }
+
   @Override
   public boolean hasDefinitelySetAndUnsetBitsInformation() {
     return true;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java
index 11f395b..997a5ba 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromIntervalValue.java
@@ -22,7 +22,7 @@
   }
 
   @Override
-  public boolean containsInt(int value) {
+  public boolean maybeContainsInt(int value) {
     return minInclusive <= value && value <= maxInclusive;
   }
 
@@ -31,6 +31,7 @@
     return maxInclusive - minInclusive + 1;
   }
 
+  @Override
   public long getMinInclusive() {
     return minInclusive;
   }
@@ -62,8 +63,12 @@
 
   @Override
   public boolean mayOverlapWith(ConstantOrNonConstantNumberValue other) {
+    if (other.isDefiniteBitsNumberValue()) {
+      // Conservatively return true.
+      return true;
+    }
     if (other.isSingleNumberValue()) {
-      return containsInt(other.asSingleNumberValue().getIntValue());
+      return maybeContainsInt(other.asSingleNumberValue().getIntValue());
     }
     if (other.isNumberFromIntervalValue()) {
       return mayOverlapWith(other.asNumberFromIntervalValue());
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromSetValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromSetValue.java
index 629ebf1..dcdcc7e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromSetValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/NumberFromSetValue.java
@@ -19,9 +19,16 @@
   private static final int MAX_SIZE = 30;
 
   private final IntSet numbers;
+  private final int min;
 
   private NumberFromSetValue(IntSet numbers) {
+    assert !numbers.isEmpty();
     this.numbers = numbers;
+    int min = Integer.MAX_VALUE;
+    for (int number : numbers) {
+      min = Math.min(min, number);
+    }
+    this.min = min;
   }
 
   static Builder builder() {
@@ -37,7 +44,7 @@
   }
 
   @Override
-  public boolean containsInt(int value) {
+  public boolean maybeContainsInt(int value) {
     return numbers.contains(value);
   }
 
@@ -47,6 +54,11 @@
   }
 
   @Override
+  public long getMinInclusive() {
+    return min;
+  }
+
+  @Override
   public boolean isNumberFromSetValue() {
     return true;
   }
@@ -74,12 +86,15 @@
 
   @Override
   public boolean mayOverlapWith(ConstantOrNonConstantNumberValue other) {
-    if (other.isSingleNumberValue()) {
-      return containsInt(other.asSingleNumberValue().getIntValue());
+    if (other.isDefiniteBitsNumberValue()) {
+      return true;
     }
-    assert other.isNonConstantNumberValue();
+    if (other.isSingleNumberValue()) {
+      return maybeContainsInt(other.asSingleNumberValue().getIntValue());
+    }
+    assert other.isNumberFromIntervalValue() || other.isNumberFromSetValue();
     for (int number : numbers) {
-      if (other.containsInt(number)) {
+      if (other.maybeContainsInt(number)) {
         return true;
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
index e29e468..ff302c8 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
@@ -32,11 +32,16 @@
   }
 
   @Override
-  public boolean containsInt(int value) {
+  public boolean maybeContainsInt(int value) {
     return value == getIntValue();
   }
 
   @Override
+  public long getMinInclusive() {
+    return value;
+  }
+
+  @Override
   public boolean hasDefinitelySetAndUnsetBitsInformation() {
     return true;
   }
@@ -116,11 +121,14 @@
 
   @Override
   public boolean mayOverlapWith(ConstantOrNonConstantNumberValue other) {
+    if (other.isDefiniteBitsNumberValue()) {
+      return true;
+    }
     if (other.isSingleNumberValue()) {
       return equals(other.asSingleNumberValue());
     }
-    assert other.isNonConstantNumberValue();
-    return other.asNonConstantNumberValue().containsInt(getIntValue());
+    assert other.isNumberFromIntervalValue() || other.isNumberFromSetValue();
+    return other.asNonConstantNumberValue().maybeContainsInt(getIntValue());
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
index 92f7380..a926e50 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
@@ -6,14 +6,13 @@
 import com.android.tools.r8.cf.code.CfArithmeticBinop;
 import com.android.tools.r8.dex.code.DexInstruction;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.ir.analysis.constant.Bottom;
-import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
-import com.android.tools.r8.ir.analysis.constant.LatticeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.lightir.LirBuilder;
-import java.util.function.Function;
 
 public abstract class ArithmeticBinop extends Binop {
 
@@ -132,34 +131,32 @@
   }
 
   @Override
-  public LatticeElement evaluate(IRCode code, Function<Value, LatticeElement> getLatticeElement) {
-    LatticeElement leftLattice = getLatticeElement.apply(leftValue());
-    LatticeElement rightLattice = getLatticeElement.apply(rightValue());
-    if (leftLattice.isConst() && rightLattice.isConst()) {
-      ConstNumber leftConst = leftLattice.asConst().getConstNumber();
-      ConstNumber rightConst = rightLattice.asConst().getConstNumber();
-      ConstNumber newConst;
+  public AbstractValue getAbstractValue(
+      AppView<?> appView, ProgramMethod context, AbstractValueSupplier abstractValueSupplier) {
+    if (outValue.hasLocalInfo()) {
+      return AbstractValue.unknown();
+    }
+    AbstractValue leftAbstractValue = abstractValueSupplier.getAbstractValue(leftValue());
+    AbstractValue rightAbstractValue = abstractValueSupplier.getAbstractValue(rightValue());
+    if (leftAbstractValue.isSingleNumberValue() && rightAbstractValue.isSingleNumberValue()) {
+      SingleNumberValue leftConst = leftAbstractValue.asSingleNumberValue();
+      SingleNumberValue rightConst = rightAbstractValue.asSingleNumberValue();
+      long newConst;
       if (type == NumericType.INT) {
-        int result = foldIntegers(leftConst.getIntValue(), rightConst.getIntValue());
-        Value value = code.createValue(TypeElement.getInt(), getLocalInfo());
-        newConst = new ConstNumber(value, result);
+        newConst = foldIntegers(leftConst.getIntValue(), rightConst.getIntValue());
       } else if (type == NumericType.LONG) {
-        long result = foldLongs(leftConst.getLongValue(), rightConst.getLongValue());
-        Value value = code.createValue(TypeElement.getLong(), getLocalInfo());
-        newConst = new ConstNumber(value, result);
+        newConst = foldLongs(leftConst.getLongValue(), rightConst.getLongValue());
       } else if (type == NumericType.FLOAT) {
         float result = foldFloat(leftConst.getFloatValue(), rightConst.getFloatValue());
-        Value value = code.createValue(TypeElement.getFloat(), getLocalInfo());
-        newConst = new ConstNumber(value, Float.floatToIntBits(result));
+        newConst = Float.floatToIntBits(result);
       } else {
         assert type == NumericType.DOUBLE;
         double result = foldDouble(leftConst.getDoubleValue(), rightConst.getDoubleValue());
-        Value value = code.createValue(TypeElement.getDouble(), getLocalInfo());
-        newConst = new ConstNumber(value, Double.doubleToLongBits(result));
+        newConst = Double.doubleToLongBits(result);
       }
-      return new ConstLatticeElement(newConst);
+      return appView.abstractValueFactory().createSingleNumberValue(newConst);
     }
-    return Bottom.getInstance();
+    return AbstractValue.unknown();
   }
 
   abstract CfArithmeticBinop.Opcode getCfOpcode();
diff --git a/src/main/java/com/android/tools/r8/ir/code/Cmp.java b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
index bc4cf73..5c64fc6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Cmp.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
@@ -13,17 +13,16 @@
 import com.android.tools.r8.dex.code.DexInstruction;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.ir.analysis.constant.Bottom;
-import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
-import com.android.tools.r8.ir.analysis.constant.LatticeElement;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.ConstantOrNonConstantNumberValue;
+import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.lightir.LirBuilder;
-import com.android.tools.r8.utils.LongInterval;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.StringUtils.BraceType;
-import java.util.function.Function;
 
 public class Cmp extends Binop {
 
@@ -161,12 +160,16 @@
   }
 
   @Override
-  public LatticeElement evaluate(IRCode code, Function<Value, LatticeElement> getLatticeElement) {
-    LatticeElement leftLattice = getLatticeElement.apply(leftValue());
-    LatticeElement rightLattice = getLatticeElement.apply(rightValue());
-    if (leftLattice.isConst() && rightLattice.isConst()) {
-      ConstNumber leftConst = leftLattice.asConst().getConstNumber();
-      ConstNumber rightConst = rightLattice.asConst().getConstNumber();
+  public AbstractValue getAbstractValue(
+      AppView<?> appView, ProgramMethod context, AbstractValueSupplier abstractValueSupplier) {
+    if (outValue.hasLocalInfo()) {
+      return AbstractValue.unknown();
+    }
+    AbstractValue leftAbstractValue = abstractValueSupplier.getAbstractValue(leftValue());
+    AbstractValue rightAbstractValue = abstractValueSupplier.getAbstractValue(rightValue());
+    if (leftAbstractValue.isSingleNumberValue() && rightAbstractValue.isSingleNumberValue()) {
+      SingleNumberValue leftConst = leftAbstractValue.asSingleNumberValue();
+      SingleNumberValue rightConst = rightAbstractValue.asSingleNumberValue();
       int result;
       if (type == NumericType.LONG) {
         result = Integer.signum(Long.compare(leftConst.getLongValue(), rightConst.getLongValue()));
@@ -188,37 +191,28 @@
           result = (int) Math.signum(left - right);
         }
       }
-      Value value = code.createValue(TypeElement.getInt(), getLocalInfo());
-      ConstNumber newConst = new ConstNumber(value, result);
-      return new ConstLatticeElement(newConst);
-    } else if (leftLattice.isValueRange() && rightLattice.isConst()) {
-      Value leftValueRange = leftLattice.asConstRange().getConstRange();
-      ConstNumber rightConst = rightLattice.asConst().getConstNumber();
-      return buildLatticeResult(code, leftValueRange.getValueRange(),
-          rightConst.outValue().getValueRange());
-    } else if (leftLattice.isConst() && rightLattice.isValueRange()) {
-      Value rigthValueRange = rightLattice.asConstRange().getConstRange();
-      ConstNumber leftConst = leftLattice.asConst().getConstNumber();
-      return buildLatticeResult(code, leftConst.outValue().getValueRange(),
-          rigthValueRange.getValueRange());
-    } else if (leftLattice.isValueRange() && rightLattice.isValueRange()) {
-      Value rigthValueRange = rightLattice.asConstRange().getConstRange();
-      Value leftValueRange = leftLattice.asConstRange().getConstRange();
-      return buildLatticeResult(code, leftValueRange.getValueRange(),
-          rigthValueRange.getValueRange());
+      return appView.abstractValueFactory().createSingleNumberValue(result);
+    } else if (leftAbstractValue.isConstantOrNonConstantNumberValue()
+        && rightAbstractValue.isConstantOrNonConstantNumberValue()) {
+      return buildLatticeResult(
+          appView,
+          leftAbstractValue.asConstantOrNonConstantNumberValue(),
+          rightAbstractValue.asConstantOrNonConstantNumberValue());
     }
-    return Bottom.getInstance();
+    return AbstractValue.unknown();
   }
 
-  private LatticeElement buildLatticeResult(IRCode code, LongInterval leftRange,
-      LongInterval rightRange) {
-    if (leftRange.overlapsWith(rightRange)) {
-      return Bottom.getInstance();
+  private AbstractValue buildLatticeResult(
+      AppView<?> appView,
+      ConstantOrNonConstantNumberValue leftRange,
+      ConstantOrNonConstantNumberValue rightRange) {
+    if (leftRange.mayOverlapWith(rightRange)) {
+      return AbstractValue.unknown();
     }
-    int result = Integer.signum(Long.compare(leftRange.getMin(), rightRange.getMin()));
-    Value value = code.createValue(TypeElement.getInt(), getLocalInfo());
-    ConstNumber newConst = new ConstNumber(value, result);
-    return new ConstLatticeElement(newConst);
+    // Use min value as representative when values cannot overlap.
+    int result =
+        Integer.signum(Long.compare(leftRange.getMinInclusive(), rightRange.getMinInclusive()));
+    return appView.abstractValueFactory().createSingleNumberValue(result);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index ec520fb..72b2952 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -20,9 +20,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
-import com.android.tools.r8.ir.analysis.constant.Bottom;
-import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
-import com.android.tools.r8.ir.analysis.constant.LatticeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -31,7 +28,6 @@
 import com.android.tools.r8.utils.InternalOutputMode;
 import com.android.tools.r8.utils.NumberUtils;
 import java.util.Set;
-import java.util.function.Function;
 
 public class ConstNumber extends ConstInstruction {
 
@@ -318,14 +314,6 @@
   }
 
   @Override
-  public LatticeElement evaluate(IRCode code, Function<Value, LatticeElement> getLatticeElement) {
-    if (outValue.hasLocalInfo()) {
-      return Bottom.getInstance();
-    }
-    return new ConstLatticeElement(this);
-  }
-
-  @Override
   public TypeElement evaluate(AppView<?> appView) {
     return getOutType();
   }
@@ -346,6 +334,9 @@
   @Override
   public AbstractValue getAbstractValue(
       AppView<?> appView, ProgramMethod context, AbstractValueSupplier abstractValueSupplier) {
+    if (outValue.hasLocalInfo()) {
+      return AbstractValue.unknown();
+    }
     return appView.abstractValueFactory().createSingleNumberValue(value);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Div.java b/src/main/java/com/android/tools/r8/ir/code/Div.java
index a7e9822..6325179 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Div.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Div.java
@@ -15,10 +15,10 @@
 import com.android.tools.r8.dex.code.DexDivLong;
 import com.android.tools.r8.dex.code.DexDivLong2Addr;
 import com.android.tools.r8.dex.code.DexInstruction;
-import com.android.tools.r8.ir.analysis.constant.Bottom;
-import com.android.tools.r8.ir.analysis.constant.LatticeElement;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.lightir.LirBuilder;
-import java.util.function.Function;
 
 public class Div extends ArithmeticBinop {
 
@@ -132,17 +132,38 @@
   }
 
   @Override
+  public boolean instructionInstanceCanThrow(
+      AppView<?> appView,
+      ProgramMethod context,
+      AbstractValueSupplier abstractValueSupplier,
+      SideEffectAssumption assumption) {
+    if (!instructionTypeCanThrow()) {
+      return false;
+    }
+    AbstractValue rightAbstractValue = abstractValueSupplier.getAbstractValue(rightValue());
+    if (rightAbstractValue.isSingleNumberValue() && !rightAbstractValue.isZero()) {
+      return false;
+    }
+    if (rightAbstractValue.isDefiniteBitsNumberValue()
+        && rightAbstractValue.asDefiniteBitsNumberValue().getDefinitelySetIntBits() != 0) {
+      return false;
+    }
+    return true;
+  }
+
+  @Override
   public boolean instructionTypeCanThrow() {
     return type != NumericType.DOUBLE && type != NumericType.FLOAT;
   }
 
   @Override
-  public LatticeElement evaluate(IRCode code, Function<Value, LatticeElement> getLatticeElement) {
-    LatticeElement rightLattice = getLatticeElement.apply(rightValue());
-    if (rightLattice.isConst() && !rightLattice.asConst().getConstNumber().isZero()) {
-      return super.evaluate(code, getLatticeElement);
+  public AbstractValue getAbstractValue(
+      AppView<?> appView, ProgramMethod context, AbstractValueSupplier abstractValueSupplier) {
+    AbstractValue rightAbstractValue = abstractValueSupplier.getAbstractValue(rightValue());
+    if (!rightAbstractValue.isZero()) {
+      return super.getAbstractValue(appView, context, abstractValueSupplier);
     }
-    return Bottom.getInstance();
+    return AbstractValue.unknown();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index 1b21caa..99a3fe7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -23,7 +23,6 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
-import com.android.tools.r8.ir.analysis.value.UnknownValue;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.Collections;
 import java.util.List;
@@ -218,9 +217,14 @@
         appView.withClassHierarchy();
     DexEncodedField field =
         appViewWithClassHierarchy.appInfo().resolveField(getField()).getResolvedField();
-    if (field != null) {
-      return field.getOptimizationInfo().getAbstractValue();
+    if (field == null) {
+      return AbstractValue.unknown();
     }
-    return UnknownValue.getInstance();
+    AbstractValue assumeValue =
+        appView.getAssumeInfoCollection().get(field.getReference()).getAssumeValue();
+    if (!assumeValue.isUnknown()) {
+      return assumeValue;
+    }
+    return field.getOptimizationInfo().getAbstractValue();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java
index b0ecd2d..4a3db76 100644
--- a/src/main/java/com/android/tools/r8/ir/code/If.java
+++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -188,7 +188,12 @@
     assert !isZeroTest();
     assert left.outType() == right.outType();
     assert verifyTypeCompatible(left.getOutType(), type);
-    return targetFromCondition(Long.signum(left.getRawValue() - right.getRawValue()));
+    return targetFromCondition(left.getRawValue(), right.getRawValue());
+  }
+
+  public BasicBlock targetFromCondition(long left, long right) {
+    assert !isZeroTest();
+    return targetFromCondition(Long.signum(left - right));
   }
 
   public BasicBlock targetFromNonNullObject() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Inc.java b/src/main/java/com/android/tools/r8/ir/code/Inc.java
index 5d3a877..3b51d7d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Inc.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Inc.java
@@ -12,11 +12,12 @@
 import com.android.tools.r8.cf.code.CfStore;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.ir.analysis.constant.LatticeElement;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.lightir.LirBuilder;
-import java.util.function.Function;
 
 public class Inc extends Unop {
 
@@ -81,7 +82,8 @@
   }
 
   @Override
-  public LatticeElement evaluate(IRCode code, Function<Value, LatticeElement> getLatticeElement) {
+  public AbstractValue getAbstractValue(
+      AppView<?> appView, ProgramMethod context, AbstractValueSupplier abstractValueSupplier) {
     // Inc is inserted after register allocation so this is not used.
     throw new Unreachable();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index c679523..dd732b4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -17,9 +17,6 @@
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
 import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
-import com.android.tools.r8.ir.analysis.constant.Bottom;
-import com.android.tools.r8.ir.analysis.constant.ConstRangeLatticeElement;
-import com.android.tools.r8.ir.analysis.constant.LatticeElement;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
@@ -37,6 +34,7 @@
 import com.android.tools.r8.lightir.LirBuilder;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.LongInterval;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.StringUtils.BraceType;
 import com.google.common.collect.ImmutableSet;
@@ -45,7 +43,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
-import java.util.function.Function;
 import java.util.function.Predicate;
 
 public abstract class Instruction
@@ -198,6 +195,12 @@
   public AbstractValue getAbstractValue(
       AppView<?> appView, ProgramMethod context, AbstractValueSupplier abstractValueSupplier) {
     assert hasOutValue();
+    if (outValue.hasValueRange()) {
+      LongInterval longInterval = outValue.getValueRange();
+      return appView
+          .abstractValueFactory()
+          .createNumberFromIntervalValue(longInterval.getMin(), longInterval.getMax());
+    }
     return UnknownValue.getInstance();
   }
 
@@ -1496,13 +1499,6 @@
 
   public abstract boolean hasInvariantOutType();
 
-  public LatticeElement evaluate(IRCode code, Function<Value, LatticeElement> getLatticeElement) {
-    if (outValue.hasValueRange()) {
-      return new ConstRangeLatticeElement(outValue);
-    }
-    return Bottom.getInstance();
-  }
-
   // TODO(b/72693244): maybe rename to computeOutType once TypeVerificationHelper is gone?
   public TypeElement evaluate(AppView<?> appView) {
     assert outValue == null;
diff --git a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
index 5457a36..a833668 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
@@ -6,14 +6,13 @@
 import com.android.tools.r8.cf.code.CfLogicalBinop;
 import com.android.tools.r8.dex.code.DexInstruction;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.ir.analysis.constant.Bottom;
-import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
-import com.android.tools.r8.ir.analysis.constant.LatticeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.lightir.LirBuilder;
-import java.util.function.Function;
 
 public abstract class LogicalBinop extends Binop {
 
@@ -109,17 +108,19 @@
   }
 
   @Override
-  public LatticeElement evaluate(IRCode code, Function<Value, LatticeElement> getLatticeElement) {
-    LatticeElement leftLattice = getLatticeElement.apply(leftValue());
-    LatticeElement rightLattice = getLatticeElement.apply(rightValue());
-    if (leftLattice.isConst() && rightLattice.isConst()) {
-      ConstNumber leftConst = leftLattice.asConst().getConstNumber();
-      ConstNumber rightConst = rightLattice.asConst().getConstNumber();
-      ConstNumber newConst;
+  public AbstractValue getAbstractValue(
+      AppView<?> appView, ProgramMethod context, AbstractValueSupplier abstractValueSupplier) {
+    if (outValue.hasLocalInfo()) {
+      return AbstractValue.unknown();
+    }
+    AbstractValue leftAbstractValue = abstractValueSupplier.getAbstractValue(leftValue());
+    AbstractValue rightAbstractValue = abstractValueSupplier.getAbstractValue(rightValue());
+    if (leftAbstractValue.isSingleNumberValue() && rightAbstractValue.isSingleNumberValue()) {
+      SingleNumberValue leftConst = leftAbstractValue.asSingleNumberValue();
+      SingleNumberValue rightConst = rightAbstractValue.asSingleNumberValue();
+      long newConst;
       if (type == NumericType.INT) {
-        int result = foldIntegers(leftConst.getIntValue(), rightConst.getIntValue());
-        Value value = code.createValue(TypeElement.getInt(), getLocalInfo());
-        newConst = new ConstNumber(value, result);
+        newConst = foldIntegers(leftConst.getIntValue(), rightConst.getIntValue());
       } else {
         assert type == NumericType.LONG;
         long right;
@@ -129,13 +130,11 @@
         } else {
           right = rightConst.getLongValue();
         }
-        long result = foldLongs(leftConst.getLongValue(), right);
-        Value value = code.createValue(TypeElement.getLong(), getLocalInfo());
-        newConst = new ConstNumber(value, result);
+        newConst = foldLongs(leftConst.getLongValue(), right);
       }
-      return new ConstLatticeElement(newConst);
+      return appView.abstractValueFactory().createSingleNumberValue(newConst);
     }
-    return Bottom.getInstance();
+    return AbstractValue.unknown();
   }
 
   abstract CfLogicalBinop.Opcode getCfOpcode();
diff --git a/src/main/java/com/android/tools/r8/ir/code/Neg.java b/src/main/java/com/android/tools/r8/ir/code/Neg.java
index 7b7c683..222973a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Neg.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Neg.java
@@ -10,15 +10,13 @@
 import com.android.tools.r8.dex.code.DexNegInt;
 import com.android.tools.r8.dex.code.DexNegLong;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.ir.analysis.constant.Bottom;
-import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
-import com.android.tools.r8.ir.analysis.constant.LatticeElement;
-import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.lightir.LirBuilder;
-import java.util.function.Function;
 
 public class Neg extends Unop {
 
@@ -86,26 +84,28 @@
   }
 
   @Override
-  public LatticeElement evaluate(IRCode code, Function<Value, LatticeElement> getLatticeElement) {
-    LatticeElement sourceLattice = getLatticeElement.apply(source());
-    if (sourceLattice.isConst()) {
-      ConstNumber sourceConst = sourceLattice.asConst().getConstNumber();
-      TypeElement typeLattice = PrimitiveTypeElement.fromNumericType(type);
-      Value value = code.createValue(typeLattice, getLocalInfo());
-      ConstNumber newConst;
+  public AbstractValue getAbstractValue(
+      AppView<?> appView, ProgramMethod context, AbstractValueSupplier abstractValueSupplier) {
+    if (outValue.hasLocalInfo()) {
+      return AbstractValue.unknown();
+    }
+    AbstractValue sourceLattice = abstractValueSupplier.getAbstractValue(source());
+    if (sourceLattice.isSingleNumberValue()) {
+      SingleNumberValue sourceConst = sourceLattice.asSingleNumberValue();
+      long newConst;
       if (type == NumericType.INT) {
-        newConst = new ConstNumber(value, -sourceConst.getIntValue());
+        newConst = -sourceConst.getIntValue();
       } else if (type == NumericType.LONG) {
-        newConst = new ConstNumber(value, -sourceConst.getLongValue());
+        newConst = -sourceConst.getLongValue();
       } else if (type == NumericType.FLOAT) {
-        newConst = new ConstNumber(value, Float.floatToIntBits(-sourceConst.getFloatValue()));
+        newConst = Float.floatToIntBits(-sourceConst.getFloatValue());
       } else {
         assert type == NumericType.DOUBLE;
-        newConst = new ConstNumber(value, Double.doubleToLongBits(-sourceConst.getDoubleValue()));
+        newConst = Double.doubleToLongBits(-sourceConst.getDoubleValue());
       }
-      return new ConstLatticeElement(newConst);
+      return appView.abstractValueFactory().createSingleNumberValue(newConst);
     }
-    return Bottom.getInstance();
+    return AbstractValue.unknown();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Not.java b/src/main/java/com/android/tools/r8/ir/code/Not.java
index 3c6f4c7..ea07979 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Not.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Not.java
@@ -8,15 +8,13 @@
 import com.android.tools.r8.dex.code.DexNotInt;
 import com.android.tools.r8.dex.code.DexNotLong;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.ir.analysis.constant.Bottom;
-import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
-import com.android.tools.r8.ir.analysis.constant.LatticeElement;
-import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.lightir.LirBuilder;
-import java.util.function.Function;
 
 public class Not extends Unop {
 
@@ -43,22 +41,24 @@
   }
 
   @Override
-  public LatticeElement evaluate(IRCode code, Function<Value, LatticeElement> getLatticeElement) {
-    LatticeElement sourceLattice = getLatticeElement.apply(source());
-    if (sourceLattice.isConst()) {
-      ConstNumber sourceConst = sourceLattice.asConst().getConstNumber();
-      TypeElement type = PrimitiveTypeElement.fromNumericType(this.type);
-      Value value = code.createValue(type, getLocalInfo());
-      ConstNumber newConst;
+  public AbstractValue getAbstractValue(
+      AppView<?> appView, ProgramMethod context, AbstractValueSupplier abstractValueSupplier) {
+    if (outValue.hasLocalInfo()) {
+      return AbstractValue.unknown();
+    }
+    AbstractValue sourceLattice = abstractValueSupplier.getAbstractValue(source());
+    if (sourceLattice.isSingleNumberValue()) {
+      SingleNumberValue sourceConst = sourceLattice.asSingleNumberValue();
+      long newConst;
       if (this.type == NumericType.INT) {
-        newConst = new ConstNumber(value, ~sourceConst.getIntValue());
+        newConst = ~sourceConst.getIntValue();
       } else {
         assert this.type == NumericType.LONG;
-        newConst = new ConstNumber(value, ~sourceConst.getLongValue());
+        newConst = ~sourceConst.getLongValue();
       }
-      return new ConstLatticeElement(newConst);
+      return appView.abstractValueFactory().createSingleNumberValue(newConst);
     }
-    return Bottom.getInstance();
+    return AbstractValue.unknown();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Rem.java b/src/main/java/com/android/tools/r8/ir/code/Rem.java
index df5ffc1..fae9c59 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Rem.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Rem.java
@@ -15,9 +15,9 @@
 import com.android.tools.r8.dex.code.DexRemIntLit8;
 import com.android.tools.r8.dex.code.DexRemLong;
 import com.android.tools.r8.dex.code.DexRemLong2Addr;
-import com.android.tools.r8.ir.analysis.constant.Bottom;
-import com.android.tools.r8.ir.analysis.constant.LatticeElement;
-import java.util.function.Function;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
 
 public class Rem extends ArithmeticBinop {
 
@@ -131,17 +131,41 @@
   }
 
   @Override
+  public boolean instructionInstanceCanThrow(
+      AppView<?> appView,
+      ProgramMethod context,
+      AbstractValueSupplier abstractValueSupplier,
+      SideEffectAssumption assumption) {
+    if (!instructionTypeCanThrow()) {
+      return false;
+    }
+    AbstractValue rightAbstractValue = abstractValueSupplier.getAbstractValue(rightValue());
+    if (rightAbstractValue.isSingleNumberValue() && !rightAbstractValue.isZero()) {
+      return false;
+    }
+    if (rightAbstractValue.isDefiniteBitsNumberValue()
+        && rightAbstractValue.asDefiniteBitsNumberValue().getDefinitelySetIntBits() != 0) {
+      return false;
+    }
+    return true;
+  }
+
+  @Override
   public boolean instructionTypeCanThrow() {
     return type != NumericType.DOUBLE && type != NumericType.FLOAT;
   }
 
   @Override
-  public LatticeElement evaluate(IRCode code, Function<Value, LatticeElement> getLatticeElement) {
-    LatticeElement rightLattice = getLatticeElement.apply(rightValue());
-    if (rightLattice.isConst() && !rightLattice.asConst().getConstNumber().isZero()) {
-      return super.evaluate(code, getLatticeElement);
+  public AbstractValue getAbstractValue(
+      AppView<?> appView, ProgramMethod context, AbstractValueSupplier abstractValueSupplier) {
+    if (outValue.hasLocalInfo()) {
+      return AbstractValue.unknown();
     }
-    return Bottom.getInstance();
+    AbstractValue rightLattice = abstractValueSupplier.getAbstractValue(rightValue());
+    if (!rightLattice.isZero()) {
+      return super.getAbstractValue(appView, context, abstractValueSupplier);
+    }
+    return AbstractValue.unknown();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StringSwitch.java b/src/main/java/com/android/tools/r8/ir/code/StringSwitch.java
index 1b4372e..506cb80 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StringSwitch.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StringSwitch.java
@@ -14,6 +14,8 @@
 import com.android.tools.r8.lightir.LirBuilder;
 import com.android.tools.r8.utils.ThrowingBiConsumer;
 import com.android.tools.r8.utils.ThrowingConsumer;
+import java.util.IdentityHashMap;
+import java.util.Map;
 
 public class StringSwitch extends Switch {
 
@@ -30,6 +32,14 @@
     return keys[0];
   }
 
+  public Map<DexString, BasicBlock> getKeyToTargetMap() {
+    Map<DexString, BasicBlock> result = new IdentityHashMap<>();
+    for (int i = 0; i < keys.length; i++) {
+      result.put(getKey(i), targetBlock(i));
+    }
+    return result;
+  }
+
   @Override
   public int opcode() {
     return Opcodes.STRING_SWITCH;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/BranchSimplifier.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/BranchSimplifier.java
index e42475f..6e9d686 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/BranchSimplifier.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/BranchSimplifier.java
@@ -228,7 +228,7 @@
     if (theIf.getType() == IfType.EQ || theIf.getType() == IfType.NE) {
       AbstractValue lhsAbstractValue = lhs.getAbstractValue(appView, code.context());
       if (lhsAbstractValue.isConstantOrNonConstantNumberValue()
-          && !lhsAbstractValue.asConstantOrNonConstantNumberValue().containsInt(0)) {
+          && !lhsAbstractValue.asConstantOrNonConstantNumberValue().maybeContainsInt(0)) {
         // Value doesn't contain zero at all.
         simplifyIfWithKnownCondition(code, block, theIf, theIf.targetFromCondition(1));
         return true;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index 40d0bf0..7190f82 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -793,8 +793,7 @@
 
     private void clearMostRecentStaticFieldWrite(StaticGet staticGet, DexClassAndField field) {
       // If the instruction can throw, we need to clear all most-recent-writes, since subsequent
-      // field
-      // writes (if any) are not guaranteed to be executed.
+      // field writes (if any) are not guaranteed to be executed.
       if (staticGet.instructionInstanceCanThrow(appView, method)) {
         activeState.clearMostRecentFieldWrites();
       } else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/controlflow/SwitchCaseAnalyzer.java b/src/main/java/com/android/tools/r8/ir/optimize/controlflow/SwitchCaseAnalyzer.java
index 4e01bc5..538f997 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/controlflow/SwitchCaseAnalyzer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/controlflow/SwitchCaseAnalyzer.java
@@ -45,7 +45,7 @@
     if (theSwitch.isIntSwitch()) {
       int key = theSwitch.asIntSwitch().getKey(index);
       if (switchAbstractValue.isConstantOrNonConstantNumberValue()
-          && !switchAbstractValue.asConstantOrNonConstantNumberValue().containsInt(key)) {
+          && !switchAbstractValue.asConstantOrNonConstantNumberValue().maybeContainsInt(key)) {
         return true;
       }
       // TODO(b/150836439): Reimplement using AbstractValue.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java
index 9370d5a..64c5ea4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java
@@ -277,11 +277,18 @@
       abstractValue = appView.abstractValueFactory().createSingleNumberValue(0);
     } else if (appView.appInfo().isFieldWrittenByFieldPutInstruction(target)) {
       abstractValue = definition.getOptimizationInfo().getAbstractValue();
-      if (abstractValue.isUnknown() && !definition.isStatic()) {
+      if (!definition.isStatic()) {
         AbstractValue abstractReceiverValue =
             current.asInstanceGet().object().getAbstractValue(appView, code.context());
         if (abstractReceiverValue.hasObjectState()) {
-          abstractValue = abstractReceiverValue.getObjectState().getAbstractFieldValue(definition);
+          AbstractValue abstractValueFromObjectState =
+              abstractReceiverValue.getObjectState().getAbstractFieldValue(definition);
+          if (!abstractValueFromObjectState.isUnknown()) {
+            // Prefer the abstract value from the current context, as this should be more precise
+            // than the abstract value we computed for the field. If this is not always true, the
+            // meet of the two values could be computed.
+            abstractValue = abstractValueFromObjectState;
+          }
         }
       }
     } else if (definition.isStatic()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfo.java
index 53dd0bf..0004e86 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfo.java
@@ -95,7 +95,7 @@
     if (other.isUnknown()) {
       return value;
     }
-    return AbstractValue.unknown();
+    return AbstractValue.bottom();
   }
 
   private static boolean internalMeetIsSideEffectFree(