Version 1.4.79
Cherry-pick:
Remove unused import.
CL: https://r8-review.googlesource.com/c/r8/+/35300
Cherry-pick:
Process double inlining in parallel.
CL: https://r8-review.googlesource.com/c/r8/+/35201
Cherry-pick:
Reproduce b/128987064: illegal invoke-super during double-inlining processing.
CL: https://r8-review.googlesource.com/c/r8/+/36220
Bug: 121235635
Bug: 128987064
Change-Id: Ic70e70461f5c3f2d09987143380b50063d0166bb
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 11d013c..d7a4efa 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.4.78";
+ public static final String LABEL = "1.4.79";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index edd33c8..32fc687 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -559,7 +559,7 @@
if (inliner != null) {
printPhase("Double caller inlining");
assert graphLenseForIR == graphLense();
- inliner.processDoubleInlineCallers(this, feedback);
+ inliner.processDoubleInlineCallers(this, executorService, feedback);
feedback.updateVisibleOptimizationInfo();
assert graphLenseForIR == graphLense();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 2b0d831..30fb191 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -32,6 +32,7 @@
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.MainDexClasses;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.HashMap;
@@ -39,8 +40,10 @@
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
public class Inliner {
@@ -160,24 +163,29 @@
return target;
}
- public synchronized void processDoubleInlineCallers(
- IRConverter converter, OptimizationFeedback feedback) {
- if (doubleInlineCallers.size() > 0) {
- applyDoubleInlining = true;
- List<DexEncodedMethod> methods = doubleInlineCallers
- .stream()
- .sorted(DexEncodedMethod::slowCompare)
- .collect(Collectors.toList());
- for (DexEncodedMethod method : methods) {
- converter.processMethod(
- method,
- feedback,
- x -> false,
- CallSiteInformation.empty(),
- Outliner::noProcessing);
- assert method.isProcessed();
- }
+ public void processDoubleInlineCallers(
+ IRConverter converter, ExecutorService executorService, OptimizationFeedback feedback)
+ throws ExecutionException {
+ if (doubleInlineCallers.isEmpty()) {
+ return;
}
+ applyDoubleInlining = true;
+ List<Future<?>> futures = new ArrayList<>();
+ for (DexEncodedMethod method : doubleInlineCallers) {
+ futures.add(
+ executorService.submit(
+ () -> {
+ converter.processMethod(
+ method,
+ feedback,
+ doubleInlineCallers::contains,
+ CallSiteInformation.empty(),
+ Outliner::noProcessing);
+ assert method.isProcessed();
+ return null;
+ }));
+ }
+ ThreadUtils.awaitFutures(futures);
}
/**
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index 236cb92..f53c120 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -94,6 +94,7 @@
String javaOutput = runOnJava(main);
TestRunResult result = testForR8(backend)
.addProgramClasses(classes)
+ .enableInliningAnnotations()
.addKeepMainRule(main)
.addKeepRules(
"-dontobfuscate", "-allowaccessmodification", "-keepattributes LineNumberTable")
@@ -172,7 +173,6 @@
String javaOutput = runOnJava(main);
TestRunResult result = testForR8(backend)
.addProgramClasses(classes)
- .enableProguardTestOptions()
.enableInliningAnnotations()
.addKeepMainRule(main)
.addKeepRules(
@@ -277,6 +277,7 @@
String javaOutput = runOnJava(main);
TestRunResult result = testForR8(backend)
.addProgramClasses(classes)
+ .enableInliningAnnotations()
.addKeepMainRule(main)
.addKeepRules(
"-dontobfuscate", "-allowaccessmodification", "-keepattributes LineNumberTable")
@@ -317,6 +318,8 @@
String javaOutput = runOnJava(main);
TestRunResult result = testForR8(backend)
.addProgramClasses(classes)
+ .enableProguardTestOptions()
+ .enableInliningAnnotations()
.addKeepMainRule(main)
.addKeepRules(
"-dontobfuscate", "-allowaccessmodification", "-keepattributes LineNumberTable")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/code/C.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/code/C.java
index 38e75c4..dcfb5eb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/code/C.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/code/C.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.classinliner.code;
+import com.android.tools.r8.NeverInline;
+
public class C {
public static class L {
public final int x;
@@ -25,15 +27,18 @@
}
}
- public synchronized static int method1() {
+ @NeverInline
+ public static int method1() {
return new L(1).x;
}
- public synchronized static int method2() {
+ @NeverInline
+ public static int method2() {
return new L(1).getX();
}
- public synchronized static int method3() {
+ @NeverInline
+ public static int method3() {
return F.I.getX();
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java
index 26dc8cc..8bba53e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.classinliner.invalidroot;
+import com.android.tools.r8.NeverInline;
+
public class InvalidRootsTestClass {
private static int ID = 0;
@@ -19,7 +21,8 @@
test.testRootInvalidatesAfterInlining();
}
- private synchronized void testExtraNeverReturnsNormally() {
+ @NeverInline
+ private void testExtraNeverReturnsNormally() {
testExtraNeverReturnsNormallyA();
testExtraNeverReturnsNormallyB();
@@ -31,7 +34,8 @@
}
}
- private synchronized void testExtraNeverReturnsNormallyA() {
+ @NeverInline
+ private void testExtraNeverReturnsNormallyA() {
try {
neverReturnsNormallyExtra(next(), null);
} catch (RuntimeException re) {
@@ -39,7 +43,8 @@
}
}
- private synchronized void testExtraNeverReturnsNormallyB() {
+ @NeverInline
+ private void testExtraNeverReturnsNormallyB() {
try {
neverReturnsNormallyExtra(next(), null);
} catch (RuntimeException re) {
@@ -47,7 +52,8 @@
}
}
- private synchronized void testDirectNeverReturnsNormally() {
+ @NeverInline
+ private void testDirectNeverReturnsNormally() {
try {
NeverReturnsNormally a = new NeverReturnsNormally();
System.out.println(a.foo());
@@ -56,7 +62,8 @@
}
}
- private synchronized void testInitNeverReturnsNormally() {
+ @NeverInline
+ private void testInitNeverReturnsNormally() {
try {
new InitNeverReturnsNormally();
} catch (RuntimeException re) {
@@ -85,7 +92,8 @@
}
}
- private synchronized void testRootInvalidatesAfterInlining() {
+ @NeverInline
+ private void testRootInvalidatesAfterInlining() {
A a = new A();
try {
notInlinedExtraMethod(next(), a);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java
index 7e3cba2..a2576bf 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.classinliner.trivial;
+import com.android.tools.r8.NeverInline;
+
public class TrivialTestClass {
private static int ID = 0;
@@ -25,35 +27,42 @@
test.testCycles();
}
- private synchronized void testInner() {
+ @NeverInline
+ private void testInner() {
Inner inner = new Inner("inner{", 123, next() + "}");
System.out.println(inner.toString() + " " + inner.getPrefix() + " = " + inner.prefix);
}
- private synchronized void testConstructorMapping1() {
+ @NeverInline
+ private void testConstructorMapping1() {
ReferencedFields o = new ReferencedFields(next());
System.out.println(o.getA());
}
- private synchronized void testConstructorMapping2() {
+ @NeverInline
+ private void testConstructorMapping2() {
ReferencedFields o = new ReferencedFields(next());
System.out.println(o.getB());
}
- private synchronized void testConstructorMapping3() {
+ @NeverInline
+ private void testConstructorMapping3() {
ReferencedFields o = new ReferencedFields(next(), next());
System.out.println(o.getA() + o.getB() + o.getConcat());
}
- private synchronized void testEmptyClass() {
+ @NeverInline
+ private void testEmptyClass() {
new EmptyClass();
}
- private synchronized void testEmptyClassWithInitializer() {
+ @NeverInline
+ private void testEmptyClassWithInitializer() {
new EmptyClassWithInitializer();
}
- private synchronized void testClassWithFinalizer() {
+ @NeverInline
+ private void testClassWithFinalizer() {
new ClassWithFinal();
}
@@ -61,7 +70,8 @@
iface.foo();
}
- private synchronized void testCallOnIface1() {
+ @NeverInline
+ private void testCallOnIface1() {
callOnIface1(new Iface1Impl(next()));
}
@@ -69,14 +79,18 @@
iface.foo();
}
- private synchronized void testCallOnIface2() {
+ @NeverInline
+ private void testCallOnIface2() {
callOnIface2(new Iface2Impl(next()));
System.out.println(Iface2Impl.CONSTANT); // Keep constant referenced
}
- private synchronized void testCycles() {
+ @NeverInline
+ private void testCycles() {
new CycleReferenceAB("first").foo(3);
new CycleReferenceBA("second").foo(4);
+ new CycleReferenceAB("third").foo(5);
+ new CycleReferenceBA("fourth").foo(6);
}
public class Inner {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/DoubleInliningInvokeSuperTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/DoubleInliningInvokeSuperTest.java
new file mode 100644
index 0000000..5d8b1f0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/DoubleInliningInvokeSuperTest.java
@@ -0,0 +1,83 @@
+// 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.ir.optimize.inliner;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import org.junit.Test;
+
+// Regression test for b/128987064
+public class DoubleInliningInvokeSuperTest extends TestBase {
+
+ @Test
+ public void test() throws Exception {
+ testForR8(Backend.DEX)
+ .addInnerClasses(DoubleInliningInvokeSuperTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules("-keepclassmembers class * { void fooCaller(...); }")
+ .enableClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .enableMergeAnnotations()
+ .run(TestClass.class)
+ .assertSuccess();
+ }
+
+ @NeverClassInline
+ @NeverMerge
+ static class A {
+ int x;
+ @NeverInline
+ A foo(int x) {
+ this.x = x;
+ return this;
+ }
+ }
+
+ @NeverClassInline
+ @NeverMerge
+ static class B extends A {
+ // B#foo is invoked twice by other wrappers in the same class.
+ @Override
+ B foo(int x) {
+ // this invoke-super should not be inlined to the class outside of the class hierarchy.
+ super.foo(x);
+ return this;
+ }
+
+ // place-holder to make B#foo to be a double-inline selected target.
+ B fooWrapper(Integer x) {
+ int y = System.currentTimeMillis() > 0 ? x * 2 : x;
+ return foo(y);
+ }
+
+ // Another B#foo caller.
+ B anotherWrapper(Integer x) {
+ int y = System.currentTimeMillis() > 0 ? x * 2 : x;
+ // invoke-super in B#foo is inlined here during double-inlining.
+ return foo(y);
+ }
+ }
+
+ static class TestClass {
+ // place-holder to make B#fooWrapper live. This one is force kept.
+ static void fooCaller(B b) {
+ System.out.println(b.fooWrapper(8).x);
+ }
+
+ public static void main(String[] args) {
+ B instance = new B();
+ // By invoking B#anotherWrapper twice, `main` is regarded as double-inline caller.
+ // Due to the name order, B#fooWrapper is processed first, and invoke-super is moved.
+ // Its inlining constraints aren't updated yet since inliner does not finish processing all
+ // the methods in the double-inline pool. Now, when processing `main`, invoke-super in
+ // B#anotherWrapper (which is inlined from B#foo) is flown to here, resulting in illegal
+ // invoke-super at the 2nd tree shaking phase.
+ System.out.println(instance.anotherWrapper(8).x);
+ System.out.println(instance.anotherWrapper(8).x);
+ }
+ }
+
+}