Introduce a distinct ValueType for the single-or-null-object case.

Change-Id: I067e31349f3a31a55a8469130fd32813cd18f3d0
diff --git a/src/main/java/com/android/tools/r8/code/Const.java b/src/main/java/com/android/tools/r8/code/Const.java
index 7f9d8b8..1d8313d 100644
--- a/src/main/java/com/android/tools/r8/code/Const.java
+++ b/src/main/java/com/android/tools/r8/code/Const.java
@@ -57,6 +57,8 @@
 
   @Override
   public void buildIR(IRBuilder builder) {
-    builder.addConst(ValueType.INT_OR_FLOAT, AA, decodedValue());
+    int value = decodedValue();
+    ValueType type = value == 0 ? ValueType.INT_OR_FLOAT_OR_NULL : ValueType.INT_OR_FLOAT;
+    builder.addConst(type, AA, value);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Const16.java b/src/main/java/com/android/tools/r8/code/Const16.java
index baa51ea..24dae68 100644
--- a/src/main/java/com/android/tools/r8/code/Const16.java
+++ b/src/main/java/com/android/tools/r8/code/Const16.java
@@ -51,6 +51,8 @@
 
   @Override
   public void buildIR(IRBuilder builder) {
-    builder.addConst(ValueType.INT_OR_FLOAT, AA, decodedValue());
+    int value = decodedValue();
+    ValueType type = value == 0 ? ValueType.INT_OR_FLOAT_OR_NULL : ValueType.INT_OR_FLOAT;
+    builder.addConst(type, AA, value);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Const4.java b/src/main/java/com/android/tools/r8/code/Const4.java
index 31364ea..497a9ed 100644
--- a/src/main/java/com/android/tools/r8/code/Const4.java
+++ b/src/main/java/com/android/tools/r8/code/Const4.java
@@ -57,6 +57,8 @@
 
   @Override
   public void buildIR(IRBuilder builder) {
-    builder.addConst(ValueType.INT_OR_FLOAT, A, decodedValue());
+    int value = decodedValue();
+    ValueType type = value == 0 ? ValueType.INT_OR_FLOAT_OR_NULL : ValueType.INT_OR_FLOAT;
+    builder.addConst(type, A, value);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/ConstHigh16.java b/src/main/java/com/android/tools/r8/code/ConstHigh16.java
index 6cf0851..ecaf61f 100644
--- a/src/main/java/com/android/tools/r8/code/ConstHigh16.java
+++ b/src/main/java/com/android/tools/r8/code/ConstHigh16.java
@@ -57,6 +57,8 @@
 
   @Override
   public void buildIR(IRBuilder builder) {
-    builder.addConst(ValueType.INT_OR_FLOAT, AA, decodedValue());
+    int value = decodedValue();
+    ValueType type = value == 0 ? ValueType.INT_OR_FLOAT_OR_NULL : ValueType.INT_OR_FLOAT;
+    builder.addConst(type, AA, value);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Format21t.java b/src/main/java/com/android/tools/r8/code/Format21t.java
index acd444b..9f15752 100644
--- a/src/main/java/com/android/tools/r8/code/Format21t.java
+++ b/src/main/java/com/android/tools/r8/code/Format21t.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -61,7 +62,7 @@
   public void buildIR(IRBuilder builder) {
     int offset = getOffset();
     int size = getSize();
-    builder.addIfZero(getType(), AA, offset + BBBB, offset + size);
+    builder.addIfZero(getType(), ValueType.INT_OR_FLOAT_OR_NULL, AA, offset + BBBB, offset + size);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format22t.java b/src/main/java/com/android/tools/r8/code/Format22t.java
index 21b7dcd..9bf727c 100644
--- a/src/main/java/com/android/tools/r8/code/Format22t.java
+++ b/src/main/java/com/android/tools/r8/code/Format22t.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
 import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 import com.android.tools.r8.naming.ClassNameMapper;
 import java.nio.ShortBuffer;
@@ -56,6 +57,8 @@
 
   public abstract Type getType();
 
+  public abstract ValueType getOperandType();
+
   @Override
   public int[] getTargets() {
     return new int[]{CCCC, getSize()};
@@ -65,7 +68,7 @@
   public void buildIR(IRBuilder builder) {
     int offset = getOffset();
     int size = getSize();
-    builder.addIf(getType(), A, B, offset + CCCC, offset + size);
+    builder.addIf(getType(), getOperandType(), A, B, offset + CCCC, offset + size);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/code/IfEq.java b/src/main/java/com/android/tools/r8/code/IfEq.java
index 4cedf62..7a75ebe 100644
--- a/src/main/java/com/android/tools/r8/code/IfEq.java
+++ b/src/main/java/com/android/tools/r8/code/IfEq.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.code.ValueType;
 
 public class IfEq extends Format22t {
 
@@ -38,4 +39,9 @@
   public Type getType() {
     return Type.EQ;
   }
+
+  @Override
+  public ValueType getOperandType() {
+    return ValueType.INT_OR_FLOAT_OR_NULL;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/code/IfGe.java b/src/main/java/com/android/tools/r8/code/IfGe.java
index 4946529..42d3740 100644
--- a/src/main/java/com/android/tools/r8/code/IfGe.java
+++ b/src/main/java/com/android/tools/r8/code/IfGe.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.code.ValueType;
 
 public class IfGe extends Format22t {
 
@@ -38,4 +39,9 @@
   public Type getType() {
     return Type.GE;
   }
+
+  @Override
+  public ValueType getOperandType() {
+    return ValueType.INT;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/code/IfGt.java b/src/main/java/com/android/tools/r8/code/IfGt.java
index 2d3f84a..ee4d9bd 100644
--- a/src/main/java/com/android/tools/r8/code/IfGt.java
+++ b/src/main/java/com/android/tools/r8/code/IfGt.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.code.ValueType;
 
 public class IfGt extends Format22t {
 
@@ -38,4 +39,9 @@
   public Type getType() {
     return Type.GT;
   }
+
+  @Override
+  public ValueType getOperandType() {
+    return ValueType.INT;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/code/IfLe.java b/src/main/java/com/android/tools/r8/code/IfLe.java
index 4710825..7b4e893 100644
--- a/src/main/java/com/android/tools/r8/code/IfLe.java
+++ b/src/main/java/com/android/tools/r8/code/IfLe.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.code.ValueType;
 
 public class IfLe extends Format22t {
 
@@ -38,4 +39,9 @@
   public Type getType() {
     return Type.LE;
   }
+
+  @Override
+  public ValueType getOperandType() {
+    return ValueType.INT;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/code/IfLt.java b/src/main/java/com/android/tools/r8/code/IfLt.java
index 631b971..78a04a5 100644
--- a/src/main/java/com/android/tools/r8/code/IfLt.java
+++ b/src/main/java/com/android/tools/r8/code/IfLt.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.code.ValueType;
 
 public class IfLt extends Format22t {
 
@@ -38,4 +39,9 @@
   public Type getType() {
     return Type.LT;
   }
+
+  @Override
+  public ValueType getOperandType() {
+    return ValueType.INT;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/code/IfNe.java b/src/main/java/com/android/tools/r8/code/IfNe.java
index d7004fd..5a97dfc 100644
--- a/src/main/java/com/android/tools/r8/code/IfNe.java
+++ b/src/main/java/com/android/tools/r8/code/IfNe.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.code.ValueType;
 
 public class IfNe extends Format22t {
 
@@ -38,4 +39,9 @@
   public Type getType() {
     return Type.NE;
   }
+
+  @Override
+  public ValueType getOperandType() {
+    return ValueType.INT_OR_FLOAT_OR_NULL;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Move.java b/src/main/java/com/android/tools/r8/code/Move.java
index 0dc06f9..90e44f4 100644
--- a/src/main/java/com/android/tools/r8/code/Move.java
+++ b/src/main/java/com/android/tools/r8/code/Move.java
@@ -37,6 +37,6 @@
 
   @Override
   public void buildIR(IRBuilder builder) {
-    builder.addMove(ValueType.INT_OR_FLOAT, A, B);
+    builder.addMove(ValueType.INT_OR_FLOAT_OR_NULL, A, B);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/Move16.java b/src/main/java/com/android/tools/r8/code/Move16.java
index aae6740..6772538 100644
--- a/src/main/java/com/android/tools/r8/code/Move16.java
+++ b/src/main/java/com/android/tools/r8/code/Move16.java
@@ -36,6 +36,6 @@
 
   @Override
   public void buildIR(IRBuilder builder) {
-    builder.addMove(ValueType.INT_OR_FLOAT, AAAA, BBBB);
+    builder.addMove(ValueType.INT_OR_FLOAT_OR_NULL, AAAA, BBBB);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/MoveFrom16.java b/src/main/java/com/android/tools/r8/code/MoveFrom16.java
index 7d33a0b..a81a8a0 100644
--- a/src/main/java/com/android/tools/r8/code/MoveFrom16.java
+++ b/src/main/java/com/android/tools/r8/code/MoveFrom16.java
@@ -36,6 +36,6 @@
 
   @Override
   public void buildIR(IRBuilder builder) {
-    builder.addMove(ValueType.INT_OR_FLOAT, AA, BBBB);
+    builder.addMove(ValueType.INT_OR_FLOAT_OR_NULL, AA, BBBB);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/code/MoveType.java b/src/main/java/com/android/tools/r8/code/MoveType.java
index 6ade361..a9ff6e0 100644
--- a/src/main/java/com/android/tools/r8/code/MoveType.java
+++ b/src/main/java/com/android/tools/r8/code/MoveType.java
@@ -18,6 +18,7 @@
       case INT:
       case FLOAT:
       case INT_OR_FLOAT:
+      case INT_OR_FLOAT_OR_NULL:
         return SINGLE;
       case LONG:
       case DOUBLE:
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
index b089e46..2b02211 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -471,12 +471,16 @@
     if (normalExits.get(0).exit().asReturn().isReturnVoid()) {
       newReturn = new Return();
     } else {
+      ValueType returnType = null;
       boolean same = true;
       List<Value> operands = new ArrayList<>(normalExits.size());
       for (BasicBlock exitBlock : normalExits) {
-        Value retValue = exitBlock.exit().asReturn().returnValue();
+        Return exit = exitBlock.exit().asReturn();
+        Value retValue = exit.returnValue();
         operands.add(retValue);
         same = same && retValue == operands.get(0);
+        assert returnType == null || returnType == exit.getReturnType();
+        returnType = exit.getReturnType();
       }
       Value value;
       if (same) {
@@ -486,12 +490,12 @@
             new Phi(
                 code.valueNumberGenerator.next(),
                 newExitBlock,
-                operands.get(0).outType(),
+                returnType,
                 null);
         phi.addOperands(operands);
         value = phi;
       }
-      newReturn = new Return(value, value.outType());
+      newReturn = new Return(value, returnType);
     }
     // The newly constructed return will be eliminated as part of inlining so we set position none.
     newReturn.setPosition(Position.none());
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index ba5c27f..b887e97 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -38,7 +38,7 @@
   }
 
   private boolean preciseTypeUnknown() {
-    return outType() == ValueType.INT_OR_FLOAT || outType() == ValueType.LONG_OR_DOUBLE;
+    return !outType().isPreciseType();
   }
 
   public Value dest() {
@@ -52,7 +52,8 @@
   public int getIntValue() {
     assert outType() == ValueType.INT
         || outType() == ValueType.INT_OR_FLOAT
-        || outType() == ValueType.OBJECT;
+        || outType() == ValueType.INT_OR_FLOAT_OR_NULL
+        || outType() == ValueType.OBJECT; // Used for is-null conditionals.
     return (int) value;
   }
 
@@ -62,7 +63,9 @@
   }
 
   public float getFloatValue() {
-    assert outType() == ValueType.FLOAT || outType() == ValueType.INT_OR_FLOAT;
+    assert outType() == ValueType.FLOAT
+        || outType() == ValueType.INT_OR_FLOAT
+        || outType() == ValueType.INT_OR_FLOAT_OR_NULL;
     return Float.intBitsToFloat((int) value);
   }
 
@@ -104,7 +107,7 @@
     }
 
     int register = builder.allocatedRegister(dest(), getNumber());
-    if (outType().isSingle() || outType().isObject()) {
+    if (outType().isObjectOrSingle()) {
       assert NumberUtils.is32Bit(value);
       if ((register & 0xf) == register && NumberUtils.is4Bit(value)) {
         builder.add(this, new Const4(register, (int) value));
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 5a9673f..faab702 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
@@ -30,14 +30,6 @@
   // contain this phi.
   private List<Map<Integer, Value>> definitionUsers = new ArrayList<>();
 
-  // The computed out type is not always the same as 'this.type' because of the type
-  // confusion around null and constant zero. The null object can be used in a single
-  // context (if tests) and the single 0 can be used as null. A phi can therefore
-  // have either of the creation types 'single' and 'object' depending on the use that
-  // triggered the creation of the phi. We therefore have to delay the output type
-  // computation of the phi until all operands are known.
-  private ValueType outType = null;
-
   public Phi(int number, BasicBlock block, ValueType type, DebugLocalInfo local) {
     super(number, type, local);
     this.block = block;
@@ -245,6 +237,11 @@
     }
     builder.append(" <- phi");
     StringUtils.append(builder, ListUtils.map(operands, Value::toString));
+    ValueType computed = computeOutType(null);
+    builder.append(" : ").append(type);
+    if (type != computed) {
+      builder.append(" / ").append(computed);
+    }
     return builder.toString();
   }
 
@@ -272,12 +269,6 @@
     definitionUsers = null;
   }
 
-  private boolean isSingleConstZero(Value value) {
-    return value.definition != null
-        && value.outType().isSingle()
-        && value.isZero();
-  }
-
   /**
    * Determine if the only possible values for the phi are the integers 0 or 1.
    */
@@ -314,40 +305,53 @@
   }
 
   private ValueType computeOutType(Set<Phi> active) {
-    if (outType != null) {
-      return outType;
-    }
-    active.add(this);
     // Go through non-phi operands first to determine if we have an operand that dictates the type.
     for (Value operand : operands) {
       // Since a constant zero can be either an integer or an Object (null) we skip them
       // when computing types and rely on other operands to specify the actual type.
-      if (!operand.isPhi() && !isSingleConstZero(operand)) {
-        return operand.outType();
+      if (!operand.isPhi()) {
+        if (operand.outType() != ValueType.INT_OR_FLOAT_OR_NULL) {
+          return operand.outType();
+        }
+        assert operand.isZero();
       }
     }
     // We did not find a non-phi operand that dictates the type. Recurse on phi arguments.
+    if (active == null) {
+      active = new HashSet<>();
+    }
+    active.add(this);
     for (Value operand : operands) {
       if (operand.isPhi() && !active.contains(operand.asPhi())) {
         ValueType phiType = operand.asPhi().computeOutType(active);
-        if (phiType != ValueType.INT_OR_FLOAT) {
+        if (phiType != ValueType.INT_OR_FLOAT_OR_NULL) {
           return phiType;
         }
       }
     }
-    // All operands were the constant zero or phis with out type INT_OR_FLOAT and the DEX move type
-    // is either object or single depending on the use. Since all inputs have out type INT_OF_FLOAT
-    // it is safe to return ValueType.INT_OR_FLOAT here.
-    assert type.isSingle() || type.isObject();
-    return ValueType.INT_OR_FLOAT;
+    // All operands were the constant zero or phis with out type INT_OR_FLOAT_OR_NULL.
+    return ValueType.INT_OR_FLOAT_OR_NULL;
+  }
+
+  private static boolean verifyUnknownOrCompatible(ValueType known, ValueType computed) {
+    assert computed == ValueType.INT_OR_FLOAT_OR_NULL || known.compatible(computed);
+    return true;
   }
 
   @Override
   public ValueType outType() {
-    if (outType != null) {
-      return outType;
+    if (type != ValueType.INT_OR_FLOAT_OR_NULL) {
+      assert verifyUnknownOrCompatible(type, computeOutType(null));
+      return type;
     }
-    return computeOutType(new HashSet<>());
+    // If the phi has unknown type (ie, INT_OR_FLOAT_OR_NULL) then it must be computed. This is
+    // because of the type confusion around null and constant zero. The null object can be used in
+    // a single context (if tests) and the single 0 can be used as null. If the instruction
+    // triggering the creation of a phi does not determine the type (eg, a move can be of int,
+    // float or zero/null) we need to compute the actual type based on the operands.
+    ValueType computedType = computeOutType(null);
+    assert computedType.isObjectOrSingle();
+    return computedType;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java
index 6561d27..a197c87 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Return.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -29,6 +29,7 @@
 
   public Return(Value value, ValueType returnType) {
     super(null, value);
+    assert returnType != ValueType.INT_OR_FLOAT_OR_NULL;
     this.returnType = returnType;
   }
 
@@ -49,17 +50,17 @@
     if (isReturnVoid()) {
       return new ReturnVoid();
     } else {
+      int register = builder.allocatedRegister(returnValue(), getNumber());
       switch (MoveType.fromValueType(returnType)) {
         case OBJECT:
-          assert returnValue().outType().isObject() || returnValue().outType().isSingle();
-          return new ReturnObject(builder.allocatedRegister(returnValue(), getNumber()));
+          assert returnValue().outType().isObjectOrNull();
+          return new ReturnObject(register);
         case SINGLE:
-          assert returnValue().outType().isSingle();
-          return new com.android.tools.r8.code.Return(
-              builder.allocatedRegister(returnValue(), getNumber()));
+          assert returnValue().outType().isSingleOrZero();
+          return new com.android.tools.r8.code.Return(register);
         case WIDE:
           assert returnValue().outType().isWide();
-          return new ReturnWide(builder.allocatedRegister(returnValue(), getNumber()));
+          return new ReturnWide(register);
         default:
           throw new Unreachable();
       }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ValueType.java b/src/main/java/com/android/tools/r8/ir/code/ValueType.java
index dd058f0..c41b062 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ValueType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ValueType.java
@@ -13,6 +13,7 @@
   INT,
   FLOAT,
   INT_OR_FLOAT,
+  INT_OR_FLOAT_OR_NULL,
   LONG,
   DOUBLE,
   LONG_OR_DOUBLE;
@@ -29,16 +30,34 @@
     return this == LONG || this == DOUBLE || this == LONG_OR_DOUBLE;
   }
 
-  public int requiredRegisters() {
-    return isWide() ? 2 : 1;
+  public boolean isObjectOrSingle() {
+    return !isWide();
+  }
+
+  public boolean isObjectOrNull() {
+    return isObject() || this == INT_OR_FLOAT_OR_NULL;
+  }
+
+  public boolean isSingleOrZero() {
+    return isSingle() || this == INT_OR_FLOAT_OR_NULL;
+  }
+
+  public boolean isPreciseType() {
+    return this != ValueType.INT_OR_FLOAT
+        && this != ValueType.LONG_OR_DOUBLE
+        && this != ValueType.INT_OR_FLOAT_OR_NULL;
   }
 
   public boolean compatible(ValueType other) {
-    return (isObject() && other.isObject())
+    return this == other
         || (isSingle() && other.isSingle())
         || (isWide() && other.isWide());
   }
 
+  public int requiredRegisters() {
+    return isWide() ? 2 : 1;
+  }
+
   public static ValueType fromMemberType(MemberType type) {
     switch (type) {
       case BOOLEAN:
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index e8e689c..df7deb0 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -998,24 +998,24 @@
     closeCurrentBlock();
   }
 
-  public void addIf(If.Type type, int value1, int value2,
+  public void addIf(If.Type type, ValueType operandType, int value1, int value2,
       int trueTargetOffset, int falseTargetOffset) {
     if (trueTargetOffset == falseTargetOffset) {
       addTrivialIf(trueTargetOffset, falseTargetOffset);
     } else {
       List<Value> values = new ArrayList<>(2);
-      values.add(readRegister(value1, ValueType.INT));
-      values.add(readRegister(value2, ValueType.INT));
+      values.add(readRegister(value1, operandType));
+      values.add(readRegister(value2, operandType));
       If instruction = new If(type, values);
       addNonTrivialIf(instruction, trueTargetOffset, falseTargetOffset);
     }
   }
 
-  public void addIfZero(If.Type type, int value, int trueTargetOffset, int falseTargetOffset) {
+  public void addIfZero(If.Type type, ValueType operandType, int value, int trueTargetOffset, int falseTargetOffset) {
     if (trueTargetOffset == falseTargetOffset) {
       addTrivialIf(trueTargetOffset, falseTargetOffset);
     } else {
-      If instruction = new If(type, readRegister(value, ValueType.INT));
+      If instruction = new If(type, readRegister(value, operandType));
       addNonTrivialIf(instruction, trueTargetOffset, falseTargetOffset);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 3c102c1..04a2685 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -2647,12 +2647,21 @@
       assert targets.length == 2;
       if (opcode <= Opcodes.IFLE) {
         Slot value = state.pop(Type.INT_TYPE);
-        builder.addIfZero(ifType(opcode), value.register, targets[0], targets[1]);
+        builder.addIfZero(ifType(opcode), ValueType.INT, value.register, targets[0], targets[1]);
       } else {
-        Type expectedType = opcode < Opcodes.IF_ACMPEQ ? Type.INT_TYPE : JarState.REFERENCE_TYPE;
+        ValueType valueType;
+        Type expectedType;
+        if (opcode < Opcodes.IF_ACMPEQ) {
+          valueType = ValueType.INT;
+          expectedType = Type.INT_TYPE;
+        } else {
+          valueType = ValueType.OBJECT;
+          expectedType = JarState.REFERENCE_TYPE;
+        }
         Slot value2 = state.pop(expectedType);
         Slot value1 = state.pop(expectedType);
-        builder.addIf(ifType(opcode), value1.register, value2.register, targets[0], targets[1]);
+        builder.addIf(
+            ifType(opcode), valueType, value1.register, value2.register, targets[0], targets[1]);
       }
     } else {
       switch (opcode) {
@@ -2665,7 +2674,7 @@
         case Opcodes.IFNONNULL: {
           Slot value = state.pop(JarState.REFERENCE_TYPE);
           If.Type type = opcode == Opcodes.IFNULL ? If.Type.EQ : If.Type.NE;
-          builder.addIfZero(type, value.register, targets[0], targets[1]);
+          builder.addIfZero(type, ValueType.OBJECT, value.register, targets[0], targets[1]);
           break;
         }
         case Opcodes.JSR: {
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 96ccab1..a1c529a 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
@@ -737,10 +737,7 @@
                     argumentIndex)) {
                   Value argument = invoke.arguments().get(argumentIndex);
                   assert invoke.outType().compatible(argument.outType())
-                      || (!options.outputClassFiles
-                            && invoke.outType() == ValueType.OBJECT
-                            && argument.outType().isSingle()
-                            && argument.isZero());
+                      || (!options.outputClassFiles && verifyCompatibleFromDex(invoke, argument));
                   invoke.outValue().replaceUsers(argument);
                   invoke.setOutValue(null);
                 }
@@ -753,6 +750,14 @@
     assert code.isConsistentGraph();
   }
 
+  private static boolean verifyCompatibleFromDex(Invoke invoke, Value argument) {
+    ValueType invokeType = invoke.outType();
+    ValueType argumentType = argument.outType();
+    assert argument.isZero();
+    assert (invokeType.isObject() && argumentType.isObjectOrNull())
+        || (invokeType.isSingle() && argumentType.isSingleOrZero());
+    return true;
+  }
 
   /**
    * For supporting assert javac adds the static field $assertionsDisabled to all classes which