Check for consistent SSA in CF before register allocation

Change-Id: Ifcf8a5bf2054b58d25acb988c9cd5e260fa4285e
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup.java b/src/main/java/com/android/tools/r8/ir/code/Dup.java
index 4ef2370..89542a7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Dup.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Dup.java
@@ -30,6 +30,7 @@
     assert outValue == null || !outValue.hasUsersInfo() || !outValue.isUsed() ||
         value instanceof StackValues;
     this.outValue = value;
+    this.outValue.definition = this;
     for (StackValue val : ((StackValues)value).getStackValues()) {
       val.definition = this;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup2.java b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
index 6d61f58..fcb5b8e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Dup2.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
@@ -44,6 +44,7 @@
     assert outValue == null || !outValue.hasUsersInfo() || !outValue.isUsed() ||
         value instanceof StackValues;
     this.outValue = value;
+    this.outValue.definition = this;
     for (StackValue val : ((StackValues)value).getStackValues()) {
       val.definition = this;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index cb93a5e..bef2765 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -20,6 +20,7 @@
 import com.google.common.collect.Sets;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.Deque;
@@ -34,6 +35,7 @@
 import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 public class IRCode {
 
@@ -575,7 +577,10 @@
   }
 
   private boolean verifyDefinition(Value value) {
-    assert value.definition.outValue() == value;
+    Value outValue = value.definition.outValue();
+    assert outValue == value
+        || (value instanceof StackValue
+            && Arrays.asList(((StackValues) outValue).getStackValues()).contains(value));
     return true;
   }
 
@@ -662,11 +667,15 @@
           }
           // After the throwing instruction only debug instructions and the final jump
           // instruction is allowed.
+          // TODO(mkroghj) Temporarily allow stack-operations to be after throwing instructions.
           if (seenThrowing) {
             assert instruction.isDebugInstruction()
                 || instruction.isJumpInstruction()
                 || instruction.isStore()
-                || instruction.isPop();
+                || instruction.isPop()
+                || instruction.isDup()
+                || instruction.isDup2()
+                || instruction.isSwap();
           }
         }
       }
@@ -675,7 +684,7 @@
   }
 
   public boolean verifyNoImpreciseOrBottomTypes() {
-    return verifySSATypeLattice(
+    Predicate<Value> verifyValue =
         v -> {
           assert v.getTypeLattice().isPreciseType();
           assert !v.getTypeLattice().isFineGrainedType();
@@ -685,6 +694,16 @@
           assert !(v.definition instanceof ImpreciseMemberTypeInstruction)
               || ((ImpreciseMemberTypeInstruction) v.definition).getMemberType().isPrecise();
           return true;
+        };
+    return verifySSATypeLattice(
+        v -> {
+          // StackValues is an artificial type created to allow returning multiple values from an
+          // instruction.
+          if (v instanceof StackValues) {
+            return Stream.of(((StackValues) v).getStackValues()).allMatch(verifyValue);
+          } else {
+            return verifyValue(v);
+          }
         });
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Swap.java b/src/main/java/com/android/tools/r8/ir/code/Swap.java
index 80edc80..3c623d6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Swap.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Swap.java
@@ -33,6 +33,7 @@
     assert outValue == null || !outValue.hasUsersInfo() || !outValue.isUsed() ||
         value instanceof StackValues;
     this.outValue = value;
+    this.outValue.definition = this;
     for (StackValue val : ((StackValues)value).getStackValues()) {
       val.definition = this;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 4dcc839..6b5f6c4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -166,6 +166,7 @@
         reachedFixpoint = !phiOptimizations.optimize(code);
       }
     }
+    assert code.isConsistentSSA();
     registerAllocator = new CfRegisterAllocator(code, options, typeVerificationHelper);
     registerAllocator.allocateRegisters();
     loadStoreHelper.insertPhiMoves(registerAllocator);