Account for field value propagation in -if rule evaluation

Bug: 132983111
Change-Id: I76d97b18d8a1a554b5899d38506818c81254178b
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultFieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/graph/DefaultFieldOptimizationInfo.java
new file mode 100644
index 0000000..b944a2e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DefaultFieldOptimizationInfo.java
@@ -0,0 +1,31 @@
+// 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.graph;
+
+public class DefaultFieldOptimizationInfo extends FieldOptimizationInfo {
+
+  private static final DefaultFieldOptimizationInfo INSTANCE = new DefaultFieldOptimizationInfo();
+
+  private DefaultFieldOptimizationInfo() {}
+
+  public static DefaultFieldOptimizationInfo getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public boolean valueHasBeenPropagated() {
+    return false;
+  }
+
+  @Override
+  public boolean isDefaultFieldOptimizationInfo() {
+    return true;
+  }
+
+  @Override
+  public DefaultFieldOptimizationInfo asDefaultFieldOptimizationInfo() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 50cbc41..7da40d0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -18,6 +18,8 @@
   public DexAnnotationSet annotations;
   private DexValue staticValue;
 
+  private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance();
+
   public DexEncodedField(
       DexField field,
       FieldAccessFlags accessFlags,
@@ -37,6 +39,24 @@
     return false;
   }
 
+  public FieldOptimizationInfo getOptimizationInfo() {
+    return optimizationInfo;
+  }
+
+  public synchronized MutableFieldOptimizationInfo getMutableOptimizationInfo() {
+    if (optimizationInfo.isDefaultFieldOptimizationInfo()) {
+      MutableFieldOptimizationInfo mutableOptimizationInfo = new MutableFieldOptimizationInfo();
+      optimizationInfo = mutableOptimizationInfo;
+      return mutableOptimizationInfo;
+    }
+    assert optimizationInfo.isMutableFieldOptimizationInfo();
+    return optimizationInfo.asMutableFieldOptimizationInfo();
+  }
+
+  public void setOptimizationInfo(MutableFieldOptimizationInfo info) {
+    optimizationInfo = info;
+  }
+
   @Override
   public void collectIndexedItems(
       IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index b7500f4..bfb248a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -8,7 +8,7 @@
 import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_SAME_PACKAGE;
 import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_SUBCLASS;
 import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
-import static com.android.tools.r8.graph.DexEncodedMethod.DefaultOptimizationInfoImpl.UNKNOWN_TYPE;
+import static com.android.tools.r8.graph.DexEncodedMethod.DefaultMethodOptimizationInfoImpl.UNKNOWN_TYPE;
 
 import com.android.tools.r8.OptionalBool;
 import com.android.tools.r8.cf.code.CfConstNull;
@@ -121,7 +121,8 @@
   // TODO(b/128967328): towards finer-grained inlining constraints,
   //   we need to maintain a set of states with (potentially different) contexts.
   private CompilationState compilationState = CompilationState.NOT_PROCESSED;
-  private OptimizationInfo optimizationInfo = DefaultOptimizationInfoImpl.DEFAULT_INSTANCE;
+  private MethodOptimizationInfo optimizationInfo =
+      DefaultMethodOptimizationInfoImpl.DEFAULT_INSTANCE;
   private int classFileVersion = -1;
 
   private DexEncodedMethod defaultInterfaceMethodImplementation = null;
@@ -980,8 +981,9 @@
     }
   }
 
-  public static class DefaultOptimizationInfoImpl implements OptimizationInfo {
-    public static final OptimizationInfo DEFAULT_INSTANCE = new DefaultOptimizationInfoImpl();
+  public static class DefaultMethodOptimizationInfoImpl implements MethodOptimizationInfo {
+    public static final MethodOptimizationInfo DEFAULT_INSTANCE =
+        new DefaultMethodOptimizationInfoImpl();
 
     public static Set<DexType> UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT = ImmutableSet.of();
     public static int UNKNOWN_RETURNED_ARGUMENT = -1;
@@ -1002,7 +1004,7 @@
     public static BitSet NO_NULL_PARAMETER_OR_THROW_FACTS = null;
     public static BitSet NO_NULL_PARAMETER_ON_NORMAL_EXITS_FACTS = null;
 
-    private DefaultOptimizationInfoImpl() {}
+    private DefaultMethodOptimizationInfoImpl() {}
 
     @Override
     public TypeLatticeElement getDynamicReturnType() {
@@ -1129,44 +1131,47 @@
     }
 
     @Override
-    public UpdatableOptimizationInfo mutableCopy() {
-      return new OptimizationInfoImpl();
+    public UpdatableMethodOptimizationInfo mutableCopy() {
+      return new MethodOptimizationInfoImpl();
     }
   }
 
-  public static class OptimizationInfoImpl implements UpdatableOptimizationInfo {
+  public static class MethodOptimizationInfoImpl implements UpdatableMethodOptimizationInfo {
 
     private Set<DexType> initializedClassesOnNormalExit =
-        DefaultOptimizationInfoImpl.UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT;
-    private int returnedArgument = DefaultOptimizationInfoImpl.UNKNOWN_RETURNED_ARGUMENT;
-    private boolean mayHaveSideEffects = DefaultOptimizationInfoImpl.UNKNOWN_MAY_HAVE_SIDE_EFFECTS;
-    private boolean neverReturnsNull = DefaultOptimizationInfoImpl.UNKNOWN_NEVER_RETURNS_NULL;
+        DefaultMethodOptimizationInfoImpl.UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT;
+    private int returnedArgument = DefaultMethodOptimizationInfoImpl.UNKNOWN_RETURNED_ARGUMENT;
+    private boolean mayHaveSideEffects =
+        DefaultMethodOptimizationInfoImpl.UNKNOWN_MAY_HAVE_SIDE_EFFECTS;
+    private boolean neverReturnsNull = DefaultMethodOptimizationInfoImpl.UNKNOWN_NEVER_RETURNS_NULL;
     private boolean neverReturnsNormally =
-        DefaultOptimizationInfoImpl.UNKNOWN_NEVER_RETURNS_NORMALLY;
-    private boolean returnsConstantNumber = DefaultOptimizationInfoImpl.UNKNOWN_RETURNS_CONSTANT;
+        DefaultMethodOptimizationInfoImpl.UNKNOWN_NEVER_RETURNS_NORMALLY;
+    private boolean returnsConstantNumber =
+        DefaultMethodOptimizationInfoImpl.UNKNOWN_RETURNS_CONSTANT;
     private long returnedConstantNumber =
-        DefaultOptimizationInfoImpl.UNKNOWN_RETURNED_CONSTANT_NUMBER;
-    private boolean returnsConstantString = DefaultOptimizationInfoImpl.UNKNOWN_RETURNS_CONSTANT;
+        DefaultMethodOptimizationInfoImpl.UNKNOWN_RETURNED_CONSTANT_NUMBER;
+    private boolean returnsConstantString =
+        DefaultMethodOptimizationInfoImpl.UNKNOWN_RETURNS_CONSTANT;
     private DexString returnedConstantString =
-        DefaultOptimizationInfoImpl.UNKNOWN_RETURNED_CONSTANT_STRING;
+        DefaultMethodOptimizationInfoImpl.UNKNOWN_RETURNED_CONSTANT_STRING;
     private TypeLatticeElement returnsObjectOfType = UNKNOWN_TYPE;
     private InlinePreference inlining = InlinePreference.Default;
     private boolean useIdentifierNameString =
-        DefaultOptimizationInfoImpl.DOES_NOT_USE_IDNETIFIER_NAME_STRING;
+        DefaultMethodOptimizationInfoImpl.DOES_NOT_USE_IDNETIFIER_NAME_STRING;
     private boolean checksNullReceiverBeforeAnySideEffect =
-        DefaultOptimizationInfoImpl.UNKNOWN_CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT;
+        DefaultMethodOptimizationInfoImpl.UNKNOWN_CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT;
     private boolean triggersClassInitBeforeAnySideEffect =
-        DefaultOptimizationInfoImpl.UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT;
+        DefaultMethodOptimizationInfoImpl.UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT;
     // Stores information about instance methods and constructors for
     // class inliner, null value indicates that the method is not eligible.
     private ClassInlinerEligibility classInlinerEligibility =
-        DefaultOptimizationInfoImpl.UNKNOWN_CLASS_INLINER_ELIGIBILITY;
+        DefaultMethodOptimizationInfoImpl.UNKNOWN_CLASS_INLINER_ELIGIBILITY;
     private TrivialInitializer trivialInitializerInfo =
-        DefaultOptimizationInfoImpl.UNKNOWN_TRIVIAL_INITIALIZER;
+        DefaultMethodOptimizationInfoImpl.UNKNOWN_TRIVIAL_INITIALIZER;
     private boolean initializerEnablingJavaAssertions =
-        DefaultOptimizationInfoImpl.UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS;
+        DefaultMethodOptimizationInfoImpl.UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS;
     private ParameterUsagesInfo parametersUsages =
-        DefaultOptimizationInfoImpl.UNKNOWN_PARAMETER_USAGE_INFO;
+        DefaultMethodOptimizationInfoImpl.UNKNOWN_PARAMETER_USAGE_INFO;
     // Stores information about nullability hint per parameter. If set, that means, the method
     // somehow (e.g., null check, such as arg != null, or using checkParameterIsNotNull) ensures
     // the corresponding parameter is not null, or throws NPE before any other side effects.
@@ -1185,11 +1190,11 @@
     private BitSet nonNullParamOnNormalExits = null;
     private boolean reachabilitySensitive = false;
 
-    private OptimizationInfoImpl() {
+    private MethodOptimizationInfoImpl() {
       // Intentionally left empty, just use the default values.
     }
 
-    private OptimizationInfoImpl(OptimizationInfoImpl template) {
+    private MethodOptimizationInfoImpl(MethodOptimizationInfoImpl template) {
       returnedArgument = template.returnedArgument;
       neverReturnsNull = template.neverReturnsNull;
       neverReturnsNormally = template.neverReturnsNormally;
@@ -1456,26 +1461,26 @@
     }
 
     @Override
-    public UpdatableOptimizationInfo mutableCopy() {
-      assert this != DefaultOptimizationInfoImpl.DEFAULT_INSTANCE;
-      return new OptimizationInfoImpl(this);
+    public UpdatableMethodOptimizationInfo mutableCopy() {
+      assert this != DefaultMethodOptimizationInfoImpl.DEFAULT_INSTANCE;
+      return new MethodOptimizationInfoImpl(this);
     }
   }
 
-  public OptimizationInfo getOptimizationInfo() {
+  public MethodOptimizationInfo getOptimizationInfo() {
     checkIfObsolete();
     return optimizationInfo;
   }
 
-  public synchronized UpdatableOptimizationInfo getMutableOptimizationInfo() {
+  public synchronized UpdatableMethodOptimizationInfo getMutableOptimizationInfo() {
     checkIfObsolete();
-    if (optimizationInfo == DefaultOptimizationInfoImpl.DEFAULT_INSTANCE) {
+    if (optimizationInfo == DefaultMethodOptimizationInfoImpl.DEFAULT_INSTANCE) {
       optimizationInfo = optimizationInfo.mutableCopy();
     }
-    return (UpdatableOptimizationInfo) optimizationInfo;
+    return (UpdatableMethodOptimizationInfo) optimizationInfo;
   }
 
-  public void setOptimizationInfo(UpdatableOptimizationInfo info) {
+  public void setOptimizationInfo(UpdatableMethodOptimizationInfo info) {
     checkIfObsolete();
     optimizationInfo = info;
   }
@@ -1503,7 +1508,7 @@
     private ParameterAnnotationsList parameterAnnotations;
     private Code code;
     private CompilationState compilationState;
-    private OptimizationInfo optimizationInfo;
+    private MethodOptimizationInfo optimizationInfo;
     private final int classFileVersion;
 
     private Builder(DexEncodedMethod from) {
@@ -1574,7 +1579,7 @@
     }
 
     public Builder unsetOptimizationInfo() {
-      optimizationInfo = DefaultOptimizationInfoImpl.DEFAULT_INSTANCE;
+      optimizationInfo = DefaultMethodOptimizationInfoImpl.DEFAULT_INSTANCE;
       return this;
     }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index 4f12fda..782f04a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -9,10 +9,10 @@
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
+import com.android.tools.r8.ir.code.ConstInstruction;
 import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.ConstString;
 import com.android.tools.r8.ir.code.DexItemBasedConstString;
-import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.optimize.ReflectionOptimizer.ClassNameComputationInfo;
 import com.android.tools.r8.utils.EncodedValueUtils;
@@ -138,7 +138,7 @@
   public abstract Object getBoxedValue();
 
   // Returns a const instruction for the non default value.
-  public Instruction asConstInstruction(
+  public ConstInstruction asConstInstruction(
       boolean hasClassInitializer, Value dest, InternalOptions options) {
     return null;
   }
@@ -214,7 +214,7 @@
     }
 
     @Override
-    public Instruction asConstInstruction(
+    public ConstInstruction asConstInstruction(
         boolean hasClassInitializer, Value dest, InternalOptions options) {
       return null;
     }
@@ -302,7 +302,7 @@
     }
 
     @Override
-    public Instruction asConstInstruction(
+    public ConstInstruction asConstInstruction(
         boolean hasClassInitializer, Value dest, InternalOptions options) {
       return (this == DEFAULT && hasClassInitializer) ? null : new ConstNumber(dest, value);
     }
@@ -359,7 +359,7 @@
     }
 
     @Override
-    public Instruction asConstInstruction(
+    public ConstInstruction asConstInstruction(
         boolean hasClassInitializer, Value dest, InternalOptions options) {
       return (this == DEFAULT && hasClassInitializer) ? null : new ConstNumber(dest, value);
     }
@@ -420,7 +420,7 @@
     }
 
     @Override
-    public Instruction asConstInstruction(
+    public ConstInstruction asConstInstruction(
         boolean hasClassInitializer, Value dest, InternalOptions options) {
       return (this == DEFAULT && hasClassInitializer) ? null : new ConstNumber(dest, value);
     }
@@ -477,7 +477,7 @@
     }
 
     @Override
-    public Instruction asConstInstruction(
+    public ConstInstruction asConstInstruction(
         boolean hasClassInitializer, Value dest, InternalOptions options) {
       return (this == DEFAULT && hasClassInitializer) ? null : new ConstNumber(dest, value);
     }
@@ -534,7 +534,7 @@
     }
 
     @Override
-    public Instruction asConstInstruction(
+    public ConstInstruction asConstInstruction(
         boolean hasClassInitializer, Value dest, InternalOptions options) {
       return (this == DEFAULT && hasClassInitializer) ? null : new ConstNumber(dest, value);
     }
@@ -739,7 +739,7 @@
     }
 
     @Override
-    public Instruction asConstInstruction(
+    public ConstInstruction asConstInstruction(
         boolean hasClassInitializer, Value dest, InternalOptions options) {
       ConstString instruction =
           new ConstString(dest, value, ThrowingInfo.defaultForConstString(options));
@@ -784,7 +784,7 @@
     }
 
     @Override
-    public Instruction asConstInstruction(
+    public ConstInstruction asConstInstruction(
         boolean hasClassInitializer, Value dest, InternalOptions options) {
       DexItemBasedConstString instruction =
           new DexItemBasedConstString(
@@ -1122,7 +1122,7 @@
     }
 
     @Override
-    public Instruction asConstInstruction(
+    public ConstInstruction asConstInstruction(
         boolean hasClassInitializer, Value dest, InternalOptions options) {
       return (this == DEFAULT && hasClassInitializer) ? null : new ConstNumber(dest, value ? 1 : 0);
     }
diff --git a/src/main/java/com/android/tools/r8/graph/FieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/graph/FieldOptimizationInfo.java
new file mode 100644
index 0000000..a8c6eac
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/FieldOptimizationInfo.java
@@ -0,0 +1,26 @@
+// 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.graph;
+
+public abstract class FieldOptimizationInfo {
+
+  public abstract boolean valueHasBeenPropagated();
+
+  public boolean isDefaultFieldOptimizationInfo() {
+    return false;
+  }
+
+  public DefaultFieldOptimizationInfo asDefaultFieldOptimizationInfo() {
+    return null;
+  }
+
+  public boolean isMutableFieldOptimizationInfo() {
+    return false;
+  }
+
+  public MutableFieldOptimizationInfo asMutableFieldOptimizationInfo() {
+    return null;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java b/src/main/java/com/android/tools/r8/graph/MethodOptimizationInfo.java
similarity index 94%
rename from src/main/java/com/android/tools/r8/graph/OptimizationInfo.java
rename to src/main/java/com/android/tools/r8/graph/MethodOptimizationInfo.java
index ca2361d..977160c 100644
--- a/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodOptimizationInfo.java
@@ -11,7 +11,7 @@
 import java.util.BitSet;
 import java.util.Set;
 
-public interface OptimizationInfo {
+public interface MethodOptimizationInfo {
 
   enum InlinePreference {
     NeverInline,
@@ -67,5 +67,5 @@
 
   boolean mayHaveSideEffects();
 
-  UpdatableOptimizationInfo mutableCopy();
+  UpdatableMethodOptimizationInfo mutableCopy();
 }
diff --git a/src/main/java/com/android/tools/r8/graph/MutableFieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/graph/MutableFieldOptimizationInfo.java
new file mode 100644
index 0000000..814cad6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/MutableFieldOptimizationInfo.java
@@ -0,0 +1,36 @@
+// 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.graph;
+
+/**
+ * Optimization info for fields.
+ *
+ * <p>NOTE: Unlike the optimization info for methods, the field optimization info is currently being
+ * updated directly, meaning that updates may become visible to concurrently processed methods in
+ * the {@link com.android.tools.r8.ir.conversion.IRConverter}.
+ */
+public class MutableFieldOptimizationInfo extends FieldOptimizationInfo {
+
+  private boolean valueHasBeenPropagated = false;
+
+  public void markAsPropagated() {
+    valueHasBeenPropagated = true;
+  }
+
+  @Override
+  public boolean valueHasBeenPropagated() {
+    return valueHasBeenPropagated;
+  }
+
+  @Override
+  public boolean isMutableFieldOptimizationInfo() {
+    return true;
+  }
+
+  @Override
+  public MutableFieldOptimizationInfo asMutableFieldOptimizationInfo() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/UpdatableOptimizationInfo.java b/src/main/java/com/android/tools/r8/graph/UpdatableMethodOptimizationInfo.java
similarity index 94%
rename from src/main/java/com/android/tools/r8/graph/UpdatableOptimizationInfo.java
rename to src/main/java/com/android/tools/r8/graph/UpdatableMethodOptimizationInfo.java
index ccb0a63..78b10cd 100644
--- a/src/main/java/com/android/tools/r8/graph/UpdatableOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/UpdatableMethodOptimizationInfo.java
@@ -10,7 +10,7 @@
 import java.util.BitSet;
 import java.util.Set;
 
-public interface UpdatableOptimizationInfo extends OptimizationInfo {
+public interface UpdatableMethodOptimizationInfo extends MethodOptimizationInfo {
 
   void markInitializesClassesOnNormalExit(Set<DexType> initializedClasses);
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDelayed.java
index 091bb29..ac143e3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OptimizationFeedbackDelayed.java
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ParameterUsagesInfo;
-import com.android.tools.r8.graph.UpdatableOptimizationInfo;
+import com.android.tools.r8.graph.UpdatableMethodOptimizationInfo;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.utils.IteratorUtils;
@@ -22,13 +22,13 @@
 public class OptimizationFeedbackDelayed implements OptimizationFeedback {
 
   // Caching of updated optimization info and processed status.
-  private final Map<DexEncodedMethod, UpdatableOptimizationInfo> optimizationInfos =
+  private final Map<DexEncodedMethod, UpdatableMethodOptimizationInfo> optimizationInfos =
       new IdentityHashMap<>();
   private final Map<DexEncodedMethod, ConstraintWithTarget> processed = new IdentityHashMap<>();
 
-  private synchronized UpdatableOptimizationInfo getOptimizationInfoForUpdating(
+  private synchronized UpdatableMethodOptimizationInfo getOptimizationInfoForUpdating(
       DexEncodedMethod method) {
-    UpdatableOptimizationInfo info = optimizationInfos.get(method);
+    UpdatableMethodOptimizationInfo info = optimizationInfos.get(method);
     if (info != null) {
       return info;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
index d90d213..97e0417 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.OptimizationInfo;
+import com.android.tools.r8.graph.MethodOptimizationInfo;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.Assume;
 import com.android.tools.r8.ir.code.Assume.DynamicTypeAssumption;
@@ -73,7 +73,7 @@
           continue;
         }
 
-        OptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
+        MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
         if (optimizationInfo.returnsArgument()) {
           continue;
         }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index dd1064c..00a1acc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -35,6 +35,7 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.ProguardMemberRule;
+import com.android.tools.r8.shaking.ProguardMemberRuleReturnValue;
 import com.google.common.collect.Sets;
 import java.util.ListIterator;
 import java.util.Set;
@@ -109,34 +110,40 @@
 
   private Instruction constantReplacementFromProguardRule(
       ProguardMemberRule rule, IRCode code, Instruction instruction) {
-    // Check if this value can be assumed constant.
-    Instruction replacement = null;
-    TypeLatticeElement typeLattice = instruction.outValue().getTypeLattice();
-    if (rule != null && rule.hasReturnValue() && rule.getReturnValue().isSingleValue()) {
-      replacement = createConstNumberReplacement(
-          code, rule.getReturnValue().getSingleValue(), typeLattice, instruction.getLocalInfo());
+    if (rule == null || !rule.hasReturnValue()) {
+      return null;
     }
-    if (replacement == null
-        && rule != null
-        && rule.hasReturnValue()
-        && rule.getReturnValue().isField()) {
-      DexField field = rule.getReturnValue().getField();
+
+    ProguardMemberRuleReturnValue returnValueRule = rule.getReturnValue();
+    TypeLatticeElement typeLattice = instruction.outValue().getTypeLattice();
+
+    // Check if this value can be assumed constant.
+    if (returnValueRule.isSingleValue()) {
+      return createConstNumberReplacement(
+          code, returnValueRule.getSingleValue(), typeLattice, instruction.getLocalInfo());
+    }
+
+    if (returnValueRule.isField()) {
+      DexField field = returnValueRule.getField();
       assert typeLattice
           == TypeLatticeElement.fromDexType(field.type, Nullability.maybeNull(), appView);
+
       DexEncodedField staticField = appView.appInfo().lookupStaticTarget(field.holder, field);
-      if (staticField != null) {
-        Value value = code.createValue(typeLattice, instruction.getLocalInfo());
-        replacement =
-            staticField.getStaticValue().asConstInstruction(false, value, appView.options());
-        if (replacement.isDexItemBasedConstString()) {
-          code.method.getMutableOptimizationInfo().markUseIdentifierNameString();
-        }
-      } else {
+      if (staticField == null) {
         throw new CompilationError(field.holder.toSourceString() + "." + field.name.toString()
             + " used in assumevalues rule does not exist.");
       }
+
+      Value value = code.createValue(typeLattice, instruction.getLocalInfo());
+      ConstInstruction replacement =
+          staticField.getStaticValue().asConstInstruction(false, value, appView.options());
+      if (replacement.isDexItemBasedConstString()) {
+        code.method.getMutableOptimizationInfo().markUseIdentifierNameString();
+      }
+      return replacement;
     }
-    return replacement;
+
+    return null;
   }
 
   private static ConstNumber createConstNumberReplacement(
@@ -306,6 +313,7 @@
       if (replacement.isDexItemBasedConstString()) {
         code.method.getMutableOptimizationInfo().markUseIdentifierNameString();
       }
+      target.getMutableOptimizationInfo().markAsPropagated();
       return;
     }
     ProguardMemberRuleLookup lookup = lookupMemberRule(target);
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 c09a2da..6110b68 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
@@ -16,7 +16,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.OptimizationInfo;
+import com.android.tools.r8.graph.MethodOptimizationInfo;
 import com.android.tools.r8.graph.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -667,7 +667,7 @@
       return new InliningInfo(singleTarget, eligibleClass);
     }
 
-    OptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
+    MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
 
     ClassInlinerEligibility eligibility = optimizationInfo.getClassInlinerEligibility();
     if (eligibility == null) {
@@ -743,7 +743,7 @@
       }
     }
 
-    OptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
+    MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
 
     // Go through all arguments, see if all usages of eligibleInstance are good.
     if (!isEligibleParameterUsages(invoke, arguments, singleTarget, defaultOracle)) {
@@ -778,7 +778,7 @@
       }
 
       // Have parameter usage info?
-      OptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
+      MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
       ParameterUsage parameterUsage = optimizationInfo.getParameterUsages(argIndex);
       if (!isEligibleParameterUsage(parameterUsage, invoke, defaultOracle)) {
         return false;
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index b0c7777..8ae75ff 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1672,9 +1672,10 @@
               consequentSetBuilder.getIfRuleEvaluator(
                   liveFields.getItems(),
                   liveMethods.getItems(),
+                  liveTypes,
                   targetedMethods.getItems(),
                   executorService);
-          ConsequentRootSet consequentRootSet = ifRuleEvaluator.run(liveTypes);
+          ConsequentRootSet consequentRootSet = ifRuleEvaluator.run();
           // TODO(b/132600955): This modifies the root set. Should the consequent be persistent?
           rootSet.addConsequentRootSet(consequentRootSet);
           enqueueRootItems(consequentRootSet.noShrinking);
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 9664821..648d8d1 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -295,15 +295,19 @@
   IfRuleEvaluator getIfRuleEvaluator(
       Set<DexEncodedField> liveFields,
       Set<DexEncodedMethod> liveMethods,
+      Set<DexType> liveTypes,
       Set<DexEncodedMethod> targetedMethods,
       ExecutorService executorService) {
-    return new IfRuleEvaluator(liveFields, liveMethods, targetedMethods, executorService);
+    return new IfRuleEvaluator(
+        liveFields, liveMethods, liveTypes, targetedMethods, executorService);
   }
 
   class IfRuleEvaluator {
 
     private final Set<DexEncodedField> liveFields;
     private final Set<DexEncodedMethod> liveMethods;
+    private final Set<DexType> liveTypes;
+
     private final Set<DexEncodedMethod> targetedMethods;
 
     private final ExecutorService executorService;
@@ -313,15 +317,17 @@
     public IfRuleEvaluator(
         Set<DexEncodedField> liveFields,
         Set<DexEncodedMethod> liveMethods,
+        Set<DexType> liveTypes,
         Set<DexEncodedMethod> targetedMethods,
         ExecutorService executorService) {
       this.liveFields = liveFields;
       this.liveMethods = liveMethods;
+      this.liveTypes = liveTypes;
       this.targetedMethods = targetedMethods;
       this.executorService = executorService;
     }
 
-    public ConsequentRootSet run(Set<DexType> liveTypes) throws ExecutionException {
+    public ConsequentRootSet run() throws ExecutionException {
       application.timing.begin("Find consequent items for -if rules...");
       try {
         if (rules != null) {
@@ -331,9 +337,8 @@
             // Depending on which types that trigger the -if rule, the application of the subsequent
             // -keep rule may vary (due to back references). So, we need to try all pairs of -if
             // rule and live types.
-            for (DexType type : liveTypes) {
-              DexClass clazz = appView.definitionFor(type);
-              if (clazz == null) {
+            for (DexProgramClass clazz : appView.appInfo().classes()) {
+              if (!isEffectivelyLive(clazz)) {
                 continue;
               }
 
@@ -342,7 +347,9 @@
 
               // Check if one of the types that have been merged into `clazz` satisfies the if-rule.
               if (options.enableVerticalClassMerging && appView.verticallyMergedClasses() != null) {
-                for (DexType sourceType : appView.verticallyMergedClasses().getSourcesFor(type)) {
+                Iterable<DexType> sources =
+                    appView.verticallyMergedClasses().getSourcesFor(clazz.type);
+                for (DexType sourceType : sources) {
                   // Note that, although `sourceType` has been merged into `type`, the dex class for
                   // `sourceType` is still available until the second round of tree shaking. This
                   // way
@@ -369,6 +376,20 @@
           dependentKeepClassCompatRule);
     }
 
+    private boolean isEffectivelyLive(DexProgramClass clazz) {
+      // A type is effectively live if it is truly live, or if the value of one of its fields has
+      // been inlined by the member value propagation.
+      if (liveTypes.contains(clazz.type)) {
+        return true;
+      }
+      for (DexEncodedField field : clazz.fields()) {
+        if (field.getOptimizationInfo().valueHasBeenPropagated()) {
+          return true;
+        }
+      }
+      return false;
+    }
+
     /**
      * Determines if `sourceClass` satisfies the given if-rule. If `sourceClass` has not been merged
      * into another class, then `targetClass` is the same as `sourceClass`. Otherwise, `targetClass`
@@ -406,7 +427,7 @@
           filteredMembers,
           targetClass.fields(
               f ->
-                  liveFields.contains(f)
+                  (liveFields.contains(f) || f.getOptimizationInfo().valueHasBeenPropagated())
                       && appView.graphLense().getOriginalFieldSignature(f.field).holder
                           == sourceClass.type));
       Iterables.addAll(
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithFieldValuePropagationTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithFieldValuePropagationTest.java
new file mode 100644
index 0000000..908297c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithFieldValuePropagationTest.java
@@ -0,0 +1,93 @@
+// 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.shaking.ifrule.membervaluepropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class IfWithFieldValuePropagationTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().build();
+  }
+
+  public IfWithFieldValuePropagationTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(TestClass.class, R.class, Layout.class)
+        .addKeepMainRule(TestClass.class)
+        .addKeepRules(
+            "-if class " + R.class.getTypeName() + " {",
+            "  static int ID;",
+            "}",
+            "-keep class " + Layout.class.getTypeName())
+        .addLibraryClasses(Library.class)
+        .addLibraryFiles(runtimeJar(parameters))
+        .setMinApi(parameters.getRuntime())
+        .compile()
+        .inspect(this::verifyOutput)
+        .addRunClasspathFiles(
+            testForR8(parameters.getBackend())
+                .addProgramClasses(Library.class)
+                .addKeepAllClassesRule()
+                .setMinApi(parameters.getRuntime())
+                .compile()
+                .writeToZip())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Layout.toString()");
+  }
+
+  private void verifyOutput(CodeInspector inspector) {
+    // R.ID has been inlined.
+    assertThat(inspector.clazz(R.class), not(isPresent()));
+
+    // Layout is kept by the conditional rule.
+    assertThat(inspector.clazz(Layout.class), isPresent());
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.out.println(Library.getViewById(R.ID));
+    }
+  }
+
+  static class Library {
+
+    public static Object getViewById(int viewId) {
+      return new Layout();
+    }
+  }
+
+  static class R {
+
+    public static int ID = 42;
+  }
+
+  static class Layout {
+
+    @Override
+    public String toString() {
+      return "Layout.toString()";
+    }
+  }
+}