Enable constructor inlining for CF
This has been valid since b/166738818 was fixed by properly tracking the
"this" value to all non-normal exits in instance initializers.
This CL simplifies the regression test after manually verifying that it
still fails if the above mentioned fix is manually reverted.
Fixes: b/136458109
Change-Id: I77997cfe84729ecdba37f7d1a578c741ef8c2010
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
index f16d699..9bb9a8a 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
@@ -306,9 +306,7 @@
: CfFrameState.error("No destination frame");
}
- @SuppressWarnings("UnusedVariable")
private TraversalContinuation<CfCodeDiagnostics, CfFrameState> computeInitialState() {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
CfFrameState state = new ConcreteCfFrameState();
int localIndex = 0;
DexMethod context = previousMethod.orElse(method.getReference());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index c6bd2a2..af606fb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -144,13 +144,6 @@
return false;
}
- // We don't inline into constructors when producing class files since this can mess up
- // the stackmap, see b/136250031
- if (method.getDefinition().isInstanceInitializer() && options.isGeneratingClassFiles()) {
- whyAreYouNotInliningReporter.reportNoInliningIntoConstructorsWhenGeneratingClassFiles();
- return false;
- }
-
if (method.isStructurallyEqualTo(singleTarget)) {
// Cannot handle recursive inlining at this point.
// Force inlined method should never be recursive.
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java
index 3e84ae4..f2db164 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
@@ -34,7 +33,7 @@
.inspect(
codeInspector -> {
assertThat(codeInspector.clazz(A.class), isAbsent());
- assertThat(codeInspector.clazz(B.class), isPresentIf(parameters.isCfRuntime()));
+ assertThat(codeInspector.clazz(B.class), isAbsent());
assertThat(codeInspector.clazz(C.class), isPresent());
assertThat(codeInspector.clazz(D.class), isAbsent());
});
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
index e6e1d11..7dee4de 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
@@ -45,8 +45,7 @@
assertThat(firstInitSubject, isPresent());
assertThat(firstInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
- MethodSubject otherInitSubject =
- aClassSubject.init("int", parameters.isCfRuntime() ? "java.lang.Object" : "int");
+ MethodSubject otherInitSubject = aClassSubject.init("int", "int");
assertThat(otherInitSubject, isPresent());
assertThat(otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
index ecb49d6..2be25d3 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
@@ -45,8 +45,7 @@
assertThat(firstInitSubject, isPresent());
assertThat(firstInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
- MethodSubject otherInitSubject =
- aClassSubject.init("int", parameters.isCfRuntime() ? "java.lang.Object" : "int");
+ MethodSubject otherInitSubject = aClassSubject.init("int", "int");
assertThat(otherInitSubject, isPresent());
assertThat(otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentArgumentAndAssignmentOrderMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentArgumentAndAssignmentOrderMergingTest.java
index c8b807f..2ad2931 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentArgumentAndAssignmentOrderMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentArgumentAndAssignmentOrderMergingTest.java
@@ -53,10 +53,8 @@
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
// TODO(b/189296638): Enable constructor merging by changing the constructor
- // arguments.
assertEquals(
- parameters.isCfRuntime() ? 4 : 2,
- aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+ 2, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("CD", "CD");
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentArgumentOrderMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentArgumentOrderMergingTest.java
index 19ed979..8c5633c 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentArgumentOrderMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndDifferentArgumentOrderMergingTest.java
@@ -52,12 +52,9 @@
inspector -> {
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
-
// TODO(b/189296638): Enable constructor merging by changing the constructor
- // arguments.
assertEquals(
- parameters.isCfRuntime() ? 4 : 2,
- aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+ 2, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("C0", "D1");
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java
index fa03ace..1d995fb 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithClassIdAndExtraNullsMergingTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.classmerging.horizontal;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -54,13 +53,9 @@
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
assertEquals(
- parameters.isCfRuntime() ? 3 : 2,
- aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+ 2, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
assertThat(aClassSubject.init("java.lang.Object", "int"), isPresent());
assertThat(aClassSubject.init("java.lang.Object", "int", "int"), isPresent());
- assertThat(
- aClassSubject.init("java.lang.Object", "int", "java.lang.Object"),
- isPresentIf(parameters.isCfRuntime()));
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("C", "0", "C", "D");
diff --git a/src/test/java/com/android/tools/r8/dagger/DaggerBasicSingletonUsingBindsTest.java b/src/test/java/com/android/tools/r8/dagger/DaggerBasicSingletonUsingBindsTest.java
index 0fc4708..627ad79 100644
--- a/src/test/java/com/android/tools/r8/dagger/DaggerBasicSingletonUsingBindsTest.java
+++ b/src/test/java/com/android/tools/r8/dagger/DaggerBasicSingletonUsingBindsTest.java
@@ -80,9 +80,6 @@
"basic.MainUsingBinds",
"dagger.internal.DoubleCheck",
"javax.inject.Provider");
- if (parameters.isCfRuntime()) {
- expectedClasses.add("basic.DaggerMainComponentUsingBinds");
- }
if (target.equals("1.8") || parameters.isDexRuntime()) {
expectedClasses.add("basic.DaggerMainComponentUsingBinds$Builder");
}
diff --git a/src/test/java/com/android/tools/r8/dagger/DaggerBasicSingletonUsingProvidesTest.java b/src/test/java/com/android/tools/r8/dagger/DaggerBasicSingletonUsingProvidesTest.java
index 16117c6..5c06a52 100644
--- a/src/test/java/com/android/tools/r8/dagger/DaggerBasicSingletonUsingProvidesTest.java
+++ b/src/test/java/com/android/tools/r8/dagger/DaggerBasicSingletonUsingProvidesTest.java
@@ -60,9 +60,6 @@
"basic.MainUsingProvides",
"dagger.internal.DoubleCheck",
"javax.inject.Provider");
- if (parameters.isCfRuntime()) {
- expectedClasses.add("basic.DaggerMainComponentUsingProvides");
- }
if (target.equals("1.8") || parameters.isDexRuntime()) {
expectedClasses.add("basic.DaggerMainComponentUsingProvides$Builder");
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexShrinkingFieldsTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexShrinkingFieldsTest.java
index 121a0f3..4a1e82c 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexShrinkingFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexShrinkingFieldsTest.java
@@ -89,11 +89,7 @@
.setMinApi(parameters)
.addOptionsModification(options -> options.emitNestAnnotationsInDex = true)
.compile()
- // TODO(b/136250031, b/136458109): Allow inlining of constructors into constructors when
- // compiling to CF.
- .inspect(
- inspector ->
- assertEquals(parameters.isCfRuntime() ? 2 : 1, inspector.allClasses().size()))
+ .inspect(inspector -> assertEquals(1, inspector.allClasses().size()))
.run(parameters.getRuntime(), "Host")
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress136250031.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress136250031.java
index eb6d129..c9f8098 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress136250031.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress136250031.java
@@ -4,9 +4,13 @@
package com.android.tools.r8.ir.optimize.inliner;
+import static org.junit.Assert.assertTrue;
+
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -21,7 +25,19 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ return TestParameters.builder()
+ .withCfRuntimes()
+ .withDefaultDexRuntime()
+ .withMaximumApiLevel()
+ .build();
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addInnerClasses(Regress136250031.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("42");
}
@Test
@@ -34,12 +50,29 @@
inspector -> inspector.assertMergedIntoSubtype(A.class).assertNoOtherClassesMerged())
.setMinApi(parameters)
.run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("42");
+ .assertSuccessWithOutputLines("42")
+ .inspect(
+ inspector -> {
+ MethodSubject initMethod = inspector.clazz(B.class).init();
+ assertTrue(
+ "Expected 'throw null' in " + initMethod.getMethod().codeToString(),
+ initMethod.streamInstructions().anyMatch(InstructionSubject::isThrow));
+ });
}
static class TestClass {
+
+ // This method is inlined into the constructor for B between entry and the subsequent <init>
+ // on Object (after vertically merging A into B).
+ public static final String maybeAbort() {
+ if (System.currentTimeMillis() < 0) {
+ throw null;
+ }
+ return "42";
+ }
+
public static void main(String[] args) {
- new B(new C());
+ new B();
}
}
@@ -50,21 +83,10 @@
}
public static class B extends A {
- B(C c) {
- super(c.instance.toString());
- }
- }
-
- public static class C {
- public C instance;
-
- C() {
- instance = System.currentTimeMillis() > 0 ? this : null;
- }
-
- @Override
- public String toString() {
- return "42";
+ B() {
+ // The merge and inlining will result in a jump to an non-normal exit.
+ // The frame must retain the uninitialized this.
+ super(TestClass.maybeAbort());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineWithMonitorInConstructorInline.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineWithMonitorInConstructorInline.java
index a2b9b48..7ce443c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineWithMonitorInConstructorInline.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineWithMonitorInConstructorInline.java
@@ -6,8 +6,8 @@
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.containsConstString;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -62,40 +62,36 @@
ClassSubject fooClassSubject = inspector.clazz(Bar.class);
assertThat(fooClassSubject, isPresent());
- // When compiling to CF we do not allow inlining methods into constructors.
- // TODO(b/136458109): Util class should be absent when compiling to CF.
ClassSubject utilClassSubject = inspector.clazz(Util.class);
- assertThat(utilClassSubject, isPresentIf(parameters.isCfRuntime()));
+ assertThat(utilClassSubject, isAbsent());
- // The Util class is fully inlined when compiling to DEX. Verify that the two monitor
- // instructions are not inlined into the same method.
- if (parameters.isDexRuntime()) {
- if (parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.M)) {
- // Find the synthetic constructor with an added `int classId` parameter. Verify that only
- // Foo.<init> has been inlined this constructor.
- MethodSubject syntheticInit = fooClassSubject.init("int");
- assertThat(syntheticInit, isPresent());
- assertThat(syntheticInit, containsConstString("foo"));
- assertThat(syntheticInit, not(containsConstString("bar")));
- assertEquals(1, numberOfMonitorEnterInstructions(syntheticInit));
+ // Verify that the two monitor instructions are not inlined into the same method.
+ if (parameters.isCfRuntime()
+ || parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.M)) {
+ // Find the synthetic constructor with an added `int classId` parameter. Verify that only
+ // Foo.<init> has been inlined this constructor.
+ MethodSubject syntheticInit = fooClassSubject.init("int");
+ assertThat(syntheticInit, isPresent());
+ assertThat(syntheticInit, containsConstString("foo"));
+ assertThat(syntheticInit, not(containsConstString("bar")));
+ assertEquals(1, numberOfMonitorEnterInstructions(syntheticInit));
- // Find the non-synthetic constructor corresponding to Bar.<init>.
- MethodSubject barInit = fooClassSubject.init();
- assertThat(barInit, isPresent());
- assertThat(barInit, containsConstString("bar"));
- assertThat(barInit, not(containsConstString("foo")));
- assertEquals(1, numberOfMonitorEnterInstructions(barInit));
+ // Find the non-synthetic constructor corresponding to Bar.<init>.
+ MethodSubject barInit = fooClassSubject.init();
+ assertThat(barInit, isPresent());
+ assertThat(barInit, containsConstString("bar"));
+ assertThat(barInit, not(containsConstString("foo")));
+ assertEquals(1, numberOfMonitorEnterInstructions(barInit));
- // Finally verify that the synthetic constructor calls the non-synthetic constructor, due to
- // inlining being prohibited.
- assertThat(syntheticInit, invokesMethod(barInit));
- } else {
- MethodSubject syntheticInit = fooClassSubject.uniqueInstanceInitializer();
- assertThat(syntheticInit, isPresent());
- assertThat(syntheticInit, containsConstString("bar"));
- assertThat(syntheticInit, containsConstString("foo"));
- assertEquals(2, numberOfMonitorEnterInstructions(syntheticInit));
- }
+ // Finally verify that the synthetic constructor calls the non-synthetic constructor, due to
+ // inlining being prohibited.
+ assertThat(syntheticInit, invokesMethod(barInit));
+ } else {
+ MethodSubject syntheticInit = fooClassSubject.uniqueInstanceInitializer();
+ assertThat(syntheticInit, isPresent());
+ assertThat(syntheticInit, containsConstString("bar"));
+ assertThat(syntheticInit, containsConstString("foo"));
+ assertEquals(2, numberOfMonitorEnterInstructions(syntheticInit));
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java
index 52a8deb..c1a3053 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterVerticalMergingMethodTest.java
@@ -113,10 +113,9 @@
assertThat(inspector.clazz(LibrarySubclass.class), isPresent());
List<FoundMethodSubject> methods =
inspector.clazz(LibrarySubclass.class).allMethods();
- assertEquals(backend.isCf() ? 4 : 3, methods.size());
+ assertEquals(3, methods.size());
assertEquals(
- backend.isCf() ? 2 : 1,
- methods.stream().filter(FoundMethodSubject::isInstanceInitializer).count());
+ 1, methods.stream().filter(FoundMethodSubject::isInstanceInitializer).count());
assertEquals(
1, methods.stream().filter(m -> m.getFinalName().contains("main")).count());
assertEquals(
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/KeepIfPresentRuleWithVerticalClassMergingTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/KeepIfPresentRuleWithVerticalClassMergingTest.java
index 01cf6ce..fbde552 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/KeepIfPresentRuleWithVerticalClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/KeepIfPresentRuleWithVerticalClassMergingTest.java
@@ -54,7 +54,7 @@
assertThat(classBSubject, isPresent());
assertThat(classBSubject.init(), isPresent());
assertThat(classBSubject.uniqueMethodWithOriginalName("greet"), isPresent());
- assertEquals(parameters.isCfRuntime() ? 3 : 2, classBSubject.allMethods().size());
+ assertEquals(2, classBSubject.allMethods().size());
})
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutputLines("Hello world!");