Bypass clinit for inlining of newBuilder() methods in proto lite

Change-Id: Idb92e59886bb0b63c5a7ec1275640f1475a43e0a
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 988bf75..0acef70 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -560,6 +560,11 @@
     return lookupTarget(directMethods, method);
   }
 
+  /** Find direct method in this class matching {@param predicate}. */
+  public DexEncodedMethod lookupDirectMethod(Predicate<DexEncodedMethod> predicate) {
+    return PredicateUtils.findFirst(directMethods, predicate);
+  }
+
   /** Find virtual method in this class matching {@param method}. */
   public DexEncodedMethod lookupVirtualMethod(DexMethod method) {
     return lookupTarget(virtualMethods, method);
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 3a866ef..0e8da38 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
@@ -36,8 +36,9 @@
   public static void addInliningHeuristicsForBuilderInlining(
       AppView<? extends AppInfoWithSubtyping> appView,
       Set<DexMethod> alwaysInline,
-      Set<DexMethod> neverInline) {
-    new RootSetExtension(appView, alwaysInline, neverInline).extend();
+      Set<DexMethod> neverInline,
+      Set<DexMethod> bypassClinitforInlining) {
+    new RootSetExtension(appView, alwaysInline, neverInline, bypassClinitforInlining).extend();
   }
 
   private static class RootSetExtension {
@@ -47,15 +48,18 @@
 
     private final Set<DexMethod> alwaysInline;
     private final Set<DexMethod> neverInline;
+    private final Set<DexMethod> bypassClinitforInlining;
 
     RootSetExtension(
         AppView<? extends AppInfoWithSubtyping> appView,
         Set<DexMethod> alwaysInline,
-        Set<DexMethod> neverInline) {
+        Set<DexMethod> neverInline,
+        Set<DexMethod> bypassClinitforInlining) {
       this.appView = appView;
       this.references = appView.protoShrinker().references;
       this.alwaysInline = alwaysInline;
       this.neverInline = neverInline;
+      this.bypassClinitforInlining = bypassClinitforInlining;
     }
 
     void extend() {
@@ -64,12 +68,27 @@
       neverInlineIsInitializedFromGeneratedMessageLite();
 
       // * extends GeneratedMessageLite heuristics.
+      bypassClinitforInliningNewBuilderMethods();
       alwaysInlineDynamicMethodFromGeneratedMessageLiteImplementations();
 
       // GeneratedMessageLite$Builder heuristics.
       alwaysInlineBuildPartialFromGeneratedMessageLiteBuilder();
     }
 
+    private void bypassClinitforInliningNewBuilderMethods() {
+      for (DexType type : appView.appInfo().subtypes(references.generatedMessageLiteType)) {
+        DexProgramClass clazz = appView.definitionFor(type).asProgramClass();
+        if (clazz != null) {
+          DexEncodedMethod newBuilderMethod =
+              clazz.lookupDirectMethod(
+                  method -> method.method.name == references.newBuilderMethodName);
+          if (newBuilderMethod != null) {
+            bypassClinitforInlining.add(newBuilderMethod.method);
+          }
+        }
+      }
+    }
+
     private void alwaysInlineBuildPartialFromGeneratedMessageLiteBuilder() {
       alwaysInline.add(references.generatedMessageLiteBuilderMethods.buildPartialMethod);
     }
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 0b26a38..e8967b4 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
@@ -30,6 +30,7 @@
 
   public final DexString dynamicMethodName;
   public final DexString findLiteExtensionByNumberName;
+  public final DexString newBuilderMethodName;
 
   public final DexProto dynamicMethodProto;
   public final DexProto findLiteExtensionByNumberProto;
@@ -62,6 +63,7 @@
     // Names.
     dynamicMethodName = factory.createString("dynamicMethod");
     findLiteExtensionByNumberName = factory.createString("findLiteExtensionByNumber");
+    newBuilderMethodName = factory.createString("newBuilder");
 
     // Protos.
     dynamicMethodProto =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index b43a294..6b7c389 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -185,6 +185,10 @@
       return true;
     }
 
+    if (appView.rootSet().bypassClinitForInlining.contains(target.method)) {
+      return true;
+    }
+
     whyAreYouNotInliningReporter.reportMustTriggerClassInitialization();
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 4cacde6..d16033f 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
-import static com.android.tools.r8.graph.GraphLense.rewriteReferenceKeys;
 
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
@@ -57,7 +56,6 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
-import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
@@ -74,6 +72,7 @@
   private final Set<DexMethod> alwaysInline = Sets.newIdentityHashSet();
   private final Set<DexMethod> forceInline = Sets.newIdentityHashSet();
   private final Set<DexMethod> neverInline = Sets.newIdentityHashSet();
+  private final Set<DexMethod> bypassClinitforInlining = Sets.newIdentityHashSet();
   private final Set<DexMethod> whyAreYouNotInlining = Sets.newIdentityHashSet();
   private final Set<DexMethod> keepParametersWithConstantValue = Sets.newIdentityHashSet();
   private final Set<DexMethod> keepUnusedArguments = Sets.newIdentityHashSet();
@@ -283,7 +282,7 @@
     }
     if (appView.options().protoShrinking().enableGeneratedMessageLiteBuilderShrinking) {
       GeneratedMessageLiteBuilderShrinker.addInliningHeuristicsForBuilderInlining(
-          appView, alwaysInline, neverInline);
+          appView, alwaysInline, neverInline, bypassClinitforInlining);
     }
     assert Sets.intersection(neverInline, alwaysInline).isEmpty()
             && Sets.intersection(neverInline, forceInline).isEmpty()
@@ -297,6 +296,7 @@
         alwaysInline,
         forceInline,
         neverInline,
+        bypassClinitforInlining,
         whyAreYouNotInlining,
         keepParametersWithConstantValue,
         keepUnusedArguments,
@@ -1058,6 +1058,7 @@
     public final Set<DexMethod> alwaysInline;
     public final Set<DexMethod> forceInline;
     public final Set<DexMethod> neverInline;
+    public final Set<DexMethod> bypassClinitForInlining;
     public final Set<DexMethod> whyAreYouNotInlining;
     public final Set<DexMethod> keepConstantArguments;
     public final Set<DexMethod> keepUnusedArguments;
@@ -1082,6 +1083,7 @@
         Set<DexMethod> alwaysInline,
         Set<DexMethod> forceInline,
         Set<DexMethod> neverInline,
+        Set<DexMethod> bypassClinitForInlining,
         Set<DexMethod> whyAreYouNotInlining,
         Set<DexMethod> keepConstantArguments,
         Set<DexMethod> keepUnusedArguments,
@@ -1103,6 +1105,7 @@
       this.alwaysInline = Collections.unmodifiableSet(alwaysInline);
       this.forceInline = Collections.unmodifiableSet(forceInline);
       this.neverInline = neverInline;
+      this.bypassClinitForInlining = bypassClinitForInlining;
       this.whyAreYouNotInlining = whyAreYouNotInlining;
       this.keepConstantArguments = keepConstantArguments;
       this.keepUnusedArguments = keepUnusedArguments;
@@ -1137,15 +1140,6 @@
       }
     }
 
-    private static <T extends DexReference, S> Map<T, Map<T, S>> rewriteDependentReferenceKeys(
-        Map<T, Map<T, S>> original, Function<T, T> rewrite) {
-      Map<T, Map<T, S>> result = new IdentityHashMap<>();
-      for (T item : original.keySet()) {
-        result.put(rewrite.apply(item), rewriteReferenceKeys(original.get(item), rewrite));
-      }
-      return result;
-    }
-
     void addConsequentRootSet(ConsequentRootSet consequentRootSet) {
       neverInline.addAll(consequentRootSet.neverInline);
       neverClassInline.addAll(consequentRootSet.neverClassInline);