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()) {