Remove vertical class merger policy to ensure valid force inlining
Fixes: b/321909687
Change-Id: Id41516a09dcde40ec22575e222ee0f293969ed4b
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index 8f4c441..c2fb0e6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -23,8 +23,6 @@
public interface MethodOptimizationFeedback {
- void markForceInline(DexEncodedMethod method);
-
void markInlinedIntoSingleCallSite(DexEncodedMethod method);
void markMethodCannotBeKept(DexEncodedMethod method);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index f4de742..8add480 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -162,11 +162,6 @@
// METHOD OPTIMIZATION INFO:
@Override
- public void markForceInline(DexEncodedMethod method) {
- getMethodOptimizationInfoForUpdating(method).markForceInline();
- }
-
- @Override
public synchronized void markInlinedIntoSingleCallSite(DexEncodedMethod method) {
getMethodOptimizationInfoForUpdating(method).markInlinedIntoSingleCallSite();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index 05a1cdc..bb8f841 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -55,9 +55,6 @@
// METHOD OPTIMIZATION INFO:
@Override
- public void markForceInline(DexEncodedMethod method) {}
-
- @Override
public void markInlinedIntoSingleCallSite(DexEncodedMethod method) {}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 339d9eb6..34bb23b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -83,11 +83,6 @@
}
@Override
- public void markForceInline(DexEncodedMethod method) {
- // Ignored.
- }
-
- @Override
public void markInlinedIntoSingleCallSite(DexEncodedMethod method) {
method.getMutableOptimizationInfo().markInlinedIntoSingleCallSite();
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
index 264441e..c82450b 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
@@ -29,7 +29,6 @@
import com.android.tools.r8.verticalclassmerging.policies.NoNestedMergingPolicy;
import com.android.tools.r8.verticalclassmerging.policies.NoNonSerializableClassIntoSerializableClassPolicy;
import com.android.tools.r8.verticalclassmerging.policies.NoServiceInterfacesPolicy;
-import com.android.tools.r8.verticalclassmerging.policies.SafeConstructorInliningPolicy;
import com.android.tools.r8.verticalclassmerging.policies.SameApiReferenceLevelPolicy;
import com.android.tools.r8.verticalclassmerging.policies.SameFeatureSplitPolicy;
import com.android.tools.r8.verticalclassmerging.policies.SameMainDexGroupPolicy;
@@ -76,7 +75,6 @@
new NoServiceInterfacesPolicy(appView),
new NoAnnotationClassesPolicy(),
new NoNonSerializableClassIntoSerializableClassPolicy(appView),
- new SafeConstructorInliningPolicy(appView),
new NoEnclosingMethodAttributesPolicy(),
new NoInnerClassAttributesPolicy(),
new SameNestPolicy(),
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/SafeConstructorInliningPolicy.java b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/SafeConstructorInliningPolicy.java
deleted file mode 100644
index 15bcf11..0000000
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/SafeConstructorInliningPolicy.java
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2023, 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.verticalclassmerging.policies;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.MainDexInfo;
-import com.android.tools.r8.utils.TraversalContinuation;
-import com.android.tools.r8.verticalclassmerging.VerticalMergeGroup;
-import com.google.common.collect.Iterables;
-
-public class SafeConstructorInliningPolicy extends VerticalClassMergerPolicy {
-
- private final AppView<AppInfoWithLiveness> appView;
- private final MainDexInfo mainDexInfo;
-
- public SafeConstructorInliningPolicy(AppView<AppInfoWithLiveness> appView) {
- this.appView = appView;
- this.mainDexInfo = appView.appInfo().getMainDexInfo();
- }
-
- @Override
- public boolean canMerge(VerticalMergeGroup group) {
- DexProgramClass sourceClass = group.getSource();
- DexProgramClass targetClass = group.getTarget();
- // If there is a constructor in the target, make sure that all source constructors can be
- // inlined.
- if (Iterables.isEmpty(targetClass.programInstanceInitializers())) {
- return true;
- }
- TraversalContinuation<?, ?> result =
- sourceClass.traverseProgramInstanceInitializers(
- method -> TraversalContinuation.breakIf(disallowInlining(method, targetClass)));
- return result.shouldContinue();
- }
-
- private boolean disallowInlining(ProgramMethod method, DexProgramClass context) {
- if (!appView.options().inlinerOptions().enableInlining) {
- return true;
- }
- Code code = method.getDefinition().getCode();
- if (code.isCfCode()) {
- CfCode cfCode = code.asCfCode();
- ConstraintWithTarget constraint =
- cfCode.computeInliningConstraint(appView, appView.graphLens(), method);
- if (constraint.isNever()) {
- return true;
- }
- // Constructors can have references beyond the root main dex classes. This can increase the
- // size of the main dex dependent classes and we should bail out.
- if (mainDexInfo.disallowInliningIntoContext(appView, context, method)) {
- return true;
- }
- return false;
- }
- if (code.isDefaultInstanceInitializerCode()) {
- return false;
- }
- return true;
- }
-
- @Override
- public String getName() {
- return "SafeConstructorInliningPolicy";
- }
-}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/SuperConstructorCallsVirtualMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/SuperConstructorCallsVirtualMethodTest.java
index f3335fa..fe4a7d8 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/SuperConstructorCallsVirtualMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/SuperConstructorCallsVirtualMethodTest.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.classmerging.horizontal;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.core.IsNot.not;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
@@ -23,6 +23,9 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters)
@@ -30,9 +33,9 @@
.assertSuccessWithOutputLines("5", "foo hello", "B", "bar world", "5", "B")
.inspect(
codeInspector -> {
- assertThat(codeInspector.clazz(Parent.class), isPresent());
+ assertThat(codeInspector.clazz(Parent.class), isAbsent());
assertThat(codeInspector.clazz(A.class), isPresent());
- assertThat(codeInspector.clazz(B.class), not(isPresent()));
+ assertThat(codeInspector.clazz(B.class), isAbsent());
});
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerInitTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerInitTest.java
index 466e9fd..f1b4b6a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerInitTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerInitTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.classmerging.vertical;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -13,13 +14,12 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
-import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
@@ -28,38 +28,29 @@
// force-inlining, so the renamed constructor broke the init chain.
public class VerticalClassMergerInitTest extends TestBase {
- private final TestParameters parameters;
+ @Parameter(0)
+ public TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimes().build();
- }
-
- public VerticalClassMergerInitTest(TestParameters parameters) {
- this.parameters = parameters;
+ return getTestParameters().withDexRuntimes().withApiLevel(AndroidApiLevel.K_WATCH).build();
}
@Test
public void testMergingClassWithConstructorNotInMainDex()
throws IOException, CompilationFailedException, ExecutionException {
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend())
.addInnerClasses(VerticalClassMergerInitTest.class)
.addKeepMainRule(Main.class)
.addMainDexRules("-keep class " + Main.class.getTypeName())
- .enableNeverClassInliningAnnotations()
- .setMinApi(AndroidApiLevel.K_WATCH)
- .addOptionsModification(
- options -> {
- options.forceProguardCompatibility = true;
- })
- // The initializer is small in this example so only allow FORCE inlining.
- .addOptionsModification(InlinerOptions::setOnlyForceInlining)
.addVerticallyMergedClassesInspector(
- VerticallyMergedClassesInspector::assertNoClassesMerged)
+ inspector -> inspector.assertMergedIntoSubtype(Base.class))
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters)
.compile()
.inspect(
inspector -> {
- assertThat(inspector.clazz(Base.class), isPresent());
+ assertThat(inspector.clazz(Base.class), isAbsent());
assertThat(inspector.clazz(Child.class), isPresent());
})
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
index 43808b8..cd72b73 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
@@ -166,6 +166,7 @@
inspector,
SyntheticItemsTestUtils.syntheticRecordTagClass(),
false,
+ false,
parameters.canUseNestBasedAccessesWhenDesugaring(),
!isRecordsDesugaredForD8(parameters));
}
@@ -176,6 +177,7 @@
inspector,
RECORD_REFERENCE,
parameters.canHaveNonReboundConstructorInvoke(),
+ true,
parameters.canUseNestBasedAccesses(),
!isRecordsDesugaredForR8(parameters));
}
@@ -185,6 +187,7 @@
CodeInspector inspector,
ClassReference recordClassReference,
boolean canHaveNonReboundConstructorInvoke,
+ boolean canMergeRecordTag,
boolean canUseNestBasedAccesses,
boolean canUseRecords) {
ClassSubject mainClassSubject = inspector.clazz(MAIN_REFERENCE);
@@ -194,8 +197,7 @@
assertThat(mainMethodSubject, isPresent());
ClassSubject recordTagClassSubject = inspector.clazz(recordClassReference);
- assertThat(
- recordTagClassSubject, isAbsentIf(canHaveNonReboundConstructorInvoke || canUseRecords));
+ assertThat(recordTagClassSubject, isAbsentIf(canMergeRecordTag || canUseRecords));
if (recordTagClassSubject.isPresent()) {
assertEquals(
canHaveNonReboundConstructorInvoke ? 0 : 1, recordTagClassSubject.allMethods().size());
@@ -207,16 +209,25 @@
ClassSubject personRecordClassSubject = inspector.clazz(PERSON_REFERENCE);
assertThat(personRecordClassSubject, isPresent());
assertEquals(
- canHaveNonReboundConstructorInvoke
- ? inspector.getTypeSubject(Object.class.getTypeName())
- : canUseRecords
- ? inspector.getTypeSubject(RECORD_REFERENCE.getTypeName())
+ canUseRecords
+ ? inspector.getTypeSubject(RECORD_REFERENCE.getTypeName())
+ : canMergeRecordTag
+ ? inspector.getTypeSubject(Object.class.getTypeName())
: recordTagClassSubject.asTypeSubject(),
personRecordClassSubject.getSuperType());
- assertEquals(canUseRecords ? 6 : 10, personRecordClassSubject.allMethods().size());
+ assertEquals(
+ canUseRecords ? 6 : canHaveNonReboundConstructorInvoke || !canMergeRecordTag ? 10 : 11,
+ personRecordClassSubject.allMethods().size());
+
+ MethodSubject personDefaultInstanceInitializerSubject = personRecordClassSubject.init();
+ assertThat(
+ personDefaultInstanceInitializerSubject,
+ isPresentIf(!canHaveNonReboundConstructorInvoke && canMergeRecordTag && !canUseRecords));
MethodSubject personInstanceInitializerSubject =
- personRecordClassSubject.uniqueInstanceInitializer();
+ canMergeRecordTag
+ ? personRecordClassSubject.init(String.class.getTypeName())
+ : personRecordClassSubject.init(String.class.getTypeName(), "int");
assertThat(personInstanceInitializerSubject, isPresent());
// Name getters.
@@ -306,6 +317,9 @@
i.assertContainsMethodRules(
nameNestAccessorMethodSubject, ageNestAccessorMethodSubject))
.applyIf(
+ !canHaveNonReboundConstructorInvoke && canMergeRecordTag && !canUseRecords,
+ i -> i.assertContainsMethodRule(personDefaultInstanceInitializerSubject))
+ .applyIf(
!canUseRecords,
i ->
i.assertContainsClassRules(hashCodeHelperClassSubject, toStringHelperClassSubject)
@@ -315,7 +329,7 @@
hashCodeHelperMethodSubject,
toStringHelperMethodSubject)
.applyIf(
- !canHaveNonReboundConstructorInvoke,
+ !canMergeRecordTag,
j ->
j.assertContainsClassRules(recordTagClassSubject)
.assertContainsMethodRule(recordTagInstanceInitializerSubject)))
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
index fdb513c..9f63d3a 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -175,11 +175,11 @@
private static void inspectShaking9(PrintUsageInspector inspector) {
Optional<ClassSubject> superClass = inspector.clazz("shaking9.Superclass");
- assertFalse(superClass.isPresent());
+ assertTrue(superClass.isPresent());
Optional<ClassSubject> subClass = inspector.clazz("shaking9.Subclass");
assertTrue(subClass.isPresent());
assertTrue(subClass.get().method("void", "aMethod", ImmutableList.of()));
- assertFalse(subClass.get().method("void", "<init>", ImmutableList.of()));
+ assertTrue(subClass.get().method("void", "<init>", ImmutableList.of()));
}
private static void inspectShaking12(PrintUsageInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking14Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking14Test.java
index 4733d3c..610a9c6 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking14Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking14Test.java
@@ -3,13 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.examples;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.shaking.TreeShakingTest;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.util.List;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -48,12 +50,9 @@
private static void shaking14EnsureRightStaticMethodsLive(CodeInspector inspector) {
ClassSubject superclass = inspector.clazz("shaking14.Superclass");
- Assert.assertFalse(superclass.method("int", "aMethod", ImmutableList.of("int")).isPresent());
- Assert.assertFalse(
- superclass.method("double", "anotherMethod", ImmutableList.of("double")).isPresent());
+ assertThat(superclass, isAbsent());
+
ClassSubject subclass = inspector.clazz("shaking14.Subclass");
- Assert.assertTrue(subclass.method("int", "aMethod", ImmutableList.of("int")).isPresent());
- Assert.assertTrue(
- subclass.method("double", "anotherMethod", ImmutableList.of("double")).isPresent());
+ assertThat(subclass, isAbsent());
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking9Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking9Test.java
index 3b80981..99cbc57 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking9Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking9Test.java
@@ -4,9 +4,7 @@
package com.android.tools.r8.shaking.examples;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbstract;
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.TestParameters;
@@ -45,10 +43,7 @@
@Test
public void testKeeprules() throws Exception {
runTest(
- this::shaking9OnlySuperMethodsKept,
- null,
- null,
- ImmutableList.of("src/test/examples/shaking9/keep-rules.txt"));
+ this::inspect, null, null, ImmutableList.of("src/test/examples/shaking9/keep-rules.txt"));
}
@Test
@@ -57,19 +52,12 @@
null, null, null, ImmutableList.of("src/test/examples/shaking9/keep-rules-printusage.txt"));
}
- private void shaking9OnlySuperMethodsKept(CodeInspector inspector) {
+ private void inspect(CodeInspector inspector) {
ClassSubject superclass = inspector.clazz("shaking9.Superclass");
- if (parameters.canHaveNonReboundConstructorInvoke()) {
- assertThat(superclass, isAbsent());
- } else {
- assertThat(superclass, isAbstract());
- assertThat(superclass.method("void", "aMethod", ImmutableList.of()), isPresent());
- }
+ assertThat(superclass, isAbsent());
ClassSubject subclass = inspector.clazz("shaking9.Subclass");
assertThat(subclass, isPresent());
- assertThat(
- subclass.method("void", "aMethod", ImmutableList.of()),
- isPresentIf(parameters.canHaveNonReboundConstructorInvoke() && !getMinify().isMinify()));
+ assertThat(subclass.method("void", "aMethod", ImmutableList.of()), isAbsent());
}
}