Extend instance initializer info with argument initialization info

Change-Id: I998e25e50fde7f68ad8d7dff1d9112e3645cdb73
Bug: 147652121
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 9f7b4cf..3ce7b0a 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
 import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
 import com.android.tools.r8.ir.optimize.CallSiteOptimizationInfoPropagator;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
 import com.android.tools.r8.ir.optimize.library.LibraryMethodOptimizer;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
@@ -45,6 +46,8 @@
   private final InternalOptions options;
   private RootSet rootSet;
   private final AbstractValueFactory abstractValueFactory = new AbstractValueFactory();
+  private final InstanceFieldInitializationInfoFactory instanceFieldInitializationInfoFactory =
+      new InstanceFieldInitializationInfoFactory();
 
   // Desugared library prefix rewriter.
   public final PrefixRewritingMapper rewritePrefix;
@@ -125,6 +128,10 @@
     return abstractValueFactory;
   }
 
+  public InstanceFieldInitializationInfoFactory instanceFieldInitializationInfoFactory() {
+    return instanceFieldInitializationInfoFactory;
+  }
+
   public T appInfo() {
     return appInfo;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index 29dc41b..2289a28 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -275,7 +275,7 @@
     return true;
   }
 
-  private void updateFieldOptimizationInfo(DexEncodedField field, Value value) {
+  void updateFieldOptimizationInfo(DexEncodedField field, Value value) {
     // Abstract value.
     Value root = value.getAliasedValue();
     AbstractValue abstractValue = computeAbstractValue(root);
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 d636715..6c1fe58 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
@@ -5,15 +5,29 @@
 package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.ir.code.Argument;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.ir.optimize.info.field.EmptyInstanceFieldInitializationInfoCollection;
+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.shaking.AppInfoWithLiveness;
 
 public class InstanceFieldValueAnalysis extends FieldValueAnalysis {
 
+  // Information about how this instance constructor initializes the fields on the newly created
+  // instance.
+  private final InstanceFieldInitializationInfoCollection.Builder builder =
+      InstanceFieldInitializationInfoCollection.builder();
+
+  private final InstanceFieldInitializationInfoFactory factory;
+
   private InstanceFieldValueAnalysis(
       AppView<AppInfoWithLiveness> appView,
       IRCode code,
@@ -21,9 +35,14 @@
       DexProgramClass clazz,
       DexEncodedMethod method) {
     super(appView, code, feedback, clazz, method);
+    factory = appView.instanceFieldInitializationInfoFactory();
   }
 
-  public static void run(
+  /**
+   * Returns information about how this instance constructor initializes the fields on the newly
+   * created instance.
+   */
+  public static InstanceFieldInitializationInfoCollection run(
       AppView<?> appView,
       IRCode code,
       ClassInitializerDefaultsResult classInitializerDefaultsResult,
@@ -34,16 +53,32 @@
     assert method.isInstanceInitializer();
     DexProgramClass clazz = appView.definitionFor(method.method.holder).asProgramClass();
     if (!appView.options().enableValuePropagationForInstanceFields) {
-      return;
+      return EmptyInstanceFieldInitializationInfoCollection.getInstance();
     }
     DexEncodedMethod otherInstanceInitializer =
         clazz.lookupDirectMethod(other -> other.isInstanceInitializer() && other != method);
     if (otherInstanceInitializer != null) {
       // Conservatively bail out.
       // TODO(b/125282093): Handle multiple instance initializers on the same class.
-      return;
+      return EmptyInstanceFieldInitializationInfoCollection.getInstance();
     }
-    new InstanceFieldValueAnalysis(appView.withLiveness(), code, feedback, clazz, method)
-        .computeFieldOptimizationInfo(classInitializerDefaultsResult);
+    InstanceFieldValueAnalysis analysis =
+        new InstanceFieldValueAnalysis(appView.withLiveness(), code, feedback, clazz, method);
+    analysis.computeFieldOptimizationInfo(classInitializerDefaultsResult);
+    return analysis.builder.build();
+  }
+
+  @Override
+  void updateFieldOptimizationInfo(DexEncodedField field, Value value) {
+    super.updateFieldOptimizationInfo(field, value);
+
+    // If this instance field is initialized with an argument, then record this in the instance
+    // field initialization info.
+    Value root = value.getAliasedValue();
+    if (root.isDefinedByInstructionSatisfying(Instruction::isArgument)) {
+      Argument argument = root.definition.asArgument();
+      builder.recordInitializationInfo(
+          field, factory.createArgumentInitializationInfo(argument.getArgumentIndex()));
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Argument.java b/src/main/java/com/android/tools/r8/ir/code/Argument.java
index 93c39ec..046af1b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Argument.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Argument.java
@@ -29,6 +29,20 @@
     outValue.markAsArgument();
   }
 
+  public int getArgumentIndex() {
+    int index = 0;
+    InstructionIterator instructionIterator = getBlock().iterator();
+    while (instructionIterator.hasNext()) {
+      Instruction instruction = instructionIterator.next();
+      assert instruction.isArgument();
+      if (instruction == this) {
+        break;
+      }
+      index++;
+    }
+    return index;
+  }
+
   @Override
   public int opcode() {
     return Opcodes.ARGUMENT;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index eecf8c3..95c2608 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -30,6 +30,7 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.function.Predicate;
 
 public class Phi extends Value implements InstructionOrPhi {
 
@@ -62,6 +63,11 @@
   }
 
   @Override
+  public boolean isDefinedByInstructionSatisfying(Predicate<Instruction> predicate) {
+    return false;
+  }
+
+  @Override
   public boolean isPhi() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 8e75d62..e46aca6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -893,6 +893,10 @@
     return root.definition.getAbstractValue(appView, context);
   }
 
+  public boolean isDefinedByInstructionSatisfying(Predicate<Instruction> predicate) {
+    return predicate.test(definition);
+  }
+
   public boolean isPhi() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 94df8fb..5c76d71 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -77,6 +77,7 @@
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
 import com.android.tools.r8.ir.optimize.lambda.LambdaMerger;
 import com.android.tools.r8.ir.optimize.staticizer.ClassStaticizer;
 import com.android.tools.r8.ir.optimize.string.StringBuilderOptimizer;
@@ -1563,18 +1564,19 @@
       return;
     }
 
-    methodOptimizationInfoCollector
-        .collectMethodOptimizationInfo(code.method, code, feedback, dynamicTypeOptimization);
-
+    InstanceFieldInitializationInfoCollection instanceFieldInitializationInfos = null;
     if (method.isInitializer()) {
       if (method.isClassInitializer()) {
         StaticFieldValueAnalysis.run(
             appView, code, classInitializerDefaultsResult, feedback, code.method);
       } else {
-        InstanceFieldValueAnalysis.run(
-            appView, code, classInitializerDefaultsResult, feedback, code.method);
+        instanceFieldInitializationInfos =
+            InstanceFieldValueAnalysis.run(
+                appView, code, classInitializerDefaultsResult, feedback, code.method);
       }
     }
+    methodOptimizationInfoCollector.collectMethodOptimizationInfo(
+        code.method, code, feedback, dynamicTypeOptimization, instanceFieldInitializationInfos);
   }
 
   public void removeDeadCodeAndFinalizeIR(
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 d1a6c3a..b9345e9 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
@@ -84,6 +84,7 @@
 import com.android.tools.r8.ir.optimize.classinliner.ClassInlinerReceiverAnalysis;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsageBuilder;
+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.NonTrivialInstanceInitializerInfo;
@@ -120,7 +121,8 @@
       DexEncodedMethod method,
       IRCode code,
       OptimizationFeedback feedback,
-      DynamicTypeOptimization dynamicTypeOptimization) {
+      DynamicTypeOptimization dynamicTypeOptimization,
+      InstanceFieldInitializationInfoCollection instanceFieldInitializationInfos) {
     identifyClassInlinerEligibility(method, code, feedback);
     identifyParameterUsages(method, code, feedback);
     identifyReturnsArgument(method, code, feedback);
@@ -129,7 +131,7 @@
     }
     computeDynamicReturnType(dynamicTypeOptimization, feedback, method, code);
     computeInitializedClassesOnNormalExit(feedback, method, code);
-    computeInstanceInitializerInfo(method, code, feedback);
+    computeInstanceInitializerInfo(method, code, feedback, instanceFieldInitializationInfos);
     computeMayHaveSideEffects(feedback, method, code);
     computeReturnValueOnlyDependsOnArguments(feedback, method, code);
     computeNonNullParamOrThrow(feedback, method, code);
@@ -354,13 +356,18 @@
   }
 
   private void computeInstanceInitializerInfo(
-      DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
+      DexEncodedMethod method,
+      IRCode code,
+      OptimizationFeedback feedback,
+      InstanceFieldInitializationInfoCollection instanceFieldInitializationInfos) {
     assert !appView.appInfo().isPinned(method.method);
 
     if (!method.isInstanceInitializer()) {
       return;
     }
 
+    assert instanceFieldInitializationInfos != null;
+
     if (method.accessFlags.isNative()) {
       return;
     }
@@ -375,7 +382,10 @@
       return;
     }
 
-    InstanceInitializerInfo instanceInitializerInfo = analyzeInstanceInitializer(code, clazz);
+    NonTrivialInstanceInitializerInfo.Builder builder =
+        NonTrivialInstanceInitializerInfo.builder(instanceFieldInitializationInfos);
+    InstanceInitializerInfo instanceInitializerInfo =
+        analyzeInstanceInitializer(code, clazz, builder);
     feedback.setInstanceInitializerInfo(
         method,
         instanceInitializerInfo != null
@@ -398,13 +408,13 @@
   // ** Assigns arguments or non-throwing constants to fields of this class.
   //
   // (Note that this initializer does not have to have zero arguments.)
-  private InstanceInitializerInfo analyzeInstanceInitializer(IRCode code, DexClass clazz) {
+  private InstanceInitializerInfo analyzeInstanceInitializer(
+      IRCode code, DexClass clazz, NonTrivialInstanceInitializerInfo.Builder builder) {
     if (clazz.definesFinalizer(options.itemFactory)) {
       // Defining a finalize method can observe the side-effect of Object.<init> GC registration.
       return null;
     }
 
-    NonTrivialInstanceInitializerInfo.Builder builder = NonTrivialInstanceInitializerInfo.builder();
     Value receiver = code.getThis();
     boolean hasCatchHandler = false;
     for (BasicBlock block : code.blocks) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
new file mode 100644
index 0000000..54a50fe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
@@ -0,0 +1,34 @@
+// 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.field;
+
+import com.android.tools.r8.graph.DexEncodedField;
+
+/**
+ * Represents that no information is known about the way a constructor initializes the instance
+ * fields of the newly created instance.
+ */
+public class EmptyInstanceFieldInitializationInfoCollection
+    extends InstanceFieldInitializationInfoCollection {
+
+  private static final EmptyInstanceFieldInitializationInfoCollection INSTANCE =
+      new EmptyInstanceFieldInitializationInfoCollection();
+
+  private EmptyInstanceFieldInitializationInfoCollection() {}
+
+  public static EmptyInstanceFieldInitializationInfoCollection getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public InstanceFieldInitializationInfo get(DexEncodedField field) {
+    return UnknownInstanceFieldInitializationInfo.getInstance();
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
new file mode 100644
index 0000000..c56c562
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldArgumentInitializationInfo.java
@@ -0,0 +1,33 @@
+// 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.field;
+
+/**
+ * Used to represent that a constructor initializes an instance field on the newly created instance
+ * with argument number {@link #argumentIndex} from the constructor's argument list.
+ */
+public class InstanceFieldArgumentInitializationInfo extends InstanceFieldInitializationInfo {
+
+  private final int argumentIndex;
+
+  /** Intentionally package private, use {@link InstanceFieldInitializationInfoFactory} instead. */
+  InstanceFieldArgumentInitializationInfo(int argumentIndex) {
+    this.argumentIndex = argumentIndex;
+  }
+
+  public int getArgumentIndex() {
+    return argumentIndex;
+  }
+
+  @Override
+  public boolean isArgumentInitializationInfo() {
+    return true;
+  }
+
+  @Override
+  public InstanceFieldArgumentInitializationInfo asArgumentInitializationInfo() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java
new file mode 100644
index 0000000..312d7a0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfo.java
@@ -0,0 +1,27 @@
+// 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.field;
+
+/**
+ * Information about the way a constructor initializes an instance field on the newly created
+ * instance.
+ *
+ * <p>For example, this can be used to represent that a constructor always initializes a particular
+ * instance field with a constant, or with an argument from the constructor's argument list.
+ */
+public abstract class InstanceFieldInitializationInfo {
+
+  public boolean isArgumentInitializationInfo() {
+    return false;
+  }
+
+  public InstanceFieldArgumentInitializationInfo asArgumentInitializationInfo() {
+    return null;
+  }
+
+  public boolean isUnknown() {
+    return false;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
new file mode 100644
index 0000000..4912d46
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
@@ -0,0 +1,46 @@
+// 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.field;
+
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * A mapping from instance fields of a class to information about how a particular constructor
+ * initializes these instance fields.
+ *
+ * <p>Returns {@link UnknownInstanceFieldInitializationInfo} if no information is known about the
+ * initialization of a given instance field.
+ */
+public abstract class InstanceFieldInitializationInfoCollection {
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public abstract InstanceFieldInitializationInfo get(DexEncodedField field);
+
+  public abstract boolean isEmpty();
+
+  public static class Builder {
+
+    Map<DexField, InstanceFieldInitializationInfo> infos = new IdentityHashMap<>();
+
+    public void recordInitializationInfo(
+        DexEncodedField field, InstanceFieldInitializationInfo info) {
+      assert !infos.containsKey(field.field);
+      infos.put(field.field, info);
+    }
+
+    public InstanceFieldInitializationInfoCollection build() {
+      if (infos.isEmpty()) {
+        return EmptyInstanceFieldInitializationInfoCollection.getInstance();
+      }
+      return new NonTrivialInstanceFieldInitializationInfoCollection(infos);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoFactory.java
new file mode 100644
index 0000000..42209b4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoFactory.java
@@ -0,0 +1,19 @@
+// 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.field;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+public class InstanceFieldInitializationInfoFactory {
+
+  private ConcurrentHashMap<Integer, InstanceFieldArgumentInitializationInfo>
+      argumentInitializationInfos = new ConcurrentHashMap<>();
+
+  public InstanceFieldArgumentInitializationInfo createArgumentInitializationInfo(
+      int argumentIndex) {
+    return argumentInitializationInfos.computeIfAbsent(
+        argumentIndex, InstanceFieldArgumentInitializationInfo::new);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
new file mode 100644
index 0000000..bdaaf78
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
@@ -0,0 +1,33 @@
+// 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.field;
+
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import java.util.Map;
+
+/** See {@link InstanceFieldArgumentInitializationInfo}. */
+public class NonTrivialInstanceFieldInitializationInfoCollection
+    extends InstanceFieldInitializationInfoCollection {
+
+  private final Map<DexField, InstanceFieldInitializationInfo> infos;
+
+  NonTrivialInstanceFieldInitializationInfoCollection(
+      Map<DexField, InstanceFieldInitializationInfo> infos) {
+    assert !infos.isEmpty();
+    assert infos.values().stream().noneMatch(InstanceFieldInitializationInfo::isUnknown);
+    this.infos = infos;
+  }
+
+  @Override
+  public InstanceFieldInitializationInfo get(DexEncodedField field) {
+    return infos.getOrDefault(field.field, UnknownInstanceFieldInitializationInfo.getInstance());
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return false;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
new file mode 100644
index 0000000..66e882e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/UnknownInstanceFieldInitializationInfo.java
@@ -0,0 +1,26 @@
+// 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.field;
+
+/**
+ * Represents that no information is known about the way a particular constructor initializes an
+ * instance field of the newly created instance.
+ */
+public class UnknownInstanceFieldInitializationInfo extends InstanceFieldInitializationInfo {
+
+  private static final UnknownInstanceFieldInitializationInfo INSTANCE =
+      new UnknownInstanceFieldInitializationInfo();
+
+  private UnknownInstanceFieldInitializationInfo() {}
+
+  public static UnknownInstanceFieldInitializationInfo getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public boolean isUnknown() {
+    return true;
+  }
+}
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 ef3ca08..20aff53 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
@@ -7,6 +7,8 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
+import com.android.tools.r8.ir.optimize.info.field.EmptyInstanceFieldInitializationInfoCollection;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
 
 public class DefaultInstanceInitializerInfo extends InstanceInitializerInfo {
 
@@ -30,6 +32,11 @@
   }
 
   @Override
+  public InstanceFieldInitializationInfoCollection fieldInitializationInfos() {
+    return EmptyInstanceFieldInitializationInfoCollection.getInstance();
+  }
+
+  @Override
   public AbstractFieldSet readSet() {
     return UnknownFieldSet.getInstance();
   }
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 5452bce..b27888e 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
@@ -6,11 +6,14 @@
 
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
 
 public abstract class InstanceInitializerInfo {
 
   public abstract DexMethod getParent();
 
+  public abstract InstanceFieldInitializationInfoCollection fieldInitializationInfos();
+
   /**
    * Returns an abstraction of the set of fields that may be as a result of executing this
    * initializer.
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 70017cc..4e19cef 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
@@ -10,6 +10,7 @@
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
 import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
 
 public final class NonTrivialInstanceInitializerInfo extends InstanceInitializerInfo {
 
@@ -18,12 +19,18 @@
   private static final int RECEIVER_NEVER_ESCAPE_OUTSIDE_CONSTRUCTOR_CHAIN = 1 << 2;
 
   private final int data;
+  private final InstanceFieldInitializationInfoCollection fieldInitializationInfos;
   private final AbstractFieldSet readSet;
   private final DexMethod parent;
 
-  private NonTrivialInstanceInitializerInfo(int data, AbstractFieldSet readSet, DexMethod parent) {
+  private NonTrivialInstanceInitializerInfo(
+      int data,
+      InstanceFieldInitializationInfoCollection fieldInitializationInfos,
+      AbstractFieldSet readSet,
+      DexMethod parent) {
     assert verifyNoUnknownBits(data);
     this.data = data;
+    this.fieldInitializationInfos = fieldInitializationInfos;
     this.readSet = readSet;
     this.parent = parent;
   }
@@ -37,8 +44,9 @@
     return true;
   }
 
-  public static Builder builder() {
-    return new Builder();
+  public static Builder builder(
+      InstanceFieldInitializationInfoCollection instanceFieldInitializationInfos) {
+    return new Builder(instanceFieldInitializationInfos);
   }
 
   @Override
@@ -47,6 +55,11 @@
   }
 
   @Override
+  public InstanceFieldInitializationInfoCollection fieldInitializationInfos() {
+    return fieldInitializationInfos;
+  }
+
+  @Override
   public AbstractFieldSet readSet() {
     return readSet;
   }
@@ -68,6 +81,8 @@
 
   public static class Builder {
 
+    private final InstanceFieldInitializationInfoCollection instanceFieldInitializationInfos;
+
     private int data =
         INSTANCE_FIELD_INITIALIZATION_INDEPENDENT_OF_ENVIRONMENT
             | NO_OTHER_SIDE_EFFECTS_THAN_INSTANCE_FIELD_ASSIGNMENTS
@@ -75,8 +90,15 @@
     private AbstractFieldSet readSet = EmptyFieldSet.getInstance();
     private DexMethod parent;
 
+    public Builder(InstanceFieldInitializationInfoCollection instanceFieldInitializationInfos) {
+      this.instanceFieldInitializationInfos = instanceFieldInitializationInfos;
+    }
+
     private boolean isTrivial() {
-      return data == 0 && readSet.isTop() && parent == null;
+      return instanceFieldInitializationInfos.isEmpty()
+          && data == 0
+          && readSet.isTop()
+          && parent == null;
     }
 
     public Builder markFieldAsRead(DexEncodedField field) {
@@ -158,7 +180,8 @@
     public InstanceInitializerInfo build() {
       return isTrivial()
           ? DefaultInstanceInitializerInfo.getInstance()
-          : new NonTrivialInstanceInitializerInfo(data, readSet, parent);
+          : new NonTrivialInstanceInitializerInfo(
+              data, instanceFieldInitializationInfos, readSet, parent);
     }
   }
 }