Merge "Compare DEX instructions as last step in identicalAfterRegisterAllocation"
diff --git a/.gitignore b/.gitignore
index b8248ed..482b1bf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,6 +45,8 @@
third_party/dart-sdk.tar.gz
third_party/desugar/desugar_*/
third_party/desugar/desugar_*.tar.gz
+third_party/internal/*
+!third_party/internal/*.sha1
third_party/gmail/*
!third_party/gmail/*.sha1
third_party/gmscore/*
diff --git a/build.gradle b/build.gradle
index 6523153..80b4c80 100644
--- a/build.gradle
+++ b/build.gradle
@@ -346,6 +346,7 @@
"classlib",
"cf_segments",
"desugar/desugar_20180308",
+ "internal/issue-127524985",
"framework",
"gmail/gmail_android_170604.16",
"gmail/gmail_android_180826.15",
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 4955807..0ba6cb1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -343,7 +343,7 @@
public void setCode(IRCode ir, RegisterAllocator registerAllocator, InternalOptions options) {
checkIfObsolete();
- final DexBuilder builder = new DexBuilder(ir, registerAllocator, options);
+ final DexBuilder builder = new DexBuilder(ir, registerAllocator);
setCode(builder.build());
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 4714251..f845dc5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -469,6 +469,11 @@
return false;
}
}
+ // Finally check that the dex instructions for the generated code actually are the same.
+ if (allocator.options().isGeneratingDex()
+ && !DexBuilder.identicalInstructionsAfterBuildingDexCode(this, other, allocator)) {
+ return false;
+ }
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index f295ddd..2eaa046 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -70,6 +70,7 @@
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
@@ -120,15 +121,40 @@
BasicBlock nextBlock;
- public DexBuilder(
- IRCode ir,
- RegisterAllocator registerAllocator,
- InternalOptions options) {
+ public DexBuilder(IRCode ir, RegisterAllocator registerAllocator) {
+ this(ir, registerAllocator, ir.options);
assert ir != null;
assert registerAllocator != null;
+ }
+
+ private DexBuilder(IRCode ir, RegisterAllocator registerAllocator, InternalOptions options) {
this.ir = ir;
this.registerAllocator = registerAllocator;
this.options = options;
+ if (isBuildingForComparison()) {
+ instructionToInfo = new Info[1];
+ }
+ }
+
+ public static boolean identicalInstructionsAfterBuildingDexCode(
+ com.android.tools.r8.ir.code.Instruction a,
+ com.android.tools.r8.ir.code.Instruction b,
+ RegisterAllocator allocator) {
+ DexBuilder builder = new DexBuilder(null, allocator, allocator.options());
+ Info infoA = buildInfoForComparison(a, builder);
+ Info infoB = buildInfoForComparison(b, builder);
+ return infoA.identicalInstructions(infoB, builder);
+ }
+
+ private static Info buildInfoForComparison(
+ com.android.tools.r8.ir.code.Instruction instruction, DexBuilder builder) {
+ instruction.buildDex(builder);
+ assert builder.instructionToInfo.length == 1;
+ return builder.instructionToInfo[0];
+ }
+
+ private boolean isBuildingForComparison() {
+ return ir == null;
}
private void reset() {
@@ -561,7 +587,8 @@
public void add(com.android.tools.r8.ir.code.Instruction instr, Instruction dex) {
assert !instr.isGoto();
- assert !instr.isDexItemBasedConstString()
+ assert isBuildingForComparison()
+ || !instr.isDexItemBasedConstString()
|| ir.method.getOptimizationInfo().useIdentifierNameString();
add(instr, new FixedSizeInfo(instr, dex));
}
@@ -597,6 +624,11 @@
}
private void add(com.android.tools.r8.ir.code.Instruction ir, Info info) {
+ if (isBuildingForComparison()) {
+ // We are building for instruction comparison, so just set the info.
+ setSingleInfo(info);
+ return;
+ }
assert ir != null;
assert info != null;
assert getInfo(ir) == null;
@@ -623,6 +655,11 @@
instructionToInfo[instructionNumberToIndex(instruction.getNumber())] = info;
}
+ private void setSingleInfo(Info info) {
+ assert instructionToInfo.length == 1;
+ instructionToInfo[0] = info;
+ }
+
private Info getTargetInfo(BasicBlock block) {
InstructionIterator iterator = block.iterator();
com.android.tools.r8.ir.code.Instruction instruction = null;
@@ -887,6 +924,8 @@
public com.android.tools.r8.ir.code.Instruction getIR() {
return ir;
}
+
+ public abstract boolean identicalInstructions(Info other, DexBuilder builder);
}
private static class FixedSizeInfo extends Info {
@@ -923,6 +962,12 @@
public void addInstructions(DexBuilder builder, List<Instruction> instructions) {
instructions.add(instruction);
}
+
+ @Override
+ public boolean identicalInstructions(Info other, DexBuilder builder) {
+ return other instanceof FixedSizeInfo
+ && instruction.equals(((FixedSizeInfo) other).instruction);
+ }
}
private static class MultiFixedSizeInfo extends Info {
@@ -970,6 +1015,12 @@
public int getSize() {
return size;
}
+
+ @Override
+ public boolean identicalInstructions(Info other, DexBuilder builder) {
+ return other instanceof MultiFixedSizeInfo
+ && Arrays.equals(instructions, ((MultiFixedSizeInfo) other).instructions);
+ }
}
private static class FallThroughInfo extends Info {
@@ -1001,6 +1052,11 @@
public int maxSize() {
return 0;
}
+
+ @Override
+ public boolean identicalInstructions(Info other, DexBuilder builder) {
+ return other instanceof FallThroughInfo;
+ }
}
private static class GotoInfo extends Info {
@@ -1127,6 +1183,11 @@
instructions.add(dex);
}
}
+
+ @Override
+ public boolean identicalInstructions(Info other, DexBuilder builder) {
+ return other instanceof GotoInfo;
+ }
}
public static class IfInfo extends Info {
@@ -1137,6 +1198,18 @@
super(branch);
}
+ private int getRegister(int operandIndex, DexBuilder builder) {
+ If branch = getBranch();
+ return builder.allocatedRegister(branch.inValues().get(operandIndex), branch.getNumber());
+ }
+
+ private int[] getRegisters(DexBuilder builder) {
+ if (getBranch().isZeroTest()) {
+ return new int[] {getRegister(0, builder)};
+ }
+ return new int[] {getRegister(0, builder), getRegister(1, builder)};
+ }
+
private If getBranch() {
return (If) getIR();
}
@@ -1167,7 +1240,7 @@
int source = builder.getInfo(branch).getOffset();
int target = builder.getInfo(branch.getTrueTarget().entry()).getOffset();
int relativeOffset = target - source;
- int register1 = builder.allocatedRegister(branch.inValues().get(0), branch.getNumber());
+ int register1 = getRegister(0, builder);
if (relativeOffset < 0) {
builder.hasBackwardsBranch = true;
@@ -1203,7 +1276,7 @@
break;
}
} else {
- int register2 = builder.allocatedRegister(branch.inValues().get(1), branch.getNumber());
+ int register2 = getRegister(1, builder);
switch (getBranch().getType()) {
case EQ:
instruction = new IfEq(register1, register2, relativeOffset);
@@ -1252,6 +1325,16 @@
public int getSize() {
return size;
}
+
+ @Override
+ public boolean identicalInstructions(Info other, DexBuilder builder) {
+ if (!(other instanceof IfInfo)) {
+ return false;
+ }
+ IfInfo otherInfo = (IfInfo) other;
+ return getBranch().getType() == otherInfo.getBranch().getType()
+ && Arrays.equals(getRegisters(builder), otherInfo.getRegisters(builder));
+ }
}
public static class MoveInfo extends Info {
@@ -1275,6 +1358,16 @@
}
@Override
+ public boolean identicalInstructions(Info other, DexBuilder builder) {
+ if (!(other instanceof MoveInfo)) {
+ return false;
+ }
+ MoveInfo moveInfo = (MoveInfo) other;
+ return srcRegister(builder) == moveInfo.srcRegister(builder)
+ && destRegister(builder) == moveInfo.destRegister(builder);
+ }
+
+ @Override
public int computeSize(DexBuilder builder) {
int srcRegister = srcRegister(builder);
int destRegister = destRegister(builder);
diff --git a/src/test/java/com/android/tools/r8/DXTestBuilder.java b/src/test/java/com/android/tools/r8/DXTestBuilder.java
index e9e7f9c..8462f7b 100644
--- a/src/test/java/com/android/tools/r8/DXTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/DXTestBuilder.java
@@ -3,12 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.D8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringUtils;
import java.io.IOException;
import java.nio.file.Path;
@@ -64,12 +66,33 @@
@Override
public DXTestBuilder addProgramClasses(Collection<Class<?>> classes) {
- throw new Unimplemented("No support for adding classes directly");
+ return addProgramClassFileData(
+ ListUtils.map(
+ classes,
+ c -> {
+ try {
+ return ToolHelper.getClassAsBytes(c);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }));
}
@Override
public DXTestBuilder addProgramClassFileData(Collection<byte[]> classes) {
- throw new Unimplemented("No support for adding classfile data directly");
+ try {
+ Path out = getState().getNewTempFolder().resolve("out.jar");
+ ArchiveConsumer consumer = new ArchiveConsumer(out);
+ for (byte[] clazz : classes) {
+ String descriptor = TestBase.extractClassDescriptor(clazz);
+ consumer.accept(ByteDataView.of(clazz), descriptor, null);
+ }
+ consumer.finished(null);
+ addProgramFiles(out);
+ return self();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 8fad46b..063e871 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -721,7 +721,7 @@
* Run application on Art with the specified main class.
*/
protected ProcessResult runOnArtRaw(AndroidApp app, Class mainClass) throws IOException {
- return runOnArtRaw(app, mainClass.getCanonicalName());
+ return runOnArtRaw(app, mainClass.getTypeName());
}
/**
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index 12735cf..1c3f77c 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -106,6 +106,11 @@
vm -> startInclusive.isOlderThanOrEqual(vm) && vm.isOlderThanOrEqual(endInclusive));
}
+ /** Add all available DEX runtimes that support native multidex. */
+ public TestParametersBuilder withNativeMultidexDexRuntimes() {
+ return withDexRuntimesStartingFromIncluding(DexVm.Version.V5_1_1);
+ }
+
/** Add all available DEX runtimes starting from and including {@param startInclusive}. */
public TestParametersBuilder withDexRuntimesStartingFromIncluding(DexVm.Version startInclusive) {
return withDexRuntimeFilter(vm -> startInclusive.isOlderThanOrEqual(vm));
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 5e3e100..7d2d474 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -121,6 +121,10 @@
return self();
}
+ public T addKeepAllAttributes() {
+ return addKeepRules("-keepattributes *");
+ }
+
public abstract T addApplyMapping(String proguardMap);
private static String getMethodLine(MethodReference method) {
diff --git a/src/test/java/com/android/tools/r8/internal/Regression127524985.java b/src/test/java/com/android/tools/r8/internal/Regression127524985.java
new file mode 100644
index 0000000..7de4078
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/Regression127524985.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2019, 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.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class Regression127524985 extends TestBase {
+
+ private static final String MAIN = "com.android.tools.r8.internal.Regression127524985$Main";
+
+ private static final Path JAR =
+ Paths.get("third_party/internal/issue-127524985/issue-127524985.jar");
+
+ private static final String EXPECTED = StringUtils.lines("true");
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ private final TestParameters parameters;
+
+ public Regression127524985(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Throwable {
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addClasspath(JAR)
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+ (parameters.isDexRuntime()
+ ? testForD8()
+ : testForR8(parameters.getBackend())
+ .debug()
+ .noTreeShaking()
+ .noMinification()
+ .addKeepAllAttributes()
+ .addKeepRules("-dontwarn *"))
+ .addProgramFiles(JAR)
+ .apply(parameters::setMinApiForRuntime)
+ .compile()
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+}
diff --git a/third_party/internal/issue-127524985.tar.gz.sha1 b/third_party/internal/issue-127524985.tar.gz.sha1
new file mode 100644
index 0000000..a1b57e3
--- /dev/null
+++ b/third_party/internal/issue-127524985.tar.gz.sha1
@@ -0,0 +1 @@
+c1d03d36435cc08d1ee218c94e8dbfc74df975f1
\ No newline at end of file