Towards context sensitive instance initializer info

This makes the instance initializer info on MethodOptimizationInfo context sensitive.

Since the computed instance initializer info is currently true in all contexts a special AlwaysTrueInstanceInitializerInfoContext is used.

Follow up work will need to compute different instance initializer info instances that are only true in specific contexts (e.g., when a parameter is guaranteed to have a specific value).

Change-Id: I993eafee4b4599416bd532a5c8fa88c33514accf
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
index 6e46731..2cf0214 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
@@ -21,7 +21,7 @@
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.InvokeNewArray;
 import com.android.tools.r8.ir.code.NewArrayEmpty;
 import com.android.tools.r8.ir.code.NewArrayFilledData;
@@ -255,7 +255,7 @@
     }
 
     // Find the single constructor invocation.
-    InvokeMethod constructorInvoke =
+    InvokeDirect constructorInvoke =
         newInstance.getUniqueConstructorInvoke(appView.dexItemFactory());
     if (constructorInvoke == null || constructorInvoke.getInvokedMethod().holder != clazz.type) {
       // Didn't find a (valid) constructor invocation, give up.
@@ -269,7 +269,7 @@
     }
 
     InstanceInitializerInfo initializerInfo =
-        constructor.getOptimizationInfo().getInstanceInitializerInfo();
+        constructor.getOptimizationInfo().getInstanceInitializerInfo(constructorInvoke);
 
     List<DexEncodedField> fields = clazz.getDirectAndIndirectInstanceFields(appView);
     if (!fields.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index d0fd119..d0a86ef 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -162,7 +162,7 @@
         singleTarget
             .getDefinition()
             .getOptimizationInfo()
-            .getInstanceInitializerInfo()
+            .getInstanceInitializerInfo(invoke)
             .fieldInitializationInfos();
 
     // Synchronize on the lattice element (abstractInstanceFieldValuesForClass) in case we process
@@ -232,7 +232,7 @@
         InstanceFieldInitializationInfo fieldInitializationInfo =
             method
                 .getOptimizationInfo()
-                .getInstanceInitializerInfo()
+                .getContextInsensitiveInstanceInitializerInfo()
                 .fieldInitializationInfos()
                 .get(field);
         if (fieldInitializationInfo.isSingleValue()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
index 17ee50a..443ac30 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
@@ -147,7 +147,7 @@
         singleTarget
             .getDefinition()
             .getOptimizationInfo()
-            .getInstanceInitializerInfo()
+            .getInstanceInitializerInfo(invoke)
             .fieldInitializationInfos();
     for (DexEncodedField field : singleTarget.getHolder().instanceFields()) {
       assert isSubjectToOptimization(field);
@@ -169,7 +169,7 @@
         parentConstructor
             .getDefinition()
             .getOptimizationInfo()
-            .getInstanceInitializerInfo()
+            .getInstanceInitializerInfo(parentConstructorCall)
             .fieldInitializationInfos();
     infos.forEach(
         appView,
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index ca922bf..9be6c55 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -348,7 +348,7 @@
         singleTarget
             .getDefinition()
             .getOptimizationInfo()
-            .getInstanceInitializerInfo()
+            .getInstanceInitializerInfo(uniqueConstructorInvoke)
             .fieldInitializationInfos();
     if (initializationInfos.isEmpty()) {
       return ObjectState.empty();
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 c61ca0a..b4ff341 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
@@ -202,7 +202,7 @@
         return singleTarget
             .getDefinition()
             .getOptimizationInfo()
-            .getInstanceInitializerInfo()
+            .getInstanceInitializerInfo(this)
             .readSet();
       }
     }
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 8928a4d..fdaf8f0 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
@@ -255,7 +255,9 @@
     DexEncodedMethod singleTargetDefinition = singleTarget.getDefinition();
     MethodOptimizationInfo optimizationInfo = singleTargetDefinition.getOptimizationInfo();
     if (singleTargetDefinition.isInstanceInitializer()) {
-      InstanceInitializerInfo initializerInfo = optimizationInfo.getInstanceInitializerInfo();
+      assert isInvokeDirect();
+      InstanceInitializerInfo initializerInfo =
+          optimizationInfo.getInstanceInitializerInfo(asInvokeDirect());
       if (!initializerInfo.mayHaveOtherSideEffectsThanInstanceFieldAssignments()) {
         return !isInvokeDirect();
       }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
index 4379da1..44f5990 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -14,7 +14,7 @@
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
-import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.BitSet;
 import java.util.Set;
@@ -60,8 +60,8 @@
 
   void setClassInlinerEligibility(DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility);
 
-  void setInstanceInitializerInfo(
-      DexEncodedMethod method, InstanceInitializerInfo instanceInitializerInfo);
+  void setInstanceInitializerInfoCollection(
+      DexEncodedMethod method, InstanceInitializerInfoCollection instanceInitializerInfoCollection);
 
   void setInitializerEnablingJavaVmAssertions(DexEncodedMethod method);
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index aea6ae7..3745120 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -366,7 +366,7 @@
     }
 
     InstanceInitializerInfo instanceInitializerInfo =
-        singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo();
+        singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo(invoke);
     if (instanceInitializerInfo.mayHaveOtherSideEffectsThanInstanceFieldAssignments()) {
       killAllNonFinalActiveFields();
     }
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 550a0e1..e080808 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
@@ -826,7 +826,7 @@
 
     // Check that the `eligibleInstance` does not escape via the constructor.
     InstanceInitializerInfo instanceInitializerInfo =
-        singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo();
+        singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo(invoke);
     if (instanceInitializerInfo.receiverMayEscapeOutsideConstructorChain()) {
       return null;
     }
@@ -856,7 +856,11 @@
           NopWhyAreYouNotInliningReporter.getInstance())) {
         return null;
       }
-      parent = encodedParentMethod.getOptimizationInfo().getInstanceInitializerInfo().getParent();
+      parent =
+          encodedParentMethod
+              .getOptimizationInfo()
+              .getContextInsensitiveInstanceInitializerInfo()
+              .getParent();
     }
 
     return new InliningInfo(singleTarget, eligibleClass.type);
@@ -1317,7 +1321,7 @@
       return false;
     }
     InstanceInitializerInfo initializerInfo =
-        definition.getOptimizationInfo().getInstanceInitializerInfo();
+        definition.getOptimizationInfo().getContextInsensitiveInstanceInitializerInfo();
     return initializerInfo.receiverNeverEscapesOutsideConstructorChain();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 8268c65..2d46582 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -698,7 +698,7 @@
               hasInstanceInitializer = true;
               if (directMethod
                   .getOptimizationInfo()
-                  .getInstanceInitializerInfo()
+                  .getContextInsensitiveInstanceInitializerInfo()
                   .mayHaveOtherSideEffectsThanInstanceFieldAssignments()) {
                 markEnumAsUnboxable(Reason.INVALID_INIT, enumClass);
                 break;
@@ -714,6 +714,7 @@
           }
 
           if (enumClass.classInitializationMayHaveSideEffects(appView)) {
+            enumClass.classInitializationMayHaveSideEffects(appView);
             markEnumAsUnboxable(Reason.INVALID_CLINIT, enumClass);
           }
         });
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index 0e3e339..b2b10ae 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
+import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
@@ -82,7 +83,12 @@
   }
 
   @Override
-  public InstanceInitializerInfo getInstanceInitializerInfo() {
+  public InstanceInitializerInfo getContextInsensitiveInstanceInitializerInfo() {
+    return DefaultInstanceInitializerInfo.getInstance();
+  }
+
+  @Override
+  public InstanceInitializerInfo getInstanceInitializerInfo(InvokeDirect invoke) {
     return DefaultInstanceInitializerInfo.getInstance();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index f919a1e..cf7c4ee 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
@@ -68,7 +69,9 @@
 
   public abstract Set<DexType> getInitializedClassesOnNormalExit();
 
-  public abstract InstanceInitializerInfo getInstanceInitializerInfo();
+  public abstract InstanceInitializerInfo getContextInsensitiveInstanceInitializerInfo();
+
+  public abstract InstanceInitializerInfo getInstanceInitializerInfo(InvokeDirect invoke);
 
   public abstract boolean isInitializerEnablingJavaVmAssertions();
 
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 e3ab9af..09c4a5d 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
@@ -93,8 +93,8 @@
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsageBuilder;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeAnalyzer;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
-import com.android.tools.r8.ir.optimize.info.initializer.DefaultInstanceInitializerInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.ir.optimize.info.initializer.NonTrivialInstanceInitializerInfo;
 import com.android.tools.r8.ir.optimize.typechecks.CheckCastAndInstanceOfMethodSpecialization;
 import com.android.tools.r8.kotlin.Kotlin;
@@ -437,11 +437,8 @@
     NonTrivialInstanceInitializerInfo.Builder builder =
         NonTrivialInstanceInitializerInfo.builder(instanceFieldInitializationInfos);
     InstanceInitializerInfo instanceInitializerInfo = analyzeInstanceInitializer(code, builder);
-    feedback.setInstanceInitializerInfo(
-        method,
-        instanceInitializerInfo != null
-            ? instanceInitializerInfo
-            : DefaultInstanceInitializerInfo.getInstance());
+    feedback.setInstanceInitializerInfoCollection(
+        method, InstanceInitializerInfoCollection.of(instanceInitializerInfo));
   }
 
   // This method defines trivial instance initializer as follows:
@@ -586,7 +583,8 @@
                   builder.setParent(invokedMethod);
                   break;
                 }
-                builder.merge(singleTarget.getOptimizationInfo().getInstanceInitializerInfo());
+                builder.merge(
+                    singleTarget.getOptimizationInfo().getInstanceInitializerInfo(invoke));
                 for (int i = 1; i < invoke.arguments().size(); i++) {
                   Value argument =
                       invoke.arguments().get(i).getAliasedValue(aliasesThroughAssumeAndCheckCasts);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 07b7ce1..50cb793 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -14,7 +14,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
-import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.AppInfoWithLivenessModifier;
 import com.android.tools.r8.utils.IteratorUtils;
@@ -254,10 +254,11 @@
   }
 
   @Override
-  public synchronized void setInstanceInitializerInfo(
-      DexEncodedMethod method, InstanceInitializerInfo instanceInitializerInfo) {
+  public synchronized void setInstanceInitializerInfoCollection(
+      DexEncodedMethod method,
+      InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
     getMethodOptimizationInfoForUpdating(method)
-        .setInstanceInitializerInfo(instanceInitializerInfo);
+        .setInstanceInitializerInfoCollection(instanceInitializerInfoCollection);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
index 6a28331..12fda15 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -14,7 +14,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
-import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.BitSet;
 import java.util.Set;
@@ -115,8 +115,9 @@
       DexEncodedMethod method, ClassInlinerEligibilityInfo eligibility) {}
 
   @Override
-  public void setInstanceInitializerInfo(
-      DexEncodedMethod method, InstanceInitializerInfo instanceInitializerInfo) {}
+  public void setInstanceInitializerInfoCollection(
+      DexEncodedMethod method,
+      InstanceInitializerInfoCollection instanceInitializerInfoCollection) {}
 
   @Override
   public void setInitializerEnablingJavaVmAssertions(DexEncodedMethod method) {}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 9a3bb51..5840ea6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -14,7 +14,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
-import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.BitSet;
 import java.util.Set;
@@ -165,9 +165,12 @@
   }
 
   @Override
-  public void setInstanceInitializerInfo(
-      DexEncodedMethod method, InstanceInitializerInfo instanceInitializerInfo) {
-    method.getMutableOptimizationInfo().setInstanceInitializerInfo(instanceInitializerInfo);
+  public void setInstanceInitializerInfoCollection(
+      DexEncodedMethod method,
+      InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
+    method
+        .getMutableOptimizationInfo()
+        .setInstanceInitializerInfoCollection(instanceInitializerInfoCollection);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
index 6d9fc9f..877c72d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
@@ -12,11 +12,12 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.ir.analysis.value.UnknownValue;
+import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerEligibilityInfo;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
-import com.android.tools.r8.ir.optimize.info.initializer.DefaultInstanceInitializerInfo;
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.util.BitSet;
@@ -39,8 +40,8 @@
   private BridgeInfo bridgeInfo = null;
   private ClassInlinerEligibilityInfo classInlinerEligibility =
       DefaultMethodOptimizationInfo.UNKNOWN_CLASS_INLINER_ELIGIBILITY;
-  private InstanceInitializerInfo instanceInitializerInfo =
-      DefaultInstanceInitializerInfo.getInstance();
+  private InstanceInitializerInfoCollection instanceInitializerInfoCollection =
+      InstanceInitializerInfoCollection.empty();
   private ParameterUsagesInfo parametersUsages =
       DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO;
   // Stores information about nullability hint per parameter. If set, that means, the method
@@ -138,7 +139,7 @@
     inlining = template.inlining;
     bridgeInfo = template.bridgeInfo;
     classInlinerEligibility = template.classInlinerEligibility;
-    instanceInitializerInfo = template.instanceInitializerInfo;
+    instanceInitializerInfoCollection = template.instanceInitializerInfoCollection;
     parametersUsages = template.parametersUsages;
     nonNullParamOrThrow = template.nonNullParamOrThrow;
     nonNullParamOnNormalExits = template.nonNullParamOnNormalExits;
@@ -172,9 +173,8 @@
 
   public UpdatableMethodOptimizationInfo fixupInstanceInitializerInfo(
       AppView<AppInfoWithLiveness> appView, GraphLens lens) {
-    if (instanceInitializerInfo != null) {
-      instanceInitializerInfo = instanceInitializerInfo.rewrittenWithLens(appView, lens);
-    }
+    instanceInitializerInfoCollection =
+        instanceInitializerInfoCollection.rewrittenWithLens(appView, lens);
     return this;
   }
 
@@ -248,8 +248,13 @@
   }
 
   @Override
-  public InstanceInitializerInfo getInstanceInitializerInfo() {
-    return instanceInitializerInfo;
+  public InstanceInitializerInfo getContextInsensitiveInstanceInitializerInfo() {
+    return instanceInitializerInfoCollection.getContextInsensitive();
+  }
+
+  @Override
+  public InstanceInitializerInfo getInstanceInitializerInfo(InvokeDirect invoke) {
+    return instanceInitializerInfoCollection.get(invoke);
   }
 
   @Override
@@ -371,8 +376,9 @@
     this.classInlinerEligibility = eligibility;
   }
 
-  void setInstanceInitializerInfo(InstanceInitializerInfo instanceInitializerInfo) {
-    this.instanceInitializerInfo = instanceInitializerInfo;
+  void setInstanceInitializerInfoCollection(
+      InstanceInitializerInfoCollection instanceInitializerInfoCollection) {
+    this.instanceInitializerInfoCollection = instanceInitializerInfoCollection;
   }
 
   void setInitializerEnablingJavaAssertions() {
@@ -520,7 +526,7 @@
     // classInlinerEligibility: chances are the method is not an instance method anymore.
     classInlinerEligibility = DefaultMethodOptimizationInfo.UNKNOWN_CLASS_INLINER_ELIGIBILITY;
     // initializerInfo: the computed initializer info may become invalid.
-    instanceInitializerInfo = null;
+    instanceInitializerInfoCollection = InstanceInitializerInfoCollection.empty();
     // initializerEnablingJavaAssertions: `this` could trigger <clinit> of the previous holder.
     setFlag(
         INITIALIZER_ENABLING_JAVA_ASSERTIONS_FLAG,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/AlwaysTrueInstanceInitializerInfoContext.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/AlwaysTrueInstanceInitializerInfoContext.java
new file mode 100644
index 0000000..12ef51a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/AlwaysTrueInstanceInitializerInfoContext.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.info.initializer;
+
+import com.android.tools.r8.ir.code.InvokeMethod;
+
+public class AlwaysTrueInstanceInitializerInfoContext extends InstanceInitializerInfoContext {
+
+  private static final AlwaysTrueInstanceInitializerInfoContext INSTANCE =
+      new AlwaysTrueInstanceInitializerInfoContext();
+
+  private AlwaysTrueInstanceInitializerInfoContext() {}
+
+  public static AlwaysTrueInstanceInitializerInfoContext getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public boolean isAlwaysTrue() {
+    return true;
+  }
+
+  @Override
+  public boolean isSatisfiedBy(InvokeMethod invoke) {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java
new file mode 100644
index 0000000..6cf070d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.info.initializer;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public class ContextInsensitiveInstanceInitializerInfoCollection
+    extends InstanceInitializerInfoCollection {
+
+  private final NonTrivialInstanceInitializerInfo info;
+
+  ContextInsensitiveInstanceInitializerInfoCollection(NonTrivialInstanceInitializerInfo info) {
+    this.info = info;
+  }
+
+  @Override
+  public NonTrivialInstanceInitializerInfo getContextInsensitive() {
+    return info;
+  }
+
+  @Override
+  public NonTrivialInstanceInitializerInfo get(InvokeDirect invoke) {
+    return info;
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return false;
+  }
+
+  @Override
+  public ContextInsensitiveInstanceInitializerInfoCollection rewrittenWithLens(
+      AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+    NonTrivialInstanceInitializerInfo rewrittenInfo = info.rewrittenWithLens(appView, lens);
+    if (rewrittenInfo != info) {
+      return new ContextInsensitiveInstanceInitializerInfoCollection(rewrittenInfo);
+    }
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java
new file mode 100644
index 0000000..3347587
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.info.initializer;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map.Entry;
+
+public class ContextSensitiveInstanceInitializerInfoCollection
+    extends InstanceInitializerInfoCollection {
+
+  private final ImmutableMap<InstanceInitializerInfoContext, NonTrivialInstanceInitializerInfo>
+      infos;
+
+  protected ContextSensitiveInstanceInitializerInfoCollection(
+      ImmutableMap<InstanceInitializerInfoContext, NonTrivialInstanceInitializerInfo> infos) {
+    assert !infos.isEmpty();
+    this.infos = infos;
+  }
+
+  @Override
+  public InstanceInitializerInfo getContextInsensitive() {
+    NonTrivialInstanceInitializerInfo result =
+        infos.get(AlwaysTrueInstanceInitializerInfoContext.getInstance());
+    return result != null ? result : DefaultInstanceInitializerInfo.getInstance();
+  }
+
+  @Override
+  public InstanceInitializerInfo get(InvokeDirect invoke) {
+    assert infos.keySet().stream().filter(context -> context.isSatisfiedBy(invoke)).count() <= 1;
+    for (Entry<InstanceInitializerInfoContext, NonTrivialInstanceInitializerInfo> entry :
+        infos.entrySet()) {
+      if (entry.getKey().isSatisfiedBy(invoke)) {
+        return entry.getValue();
+      }
+    }
+    return DefaultInstanceInitializerInfo.getInstance();
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return false;
+  }
+
+  @Override
+  public InstanceInitializerInfoCollection rewrittenWithLens(
+      AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+    Builder builder = builder();
+    infos.forEach((context, info) -> builder.put(context, info.rewrittenWithLens(appView, lens)));
+    return builder.build();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
index c9a7f32..af59b7c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
@@ -25,6 +25,11 @@
   }
 
   @Override
+  public boolean isDefaultInstanceInitializerInfo() {
+    return true;
+  }
+
+  @Override
   public DexMethod getParent() {
     return null;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java
new file mode 100644
index 0000000..d7d3560
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.info.initializer;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public class EmptyInstanceInitializerInfoCollection extends InstanceInitializerInfoCollection {
+
+  private static final EmptyInstanceInitializerInfoCollection EMPTY =
+      new EmptyInstanceInitializerInfoCollection();
+
+  private EmptyInstanceInitializerInfoCollection() {}
+
+  public static EmptyInstanceInitializerInfoCollection getInstance() {
+    return EMPTY;
+  }
+
+  @Override
+  public DefaultInstanceInitializerInfo getContextInsensitive() {
+    return DefaultInstanceInitializerInfo.getInstance();
+  }
+
+  @Override
+  public DefaultInstanceInitializerInfo get(InvokeDirect invoke) {
+    return DefaultInstanceInitializerInfo.getInstance();
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return true;
+  }
+
+  @Override
+  public EmptyInstanceInitializerInfoCollection rewrittenWithLens(
+      AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
index 1e36f40..3865bc1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
@@ -13,6 +13,18 @@
 
 public abstract class InstanceInitializerInfo {
 
+  public boolean isDefaultInstanceInitializerInfo() {
+    return false;
+  }
+
+  public boolean isNonTrivialInstanceInitializerInfo() {
+    return false;
+  }
+
+  public NonTrivialInstanceInitializerInfo asNonTrivialInstanceInitializerInfo() {
+    return null;
+  }
+
   public abstract DexMethod getParent();
 
   public abstract InstanceFieldInitializationInfoCollection fieldInitializationInfos();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java
new file mode 100644
index 0000000..ce53a6a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.info.initializer;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.MapUtils;
+import com.google.common.collect.ImmutableMap;
+
+public abstract class InstanceInitializerInfoCollection {
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static InstanceInitializerInfoCollection empty() {
+    return EmptyInstanceInitializerInfoCollection.getInstance();
+  }
+
+  public static InstanceInitializerInfoCollection of(InstanceInitializerInfo info) {
+    if (info != null && info.isNonTrivialInstanceInitializerInfo()) {
+      return new ContextInsensitiveInstanceInitializerInfoCollection(
+          info.asNonTrivialInstanceInitializerInfo());
+    }
+    return empty();
+  }
+
+  public abstract InstanceInitializerInfo getContextInsensitive();
+
+  public abstract InstanceInitializerInfo get(InvokeDirect invoke);
+
+  public abstract boolean isEmpty();
+
+  public abstract InstanceInitializerInfoCollection rewrittenWithLens(
+      AppView<AppInfoWithLiveness> appView, GraphLens lens);
+
+  public static class Builder {
+
+    private final ImmutableMap.Builder<
+            InstanceInitializerInfoContext, NonTrivialInstanceInitializerInfo>
+        infosBuilder = ImmutableMap.builder();
+
+    private Builder() {}
+
+    public Builder put(InstanceInitializerInfoContext context, InstanceInitializerInfo info) {
+      if (info.isNonTrivialInstanceInitializerInfo()) {
+        infosBuilder.put(context, info.asNonTrivialInstanceInitializerInfo());
+      }
+      return this;
+    }
+
+    public InstanceInitializerInfoCollection build() {
+      ImmutableMap<InstanceInitializerInfoContext, NonTrivialInstanceInitializerInfo> infos =
+          infosBuilder.build();
+      if (infos.isEmpty()) {
+        return empty();
+      }
+      if (infos.size() == 1 && MapUtils.firstKey(infos).isAlwaysTrue()) {
+        return new ContextInsensitiveInstanceInitializerInfoCollection(MapUtils.firstValue(infos));
+      }
+      return new ContextSensitiveInstanceInitializerInfoCollection(infos);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoContext.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoContext.java
new file mode 100644
index 0000000..8c28004
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoContext.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.info.initializer;
+
+import com.android.tools.r8.ir.code.InvokeMethod;
+
+public abstract class InstanceInitializerInfoContext {
+
+  public boolean isAlwaysTrue() {
+    return false;
+  }
+
+  public abstract boolean isSatisfiedBy(InvokeMethod invoke);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
index 75c74dd..40b3edc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
@@ -38,6 +38,16 @@
     this.parent = parent;
   }
 
+  @Override
+  public boolean isNonTrivialInstanceInitializerInfo() {
+    return true;
+  }
+
+  @Override
+  public NonTrivialInstanceInitializerInfo asNonTrivialInstanceInitializerInfo() {
+    return this;
+  }
+
   private static boolean verifyNoUnknownBits(int data) {
     int knownBits =
         INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
index d0412b7..9b41f18 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.ir.optimize.info.LibraryOptimizationInfoInitializerFeedback;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
+import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.ir.optimize.info.initializer.NonTrivialInstanceInitializerInfo;
 import com.google.common.collect.Sets;
 import java.util.BitSet;
@@ -67,11 +68,12 @@
               .recordInitializationInfo(
                   enumMembers.ordinalField, factory.createArgumentInitializationInfo(2))
               .build();
-      feedback.setInstanceInitializerInfo(
+      feedback.setInstanceInitializerInfoCollection(
           enumConstructor,
-          NonTrivialInstanceInitializerInfo.builder(fieldInitializationInfos)
-              .setParent(dexItemFactory.objectMembers.constructor)
-              .build());
+          InstanceInitializerInfoCollection.of(
+              NonTrivialInstanceInitializerInfo.builder(fieldInitializationInfos)
+                  .setParent(dexItemFactory.objectMembers.constructor)
+                  .build()));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java b/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
index 2de5c70..d63c685 100644
--- a/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
+++ b/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
@@ -229,7 +229,7 @@
       }
 
       InstanceInitializerInfo initializerInfo =
-          singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo();
+          singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo(invoke);
       return initializerInfo.receiverNeverEscapesOutsideConstructorChain();
     }
   }
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index c899a75..ac79586 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -12,6 +12,14 @@
 
 public class MapUtils {
 
+  public static <K, V> K firstKey(Map<K, V> map) {
+    return map.keySet().iterator().next();
+  }
+
+  public static <K, V> V firstValue(Map<K, V> map) {
+    return map.values().iterator().next();
+  }
+
   public static <K, V> Map<K, V> map(
       Map<K, V> map,
       IntFunction<Map<K, V>> factory,
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
index 0f85170..d618b52 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
@@ -50,7 +50,7 @@
 
   @Override
   public V get(Object key) {
-    return getRepresentativeValue((K) key);
+    return backing.get(key);
   }
 
   @Override