Dynamic upper bound types for phis

Change-Id: I7b29c7a33cdd6a497aa95f5bc0913b3a862058cc
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index 2f0911d..16fc789 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.cf.TypeVerificationHelper;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.InvalidDebugInfoException;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexMethod;
@@ -16,11 +17,14 @@
 import com.android.tools.r8.ir.conversion.TypeConstraintResolver;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.DequeUtils;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
+import java.util.Deque;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -395,4 +399,31 @@
     }
     return result;
   }
+
+  @Override
+  public TypeLatticeElement getDynamicUpperBoundType(
+      AppView<? extends AppInfoWithSubtyping> appView) {
+    Set<Phi> reachablePhis = SetUtils.newIdentityHashSet(this);
+    Deque<Phi> worklist = DequeUtils.newArrayDeque(this);
+    while (!worklist.isEmpty()) {
+      Phi phi = worklist.removeFirst();
+      assert reachablePhis.contains(phi);
+      for (Value operand : phi.getOperands()) {
+        Phi candidate = operand.getAliasedValue().asPhi();
+        if (candidate != null && reachablePhis.add(candidate)) {
+          worklist.addLast(candidate);
+        }
+      }
+    }
+    Set<Value> visitedOperands = Sets.newIdentityHashSet();
+    TypeLatticeElement result = TypeLatticeElement.BOTTOM;
+    for (Phi phi : reachablePhis) {
+      for (Value operand : phi.getOperands()) {
+        if (!operand.getAliasedValue().isPhi() && visitedOperands.add(operand)) {
+          result = result.join(operand.getDynamicUpperBoundType(appView), appView);
+        }
+      }
+    }
+    return result;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 75f5831..d6372b5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -1114,6 +1114,14 @@
 
   public TypeLatticeElement getDynamicUpperBoundType(
       AppView<? extends AppInfoWithSubtyping> appView) {
+    Value root = getAliasedValue();
+    if (root.isPhi()) {
+      assert getSpecificAliasedValue(
+              value -> !value.isPhi() && value.definition.isAssumeDynamicType())
+          == null;
+      return root.getDynamicUpperBoundType(appView);
+    }
+
     // Try to find an alias of the receiver, which is defined by an instruction of the type
     // Assume<DynamicTypeAssumption>.
     Value aliasedValue =