Add a test for merging init type argument classes
Change-Id: Idc536eecb806b7d52bf7e4e323fa55a66627832c
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 01d4dbc..7d3eb3d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -43,6 +43,7 @@
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.CallSiteOptimizationOptions;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
@@ -216,6 +217,8 @@
private final DexItemFactory dexItemFactory = appView.dexItemFactory();
private final InternalOptions options = appView.options();
+ private final CallSiteOptimizationOptions callSiteOptimizationOptions =
+ appView.options().callSiteOptimizationOptions();
private final Map<DexMethodSignature, AllowedPrototypeChanges>
allowedPrototypeChangesForVirtualMethods = new HashMap<>();
@@ -776,13 +779,15 @@
if (instanceInitializerSignatures.add(rewrittenMethod)) {
return prototypeChanges;
}
- for (DexType extraArgumentType :
- ImmutableList.of(dexItemFactory.intType, dexItemFactory.objectType)) {
- RewrittenPrototypeDescription candidatePrototypeChanges =
- prototypeChanges.withExtraParameters(new ExtraUnusedNullParameter(extraArgumentType));
- rewrittenMethod = candidatePrototypeChanges.rewriteMethod(method, dexItemFactory);
- if (instanceInitializerSignatures.add(rewrittenMethod)) {
- return candidatePrototypeChanges;
+ if (!callSiteOptimizationOptions.isForceSyntheticsForInstanceInitializersEnabled()) {
+ for (DexType extraArgumentType :
+ ImmutableList.of(dexItemFactory.intType, dexItemFactory.objectType)) {
+ RewrittenPrototypeDescription candidatePrototypeChanges =
+ prototypeChanges.withExtraParameters(new ExtraUnusedNullParameter(extraArgumentType));
+ rewrittenMethod = candidatePrototypeChanges.rewriteMethod(method, dexItemFactory);
+ if (instanceInitializerSignatures.add(rewrittenMethod)) {
+ return candidatePrototypeChanges;
+ }
}
}
DexType extraArgumentType =
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 58cdf7e..359d95a 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1275,6 +1275,8 @@
private boolean enabled = true;
private boolean enableMethodStaticizing = true;
+ private boolean forceSyntheticsForInstanceInitializers = false;
+
public void disableOptimization() {
enabled = false;
}
@@ -1290,6 +1292,10 @@
return enabled;
}
+ public boolean isForceSyntheticsForInstanceInitializersEnabled() {
+ return forceSyntheticsForInstanceInitializers;
+ }
+
public boolean isMethodStaticizingEnabled() {
return enableMethodStaticizing;
}
@@ -1303,6 +1309,12 @@
return this;
}
+ public CallSiteOptimizationOptions setForceSyntheticsForInstanceInitializers(
+ boolean forceSyntheticsForInstanceInitializers) {
+ this.forceSyntheticsForInstanceInitializers = forceSyntheticsForInstanceInitializers;
+ return this;
+ }
+
public CallSiteOptimizationOptions setEnableMethodStaticizing(boolean enableMethodStaticizing) {
this.enableMethodStaticizing = enableMethodStaticizing;
return this;
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingOfInitArgumentTypesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingOfInitArgumentTypesTest.java
new file mode 100644
index 0000000..fc00578
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingOfInitArgumentTypesTest.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2022, 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+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)
+public class HorizontalClassMergingOfInitArgumentTypesTest extends TestBase {
+
+ @Parameter(0)
+ public boolean enableHorizontalClassMerging;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, horizontal class merging: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> {
+ options.callSiteOptimizationOptions().setForceSyntheticsForInstanceInitializers(true);
+ options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging);
+ })
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ int expectedNumberOfSynthetics =
+ 2 - BooleanUtils.intValue(enableHorizontalClassMerging);
+ assertEquals(3 + expectedNumberOfSynthetics, inspector.allClasses().size());
+ assertThat(inspector.clazz(Main.class), isPresent());
+ assertThat(inspector.clazz(A.class), isPresent());
+ assertThat(inspector.clazz(B.class), isPresent());
+ assertEquals(
+ expectedNumberOfSynthetics,
+ inspector.allClasses().stream()
+ .filter(
+ clazz ->
+ SyntheticItemsTestUtils.isExternalNonFixedInitializerTypeArgument(
+ clazz.getOriginalReference()))
+ .count());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A()", "A(Object)", "B()", "B(Object)");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ String arg = args.length > 0 ? args[1] : null;
+ System.out.println(new A());
+ System.out.println(new A(arg));
+ System.out.println(new B());
+ System.out.println(new B(arg));
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class A {
+
+ boolean unused;
+
+ A() {}
+
+ // Unused argument removal will rewrite this into A(A$$ExternalSynthetic$IA0)
+ A(Object unused) {
+ this.unused = true;
+ }
+
+ @Override
+ public String toString() {
+ return unused ? "A(Object)" : "A()";
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class B {
+
+ boolean unused;
+
+ B() {}
+
+ // Unused argument removal will rewrite this into B(B$$ExternalSynthetic$IA0)
+ B(Object unused) {
+ this.unused = true;
+ }
+
+ @Override
+ public String toString() {
+ return unused ? "B(Object)" : "B()";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index bb46834..8cc71c8 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -122,6 +122,11 @@
return SyntheticNaming.isSynthetic(reference, null, SyntheticKind.INIT_TYPE_ARGUMENT);
}
+ public static boolean isExternalNonFixedInitializerTypeArgument(ClassReference reference) {
+ return SyntheticNaming.isSynthetic(
+ reference, Phase.EXTERNAL, SyntheticKind.NON_FIXED_INIT_TYPE_ARGUMENT);
+ }
+
public static boolean isHorizontalInitializerTypeArgument(ClassReference reference) {
return SyntheticNaming.isSynthetic(
reference, null, SyntheticKind.HORIZONTAL_INIT_TYPE_ARGUMENT_1)