Account for constructor inlining in proto builder optimization

Change-Id: Ie895cd0147275c7d598f8a6f6ea7adad2187dae3
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 2ff901b..c6658c4 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
@@ -43,11 +43,11 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.EnqueuerWorklist;
+import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.ObjectUtils;
 import com.android.tools.r8.utils.PredicateSet;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.IdentityHashMap;
@@ -220,11 +220,8 @@
     assert builder.superType == references.generatedMessageLiteBuilderType
         || builder.superType == references.generatedMessageLiteExtendableBuilderType;
 
-    DexField defaultInstanceField = references.getDefaultInstanceField(dynamicMethod.getHolder());
     Value builderValue =
         code.createValue(ClassTypeElement.create(builder.superType, definitelyNotNull(), appView));
-    Value defaultInstanceValue =
-        code.createValue(ClassTypeElement.create(defaultInstanceField.type, maybeNull(), appView));
 
     // Replace `new Message.Builder()` by `new GeneratedMessageLite.Builder()`
     // (or `new GeneratedMessageLite.ExtendableBuilder()`).
@@ -239,23 +236,47 @@
     //
     // We may also see an accessibility bridge constructor, because the Builder constructor is
     // private. The accessibility bridge takes null as an argument.
+    DexField defaultInstanceField = references.getDefaultInstanceField(dynamicMethod.getHolder());
+    Box<Value> existingDefaultInstanceValue = new Box<>();
     InvokeDirect constructorInvoke =
         instructionIterator.nextUntil(
             instruction -> {
+              // After constructor inlining we may see a load of the DEFAULT_INSTANCE field.
+              if (instruction.isStaticGet()) {
+                StaticGet staticGet = instruction.asStaticGet();
+                if (staticGet.getField() == defaultInstanceField) {
+                  existingDefaultInstanceValue.set(staticGet.outValue());
+                  return false;
+                }
+              }
               assert instruction.isInvokeDirect() || instruction.isConstNumber();
               return instruction.isInvokeDirect();
             });
     assert constructorInvoke != null;
-    instructionIterator.replaceCurrentInstruction(
-        new StaticGet(defaultInstanceValue, defaultInstanceField));
-    instructionIterator.setInsertionPosition(constructorInvoke.getPosition());
-    instructionIterator.add(
-        new InvokeDirect(
-            builder.superType == references.generatedMessageLiteBuilderType
-                ? references.generatedMessageLiteBuilderMethods.constructorMethod
-                : references.generatedMessageLiteExtendableBuilderMethods.constructorMethod,
-            null,
-            ImmutableList.of(builderValue, defaultInstanceValue)));
+
+    DexMethod constructorMethod =
+        builder.superType == references.generatedMessageLiteBuilderType
+            ? references.generatedMessageLiteBuilderMethods.constructorMethod
+            : references.generatedMessageLiteExtendableBuilderMethods.constructorMethod;
+    if (existingDefaultInstanceValue.isSet()) {
+      instructionIterator.replaceCurrentInstruction(
+          InvokeDirect.builder()
+              .setArguments(builderValue, existingDefaultInstanceValue.get())
+              .setMethod(constructorMethod)
+              .build());
+    } else {
+      Value defaultInstanceValue =
+          code.createValue(
+              ClassTypeElement.create(defaultInstanceField.type, maybeNull(), appView));
+      instructionIterator.replaceCurrentInstruction(
+          new StaticGet(defaultInstanceValue, defaultInstanceField));
+      instructionIterator.setInsertionPosition(constructorInvoke.getPosition());
+      instructionIterator.add(
+          InvokeDirect.builder()
+              .setArguments(builderValue, defaultInstanceValue)
+              .setMethod(constructorMethod)
+              .build());
+    }
 
     converter.removeDeadCodeAndFinalizeIR(
         code, OptimizationFeedbackSimple.getInstance(), Timing.empty());
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index b7a8fae..1871e96 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -37,6 +37,7 @@
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collections;
 import java.util.List;
@@ -282,6 +283,10 @@
     protected DexMethod method;
     protected List<Value> arguments = Collections.emptyList();
 
+    public B setArguments(Value... arguments) {
+      return setArguments(Arrays.asList(arguments));
+    }
+
     public B setArguments(List<Value> arguments) {
       assert arguments != null;
       this.arguments = arguments;