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()";
+ }
+ }
+}