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());