Make proto builder class inlining robust to call graph cycles
Change-Id: I3664dfed9be065951ebe16373333d3a0f2b79dc0
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 91994d1..ac4e4eb 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -247,6 +247,13 @@
}
}
+ public void withGeneratedMessageLiteBuilderShrinker(
+ Consumer<GeneratedMessageLiteBuilderShrinker> consumer) {
+ if (protoShrinker != null && protoShrinker.generatedMessageLiteBuilderShrinker != null) {
+ consumer.accept(protoShrinker.generatedMessageLiteBuilderShrinker);
+ }
+ }
+
public <U> U withGeneratedMessageLiteShrinker(
Function<GeneratedMessageLiteShrinker, U> fn, U defaultValue) {
if (protoShrinker != null && protoShrinker.generatedMessageLiteShrinker != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
index 0e8da38..d1cf2e5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -11,6 +11,10 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CallGraph.Node;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.function.BooleanSupplier;
@@ -41,6 +45,21 @@
new RootSetExtension(appView, alwaysInline, neverInline, bypassClinitforInlining).extend();
}
+ public void preprocessCallGraphBeforeCycleElimination(Map<DexMethod, Node> nodes) {
+ Node node = nodes.get(references.generatedMessageLiteBuilderMethods.constructorMethod);
+ if (node != null) {
+ List<Node> calleesToBeRemoved = new ArrayList<>();
+ for (Node callee : node.getCalleesWithDeterministicOrder()) {
+ if (references.isDynamicMethodBridge(callee.method)) {
+ calleesToBeRemoved.add(callee);
+ }
+ }
+ for (Node callee : calleesToBeRemoved) {
+ callee.removeCaller(node);
+ }
+ }
+ }
+
private static class RootSetExtension {
private final AppView<? extends AppInfoWithSubtyping> appView;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
index cedb6ed..c90eaf2 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
@@ -99,6 +99,14 @@
return isDynamicMethod(encodedMethod.method);
}
+ public boolean isDynamicMethodBridge(DexMethod method) {
+ return method == generatedMessageLiteMethods.dynamicMethodBridgeMethod;
+ }
+
+ public boolean isDynamicMethodBridge(DexEncodedMethod method) {
+ return isDynamicMethodBridge(method.method);
+ }
+
public boolean isFindLiteExtensionByNumberMethod(DexMethod method) {
return method.proto == findLiteExtensionByNumberProto
&& method.name.startsWith(findLiteExtensionByNumberName);
@@ -115,6 +123,7 @@
class GeneratedMessageLiteMethods {
public final DexMethod createBuilderMethod;
+ public final DexMethod dynamicMethodBridgeMethod;
public final DexMethod isInitializedMethod;
private GeneratedMessageLiteMethods(DexItemFactory dexItemFactory) {
@@ -123,6 +132,11 @@
generatedMessageLiteType,
dexItemFactory.createProto(generatedMessageLiteBuilderType),
"createBuilder");
+ dynamicMethodBridgeMethod =
+ dexItemFactory.createMethod(
+ generatedMessageLiteType,
+ dexItemFactory.createProto(dexItemFactory.objectType, methodToInvokeType),
+ "dynamicMethod");
isInitializedMethod =
dexItemFactory.createMethod(
generatedMessageLiteType,
@@ -134,6 +148,7 @@
class GeneratedMessageLiteBuilderMethods {
public final DexMethod buildPartialMethod;
+ public final DexMethod constructorMethod;
private GeneratedMessageLiteBuilderMethods(DexItemFactory dexItemFactory) {
buildPartialMethod =
@@ -141,6 +156,11 @@
generatedMessageLiteBuilderType,
dexItemFactory.createProto(generatedMessageLiteType),
"buildPartial");
+ constructorMethod =
+ dexItemFactory.createMethod(
+ generatedMessageLiteBuilderType,
+ dexItemFactory.createProto(dexItemFactory.voidType, generatedMessageLiteType),
+ dexItemFactory.constructorMethodName);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
index 189bb92..c7939b5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
@@ -56,6 +56,9 @@
process(executorService);
assert verifyAllMethodsWithCodeExists();
+ appView.withGeneratedMessageLiteBuilderShrinker(
+ shrinker -> shrinker.preprocessCallGraphBeforeCycleElimination(nodes));
+
timing.begin("Cycle elimination");
// Sort the nodes for deterministic cycle elimination.
Set<Node> nodesWithDeterministicOrder = Sets.newTreeSet(nodes.values());