Share synthetic methods calling other synthetic methods
- Only share with a call graph tree of depth 1.
Fixes: b/382242341
Change-Id: I092844530185b3cb1e463c010fdd12a9c07d9209
diff --git a/src/main/java/com/android/tools/r8/synthesis/CallOtherMergeableSyntheticMethodUseRegistry.java b/src/main/java/com/android/tools/r8/synthesis/CallOtherMergeableSyntheticMethodUseRegistry.java
new file mode 100644
index 0000000..080fd8e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/CallOtherMergeableSyntheticMethodUseRegistry.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2025, 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.synthesis;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DefaultUseRegistryWithResult;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.google.common.collect.Iterables;
+
+public class CallOtherMergeableSyntheticMethodUseRegistry
+ extends DefaultUseRegistryWithResult<Boolean, ProgramMethod> {
+
+ public CallOtherMergeableSyntheticMethodUseRegistry(AppView<?> appView, ProgramMethod context) {
+ super(appView, context, false);
+ }
+
+ @Override
+ public void registerInvokeStatic(DexMethod method) {
+ if (appView.getSyntheticItems().isSynthetic(method.getHolderType())
+ && isSyntheticMethodAllowingGlobalMerging(method)) {
+ setResult(true);
+ }
+ }
+
+ private boolean isSyntheticMethodAllowingGlobalMerging(DexMethod method) {
+ return Iterables.all(
+ appView.getSyntheticItems().getSyntheticKinds(method.getHolderType()),
+ kind ->
+ kind.isSyntheticMethodKind() && kind.asSyntheticMethodKind().isAllowGlobalMerging());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 5a83444..0b9f17c 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.fixup.TreeFixerBase;
import com.android.tools.r8.graph.lens.GraphLens;
@@ -586,7 +587,14 @@
potentialEquivalences.forEach(
members -> {
T t = members.get(0);
- if (t.getKind().mayCallOtherSyntheticMethods(appView.getSyntheticItems().getNaming())) {
+ boolean mayCallOtherSyntheticMethods = false;
+ if (t.isMethodDefinition()) {
+ ProgramMethod method = t.asMethodDefinition().getMethod();
+ mayCallOtherSyntheticMethods =
+ method.registerCodeReferencesWithResult(
+ new CallOtherMergeableSyntheticMethodUseRegistry(appView, method));
+ }
+ if (mayCallOtherSyntheticMethods) {
indirectPotentialEquivalences.add(members);
} else {
directPotentialEquivalences.add(members);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index ebc0b1d..1ef8edc 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -259,11 +259,6 @@
public abstract boolean isSingleSyntheticMethod();
- @SuppressWarnings("ReferenceEquality")
- public boolean mayCallOtherSyntheticMethods(SyntheticNaming naming) {
- return this == naming.AUTOCLOSEABLE_DISPATCHER;
- }
-
public abstract boolean isFixedSuffixSynthetic();
public abstract boolean isGlobal();
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/GetMajorAndMinorSdkVersionBackportOutlineInBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/GetMajorAndMinorSdkVersionBackportOutlineInBackportTest.java
index 4e39653..337c252 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/GetMajorAndMinorSdkVersionBackportOutlineInBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/GetMajorAndMinorSdkVersionBackportOutlineInBackportTest.java
@@ -137,10 +137,7 @@
assertEquals(
1,
countInvokeStaticToMethod(apiOutline1.uniqueMethod(), getGetMajorSdkVersion(inspector)));
- // TODO(b/382242341):There are 4 backports, as even though they are identical two by two, they
- // are not currently shared, as synthetic sharing does not see equivalence of the backports
- // calling the API outline synthetics, which are shared.
- for (int i = 2; i < 6; i++) {
+ for (int i = 2; i < 3; i++) {
ClassSubject backport =
inspector.clazz(
SyntheticItemsTestUtils.syntheticBackportWithForwardingClass(TestClass.class, i));
@@ -174,8 +171,6 @@
} else {
ClassSubject apiOutline0 =
inspector.clazz(SyntheticItemsTestUtils.syntheticApiOutlineClass(TestClass.class, 0));
- // TODO(b/382242341): There are 4 methods merged into the outline synthetic. Two calling
- // getMajorSdkVersion and two calling getMinorSdkVersion.
assertEquals(4, apiOutline0.allMethods().size());
MethodSubject main = inspector.clazz(TestClass.class).mainMethod();
assertEquals(