Rewrite simple conversions of boolean to the corresponding integers.

Since a boolean is either 0 or 1, we can use it directly without
conversion.

R=sgjesse@google.com

Change-Id: I027840f2bb9278ce8bc512bd97942b979837acb9
diff --git a/src/main/java/com/android/tools/r8/ir/code/Argument.java b/src/main/java/com/android/tools/r8/ir/code/Argument.java
index c7b55d5..fbf6658 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Argument.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Argument.java
@@ -17,7 +17,7 @@
 
   public Argument(Value outValue) {
     super(outValue);
-    outValue.markAsArgument();;
+    outValue.markAsArgument();
   }
 
   @Override
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 661a602..e376361 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
@@ -77,6 +77,14 @@
     return value == 0;
   }
 
+  public boolean isIntegerZero() {
+    return type == ConstType.INT && getIntValue() == 0;
+  }
+
+  public boolean isIntegerOne() {
+    return type == ConstType.INT && getIntValue() == 1;
+  }
+
   public boolean isIntegerNegativeOne(NumericType type) {
     assert type == NumericType.INT || type == NumericType.LONG;
     if (type == NumericType.INT) {
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 07f6913..dfa8548 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
@@ -48,6 +48,7 @@
   private boolean neverNull = false;
   private boolean isThis = false;
   private boolean isArgument = false;
+  private boolean knownToBeBoolean = false;
   private LongInterval valueRange;
   private final DebugData debugData;
 
@@ -494,6 +495,14 @@
     return isArgument;
   }
 
+  public void setKnownToBeBoolean(boolean knownToBeBoolean) {
+    this.knownToBeBoolean = knownToBeBoolean;
+  }
+
+  public boolean knownToBeBoolean() {
+    return knownToBeBoolean;
+  }
+
   public void markAsThis() {
     assert isArgument;
     assert !isThis;
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 1f29d26..7f7b726 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
@@ -500,6 +500,13 @@
     addInstruction(new Argument(value));
   }
 
+  public void addBooleanNonThisArgument(int register) {
+    DebugLocalInfo local = getCurrentLocal(register);
+    Value value = writeRegister(register, MoveType.SINGLE, ThrowingInfo.NO_THROW, local);
+    value.setKnownToBeBoolean(true);
+    addInstruction(new Argument(value));
+  }
+
   public void addDebugUninitialized(int register, ConstType type) {
     if (!options.debug) {
       return;
@@ -613,6 +620,7 @@
     Value in1 = readRegister(array, MoveType.OBJECT);
     Value in2 = readRegister(index, MoveType.SINGLE);
     Value out = writeRegister(dest, MoveType.fromMemberType(type), ThrowingInfo.CAN_THROW);
+    out.setKnownToBeBoolean(type == MemberType.BOOLEAN);
     ArrayGet instruction = new ArrayGet(type, out, in1, in2);
     assert instruction.instructionTypeCanThrow();
     add(instruction);
@@ -883,6 +891,7 @@
       DexField field) {
     Value in = readRegister(object, MoveType.OBJECT);
     Value out = writeRegister(dest, MoveType.fromMemberType(type), ThrowingInfo.CAN_THROW);
+    out.setKnownToBeBoolean(type == MemberType.BOOLEAN);
     InstanceGet instruction = new InstanceGet(type, out, in, field);
     assert instruction.instructionTypeCanThrow();
     addInstruction(instruction);
@@ -1114,6 +1123,16 @@
     invoke.setOutValue(writeRegister(dest, type, ThrowingInfo.CAN_THROW));
   }
 
+  public void addBooleanMoveResult(MoveType type, int dest) {
+    List<Instruction> instructions = currentBlock.getInstructions();
+    Invoke invoke = instructions.get(instructions.size() - 1).asInvoke();
+    assert invoke.outValue() == null;
+    assert invoke.instructionTypeCanThrow();
+    Value outValue = writeRegister(dest, type, ThrowingInfo.CAN_THROW);
+    outValue.setKnownToBeBoolean(true);
+    invoke.setOutValue(outValue);
+  }
+
   public void addNeg(NumericType type, int dest, int value) {
     Value in = readNumericRegister(value, type);
     Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
@@ -1165,6 +1184,7 @@
 
   public void addStaticGet(MemberType type, int dest, DexField field) {
     Value out = writeRegister(dest, MoveType.fromMemberType(type), ThrowingInfo.CAN_THROW);
+    out.setKnownToBeBoolean(type == MemberType.BOOLEAN);
     StaticGet instruction = new StaticGet(type, out, field);
     assert instruction.instructionTypeCanThrow();
     addInstruction(instruction);
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 dc49630..4081dbd 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
@@ -337,7 +337,11 @@
     for (Type type : parameterTypes) {
       MoveType moveType = moveType(type);
       Slot slot = state.readLocal(argumentRegister, type);
-      builder.addNonThisArgument(slot.register, moveType);
+      if (type == Type.BOOLEAN_TYPE) {
+        builder.addBooleanNonThisArgument(slot.register);
+      } else {
+        builder.addNonThisArgument(slot.register, moveType);
+      }
       argumentRegister += moveType.requiredRegisters();
     }
   }
@@ -2551,7 +2555,11 @@
     // Move the result to the "top of stack".
     Type returnType = Type.getReturnType(methodDesc);
     if (returnType != Type.VOID_TYPE) {
-      builder.addMoveResult(moveType(returnType), state.push(returnType));
+      if (returnType == Type.BOOLEAN_TYPE) {
+        builder.addBooleanMoveResult(moveType(returnType), state.push(returnType));
+      } else {
+        builder.addMoveResult(moveType(returnType), state.push(returnType));
+      }
     }
   }
 
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 e78595d..1688031 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
@@ -1434,10 +1434,16 @@
         // First rewrite zero comparison.
         rewriteIfWithConstZero(block);
 
+        if (simplifyKnownBooleanCondition(dominator, block)) {
+          continue;
+        }
+
         // Simplify if conditions when possible.
         If theIf = block.exit().asIf();
         List<Value> inValues = theIf.inValues();
+
         int cond;
+
         if (inValues.get(0).isConstNumber()
             && (theIf.isZeroTest() || inValues.get(1).isConstNumber())) {
           // Zero test with a constant of comparison between between two constants.
@@ -1474,22 +1480,83 @@
         BasicBlock target = theIf.targetFromCondition(cond);
         BasicBlock deadTarget =
             target == theIf.getTrueTarget() ? theIf.fallthroughBlock() : theIf.getTrueTarget();
-        List<BasicBlock> removedBlocks = block.unlink(deadTarget, dominator);
-        for (BasicBlock removedBlock : removedBlocks) {
-          if (!removedBlock.isMarked()) {
-            removedBlock.mark();
-          }
-        }
-        assert theIf == block.exit();
-        block.replaceLastInstruction(new Goto());
-        assert block.exit().isGoto();
-        assert block.exit().asGoto().getTarget() == target;
+        rewriteIfToGoto(dominator, block, theIf, target, deadTarget);
       }
     }
     code.removeMarkedBlocks();
     assert code.isConsistentSSA();
   }
 
+
+  /* Identify simple diamond shapes converting boolean true/false to 1/0. We consider the forms:
+   *
+   *   ifeqz booleanValue       ifnez booleanValue
+   *      /        \              /        \
+   *      \        /              \        /
+   *      phi(0, 1)                phi(1, 0)
+   *
+   * which can be replaced by a fallthrough and the phi value can be replaced
+   * with the boolean value itself.
+   */
+  private boolean simplifyKnownBooleanCondition(DominatorTree dominator, BasicBlock block) {
+    If theIf = block.exit().asIf();
+    Value testValue = theIf.inValues().get(0);
+    if (theIf.isZeroTest() && testValue.knownToBeBoolean()) {
+      BasicBlock trueBlock = theIf.getTrueTarget();
+      BasicBlock falseBlock = theIf.fallthroughBlock();
+      if (trueBlock.isTrivialGoto() &&
+          falseBlock.isTrivialGoto() &&
+          trueBlock.getSuccessors().get(0) == falseBlock.getSuccessors().get(0)) {
+        BasicBlock targetBlock = trueBlock.getSuccessors().get(0);
+        if (targetBlock.getPredecessors().size() == 2) {
+          int trueIndex = targetBlock.getPredecessors().indexOf(trueBlock);
+          int falseIndex = trueIndex == 0 ? 1 : 0;
+          int deadPhis = 0;
+          // Locate the phis that have the same value as the boolean and replace them
+          // by the boolean in all users.
+          for (Phi phi : targetBlock.getPhis()) {
+            Value trueValue = phi.getOperand(trueIndex);
+            Value falseValue = phi.getOperand(falseIndex);
+            if (trueValue.isConstNumber() && falseValue.isConstNumber()) {
+              ConstNumber trueNumber = trueValue.getConstInstruction().asConstNumber();
+              ConstNumber falseNumber = falseValue.getConstInstruction().asConstNumber();
+              if ((theIf.getType() == Type.EQ &&
+                  trueNumber.isIntegerZero() &&
+                  falseNumber.isIntegerOne()) ||
+                  (theIf.getType() == Type.NE &&
+                      trueNumber.isIntegerOne() &&
+                      falseNumber.isIntegerZero())) {
+                phi.replaceUsers(testValue);
+                deadPhis++;
+              }
+            }
+          }
+          // If all phis were removed, there is no need for the diamond shape anymore
+          // and it can be rewritten to a goto to one of the branches.
+          if (deadPhis == targetBlock.getPhis().size()) {
+            rewriteIfToGoto(dominator, block, theIf, trueBlock, falseBlock);
+            return true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  private void rewriteIfToGoto(DominatorTree dominator, BasicBlock block, If theIf,
+      BasicBlock target, BasicBlock deadTarget) {
+    List<BasicBlock> removedBlocks = block.unlink(deadTarget, dominator);
+    for (BasicBlock removedBlock : removedBlocks) {
+      if (!removedBlock.isMarked()) {
+        removedBlock.mark();
+      }
+    }
+    assert theIf == block.exit();
+    block.replaceLastInstruction(new Goto());
+    assert block.exit().isGoto();
+    assert block.exit().asGoto().getTarget() == target;
+  }
+
   private void rewriteIfWithConstZero(BasicBlock block) {
     If theIf = block.exit().asIf();
     if (theIf.isZeroTest()) {