Retain Assume instructions in LIR

Change-Id: I4a3d433c5258ba452e27a04d0b3220f90c4d506a
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
index a97280f..ab49641 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -296,6 +296,9 @@
     InvokeDirect constructorInvoke =
         instructionIterator.nextUntil(
             instruction -> {
+              if (instruction.isAssume()) {
+                return false;
+              }
               // After constructor inlining we may see a load of the DEFAULT_INSTANCE field. This
               // can either be read directly using a StaticGet or accessed indirectly via a
               // synthetic accessor bridge (e.g., due to a -keep,allowshrinking rule).
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
index dad9f87..415822c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
@@ -8,11 +8,14 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.lens.GraphLens;
+import com.android.tools.r8.ir.code.Assume;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.Phi;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.optimize.AffectedValues;
+import com.android.tools.r8.utils.WorkList;
 import java.util.ArrayDeque;
 import java.util.Deque;
 import java.util.ListIterator;
@@ -37,25 +40,47 @@
     this.mapping = mapping;
   }
 
-  public void recomputeAndPropagateTypes(IRCode code, Set<Phi> affectedPhis) {
+  public Deque<Phi> unsetPhiTypes(Set<Phi> affectedPhis) {
+    if (affectedPhis.isEmpty()) {
+      return null;
+    }
+
     // We have updated at least one type lattice element which can cause phi's to narrow to a more
     // precise type. Because cycles in phi's can occur, we have to reset all phi's before
     // computing the new values.
-    Deque<Phi> worklist = new ArrayDeque<>(affectedPhis);
-    while (!worklist.isEmpty()) {
-      Phi phi = worklist.poll();
-      phi.setType(TypeElement.getBottom());
-      for (Phi affectedPhi : phi.uniquePhiUsers()) {
-        if (affectedPhis.add(affectedPhi)) {
-          worklist.add(affectedPhi);
+    WorkList<Assume> assumeWorklist = WorkList.newIdentityWorkList();
+    Deque<Phi> phiWorklist = new ArrayDeque<>(affectedPhis);
+    while (assumeWorklist.hasNext() || !phiWorklist.isEmpty()) {
+      assumeWorklist.process(
+          assume -> {
+            Value assumeValue = assume.outValue();
+            assumeValue.setType(TypeElement.getBottom());
+            for (Phi affectedPhi : assumeValue.uniquePhiUsers()) {
+              affectedPhis.add(affectedPhi);
+              phiWorklist.add(affectedPhi);
+            }
+          });
+      while (!phiWorklist.isEmpty()) {
+        Phi phi = phiWorklist.poll();
+        phi.setType(TypeElement.getBottom());
+        assumeWorklist.addIfNotSeen(phi.uniqueUsers(Instruction::isAssume));
+        for (Phi affectedPhi : phi.uniquePhiUsers()) {
+          if (affectedPhis.add(affectedPhi)) {
+            phiWorklist.add(affectedPhi);
+          }
         }
       }
     }
-    assert verifyAllChangedPhisAreScheduled(code, affectedPhis);
-    // Assuming all values have been rewritten correctly above, the non-phi operands to phi's are
-    // replaced with correct types and all other phi operands are BOTTOM.
-    assert verifyAllPhiOperandsAreBottom(affectedPhis);
+    return phiWorklist;
+  }
 
+  public void recomputeAndPropagateTypes(IRCode code, Set<Phi> affectedPhis, Deque<Phi> worklist) {
+    if (affectedPhis.isEmpty()) {
+      assert worklist == null;
+      return;
+    }
+
+    assert verifyAllChangedPhisAreScheduled(code, affectedPhis);
     AffectedValues affectedValues = new AffectedValues();
     worklist.addAll(affectedPhis);
     while (!worklist.isEmpty()) {
@@ -76,26 +101,6 @@
     affectedValues.narrowingWithAssumeRemoval(appView, code);
   }
 
-  @SuppressWarnings("ReferenceEquality")
-  private boolean verifyAllPhiOperandsAreBottom(Set<Phi> affectedPhis) {
-    for (Phi phi : affectedPhis) {
-      for (Value operand : phi.getOperands()) {
-        if (operand.isPhi()) {
-          Phi operandPhi = operand.asPhi();
-          TypeElement operandType = operandPhi.getType();
-          assert !affectedPhis.contains(operandPhi) || operandType.isBottom();
-          assert affectedPhis.contains(operandPhi)
-              || operandType.isPrimitiveType()
-              || operandType.isNullType()
-              || (operandType.isReferenceType()
-                  && operandType.fixupClassTypeReferences(appView, mapping) == operandType);
-        }
-      }
-    }
-    return true;
-  }
-
-  @SuppressWarnings("ReferenceEquality")
   private boolean verifyAllChangedPhisAreScheduled(IRCode code, Set<Phi> affectedPhis) {
     ListIterator<BasicBlock> blocks = code.listIterator();
     while (blocks.hasNext()) {
@@ -103,7 +108,7 @@
       for (Phi phi : block.getPhis()) {
         TypeElement phiType = phi.getType();
         TypeElement substituted = phiType.fixupClassTypeReferences(appView, mapping);
-        assert substituted == phiType || affectedPhis.contains(phi);
+        assert substituted.equals(phiType) || affectedPhis.contains(phi);
       }
     }
     return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index 0a55c89..8c24e18 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -31,6 +31,10 @@
   private DynamicType dynamicType;
   private final Instruction origin;
 
+  public Assume(DynamicType dynamicType, Value dest, Value src) {
+    this(dynamicType, dest, src, null);
+  }
+
   public Assume(DynamicType dynamicType, Value dest, Value src, Instruction origin) {
     super(dest, src);
     assert dynamicType != null;
@@ -171,7 +175,7 @@
 
   @Override
   public void buildLir(LirBuilder<Value, ?> builder) {
-    throw new Unreachable(ERROR_MESSAGE);
+    builder.addAssumeNonNull(src());
   }
 
   @Override
@@ -207,8 +211,11 @@
   @Override
   public TypeElement evaluate(AppView<?> appView) {
     if (hasNonNullAssumption()) {
-      assert src().getType().isReferenceType();
-      return src().getType().asReferenceType().asMeetWithNotNull();
+      if (src().getType().isReferenceType()) {
+        return src().getType().asReferenceType().asMeetWithNotNull();
+      }
+      // During IR building from LIR the src type may be bottom.
+      // During enum unboxing lens rewriting the src() may become a primitive int.
     }
     return src().getType();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index 9d3a0d4..ded2f34 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -28,7 +28,6 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
@@ -843,7 +842,7 @@
     BasicBlock invokePredecessor = invokeBlock.getPredecessors().get(0);
     BasicBlock invokeSuccessor = invokeBlock.getSuccessors().get(0);
 
-    Set<Value> argumentUsers = Sets.newIdentityHashSet();
+    AffectedValues argumentUsers = new AffectedValues();
 
     // Map all argument values. The first one needs special handling if there is a downcast type.
     List<Value> arguments = inlinee.collectArguments();
@@ -886,8 +885,7 @@
 
       // Map the argument value that has been cast.
       Value argument = arguments.get(i);
-      argumentUsers.addAll(argument.affectedValues());
-      argument.replaceUsers(castInstruction.outValue);
+      argument.replaceUsers(castInstruction.outValue, argumentUsers);
       removeArgumentInstruction(entryBlockIterator, argument);
       i++;
     } else {
@@ -896,18 +894,29 @@
 
     // Map the remaining argument values.
     for (; i < invoke.inValues().size(); i++) {
-      // TODO(zerny): Support inlining in --debug mode.
       assert !arguments.get(i).hasLocalInfo();
       Value argument = arguments.get(i);
-      argumentUsers.addAll(argument.affectedValues());
-      argument.replaceUsers(invoke.inValues().get(i));
+      Value callerArgument = invoke.getArgument(i);
+      // Remove redundant AssumeNonNull instructions.
+      if (callerArgument.getType().isReferenceType()
+          && callerArgument.getType().isDefinitelyNotNull()) {
+        for (Assume calleeAssume : argument.<Assume>uniqueUsers(Instruction::isAssume)) {
+          assert calleeAssume.hasNonNullAssumption();
+          assert !calleeAssume.hasDynamicTypeIgnoringNullability();
+          calleeAssume.outValue().replaceUsers(callerArgument, argumentUsers);
+          calleeAssume.remove();
+        }
+      }
+      argument.replaceUsers(callerArgument, argumentUsers);
       removeArgumentInstruction(entryBlockIterator, argument);
     }
 
     assert entryBlock.getInstructions().stream().noneMatch(Instruction::isArgument);
 
     // Actual arguments are flown to the inlinee.
-    new TypeAnalysis(appView, code).narrowing(argumentUsers);
+    new TypeAnalysis(appView, inlinee)
+        .setKeepRedundantBlocksAfterAssumeRemoval(true)
+        .narrowingWithAssumeRemoval(argumentUsers);
 
     // The inline entry is the first block now the argument instructions are gone.
     BasicBlock inlineEntry = inlinee.entryBlock();
@@ -922,9 +931,9 @@
       // Replace the invoke value with the return value if non-void.
       assert inlineeIterator.peekNext().isReturn();
       if (invoke.outValue() != null) {
-        Set<Value> affectedValues = invoke.outValue().affectedValues();
+        AffectedValues affectedValues = new AffectedValues();
         Return returnInstruction = inlineeIterator.peekNext().asReturn();
-        invoke.outValue().replaceUsers(returnInstruction.returnValue());
+        invoke.outValue().replaceUsers(returnInstruction.returnValue(), affectedValues);
         // The return type is flown to the original context.
         new TypeAnalysis(appView, code)
             .setKeepRedundantBlocksAfterAssumeRemoval(true)
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 23b827c..0a7b018 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -830,7 +830,7 @@
 
     if (assumeInserter != null) {
       timing.begin("Remove assume instructions");
-      new AssumeRemover(appView).run(code, methodProcessor, methodProcessingContext, timing);
+      new AssumeRemover(appView, true).run(code, methodProcessor, methodProcessingContext, timing);
       timing.end();
       previous = printMethod(code, "IR after removing assume instructions (SSA)", previous);
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeArgumentRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeArgumentRewriter.java
index 9dff1d9..e2e0ba3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeArgumentRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeArgumentRewriter.java
@@ -51,8 +51,8 @@
       DexMethod originalMethodReference,
       RewrittenPrototypeDescription prototypeChanges,
       Set<Phi> affectedPhis,
+      AffectedValues affectedValues,
       Set<UnusedArgument> unusedArguments) {
-    AffectedValues affectedValues = new AffectedValues();
     ArgumentInfoCollection argumentInfoCollection = prototypeChanges.getArgumentInfoCollection();
     List<Instruction> argumentPostlude = new LinkedList<>();
     int oldArgumentIndex = 0;
@@ -92,7 +92,7 @@
                   argumentInfo.asRewrittenTypeInfo(),
                   affectedPhis,
                   newArgumentIndex);
-          argument.outValue().replaceUsers(replacement.outValue());
+          argument.outValue().replaceUsers(replacement.outValue(), affectedValues);
         } else if (newArgumentIndex != oldArgumentIndex) {
           replacement =
               Argument.builder()
@@ -136,8 +136,6 @@
         instructionIterator.add(instruction);
       }
     }
-
-    affectedValues.narrowingWithAssumeRemoval(appView, code);
   }
 
   private Argument rewriteArgumentType(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index b293b33..c2be212 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -110,10 +110,12 @@
 import com.android.tools.r8.optimize.argumentpropagation.lenscoderewriter.NullCheckInserter;
 import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.LazyBox;
 import com.android.tools.r8.utils.timing.Timing;
 import com.android.tools.r8.verticalclassmerging.InterfaceTypeToClassTypeLensCodeRewriterHelper;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -245,14 +247,20 @@
     AffectedValues affectedValues = new AffectedValues();
     Set<UnusedArgument> unusedArguments = Sets.newIdentityHashSet();
     argumentRewriter.rewriteArguments(
-        code, originalMethodReference, prototypeChanges, affectedPhis, unusedArguments);
+        code,
+        originalMethodReference,
+        prototypeChanges,
+        affectedPhis,
+        affectedValues,
+        unusedArguments);
     if (graphLens.hasCustomLensCodeRewriter()) {
       assert graphLens.getPrevious() == codeLens
           || (graphLens.getPrevious().isMemberRebindingIdentityLens()
               && graphLens.getPrevious().asMemberRebindingIdentityLens().getPrevious() == codeLens);
       CustomLensCodeRewriter customLensCodeRewriter = graphLens.getCustomLensCodeRewriter();
       affectedPhis.addAll(
-          customLensCodeRewriter.rewriteCode(code, methodProcessor, prototypeChanges, graphLens));
+          customLensCodeRewriter.rewriteCode(
+              code, methodProcessor, prototypeChanges, affectedValues, graphLens));
     }
     rewritePartialDefault(
         code,
@@ -317,7 +325,7 @@
                 Value newOutValue = makeOutValue(invokeCustom, code, graphLens, codeLens);
                 InvokeCustom newInvokeCustom =
                     new InvokeCustom(newCallSite, newOutValue, invokeCustom.inValues());
-                iterator.replaceCurrentInstruction(newInvokeCustom);
+                iterator.replaceCurrentInstruction(newInvokeCustom, affectedValues);
                 if (newOutValue != null && newOutValue.getType() != invokeCustom.getOutType()) {
                   affectedPhis.addAll(newOutValue.uniquePhiUsers());
                 }
@@ -352,7 +360,7 @@
           case INIT_CLASS:
             {
               InitClass initClass = current.asInitClass();
-              new InstructionReplacer(code, current, iterator, affectedPhis)
+              new InstructionReplacer(code, current, iterator, affectedPhis, affectedValues)
                   .replaceInstructionIfTypeChanged(
                       initClass.getClassValue(),
                       (t, v) -> new InitClass(v, t),
@@ -390,7 +398,7 @@
               if (invokedHolder.isArrayType()) {
                 DexArrayType arrayType = invokedHolder.asArrayType();
                 DexType baseType = arrayType.getBaseType();
-                new InstructionReplacer(code, current, iterator, affectedPhis)
+                new InstructionReplacer(code, current, iterator, affectedPhis, affectedValues)
                     .replaceInstructionIfTypeChanged(
                         baseType,
                         (t, v) -> {
@@ -596,7 +604,7 @@
                     InvokeMethod.create(
                         actualInvokeType, actualTarget, newOutValue, newInValues, isInterface);
 
-                iterator.replaceCurrentInstruction(newInvoke);
+                iterator.replaceCurrentInstruction(newInvoke, affectedValues);
 
                 // Insert casts for the program to type check if interfaces has been vertically
                 // merged into their unique (non-interface) subclass. See also b/199561570.
@@ -641,12 +649,20 @@
               if (rewrittenField != field) {
                 newOutValue = makeOutValue(instanceGet, code, rewrittenField);
                 iterator.replaceCurrentInstruction(
-                    new InstanceGet(newOutValue, instanceGet.object(), rewrittenField));
+                    new InstanceGet(newOutValue, instanceGet.object(), rewrittenField),
+                    affectedValues);
               }
               if (newOutValue != null) {
                 if (lookup.hasReadCastType() && newOutValue.hasNonDebugUsers()) {
                   insertReadCast(
-                      code, blocks, iterator, instanceGet, lookup, newOutValue, affectedPhis);
+                      code,
+                      blocks,
+                      iterator,
+                      instanceGet,
+                      lookup,
+                      newOutValue,
+                      affectedPhis,
+                      affectedValues);
                 } else if (newOutValue.getType() != instanceGet.getOutType()) {
                   affectedPhis.addAll(newOutValue.uniquePhiUsers());
                 }
@@ -686,12 +702,20 @@
               Value newOutValue = null;
               if (rewrittenField != field) {
                 newOutValue = makeOutValue(staticGet, code, rewrittenField);
-                iterator.replaceCurrentInstruction(new StaticGet(newOutValue, rewrittenField));
+                iterator.replaceCurrentInstruction(
+                    new StaticGet(newOutValue, rewrittenField), affectedValues);
               }
               if (newOutValue != null) {
                 if (lookup.hasReadCastType() && newOutValue.hasNonDebugUsers()) {
                   insertReadCast(
-                      code, blocks, iterator, staticGet, lookup, newOutValue, affectedPhis);
+                      code,
+                      blocks,
+                      iterator,
+                      staticGet,
+                      lookup,
+                      newOutValue,
+                      affectedPhis,
+                      affectedValues);
                 } else if (newOutValue.getType() != staticGet.getOutType()) {
                   affectedPhis.addAll(newOutValue.uniquePhiUsers());
                 }
@@ -723,7 +747,7 @@
           case CHECK_CAST:
             {
               CheckCast checkCast = current.asCheckCast();
-              new InstructionReplacer(code, current, iterator, affectedPhis)
+              new InstructionReplacer(code, current, iterator, affectedPhis, affectedValues)
                   .replaceInstructionIfTypeChanged(
                       checkCast.getType(),
                       (t, v) ->
@@ -737,7 +761,7 @@
             {
               ConstClass constClass = current.asConstClass();
               Instruction replacement =
-                  new InstructionReplacer(code, current, iterator, affectedPhis)
+                  new InstructionReplacer(code, current, iterator, affectedPhis, affectedValues)
                       .replaceInstructionIfTypeChanged(
                           constClass.getType(),
                           (t, v) ->
@@ -779,7 +803,7 @@
           case INSTANCE_OF:
             {
               InstanceOf instanceOf = current.asInstanceOf();
-              new InstructionReplacer(code, current, iterator, affectedPhis)
+              new InstructionReplacer(code, current, iterator, affectedPhis, affectedValues)
                   .replaceInstructionIfTypeChanged(
                       instanceOf.type(),
                       (t, v) -> new InstanceOf(v, instanceOf.value(), t),
@@ -791,7 +815,7 @@
           case INVOKE_MULTI_NEW_ARRAY:
             {
               InvokeMultiNewArray multiNewArray = current.asInvokeMultiNewArray();
-              new InstructionReplacer(code, current, iterator, affectedPhis)
+              new InstructionReplacer(code, current, iterator, affectedPhis, affectedValues)
                   .replaceInstructionIfTypeChanged(
                       multiNewArray.getArrayType(),
                       (t, v) -> new InvokeMultiNewArray(t, v, multiNewArray.inValues()),
@@ -803,7 +827,7 @@
           case NEW_ARRAY_FILLED:
             {
               NewArrayFilled newArray = current.asNewArrayFilled();
-              new InstructionReplacer(code, current, iterator, affectedPhis)
+              new InstructionReplacer(code, current, iterator, affectedPhis, affectedValues)
                   .replaceInstructionIfTypeChanged(
                       newArray.getArrayType(),
                       (t, v) -> new NewArrayFilled(t, v, newArray.inValues()),
@@ -815,7 +839,7 @@
           case MOVE_EXCEPTION:
             {
               MoveException moveException = current.asMoveException();
-              new InstructionReplacer(code, current, iterator, affectedPhis)
+              new InstructionReplacer(code, current, iterator, affectedPhis, affectedValues)
                   .replaceInstructionIfTypeChanged(
                       moveException.getExceptionType(),
                       (t, v) -> new MoveException(v, t, options),
@@ -827,7 +851,7 @@
           case NEW_ARRAY_EMPTY:
             {
               NewArrayEmpty newArrayEmpty = current.asNewArrayEmpty();
-              new InstructionReplacer(code, current, iterator, affectedPhis)
+              new InstructionReplacer(code, current, iterator, affectedPhis, affectedValues)
                   .replaceInstructionIfTypeChanged(
                       newArrayEmpty.type,
                       (t, v) -> new NewArrayEmpty(v, newArrayEmpty.size(), t),
@@ -839,7 +863,7 @@
           case NEW_INSTANCE:
             {
               DexType type = current.asNewInstance().clazz;
-              new InstructionReplacer(code, current, iterator, affectedPhis)
+              new InstructionReplacer(code, current, iterator, affectedPhis, affectedValues)
                   .replaceInstructionIfTypeChanged(type, NewInstance::new, graphLens, codeLens);
             }
             break;
@@ -883,7 +907,11 @@
             break;
 
           case ASSUME:
-            assert false;
+            {
+              // We should only have AssumeNonNull instructions which do not require lens rewriting.
+              Assume assumeInstruction = current.asAssume();
+              assert assumeInstruction.getDynamicType().isNotNullType();
+            }
             break;
 
           default:
@@ -894,6 +922,7 @@
               if (substituted != type) {
                 current.outValue().setType(substituted);
                 affectedPhis.addAll(current.outValue().uniquePhiUsers());
+                current.outValue().addAffectedValuesTo(affectedValues);
               }
             }
             break;
@@ -901,14 +930,21 @@
       }
     }
     if (mayHaveUnreachableBlocks) {
-      code.removeUnreachableBlocks(affectedValues, affectedPhis::remove);
+      code.removeUnreachableBlocks(
+          affectedValues,
+          v -> {
+            affectedPhis.remove(v);
+            affectedValues.remove(v);
+          });
     }
-    affectedValues.narrowingWithAssumeRemoval(appView, code);
-    if (!affectedPhis.isEmpty()) {
-      new DestructivePhiTypeUpdater(appView, graphLens, codeLens)
-          .recomputeAndPropagateTypes(code, affectedPhis);
-    }
+    affectedValues.removeAssumeNonNullInstructionsAfterEnumUnboxing();
     nullCheckInserter.processWorklist();
+    DestructivePhiTypeUpdater phiUpdater =
+        new DestructivePhiTypeUpdater(appView, graphLens, codeLens);
+    Iterables.addAll(affectedPhis, IterableUtils.filter(affectedValues, Value::isPhi));
+    Deque<Phi> phisToProcess = phiUpdater.unsetPhiTypes(affectedPhis);
+    affectedValues.propagateWithAssumeRemoval(appView, code, typeAnalysis -> {});
+    phiUpdater.recomputeAndPropagateTypes(code, affectedPhis, phisToProcess);
     code.removeAllDeadAndTrivialPhis();
     code.removeRedundantBlocks();
     removeUnusedArguments(code, unusedArguments);
@@ -919,7 +955,6 @@
     assert code.isConsistentSSABeforeTypesAreCorrect(appView);
   }
 
-
   private void insertReadCast(
       IRCode code,
       BasicBlockIterator blocks,
@@ -927,12 +962,13 @@
       FieldGet fieldGet,
       FieldLookupResult lookup,
       Value newOutValue,
-      Set<Phi> affectedPhis) {
+      Set<Phi> affectedPhis,
+      AffectedValues affectedValues) {
     TypeElement castTypeElement =
         TypeElement.fromDexType(
             lookup.getReadCastType(), newOutValue.getType().nullability(), appView);
     Value castOutValue = code.createValue(castTypeElement);
-    newOutValue.replaceUsers(castOutValue);
+    newOutValue.replaceUsers(castOutValue, affectedValues);
     CheckCast checkCast =
         SafeCheckCast.builder()
             .setCastType(lookup.getReadCastType())
@@ -1225,13 +1261,19 @@
     private final Instruction current;
     private final InstructionListIterator iterator;
     private final Set<Phi> affectedPhis;
+    private final AffectedValues affectedValues;
 
     InstructionReplacer(
-        IRCode code, Instruction current, InstructionListIterator iterator, Set<Phi> affectedPhis) {
+        IRCode code,
+        Instruction current,
+        InstructionListIterator iterator,
+        Set<Phi> affectedPhis,
+        AffectedValues affectedValues) {
       this.code = code;
       this.current = current;
       this.iterator = iterator;
       this.affectedPhis = affectedPhis;
+      this.affectedValues = affectedValues;
     }
 
     @SuppressWarnings("ReferenceEquality")
@@ -1244,7 +1286,7 @@
       if (newType != type) {
         Value newOutValue = makeOutValue(current, code, graphLens, codeLens);
         Instruction newInstruction = constructor.apply(newType, newOutValue);
-        iterator.replaceCurrentInstruction(newInstruction);
+        iterator.replaceCurrentInstruction(newInstruction, affectedValues);
         if (newOutValue != null) {
           if (!newOutValue.getType().equals(current.getOutType())) {
             affectedPhis.addAll(newOutValue.uniquePhiUsers());
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
index 4e4b0ea..05c1b36 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ir.analysis.proto.ProtoReferences;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.conversion.passes.AdaptClassStringsRewriter;
+import com.android.tools.r8.ir.conversion.passes.AssumeRemover;
 import com.android.tools.r8.ir.conversion.passes.CodeRewriterPassCollection;
 import com.android.tools.r8.ir.conversion.passes.ConstResourceNumberRemover;
 import com.android.tools.r8.ir.conversion.passes.ConstResourceNumberRewriter;
@@ -203,6 +204,7 @@
     CodeRewriterPassCollection codeRewriterPassCollection =
         CodeRewriterPassCollection.createFromNullable(
             AdaptClassStringsRewriter.create(appView),
+            new AssumeRemover(appView),
             new ConstResourceNumberRemover(appView),
             new StoreStoreFenceToInvokeRewriter(appView),
             new OriginalFieldWitnessRemover(appView),
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/AssumeRemover.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/AssumeRemover.java
index 062558f..22492fa 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/AssumeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/AssumeRemover.java
@@ -17,8 +17,15 @@
 
 public class AssumeRemover extends CodeRewriterPass<AppInfo> {
 
+  private final boolean keepAssumeNonNull;
+
   public AssumeRemover(AppView<?> appView) {
+    this(appView, false);
+  }
+
+  public AssumeRemover(AppView<?> appView, boolean keepAssumeNonNull) {
     super(appView);
+    this.keepAssumeNonNull = keepAssumeNonNull;
   }
 
   @Override
@@ -47,6 +54,16 @@
           continue;
         }
 
+        Value src = assumeInstruction.src();
+        Value dest = assumeInstruction.outValue();
+        if (src.getType().isNullable() && assumeInstruction.hasNonNullAssumption()) {
+          if (keepAssumeNonNull) {
+            assumeInstruction.clearDynamicTypeAssumption();
+            continue;
+          }
+          valuesThatRequireWidening.addAll(dest.affectedValues());
+        }
+
         // Delete the Assume instruction and replace uses of the out-value by the in-value:
         //   y <- Assume(x)
         //   ...
@@ -55,11 +72,6 @@
         // becomes:
         //
         //   x.foo()
-        Value src = assumeInstruction.src();
-        Value dest = assumeInstruction.outValue();
-        valuesThatRequireWidening.addAll(dest.affectedValues());
-
-        // Replace `dest` by `src`.
         needToCheckTrivialPhis |= dest.numberOfPhiUsers() > 0;
         dest.replaceUsers(src);
         assumeInstruction.remove();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaringGraphLens.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaringGraphLens.java
index 8702d9c..d71d0f3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaringGraphLens.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/R8LibraryDesugaringGraphLens.java
@@ -43,6 +43,7 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.LirToLirDesugaredLibraryLibRewriter;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.LirToLirDesugaredLibraryRetargeter;
 import com.android.tools.r8.ir.desugar.itf.LirToLirInterfaceMethodRewriter;
+import com.android.tools.r8.ir.optimize.AffectedValues;
 import com.android.tools.r8.ir.optimize.CustomLensCodeRewriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -211,6 +212,7 @@
         IRCode code,
         MethodProcessor methodProcessor,
         RewrittenPrototypeDescription prototypeChanges,
+        AffectedValues affectedValues,
         NonIdentityGraphLens lens) {
       boolean changed = false;
       BasicBlockIterator blocks = code.listIterator();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AffectedValues.java b/src/main/java/com/android/tools/r8/ir/optimize/AffectedValues.java
index 2258b27..1f02eeb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AffectedValues.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AffectedValues.java
@@ -8,13 +8,17 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
+import com.android.tools.r8.ir.code.Assume;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.Value;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -51,6 +55,12 @@
     }
   }
 
+  public void propagate(AppView<?> appView, IRCode code) {
+    if (hasNext()) {
+      new TypeAnalysis(appView, code).propagate(this);
+    }
+  }
+
   public void propagateWithAssumeRemoval(
       AppView<?> appView, IRCode code, Consumer<TypeAnalysis> typeAnalysisConsumer) {
     if (hasNext()) {
@@ -67,6 +77,25 @@
     }
   }
 
+  public void removeAssumeNonNullInstructionsAfterEnumUnboxing() {
+    List<Assume> assumeInstructionsToRemove = new ArrayList<>();
+    removeIf(
+        value -> {
+          if (value.isDefinedByInstructionSatisfying(Instruction::isAssume)) {
+            Assume assume = value.getDefinition().asAssume();
+            if (value.getDefinition().getFirstOperand().getType().isInt()) {
+              assumeInstructionsToRemove.add(assume);
+              return true;
+            }
+          }
+          return false;
+        });
+    for (Assume assume : assumeInstructionsToRemove) {
+      assume.outValue().replaceUsers(assume.src(), this);
+      assume.remove();
+    }
+  }
+
   @Override
   public boolean add(Value value) {
     return affectedValues.add(value);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CustomLensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CustomLensCodeRewriter.java
index 1f046d2..73b12ee 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CustomLensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CustomLensCodeRewriter.java
@@ -15,11 +15,12 @@
 public interface CustomLensCodeRewriter {
 
   CustomLensCodeRewriter EMPTY =
-      (code, methodProcessor, prototypeChanges, lens) -> Sets.newIdentityHashSet();
+      (code, methodProcessor, prototypeChanges, affectedValues, lens) -> Sets.newIdentityHashSet();
 
   Set<Phi> rewriteCode(
       IRCode code,
       MethodProcessor methodProcessor,
       RewrittenPrototypeDescription prototypeChanges,
+      AffectedValues affectedValues,
       NonIdentityGraphLens lens);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index 849a899..ee8b6c7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -85,7 +85,8 @@
         if (current.isAssumeWithNonNullAssumption()) {
           Assume nonNull = current.asAssume();
           Instruction origin = nonNull.origin();
-          if (origin.isInvokeInterface()
+          if (origin != null
+              && origin.isInvokeInterface()
               && !origin.asInvokeInterface().getReceiver().hasLocalInfo()
               && devirtualizedCall.containsKey(origin.asInvokeInterface())
               && origin.asInvokeInterface().getReceiver() == nonNull.getAliasForOutValue()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index c925743..66e1512 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.proto.ProtoInliningReasonStrategy;
 import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.BasicBlockIterator;
@@ -1381,7 +1382,7 @@
     return false;
   }
 
-  /** Applies member rebinding to the inlinee and inserts assume instructions. */
+  /** Applies member value propagation to the inlinee and inserts assume instructions. */
   private void postProcessInlineeBlocks(
       IRCode code,
       BasicBlockIterator blockIterator,
@@ -1390,7 +1391,7 @@
       Timing timing) {
     BasicBlock state = IteratorUtils.peekNext(blockIterator);
 
-    // Insert assume instructions in the inlinee blocks.
+    // Run member value propagation on the inlinee blocks.
     Set<BasicBlock> inlineeBlocks = Sets.newIdentityHashSet();
     rewindBlockIterator(
         blockIterator,
@@ -1400,6 +1401,10 @@
             inlineeBlocks.add(inlineeBlock);
           }
         });
+    applyMemberValuePropagationToInlinee(code, blockIterator, inlineeBlocks);
+
+    // Insert assume instructions in the inlinee blocks.
+    rewindBlockIterator(blockIterator, block);
     insertAssumeInstructions(code, blockIterator, inlineeBlocks, timing);
 
     // Restore the old state of the iterator.
@@ -1417,6 +1422,19 @@
     assert !blockIterator.hasNext();
   }
 
+  private void applyMemberValuePropagationToInlinee(
+      IRCode code, BasicBlockIterator blockIterator, Set<BasicBlock> inlineeBlocks) {
+    AffectedValues affectedValues = new AffectedValues();
+    new R8MemberValuePropagation(appView)
+        .run(code, blockIterator, affectedValues, inlineeBlocks::contains);
+    if (!affectedValues.isEmpty()) {
+      new TypeAnalysis(appView, code)
+          .setKeepRedundantBlocksAfterAssumeRemoval(true)
+          .narrowingWithAssumeRemoval(affectedValues);
+    }
+    assert !blockIterator.hasNext();
+  }
+
   private void rewindBlockIterator(ListIterator<BasicBlock> blockIterator, BasicBlock callerBlock) {
     rewindBlockIterator(blockIterator, callerBlock, ConsumerUtils.emptyConsumer());
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 09141b9..aca7012 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -46,6 +46,7 @@
 import com.android.tools.r8.ir.code.StaticGet;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.optimize.AffectedValues;
 import com.android.tools.r8.ir.optimize.CustomLensCodeRewriter;
 import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData;
 import com.android.tools.r8.ir.optimize.enums.classification.CheckNotNullEnumUnboxerMethodClassification;
@@ -153,6 +154,7 @@
       IRCode code,
       MethodProcessor methodProcessor,
       RewrittenPrototypeDescription prototypeChanges,
+      AffectedValues affectedValues,
       NonIdentityGraphLens graphLens) {
     // We should not process the enum methods, they will be removed and they may contain invalid
     // rewriting rules.
@@ -201,6 +203,7 @@
               code,
               eventConsumer,
               affectedPhis,
+              affectedValues,
               convertedEnums,
               blocks,
               block,
@@ -216,12 +219,14 @@
               convertedEnums,
               iterator,
               affectedPhis,
+              affectedValues,
               eventConsumer);
         } else if (instruction.isStaticGet()) {
           rewriteStaticGet(
               code,
               eventConsumer,
               affectedPhis,
+              affectedValues,
               convertedEnums,
               seenBlocks,
               instructionsToRemove,
@@ -229,7 +234,12 @@
               instruction.asStaticGet());
         } else if (instruction.isInstanceGet()) {
           rewriteInstanceGet(
-              code, eventConsumer, convertedEnums, iterator, instruction.asInstanceGet());
+              code,
+              eventConsumer,
+              affectedValues,
+              convertedEnums,
+              iterator,
+              instruction.asInstanceGet());
         } else if (instruction.isArrayAccess()) {
           rewriteArrayAccess(
               code, affectedPhis, convertedEnums, iterator, instruction.asArrayAccess());
@@ -351,6 +361,7 @@
   private void rewriteInstanceGet(
       IRCode code,
       EnumUnboxerMethodProcessorEventConsumer eventConsumer,
+      AffectedValues affectedValues,
       Map<Instruction, DexType> convertedEnums,
       InstructionListIterator iterator,
       InstanceGet instanceGet) {
@@ -369,7 +380,7 @@
       }
       InvokeStatic invoke =
           new InvokeStatic(fieldMethod.getReference(), rewrittenOutValue, ImmutableList.of(in));
-      iterator.replaceCurrentInstruction(invoke);
+      iterator.replaceCurrentInstruction(invoke, affectedValues);
       if (unboxedEnumsData.isUnboxedEnum(instanceGet.getField().type)) {
         convertedEnums.put(invoke, instanceGet.getField().type);
       }
@@ -380,6 +391,7 @@
       IRCode code,
       EnumUnboxerMethodProcessorEventConsumer eventConsumer,
       Set<Phi> affectedPhis,
+      AffectedValues affectedValues,
       Map<Instruction, DexType> convertedEnums,
       Set<BasicBlock> seenBlocks,
       Set<Instruction> instructionsToRemove,
@@ -409,7 +421,7 @@
               .setFreshOutValue(appView, code)
               .setSingleArgument(sizeValue)
               .build();
-      iterator.replaceCurrentInstruction(invoke);
+      iterator.replaceCurrentInstruction(invoke, affectedValues);
 
       convertedEnums.put(invoke, holder);
 
@@ -417,11 +429,11 @@
       // clone(). If so, remove it, since SharedUtilityClass.values(size) returns a fresh
       // array. This is needed because the javac generated implementation of MyEnum.values()
       // is implemented as `return $VALUES.clone()`.
-      removeRedundantValuesArrayCloning(invoke, instructionsToRemove, seenBlocks);
+      removeRedundantValuesArrayCloning(invoke, affectedValues, instructionsToRemove, seenBlocks);
     } else if (unboxedEnumsData.hasUnboxedValueFor(field)) {
       // Replace by ordinal + 1 for null check (null is 0).
       ConstNumber intConstant = code.createIntConstant(unboxedEnumsData.getUnboxedValue(field));
-      iterator.replaceCurrentInstruction(intConstant);
+      iterator.replaceCurrentInstruction(intConstant, affectedValues);
       convertedEnums.put(intConstant, holder);
     } else {
       // Nothing to do, handled by lens code rewriting.
@@ -440,6 +452,7 @@
       IRCode code,
       EnumUnboxerMethodProcessorEventConsumer eventConsumer,
       Set<Phi> affectedPhis,
+      AffectedValues affectedValues,
       Map<Instruction, DexType> convertedEnums,
       BasicBlockIterator blocks,
       BasicBlock block,
@@ -456,17 +469,20 @@
           || invokedMethod.match(factory.enumMembers.hashCode)) {
         replaceEnumInvoke(
             iterator,
+            affectedValues,
             invoke,
             getSharedUtilityClass().ensureOrdinalMethod(appView, context, eventConsumer));
       } else if (invokedMethod.match(factory.enumMembers.equals)) {
         replaceEnumInvoke(
             iterator,
+            affectedValues,
             invoke,
             getSharedUtilityClass().ensureEqualsMethod(appView, context, eventConsumer));
       } else if (invokedMethod == factory.enumMembers.compareTo
           || invokedMethod == factory.enumMembers.compareToWithObject) {
         replaceEnumInvoke(
             iterator,
+            affectedValues,
             invoke,
             getSharedUtilityClass().ensureCompareToMethod(appView, context, eventConsumer));
       } else if (invokedMethod == factory.enumMembers.nameMethod) {
@@ -494,10 +510,10 @@
           DexClassAndMethod dexClassAndMethod = appView.definitionFor(lookupMethod);
           assert dexClassAndMethod != null;
           assert dexClassAndMethod.isProgramMethod();
-          replaceEnumInvoke(iterator, invoke, dexClassAndMethod.asProgramMethod());
+          replaceEnumInvoke(iterator, affectedValues, invoke, dexClassAndMethod.asProgramMethod());
         }
       } else if (invokedMethod == factory.objectMembers.getClass) {
-        rewriteNullCheck(iterator, invoke, context, eventConsumer);
+        rewriteNullCheck(iterator, affectedValues, invoke, context, eventConsumer);
       } else if (invoke.isInvokeVirtual() || invoke.isInvokeInterface()) {
         DexMethod refinedDispatchMethodReference =
             enumUnboxingLens.lookupRefinedDispatchMethod(
@@ -508,12 +524,13 @@
           assert refinedDispatchMethod != null;
           assert refinedDispatchMethod.isProgramMethod();
           InvokeStatic replacement =
-              replaceEnumInvoke(iterator, invoke, refinedDispatchMethod.asProgramMethod());
+              replaceEnumInvoke(
+                  iterator, affectedValues, invoke, refinedDispatchMethod.asProgramMethod());
           if (replacement.hasOutValue()
               && refinedDispatchMethodReference.getReturnType().isIntType()
               && !invokedMethod.getReturnType().isIntType()) {
             Value rewrittenOutValue = code.createValue(TypeElement.getInt());
-            replacement.outValue().replaceUsers(rewrittenOutValue);
+            replacement.outValue().replaceUsers(rewrittenOutValue, affectedValues);
             replacement.setOutValue(rewrittenOutValue);
             affectedPhis.addAll(rewrittenOutValue.uniquePhiUsers());
             convertedEnums.put(replacement, enumType);
@@ -613,6 +630,7 @@
       Map<Instruction, DexType> convertedEnums,
       InstructionListIterator instructionIterator,
       Set<Phi> affectedPhis,
+      AffectedValues affectedValues,
       EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
     ProgramMethod context = code.context();
     DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
@@ -644,7 +662,7 @@
                 valueOfMethod.getReference(),
                 rewrittenOutValue,
                 Collections.singletonList(invoke.inValues().get(1)));
-        instructionIterator.replaceCurrentInstruction(replacement);
+        instructionIterator.replaceCurrentInstruction(replacement, affectedValues);
         convertedEnums.put(replacement, enumType);
       }
       return;
@@ -657,7 +675,7 @@
         Value argument = invoke.getFirstArgument();
         DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
         if (enumType != null) {
-          rewriteNullCheck(instructionIterator, invoke, context, eventConsumer);
+          rewriteNullCheck(instructionIterator, affectedValues, invoke, context, eventConsumer);
         }
       } else if (invokedMethod == factory.objectsMethods.requireNonNullWithMessage) {
         assert invoke.arguments().size() == 2;
@@ -666,6 +684,7 @@
         if (enumType != null) {
           replaceEnumInvoke(
               instructionIterator,
+              affectedValues,
               invoke,
               getSharedUtilityClass()
                   .ensureCheckNotZeroWithMessageMethod(appView, context, eventConsumer));
@@ -690,6 +709,7 @@
           }
           replaceEnumInvoke(
               instructionIterator,
+              affectedValues,
               invoke,
               getSharedUtilityClass().ensureObjectsEqualsMethod(appView, context, eventConsumer),
               newArguments);
@@ -784,24 +804,29 @@
 
   private void rewriteNullCheck(
       InstructionListIterator iterator,
+      AffectedValues affectedValues,
       InvokeMethod invoke,
       ProgramMethod context,
       EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
     assert !invoke.hasOutValue() || !invoke.outValue().hasAnyUsers();
     replaceEnumInvoke(
         iterator,
+        affectedValues,
         invoke,
         getSharedUtilityClass().ensureCheckNotZeroMethod(appView, context, eventConsumer));
   }
 
   private void removeRedundantValuesArrayCloning(
-      InvokeStatic invoke, Set<Instruction> instructionsToRemove, Set<BasicBlock> seenBlocks) {
+      InvokeStatic invoke,
+      AffectedValues affectedValues,
+      Set<Instruction> instructionsToRemove,
+      Set<BasicBlock> seenBlocks) {
     for (Instruction user : invoke.outValue().aliasedUsers()) {
       if (user.isInvokeVirtual()) {
         InvokeVirtual cloneCandidate = user.asInvokeVirtual();
         if (cloneCandidate.getInvokedMethod().match(appView.dexItemFactory().objectMembers.clone)) {
           if (cloneCandidate.hasOutValue()) {
-            cloneCandidate.outValue().replaceUsers(invoke.outValue());
+            cloneCandidate.outValue().replaceUsers(invoke.outValue(), affectedValues);
           }
           BasicBlock cloneBlock = cloneCandidate.getBlock();
           if (cloneBlock == invoke.getBlock() || !seenBlocks.contains(cloneBlock)) {
@@ -867,12 +892,16 @@
   }
 
   private InvokeStatic replaceEnumInvoke(
-      InstructionListIterator iterator, InvokeMethod invoke, ProgramMethod method) {
-    return replaceEnumInvoke(iterator, invoke, method, invoke.arguments());
+      InstructionListIterator iterator,
+      AffectedValues affectedValues,
+      InvokeMethod invoke,
+      ProgramMethod method) {
+    return replaceEnumInvoke(iterator, affectedValues, invoke, method, invoke.arguments());
   }
 
   private InvokeStatic replaceEnumInvoke(
       InstructionListIterator iterator,
+      AffectedValues affectedValues,
       InvokeMethod invoke,
       ProgramMethod method,
       List<Value> arguments) {
@@ -883,7 +912,7 @@
             arguments);
     assert !replacement.hasOutValue()
         || !replacement.getInvokedMethod().getReturnType().isVoidType();
-    iterator.replaceCurrentInstruction(replacement);
+    iterator.replaceCurrentInstruction(replacement, affectedValues);
     return replacement;
   }
 
@@ -900,9 +929,10 @@
   }
 
   private DexType getEnumClassTypeOrNull(Value receiver, Map<Instruction, DexType> convertedEnums) {
-    TypeElement type = receiver.getType();
+    Value receiverRoot = receiver.getAliasedValue();
+    TypeElement type = receiverRoot.getType();
     if (type.isInt()) {
-      return receiver.isPhi() ? null : convertedEnums.get(receiver.getDefinition());
+      return receiverRoot.isPhi() ? null : convertedEnums.get(receiverRoot.getDefinition());
     }
     return getEnumClassTypeOrNull(type);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java
index 5c37f51..25d60f4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.ir.code.Position.SyntheticPosition;
 import com.android.tools.r8.ir.code.Return;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.ir.optimize.AffectedValues;
 import com.android.tools.r8.ir.optimize.enums.EnumUnboxerImpl;
 import com.android.tools.r8.utils.IteratorUtils;
 import com.android.tools.r8.utils.RetracerForCodePrinting;
@@ -90,15 +91,17 @@
             .build());
 
     // Remove any assume instructions linked to the argument and replace all returns by return-void.
+    AffectedValues affectedValues = new AffectedValues();
     while (instructionIterator.hasNext()) {
       Instruction instruction = instructionIterator.next();
       if (instruction.isAssume()) {
-        instruction.outValue().replaceUsers(instruction.getFirstOperand());
+        instruction.outValue().replaceUsers(instruction.getFirstOperand(), affectedValues);
         instructionIterator.removeOrReplaceByDebugLocalRead();
       } else if (instruction.isReturn() && instruction.asReturn().hasReturnValue()) {
         instructionIterator.replaceCurrentInstruction(new Return());
       }
     }
+    affectedValues.propagate(appView, code);
 
     // Transfer the IR to the given checkNotZero() method.
     return new IRCode(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerRewriter.java
index 5e26100..8423e1c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerRewriter.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.ir.code.Return;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.optimize.AffectedValues;
 import com.android.tools.r8.ir.optimize.CustomLensCodeRewriter;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.ImmutableList;
@@ -43,6 +44,7 @@
       IRCode code,
       MethodProcessor methodProcessor,
       RewrittenPrototypeDescription prototypeChanges,
+      AffectedValues affectedValues,
       NonIdentityGraphLens graphLens) {
     assert graphLens.isNumberUnboxerLens();
     Set<Phi> affectedPhis = Sets.newIdentityHashSet();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
index d82c653..9644ff7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
@@ -49,6 +49,7 @@
 import com.android.tools.r8.ir.code.Mul;
 import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Phi;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Position.OutlineCallerPosition;
 import com.android.tools.r8.ir.code.Position.OutlineCallerPosition.OutlineCallerPositionBuilder;
@@ -64,7 +65,6 @@
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.ir.conversion.SourceCode;
-import com.android.tools.r8.ir.conversion.passes.AssumeRemover;
 import com.android.tools.r8.ir.conversion.passes.MoveResultRewriter;
 import com.android.tools.r8.ir.optimize.AffectedValues;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -1280,17 +1280,25 @@
             }
 
             // Prepare to remove the instruction.
-            List<Value> inValues = orderedInValues(current, returnValue);
-            for (Value value : inValues) {
-              value.removeUser(current);
-              int argumentIndex = outline.argumentMap.get(argumentsMapIndex++);
-              if (argumentIndex >= in.size()) {
-                assert argumentIndex == in.size();
-                in.add(value);
+            if (current.isAssume()) {
+              Value src = current.getFirstOperand();
+              current.outValue().replaceUsers(src, affectedValues);
+              src.removeUser(current);
+              src.uniquePhiUsers().forEach(Phi::removeTrivialPhi);
+              current.clearOutValue();
+            } else {
+              List<Value> inValues = orderedInValues(current, returnValue);
+              for (Value value : inValues) {
+                value.removeUser(current);
+                int argumentIndex = outline.argumentMap.get(argumentsMapIndex++);
+                if (argumentIndex >= in.size()) {
+                  assert argumentIndex == in.size();
+                  in.add(value);
+                }
               }
-            }
-            if (current.outValue() != null) {
-              returnValue = current.outValue();
+              if (!outline.returnType.isVoidType() && current.hasOutValue()) {
+                returnValue = current.outValue();
+              }
             }
             // The invoke of the outline method will be placed at the last instruction index,
             // so don't mark that for removal.
@@ -1302,9 +1310,6 @@
         }
         assert lastInstruction != null;
         assert outlineMethod.proto.createShortyString().length() - 1 == in.size();
-        if (returnValue != null && !returnValue.isUsed()) {
-          returnValue = null;
-        }
         Invoke outlineInvoke = new InvokeStatic(outlineMethod, returnValue, in);
         lastInstruction.getBlock();
         outlineInvoke.setPosition(
@@ -1453,7 +1458,6 @@
           // unused out-values.
           new MoveResultRewriter(appView).run(code, Timing.empty());
           converter.deadCodeRemover.run(code, Timing.empty());
-          new AssumeRemover(appView).run(code, Timing.empty());
           consumer.accept(code);
         },
         appView.options().getThreadingModule(),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/bottomup/OutlineMarkerRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/bottomup/OutlineMarkerRewriter.java
index 38ecd97..956e2a8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/bottomup/OutlineMarkerRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/bottomup/OutlineMarkerRewriter.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.ir.conversion.LensCodeArgumentRewriter;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.passes.DexConstantOptimizer;
+import com.android.tools.r8.ir.optimize.AffectedValues;
 import com.android.tools.r8.ir.optimize.ConstantCanonicalizer;
 import com.android.tools.r8.ir.optimize.DeadCodeRemover;
 import com.android.tools.r8.lightir.Lir2IRConverter;
@@ -101,10 +102,17 @@
     }
 
     Set<Phi> affectedPhis = Collections.emptySet();
+    AffectedValues affectedValues = new AffectedValues();
     Set<UnusedArgument> unusedArguments = Collections.emptySet();
     new LensCodeArgumentRewriter(appView)
         .rewriteArguments(
-            code, method.getReference(), outline.getProtoChanges(), affectedPhis, unusedArguments);
+            code,
+            method.getReference(),
+            outline.getProtoChanges(),
+            affectedPhis,
+            affectedValues,
+            unusedArguments);
+    affectedValues.narrowingWithAssumeRemoval(appView, code);
 
     // Run shorten live ranges to push materialized constants to their uses.
     ConstantCanonicalizer constantCanonicalizer = new ConstantCanonicalizer(appView, method, code);
diff --git a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
index ed38c17..e2e5997 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.graph.proto.RemovedArgumentInfo;
 import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.graph.proto.RewrittenTypeInfo;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -32,6 +33,7 @@
 import com.android.tools.r8.ir.code.ArrayGet;
 import com.android.tools.r8.ir.code.ArrayLength;
 import com.android.tools.r8.ir.code.ArrayPut;
+import com.android.tools.r8.ir.code.Assume;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.CatchHandlers;
 import com.android.tools.r8.ir.code.CheckCast;
@@ -107,6 +109,7 @@
 import com.android.tools.r8.ir.code.Xor;
 import com.android.tools.r8.ir.conversion.ExtraParameter;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.ir.optimize.AffectedValues;
 import com.android.tools.r8.ir.optimize.outliner.bottomup.Outline;
 import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
 import com.android.tools.r8.lightir.LirBuilder.StringSwitchPayload;
@@ -153,6 +156,32 @@
     IRCode irCode = parser.getIRCode(method, conversionOptions);
     // Some instructions have bottom types (e.g., phis). Compute their actual types by widening.
     new TypeAnalysis(appView, irCode).widening();
+
+    // Remove redundant Assume instructions for non-null phis. This may be needed since during
+    // type narrowing we don't unset the type of phis. When rebuilding the phi types as part of IR
+    // building we may get more precise type information for phis.
+    List<Value> assumeValuesToRemove = new ArrayList<>();
+    for (Assume assume : irCode.<Assume>instructions(Instruction::isAssume)) {
+      TypeElement operandType = assume.src().getType();
+      if (operandType.isDefinitelyNotNull()) {
+        assumeValuesToRemove.add(assume.outValue());
+      }
+    }
+    if (!assumeValuesToRemove.isEmpty()) {
+      new TypeAnalysis(appView, irCode).narrowingWithAssumeRemoval(assumeValuesToRemove);
+    }
+
+    // Remove Assume instructions for where the operand is definitely null.
+    AffectedValues affectedValues = new AffectedValues();
+    for (Assume assume : irCode.<Assume>instructions(Instruction::isAssume)) {
+      if (assume.getFirstOperand().getType().isNullType()) {
+        assume.outValue().replaceUsers(assume.src(), affectedValues);
+        assume.src().uniquePhiUsers().forEach(Phi::removeTrivialPhi);
+        assume.remove();
+      }
+    }
+    affectedValues.propagate(appView, irCode);
+
     return irCode;
   }
 
@@ -889,6 +918,13 @@
     }
 
     @Override
+    public void onAssumeNonNull(EV value) {
+      Value src = getValue(value);
+      Value dest = getOutValueForNextInstruction(TypeElement.getBottom());
+      addInstruction(new Assume(DynamicType.definitelyNotNull(), dest, src));
+    }
+
+    @Override
     public void onCheckCast(DexType type, EV value, boolean ignoreCompatRules) {
       Value dest = getOutValueForNextInstruction(type.toTypeElement(appView, Nullability.bottom()));
       addInstruction(new CheckCast(dest, getValue(value), type, ignoreCompatRules));
diff --git a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
index e974f3c..35306bc 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -689,6 +689,10 @@
     return addOneValueInstruction(LirOpcodes.ARRAYLENGTH, array);
   }
 
+  public LirBuilder<V, EV> addAssumeNonNull(V value) {
+    return addOneValueInstruction(LirOpcodes.ASSUMENONNULL, value);
+  }
+
   public LirBuilder<V, EV> addCheckCast(DexType type, V value, boolean ignoreCompatRules) {
     int opcode = ignoreCompatRules ? LirOpcodes.CHECKCAST_IGNORE_COMPAT : LirOpcodes.CHECKCAST;
     return addInstructionTemplate(
diff --git a/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
index a278bca..af1c08e 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
@@ -217,6 +217,7 @@
   int STORESTOREFENCE = 230;
   int OUTLINEMARKER = 231;
   int STRINGCONCAT = 232;
+  int ASSUMENONNULL = 233;
 
   static String toString(int opcode) {
     switch (opcode) {
@@ -499,7 +500,9 @@
       case IFNONNULL:
         return "IFNONNULL";
 
-        // Non-CF instructions.
+      // Non-CF instructions.
+      case ASSUMENONNULL:
+        return "ASSUMENONNULL";
       case ICONST:
         return "ICONST";
       case LCONST:
diff --git a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
index 8e48282..ea6bbc5 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
@@ -487,6 +487,10 @@
     onInstruction();
   }
 
+  public void onAssumeNonNull(EV value) {
+    onInstruction();
+  }
+
   public void onCheckCast(DexType type, EV value, boolean ignoreCompatRules) {
     onInstruction();
   }
@@ -1221,6 +1225,11 @@
           onArrayLength(getNextValueOperand(view));
           return;
         }
+      case LirOpcodes.ASSUMENONNULL:
+        {
+          onAssumeNonNull(getNextValueOperand(view));
+          return;
+        }
       case LirOpcodes.CHECKCAST:
         {
           DexType type = getNextDexTypeOperand(view);
diff --git a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
index b70b0b2..70db827 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.NumberConversionType;
 import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.optimize.outliner.bottomup.Outline;
 import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
 import com.android.tools.r8.lightir.LirBuilder.StringSwitchPayload;
 import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
@@ -227,6 +228,11 @@
   }
 
   @Override
+  public void onOutlineMarker(Outline outline, List<EV> arguments) {
+    appendValueArguments(arguments);
+  }
+
+  @Override
   public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
     appendValueArguments(valueIndex);
     builder.append(fmtInsnIndex(blockIndex));
@@ -380,6 +386,11 @@
   }
 
   @Override
+  public void onAssumeNonNull(EV value) {
+    appendOutValue().append(fmtValueIndex(value));
+  }
+
+  @Override
   public void onCheckCast(DexType type, EV value, boolean ignoreCompatRules) {
     appendOutValue();
     appendValueArguments(value);
diff --git a/src/main/java/com/android/tools/r8/lightir/LirSizeEstimation.java b/src/main/java/com/android/tools/r8/lightir/LirSizeEstimation.java
index e8e6db9..1c2b87f 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirSizeEstimation.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirSizeEstimation.java
@@ -285,6 +285,8 @@
         return DexNewArray.SIZE;
       case ARRAYLENGTH:
         return DexArrayLength.SIZE;
+      case ASSUMENONNULL:
+        return 0;
       case ATHROW:
         return DexThrow.SIZE;
       case CHECKCAST:
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 7596e39..1984ac5 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -717,6 +717,13 @@
         return staticType;
       }
 
+      if (!newStaticFieldType.toTypeElement(appView).strictlyLessThan(staticFieldType, appView)) {
+        // Can happen when the dynamic type is an intersection type with >1 interfaces. In this case
+        // the conversion to DexType resorts to the class type, which may be less precise than the
+        // current static field type.
+        return staticType;
+      }
+
       if (!AccessUtils.isAccessibleInSameContextsAs(newStaticFieldType, staticType, appView)) {
         return staticType;
       }
diff --git a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
index 15e33f5..fc69f73 100644
--- a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
+++ b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
@@ -27,7 +27,6 @@
 import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
 import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
 import com.android.tools.r8.ir.conversion.callgraph.CallSiteInformation;
-import com.android.tools.r8.ir.conversion.passes.AssumeRemover;
 import com.android.tools.r8.ir.optimize.DefaultInliningOracle;
 import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider;
@@ -147,8 +146,6 @@
             IRCode code = method.buildIR(appView, MethodConversionOptions.forLirPhase(appView));
             inliner.performInlining(
                 method, code, getSimpleFeedback(), methodProcessor, Timing.empty());
-            new AssumeRemover(appView)
-                .run(code, methodProcessor, methodProcessingContext, Timing.empty());
             LirCode<Integer> lirCode =
                 new IRToLirFinalizer(appView)
                     .finalizeCode(code, BytecodeMetadataProvider.empty(), Timing.empty());
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
index 759878b..648a3e7 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
@@ -29,7 +29,6 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.conversion.IRFinalizer;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions;
-import com.android.tools.r8.ir.conversion.passes.AssumeRemover;
 import com.android.tools.r8.ir.conversion.passes.ThrowCatchOptimizer;
 import com.android.tools.r8.ir.optimize.AssumeInserter;
 import com.android.tools.r8.ir.optimize.info.OptimizationInfoRemover;
@@ -377,7 +376,6 @@
     // Run dead code elimination.
     new ThrowCatchOptimizer(appView).run(ir, Timing.empty());
     rewriter.getDeadCodeRemover().run(ir, Timing.empty());
-    new AssumeRemover(appView).run(ir, Timing.empty());
 
     // Finalize out of IR.
     IRFinalizer<?> finalizer =
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index be552b1..55d3f2e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -261,7 +261,7 @@
                 assertThat(
                     inspector.clazz(
                         "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateless$1"),
-                    isPresent());
+                    isAbsent());
                 assertThat(
                     inspector.clazz(
                         "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1"),
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index 9f6cede..3a61e7b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -75,7 +75,7 @@
               long paramNullCheckCount =
                   countCall(testMethod, "Intrinsics", "checkParameterIsNotNull");
               // One after Iterator#hasNext, and another in the filter predicate: sinceYear != null.
-              assertEquals(testParameters.isCfRuntime() ? 5 : 2, ifzCount);
+              assertEquals(2, ifzCount);
               assertEquals(0, paramNullCheckCount);
             });
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
index e22c378..c59b6dc 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
@@ -108,37 +108,8 @@
       SyntheticItemsTestUtils syntheticItems) {
     if (parameters.isCfRuntime()) {
       if (kotlinParameters.getLambdaGeneration().isClass()) {
-        inspector
-            .assertIsCompleteMergeGroup(
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testFirst$4"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testFirst$5"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testFirst$6"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testFirst$7"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testFirst$8"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testFirst$9"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testSecond$4"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testSecond$5"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testSecond$6"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testSecond$7"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testSecond$8"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testSecond$9"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testThird$1"),
-                lambdasInInput.getKStyleLambdaReferenceFromTypeName(
-                    getTestName(), "MainKt$testThird$2"))
-            .assertNoOtherClassesMerged();
+        assertEquals(1, inspector.getMergeGroups().size());
+        assertEquals(19, inspector.getSources().size());
       } else {
         inspector.assertNoClassesMerged();
       }
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java
index 48b85e2..4b0a02a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java
@@ -138,10 +138,7 @@
       }
     }
     assertEquals(
-        kotlinParameters.getLambdaGeneration().isInvokeDynamic()
-            ? 0
-            : parameters.isDexRuntime() ? 1 : 0,
-        lambdasInOutput.size());
+        kotlinParameters.getLambdaGeneration().isInvokeDynamic() ? 0 : 1, lambdasInOutput.size());
   }
 
   private String getExpectedOutput() {
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java
index e6f3ee5..1514010 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java
@@ -19,8 +19,7 @@
   // With horizontal class merging enabled the method body for debug is cleared, because the
   // forwarded call to the class specific implementation has no side effects. The call to the
   // function from main persists.
-  private static final String EXPECTED_OUTPUT = StringUtils.lines("[Base] message2", "The end");
-  private static final String EXPECTED_OUTPUT_DEX =
+  private static final String EXPECTED_OUTPUT =
       StringUtils.lines("[Base] message1", "[Base] message2", "The end");
 
   enum TestConfig {
@@ -45,16 +44,6 @@
           throw new Unreachable();
       }
     }
-
-    public String expectedOutput(TestParameters parameters) {
-      switch (this) {
-        case SPECIFIC_RULES:
-        case NON_SPECIFIC_RULES_WITH_EXTENDS:
-          return parameters.isCfRuntime() ? EXPECTED_OUTPUT : EXPECTED_OUTPUT_DEX;
-        default:
-          throw new Unreachable();
-      }
-    }
   }
 
   private final TestParameters parameters;
@@ -81,7 +70,7 @@
         .addDontObfuscate()
         .setMinApi(parameters)
         .run(parameters.getRuntime(), MAIN)
-        .assertSuccessWithOutput(config.expectedOutput(parameters));
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
   }
 
   static class BaseClass {