Don't use flow-sensative assumptions in branch subsumption.

Bug: b/259298098
Change-Id: Iffab3ad73bcd1c6726a756d80c284562cc3935da
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java b/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java
index e901e23..d274600 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java
@@ -4,12 +4,10 @@
 
 package com.android.tools.r8.ir.analysis.equivalence;
 
-import static com.google.common.base.Predicates.not;
-import static com.google.common.base.Predicates.or;
-
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.Assume;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.ConstClass;
 import com.android.tools.r8.ir.code.ConstNumber;
@@ -23,8 +21,10 @@
 import com.android.tools.r8.ir.code.Return;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.WorkList;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Predicate;
 
 /**
  * Analysis that can be used to determine if the behavior of one basic block is subsumed by the
@@ -45,19 +45,59 @@
     this.context = code.context();
   }
 
-  public boolean isSubsumedBy(BasicBlock block, BasicBlock other) {
-    return isSubsumedBy(block.iterator(), other.iterator(), null);
+  public boolean isSubsumedBy(Value conditionValue, BasicBlock block, BasicBlock other) {
+    return isSubsumedBy(conditionValue, block.iterator(), other.iterator(), null);
+  }
+
+  private boolean dependsOnConditionValue(Value conditionValue, Instruction instruction) {
+    if (instruction.isAssume()) {
+      // Assume instructions are just virtual alias instructions so they are not materializing uses.
+      return false;
+    }
+    WorkList<Assume> assumptionUses = null;
+    for (Value value : instruction.inValues()) {
+      Assume assumption = value.isPhi() ? null : value.getDefinition().asAssume();
+      if (assumption != null) {
+        if (assumptionUses == null) {
+          assumptionUses = WorkList.newIdentityWorkList();
+        }
+        assumptionUses.addIfNotSeen(assumption);
+      }
+    }
+    if (assumptionUses != null) {
+      while (assumptionUses.hasNext()) {
+        Assume next = assumptionUses.next();
+        for (Value assumptionArgument : next.inValues()) {
+          if (assumptionArgument == conditionValue) {
+            return true;
+          }
+          Assume indirectAssumption =
+              assumptionArgument.isPhi() ? null : assumptionArgument.getDefinition().asAssume();
+          if (indirectAssumption != null) {
+            assumptionUses.addIfNotSeen(indirectAssumption);
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  private Instruction skipNonDependentInstructionsUntil(
+      InstructionIterator iterator, Value conditionValue, Predicate<Instruction> predicate) {
+    return iterator.nextUntil(
+        predicate.or(i -> i.isJumpInstruction() || dependsOnConditionValue(conditionValue, i)));
   }
 
   private boolean isSubsumedBy(
-      InstructionIterator iterator, InstructionIterator otherIterator, Set<BasicBlock> visited) {
+      Value conditionValue,
+      InstructionIterator iterator,
+      InstructionIterator otherIterator,
+      Set<BasicBlock> visited) {
     // Skip over block-local instructions (i.e., instructions that define values that are not used
     // outside the block itself) that do not have side effects.
     Instruction instruction =
-        iterator.nextUntil(
-            or(
-                not(this::isBlockLocalInstructionWithoutSideEffects),
-                Instruction::isJumpInstruction));
+        skipNonDependentInstructionsUntil(
+            iterator, conditionValue, this::isNonLocalDefinitionOrSideEffecting);
 
     // If the instruction defines a value with non-local usages, then we would need a dominator
     // analysis to verify that all these non-local usages can in fact be replaced by a value
@@ -68,8 +108,8 @@
 
     // Skip over non-throwing instructions in the other block.
     Instruction otherInstruction =
-        otherIterator.nextUntil(
-            or(this::instructionMayHaveSideEffects, Instruction::isJumpInstruction));
+        skipNonDependentInstructionsUntil(
+            otherIterator, conditionValue, this::instructionMayHaveSideEffects);
     assert otherInstruction != null;
 
     if (instruction.isGoto()) {
@@ -101,7 +141,7 @@
         visited = SetUtils.newIdentityHashSet(instruction.getBlock());
       }
       if (visited.add(targetBlock)) {
-        return isSubsumedBy(targetBlock.iterator(), otherIterator, visited);
+        return isSubsumedBy(conditionValue, targetBlock.iterator(), otherIterator, visited);
       }
       // Guard against cycles in the control flow graph.
       return false;
@@ -125,8 +165,8 @@
 
       otherIterator = targetBlock.iterator();
       otherInstruction =
-          otherIterator.nextUntil(
-              or(this::instructionMayHaveSideEffects, Instruction::isJumpInstruction));
+          skipNonDependentInstructionsUntil(
+              otherIterator, conditionValue, this::instructionMayHaveSideEffects);
 
       // If following the first goto instruction leads to another goto instruction, then we need to
       // keep track of the set of visited blocks to guard against cycles in the control flow graph.
@@ -150,7 +190,7 @@
             || otherSingleTarget.getDefinition().getOptimizationInfo().mayHaveSideEffects()) {
           return false;
         }
-        return isSubsumedBy(iterator, otherIterator, visited);
+        return isSubsumedBy(conditionValue, iterator, otherIterator, visited);
       }
       return false;
     }
@@ -172,8 +212,8 @@
     return false;
   }
 
-  private boolean isBlockLocalInstructionWithoutSideEffects(Instruction instruction) {
-    return definesBlockLocalValue(instruction) && !instructionMayHaveSideEffects(instruction);
+  private boolean isNonLocalDefinitionOrSideEffecting(Instruction instruction) {
+    return !definesBlockLocalValue(instruction) || instructionMayHaveSideEffects(instruction);
   }
 
   private boolean definesBlockLocalValue(Instruction instruction) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 1d4e435..f0ca38f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1135,7 +1135,8 @@
       // behavior of the default block, or if the switch case is unreachable.
       if (switchCaseAnalyzer.switchCaseIsUnreachable(theSwitch, switchAbstractValue, i)) {
         eliminator.markSwitchCaseForRemoval(i);
-      } else if (behavioralSubsumption.isSubsumedBy(targetBlock, defaultTarget)) {
+      } else if (behavioralSubsumption.isSubsumedBy(
+          theSwitch.value(), targetBlock, defaultTarget)) {
         eliminator.markSwitchCaseForRemoval(i);
         hasSwitchCaseToDefaultRewrite = true;
       }
@@ -2642,7 +2643,8 @@
 
         // Unable to determine which branch will be taken. Check if the true target can safely be
         // rewritten to the false target.
-        if (behavioralSubsumption.isSubsumedBy(theIf.getTrueTarget(), theIf.fallthroughBlock())) {
+        if (behavioralSubsumption.isSubsumedBy(
+            theIf.inValues().get(0), theIf.getTrueTarget(), theIf.fallthroughBlock())) {
           simplifyIfWithKnownCondition(code, block, theIf, theIf.fallthroughBlock());
           simplified = true;
         }
diff --git a/src/main/java/com/android/tools/r8/utils/SetUtils.java b/src/main/java/com/android/tools/r8/utils/SetUtils.java
index 6be9cc3..834a3df 100644
--- a/src/main/java/com/android/tools/r8/utils/SetUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/SetUtils.java
@@ -16,6 +16,9 @@
 public class SetUtils {
 
   public static <T> boolean containsAnyOf(Set<T> set, Iterable<T> elements) {
+    if (set.isEmpty()) {
+      return false;
+    }
     for (T element : elements) {
       if (set.contains(element)) {
         return true;