Merge "Don't remove casts to local values."
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 38b7acc..0c0ac08 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -55,7 +55,7 @@
*/
public final class D8 {
- private static final String VERSION = "v0.1.0";
+ private static final String VERSION = "v0.2.0";
private static final int STATUS_ERROR = 1;
private D8() {}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index bdc4fb3..b7afda7 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -71,7 +71,7 @@
public class R8 {
- private static final String VERSION = "v0.1.0";
+ private static final String VERSION = "v0.2.0";
private final Timing timing = new Timing("R8");
private final InternalOptions options;
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 2cdb1d3..d44ecd9 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
@@ -1435,10 +1435,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.
@@ -1475,22 +1481,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()) {
diff --git a/src/test/java/com/android/tools/r8/d8/D8FrameworkTest.java b/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
similarity index 84%
rename from src/test/java/com/android/tools/r8/d8/D8FrameworkTest.java
rename to src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
index a449e21..3aadc15 100644
--- a/src/test/java/com/android/tools/r8/d8/D8FrameworkTest.java
+++ b/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
@@ -16,7 +16,12 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
-
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.concurrent.ExecutionException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -24,20 +29,12 @@
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.concurrent.ExecutionException;
-
/**
- * Simple test that compiles framework.jar with D8 a number of times with
- * various number of threads available to the compiler.
- * This test also tests the hidden marker inserted into classes.dex.
+ * Simple test that compiles framework.jar with D8 a number of times with various number of threads
+ * available to the compiler. This test also tests the hidden marker inserted into classes.dex.
*/
-@RunWith( Parameterized.class )
-public class D8FrameworkTest {
+@RunWith(Parameterized.class)
+public class D8FrameworkDexPassthroughMarkerTest {
private static final Path FRAMEWORK_JAR =
Paths.get("tools/linux/art-5.1.1/product/mako/system/framework/framework.jar");
@@ -52,7 +49,7 @@
private final int threads;
- public D8FrameworkTest(int threads) {
+ public D8FrameworkDexPassthroughMarkerTest(int threads) {
this.threads = threads;
}
@@ -72,7 +69,9 @@
options.numberOfThreads = threads;
});
DexApplication dexApp =
- new ApplicationReader(app, new InternalOptions(), new Timing("D8FrameworkTest")).read();
+ new ApplicationReader(
+ app, new InternalOptions(), new Timing("D8FrameworkDexPassthroughMarkerTest"))
+ .read();
Marker readMarker = dexApp.dexItemFactory.extractMarker();
assertEquals(marker, readMarker);
}
diff --git a/src/test/java/com/android/tools/r8/internal/D8Framework14082017DesugaredVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8Framework14082017DesugaredVerificationTest.java
new file mode 100644
index 0000000..4cc5818
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/D8Framework14082017DesugaredVerificationTest.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class D8Framework14082017DesugaredVerificationTest extends CompilationTestBase {
+ private static final int MIN_SDK = 24;
+ private static final String JAR = "third_party/framework/framework_14082017_desugared.jar";
+
+ @Test
+ public void verifyDebugBuild()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ runAndCheckVerification(
+ D8Command.builder()
+ .addProgramFiles(Paths.get(JAR))
+ .setMode(CompilationMode.DEBUG)
+ .setMinApiLevel(MIN_SDK)
+ .build(),
+ JAR);
+ }
+
+ @Test
+ public void verifyReleaseBuild()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ runAndCheckVerification(
+ D8Command.builder()
+ .addProgramFiles(Paths.get(JAR))
+ .setMode(CompilationMode.RELEASE)
+ .setMinApiLevel(MIN_SDK)
+ .build(),
+ JAR);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java
new file mode 100644
index 0000000..87786df
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/D8Framework14082017VerificationTest.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class D8Framework14082017VerificationTest extends CompilationTestBase {
+ private static final int MIN_SDK = 24;
+ private static final String JAR = "third_party/framework/framework_14082017.jar";
+
+ @Test
+ public void verifyDebugBuild()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ runAndCheckVerification(
+ D8Command.builder()
+ .addProgramFiles(Paths.get(JAR))
+ .setMode(CompilationMode.DEBUG)
+ .setMinApiLevel(MIN_SDK)
+ .build(),
+ JAR);
+ }
+
+ @Test
+ public void verifyReleaseBuild()
+ throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+ runAndCheckVerification(
+ D8Command.builder()
+ .addProgramFiles(Paths.get(JAR))
+ .setMode(CompilationMode.RELEASE)
+ .setMinApiLevel(MIN_SDK)
+ .build(),
+ JAR);
+ }
+}