Merge "Insert cast-instruction from inlining in entry block if possible"
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 9b90df2..9828bed 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -97,7 +97,7 @@
public abstract DexMethod getRenamedMethodSignature(DexMethod originalMethod);
- public final DexEncodedMethod mapDexEncodedMethod(
+ public DexEncodedMethod mapDexEncodedMethod(
AppInfo appInfo, DexEncodedMethod originalEncodedMethod) {
DexMethod newMethod = getRenamedMethodSignature(originalEncodedMethod.method);
if (newMethod != originalEncodedMethod.method) {
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 05553c1..5c4d581 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
@@ -733,7 +733,10 @@
return;
}
assert local != null;
- assert local == getOutgoingLocal(register);
+ assert local == getOutgoingLocal(register) :
+ "local-start mismatch: " + local + " != " + getOutgoingLocal(register)
+ + " at " + currentInstructionOffset
+ + " for source\n" + source.toString();
// TODO(b/111251032): Here we lookup a value with type based on debug info. That's just wrong!
ValueType valueType = ValueType.fromDexType(local.type);
Value incomingValue = readRegisterIgnoreLocal(register, valueType, local);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodDesugaringLense.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodDesugaringLense.java
new file mode 100644
index 0000000..a8616fb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodDesugaringLense.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2018, 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.ir.desugar;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+class InterfaceMethodDesugaringLense extends NestedGraphLense {
+ private final Map<DexEncodedMethod, DexEncodedMethod> methodsWithMovedCode;
+
+ InterfaceMethodDesugaringLense(
+ BiMap<DexMethod, DexMethod> methodMapping,
+ Map<DexEncodedMethod, DexEncodedMethod> methodsWithMovedCode,
+ GraphLense previous, DexItemFactory factory) {
+ super(
+ ImmutableMap.of(),
+ methodMapping,
+ ImmutableMap.of(),
+ ImmutableBiMap.of(),
+ methodMapping.inverse(),
+ previous,
+ factory);
+ this.methodsWithMovedCode = methodsWithMovedCode;
+ }
+
+ @Override
+ public DexEncodedMethod mapDexEncodedMethod(AppInfo appInfo, DexEncodedMethod original) {
+ return super.mapDexEncodedMethod(
+ appInfo, methodsWithMovedCode.getOrDefault(original, original));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 76a80d4..3883f4d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -368,6 +368,12 @@
processor.process(clazz.asProgramClass());
}
}
+ if (converter.enableWholeProgramOptimizations &&
+ (!processor.methodsWithMovedCode.isEmpty() || !processor.movedMethods.isEmpty())) {
+ converter.setGraphLense(
+ new InterfaceMethodDesugaringLense(processor.movedMethods,
+ processor.methodsWithMovedCode, converter.getGraphLense(), factory));
+ }
return processor.companionClasses;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index c538e86..e5900d9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -19,6 +19,8 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
@@ -40,6 +42,9 @@
// All created companion classes indexed by interface classes.
final Map<DexProgramClass, DexProgramClass> companionClasses = new IdentityHashMap<>();
+ final BiMap<DexMethod, DexMethod> movedMethods = HashBiMap.create();
+ final Map<DexEncodedMethod, DexEncodedMethod> methodsWithMovedCode = new IdentityHashMap<>();
+
InterfaceProcessor(InterfaceMethodRewriter rewriter) {
this.rewriter = rewriter;
}
@@ -74,12 +79,15 @@
assert (dexCode.getDebugInfo() == null)
|| (companionMethod.getArity() == dexCode.getDebugInfo().parameters.length);
- companionMethods.add(new DexEncodedMethod(companionMethod,
- newFlags, virtual.annotations, virtual.parameterAnnotationsList, code));
-
// Make the method abstract.
virtual.accessFlags.setAbstract();
- virtual.removeCode();
+ virtual.removeCode(); // Remove code first to void ownership.
+
+ DexEncodedMethod implMethod = new DexEncodedMethod(
+ companionMethod, newFlags, virtual.annotations, virtual.parameterAnnotationsList, code);
+ companionMethods.add(implMethod);
+
+ methodsWithMovedCode.put(virtual, implMethod);
}
// Remove bridge methods.
@@ -105,27 +113,29 @@
newFlags.setPublic();
}
+ DexMethod oldMethod = direct.method;
if (isStaticMethod(direct)) {
assert originalFlags.isPrivate() || originalFlags.isPublic()
: "Static interface method " + direct.toSourceString() + " is expected to "
+ "either be public or private in " + iface.origin;
- companionMethods.add(new DexEncodedMethod(
- rewriter.staticAsMethodOfCompanionClass(direct.method), newFlags,
+ DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(oldMethod);
+ companionMethods.add(new DexEncodedMethod(companionMethod, newFlags,
direct.annotations, direct.parameterAnnotationsList, direct.getCode()));
+ movedMethods.put(oldMethod, companionMethod);
} else {
if (originalFlags.isPrivate()) {
- assert !rewriter.factory.isClassConstructor(direct.method)
+ assert !rewriter.factory.isClassConstructor(oldMethod)
: "Unexpected private constructor " + direct.toSourceString()
+ " in " + iface.origin;
newFlags.setStatic();
- DexMethod companionMethod = rewriter.privateAsMethodOfCompanionClass(direct.method);
+ DexMethod companionMethod = rewriter.privateAsMethodOfCompanionClass(oldMethod);
Code code = direct.getCode();
if (code == null) {
throw new CompilationError("Code is missing for private instance "
- + "interface method: " + direct.method.toSourceString(), iface.origin);
+ + "interface method: " + oldMethod.toSourceString(), iface.origin);
}
DexCode dexCode = code.asDexCode();
// TODO(ager): Should we give the new first parameter an actual name? Maybe 'this'?
@@ -135,11 +145,12 @@
companionMethods.add(new DexEncodedMethod(companionMethod,
newFlags, direct.annotations, direct.parameterAnnotationsList, code));
+ movedMethods.put(oldMethod, companionMethod);
} else {
// Since there are no interface constructors at this point,
// this should only be class constructor.
- assert rewriter.factory.isClassConstructor(direct.method);
+ assert rewriter.factory.isClassConstructor(oldMethod);
remainingMethods.add(direct);
}
}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 80e407c..67fcb59 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -53,7 +53,6 @@
import org.junit.rules.TemporaryFolder;
public class TestBase {
-
protected enum Backend {
CF,
DEX
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 2af67a4..8337f6e 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -64,6 +64,7 @@
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.junit.Assume;
import org.junit.rules.TemporaryFolder;
@@ -758,8 +759,13 @@
}
public static List<Path> getClassFilesForTestDirectory(Path directory) throws IOException {
+ return getClassFilesForTestDirectory(directory, null);
+ }
+
+ public static List<Path> getClassFilesForTestDirectory(
+ Path directory, Predicate<Path> filter) throws IOException {
return Files.walk(directory)
- .filter(path -> path.toString().endsWith(".class"))
+ .filter(path -> path.toString().endsWith(".class") && (filter == null || filter.test(path)))
.collect(Collectors.toList());
}
diff --git a/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java b/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java
index c805c15..4cfb71a 100644
--- a/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/PostIncrementTestRunner.java
@@ -4,8 +4,10 @@
package com.android.tools.r8.debug;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.DebuggeeState;
import java.util.stream.Stream;
+import org.junit.Assume;
import org.junit.Test;
// See b/80385846
@@ -16,6 +18,8 @@
@Test
public void test() throws Exception {
+ Assume.assumeTrue("Older runtimes cause some kind of debug streaming issues",
+ ToolHelper.getDexVm().isNewerThan(DexVm.ART_5_1_1_HOST));
DebugTestConfig cfConfig = new CfDebugTestConfig().addPaths(ToolHelper.getClassPathForTests());
DebugTestConfig d8Config = new D8DebugTestConfig().compileAndAddClasses(temp, CLASS);
new DebugStreamComparator()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java
new file mode 100644
index 0000000..2e6fb4c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java
@@ -0,0 +1,109 @@
+// Copyright (c) 2018, 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.ir.optimize.outliner.b112247415;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+class TestClass {
+ interface Act {
+ default String get(StringBuilder builder, String arg) {
+ builder.append(arg).append(arg).append(arg);
+ return builder.toString();
+ }
+ }
+
+ public static void main(String[] args) {
+ System.out.println(get(new TestClass().toActOverridden(), new StringBuilder(), "a"));
+ System.out.println(get(new TestClass().toActDefault(), new StringBuilder(), "b"));
+ }
+
+ static String get(Act act, StringBuilder builder, String arg) {
+ act.get(builder, arg);
+ return builder.toString();
+ }
+
+ Act toActOverridden() {
+ return new Act() {
+ @Override
+ public String get(StringBuilder builder, String arg) {
+ builder.append(arg).append(arg).append(arg);
+ return builder.toString();
+ }
+ };
+ }
+
+ Act toActDefault() {
+ return new Act() {
+ };
+ }
+}
+
+@RunWith(VmTestRunner.class)
+public class B112247415 extends TestBase {
+
+ @Test
+ public void test() throws Exception {
+ String javaResult = runOnJava(TestClass.class);
+
+ R8Command.Builder builder = R8Command.builder();
+ builder.addProgramFiles(ToolHelper.getClassFilesForTestDirectory(
+ ToolHelper.getPackageDirectoryForTestPackage(TestClass.Act.class.getPackage()),
+ path -> path.getFileName().toString().startsWith("TestClass")));
+ builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+ builder.setMinApiLevel(ToolHelper.getMinApiLevelForDexVm().getLevel());
+ String config = keepMainProguardConfiguration(TestClass.class);
+ builder.addProguardConfiguration(ImmutableList.of(config), Origin.unknown());
+ AndroidApp app = ToolHelper.runR8(builder.build(), options -> {
+ // To trigger outliner, set # of expected outline candidate as threshold.
+ options.outline.threshold = 2;
+ options.enableInlining = false;
+ options.enableMinification = false;
+ });
+ ProcessResult result = runOnArtRaw(app, TestClass.class);
+ assertEquals(0, result.exitCode);
+ assertEquals(javaResult, result.stdout);
+
+ CodeInspector inspector = new CodeInspector(app);
+ for (FoundClassSubject clazz : inspector.allClasses()) {
+ clazz.getDexClass().forEachMethod(encodedMethod -> {
+ Code code = encodedMethod.getCode();
+ if (code != null && !encodedMethod.method.name.toString().startsWith("outline")) {
+ verifyAbsenceOfStringBuilderAppend(code.asDexCode().instructions);
+ }
+ });
+ }
+ }
+
+ private void verifyAbsenceOfStringBuilderAppend(Instruction[] instructions) {
+ for (Instruction instr : instructions) {
+ if (instr instanceof InvokeVirtual) {
+ InvokeVirtual invokeVirtual = (InvokeVirtual) instr;
+ DexMethod invokedMethod = invokeVirtual.getMethod();
+ if (invokedMethod.getHolder().getName().endsWith("StringBuilder")) {
+ assertNotEquals("append", invokedMethod.name.toString());
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 9cf0a85..ff762b7 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -9,7 +9,6 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.CompatProguardCommandBuilder;
-import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
@@ -42,24 +41,42 @@
import com.android.tools.r8.utils.codeinspector.FieldSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+@RunWith(Parameterized.class)
public class ForceProguardCompatibilityTest extends TestBase {
+
+ private Backend backend;
+
+ @Parameterized.Parameters(name = "Backend: {0}")
+ public static Collection<Backend> data() {
+ return Arrays.asList(Backend.values());
+ }
+
+ public ForceProguardCompatibilityTest(Backend backend) {
+ this.backend = backend;
+ }
+
private void test(Class mainClass, Class mentionedClass, boolean forceProguardCompatibility)
throws Exception {
String proguardConfig = keepMainProguardConfiguration(mainClass, true, false);
- CodeInspector inspector = new CodeInspector(
- compileWithR8(
- ImmutableList.of(mainClass, mentionedClass),
- proguardConfig,
- options -> options.forceProguardCompatibility = forceProguardCompatibility));
+ CodeInspector inspector =
+ new CodeInspector(
+ compileWithR8(
+ readClasses(ImmutableList.of(mainClass, mentionedClass)),
+ proguardConfig,
+ options -> options.forceProguardCompatibility = forceProguardCompatibility,
+ backend));
assertTrue(inspector.clazz(mainClass.getCanonicalName()).isPresent());
ClassSubject clazz = inspector.clazz(getJavacGeneratedClassName(mentionedClass));
assertTrue(clazz.isPresent());
@@ -101,7 +118,7 @@
Origin.unknown());
}
- builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+ builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
CodeInspector inspector = new CodeInspector(ToolHelper.runR8(builder.build()));
assertTrue(inspector.clazz(mainClass.getCanonicalName()).isPresent());
ClassSubject clazz = inspector.clazz(getJavacGeneratedClassName(mentionedClassWithAnnotations));
@@ -136,7 +153,7 @@
Path proguardCompatibilityRules = temp.newFile().toPath();
builder.setProguardCompatibilityRulesOutput(proguardCompatibilityRules);
- builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+ builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
CodeInspector inspector = new CodeInspector(ToolHelper.runR8(builder.build()));
ClassSubject clazz = inspector.clazz(getJavacGeneratedClassName(testClass));
assertTrue(clazz.isPresent());
@@ -193,7 +210,7 @@
"-dontobfuscate");
builder.addProguardConfiguration(proguardConfig, Origin.unknown());
- builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+ builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
CodeInspector inspector = new CodeInspector(ToolHelper.runR8(builder.build()));
assertTrue(inspector.clazz(getJavacGeneratedClassName(mainClass)).isPresent());
ClassSubject clazz = inspector.clazz(getJavacGeneratedClassName(instantiatedClass));
@@ -255,7 +272,7 @@
builder.setProguardMapOutputPath(temp.newFile().toPath());
}
- builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+ builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
CodeInspector inspector = new CodeInspector(ToolHelper.runR8(builder.build()));
assertTrue(inspector.clazz(getJavacGeneratedClassName(mainClass)).isPresent());
forNameClasses.forEach(clazz -> {
@@ -349,7 +366,7 @@
builder.setProguardMapOutputPath(temp.newFile().toPath());
}
- builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+ builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
CodeInspector inspector = new CodeInspector(ToolHelper.runR8(builder.build()));
assertTrue(inspector.clazz(getJavacGeneratedClassName(mainClass)).isPresent());
ClassSubject classSubject = inspector.clazz(getJavacGeneratedClassName(withMemberClass));
@@ -453,7 +470,7 @@
builder.setProguardMapOutputPath(temp.newFile().toPath());
}
- builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+ builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
CodeInspector inspector = new CodeInspector(ToolHelper.runR8(builder.build()));
assertTrue(inspector.clazz(getJavacGeneratedClassName(mainClass)).isPresent());
ClassSubject classSubject = inspector.clazz(getJavacGeneratedClassName(withVolatileFields));
@@ -568,7 +585,7 @@
builder.setProguardCompatibilityRulesOutput(proguardCompatibilityRules);
AndroidApp app;
- builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
+ builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
try {
app = ToolHelper.runR8(builder.build(), o -> o.enableClassInlining = false);
} catch (CompilationError e) {
@@ -577,7 +594,14 @@
}
CodeInspector inspector = new CodeInspector(app);
assertTrue(inspector.clazz(getJavacGeneratedClassName(mainClass)).isPresent());
- assertEquals(innerClasses || enclosingMethod ? "1" : "0", runOnArt(app, mainClass));
+ String result;
+ if (backend == Backend.DEX) {
+ result = runOnArt(app, mainClass);
+ } else {
+ assert backend == Backend.CF;
+ result = runOnJava(app, mainClass);
+ }
+ assertEquals(innerClasses || enclosingMethod ? "1" : "0", result);
// Check the Proguard compatibility configuration generated.
ProguardConfigurationParser parser =
@@ -623,8 +647,10 @@
"-dontobfuscate"),
Origin.unknown());
builder.addProguardConfiguration(additionalKeepRules, Origin.unknown());
- builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
- builder.setMinApiLevel(AndroidApiLevel.O.getLevel());
+ builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
+ if (backend == Backend.DEX) {
+ builder.setMinApiLevel(AndroidApiLevel.O.getLevel());
+ }
Path proguardCompatibilityRules = temp.newFile().toPath();
builder.setProguardCompatibilityRulesOutput(proguardCompatibilityRules);
AndroidApp app = ToolHelper.runR8(builder.build(), o -> o.enableClassInlining = false);