Change side effect analysis for instance initializers

Previously, instance initializers that did not have any other side effects than initializing the fields on the receiver was marked as not having any side effects.

This CL changes such that we now correctly mark instance initializers as having side effects, when it initializes instance fields on the receiver.

Note that we still have the change that an instance initializer may not have any other side effects than the instance field assignments to the receiver in the InstanceInitializerInfo.

Change-Id: Id909bb7b084e5742ad6ac535958c370ecbd28969
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
index c6fdbab..ad34d29 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.ir.code.ArrayPut;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Instruction.SideEffectAssumption;
 import com.android.tools.r8.ir.code.StaticPut;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -82,6 +83,14 @@
         continue;
       }
 
+      if (instruction.isInvokeConstructor(appView.dexItemFactory())) {
+        if (instruction.instructionMayHaveSideEffects(
+            appView, context, SideEffectAssumption.IGNORE_RECEIVER_FIELD_ASSIGNMENTS)) {
+          return ClassInitializerSideEffect.SIDE_EFFECTS_THAT_CANNOT_BE_POSTPONED;
+        }
+        continue;
+      }
+
       // For other instructions, bail out if they may have side effects.
       if (instruction.instructionMayHaveSideEffects(appView, context)) {
         return ClassInitializerSideEffect.SIDE_EFFECTS_THAT_CANNOT_BE_POSTPONED;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index eb5a275..a1c7d45 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -1502,6 +1502,15 @@
           }
         };
 
+    public static final SideEffectAssumption IGNORE_RECEIVER_FIELD_ASSIGNMENTS =
+        new SideEffectAssumption() {
+
+          @Override
+          public boolean canIgnoreInstanceFieldAssignmentsToReceiver() {
+            return true;
+          }
+        };
+
     public static final SideEffectAssumption INVOKED_METHOD_DOES_NOT_HAVE_SIDE_EFFECTS =
         new SideEffectAssumption() {
 
@@ -1528,6 +1537,10 @@
       return false;
     }
 
+    public boolean canIgnoreInstanceFieldAssignmentsToReceiver() {
+      return false;
+    }
+
     public boolean canAssumeReceiverIsNotNull() {
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index b4ff341..83dfa77 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -174,7 +174,8 @@
   @Override
   public DeadInstructionResult canBeDeadCode(AppView<?> appView, IRCode code) {
     ProgramMethod context = code.context();
-    if (instructionMayHaveSideEffects(appView, context)) {
+    if (instructionMayHaveSideEffects(
+        appView, context, SideEffectAssumption.IGNORE_RECEIVER_FIELD_ASSIGNMENTS)) {
       return DeadInstructionResult.notDead();
     }
     if (!getInvokedMethod().isInstanceInitializer(appView.dexItemFactory())) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index 94ab740..39f52cb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -259,7 +259,8 @@
 
     DexEncodedMethod singleTargetDefinition = singleTarget.getDefinition();
     MethodOptimizationInfo optimizationInfo = singleTargetDefinition.getOptimizationInfo();
-    if (singleTargetDefinition.isInstanceInitializer()) {
+    if (assumption.canIgnoreInstanceFieldAssignmentsToReceiver()
+        && singleTargetDefinition.isInstanceInitializer()) {
       assert isInvokeDirect();
       InstanceInitializerInfo initializerInfo =
           optimizationInfo.getInstanceInitializerInfo(asInvokeDirect());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index aacfec7..5f6ad1a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -505,7 +505,15 @@
           method, code, methodCallsOnInstance, inliningIRProvider, Timing.empty());
     } else {
       assert indirectMethodCallsOnInstance.stream()
-          .noneMatch(method -> method.getDefinition().getOptimizationInfo().mayHaveSideEffects());
+          .filter(method -> method.getDefinition().getOptimizationInfo().mayHaveSideEffects())
+          .allMatch(
+              method ->
+                  method.getDefinition().isInstanceInitializer()
+                      && !method
+                          .getDefinition()
+                          .getOptimizationInfo()
+                          .getContextInsensitiveInstanceInitializerInfo()
+                          .mayHaveOtherSideEffectsThanInstanceFieldAssignments());
     }
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 436f996..15afa8c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -73,6 +73,7 @@
 import com.android.tools.r8.ir.code.If;
 import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Instruction.SideEffectAssumption;
 import com.android.tools.r8.ir.code.InstructionIterator;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.InvokeMethod;
@@ -370,7 +371,8 @@
               if (singleTarget == null) {
                 return null;
               }
-              if (singleTarget.isInstanceInitializer() && invoke.getReceiver() == receiver) {
+              if (singleTarget.isInstanceInitializer()
+                  && invoke.getReceiver().getAliasedValue() == receiver) {
                 if (builder.hasParent() && builder.getParent() != singleTarget.getReference()) {
                   return null;
                 }
@@ -927,7 +929,18 @@
       mayHaveSideEffects = false;
       // Otherwise, check if there is an instruction that has side effects.
       for (Instruction instruction : code.instructions()) {
-        if (instruction.instructionMayHaveSideEffects(appView, context)) {
+        if (instruction.isInvokeConstructor(appView.dexItemFactory())
+            && instruction
+                .asInvokeDirect()
+                .getReceiver()
+                .getAliasedValue()
+                .isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
+          if (instruction.instructionMayHaveSideEffects(
+              appView, context, SideEffectAssumption.IGNORE_RECEIVER_FIELD_ASSIGNMENTS)) {
+            mayHaveSideEffects = true;
+            break;
+          }
+        } else if (instruction.instructionMayHaveSideEffects(appView, context)) {
           mayHaveSideEffects = true;
           break;
         }