Reland "Move Array length to ObjectState"
This reverts commit 17687b2579af5bc68199a815183a66efdc31a6d6.
Bug: 202365761
Change-Id: Iff5816b55a38b1ab0c824cbc9e024b2f05a42edd
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index 30060d9..0303ba7 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueNull;
@@ -25,6 +24,7 @@
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.analysis.value.objectstate.EnumValuesObjectState;
import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
+import com.android.tools.r8.ir.analysis.value.objectstate.ObjectStateAnalysis;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
@@ -35,8 +35,6 @@
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.InstanceFieldArgumentInitializationInfo;
-import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Timing;
import java.util.IdentityHashMap;
@@ -240,7 +238,7 @@
// This implicitely answers null if the value could not get computed.
if (valuesValue.isSingleFieldValue()) {
SingleFieldValue fieldValue = valuesValue.asSingleFieldValue();
- if (fieldValue.getState().isEnumValuesObjectState()) {
+ if (fieldValue.getObjectState().isEnumValuesObjectState()) {
return fieldValue;
}
}
@@ -439,62 +437,12 @@
}
private ObjectState computeObjectState(Value value) {
- assert !value.hasAliasedValue();
- if (!value.isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
- return ObjectState.empty();
- }
-
- NewInstance newInstance = value.definition.asNewInstance();
- InvokeDirect uniqueConstructorInvoke =
- newInstance.getUniqueConstructorInvoke(appView.dexItemFactory());
- if (uniqueConstructorInvoke == null) {
- return ObjectState.empty();
- }
-
- DexClassAndMethod singleTarget = uniqueConstructorInvoke.lookupSingleTarget(appView, context);
- if (singleTarget == null) {
- return ObjectState.empty();
- }
-
- InstanceFieldInitializationInfoCollection initializationInfos =
- singleTarget
- .getDefinition()
- .getOptimizationInfo()
- .getInstanceInitializerInfo(uniqueConstructorInvoke)
- .fieldInitializationInfos();
- if (initializationInfos.isEmpty()) {
- return ObjectState.empty();
- }
-
- ObjectState.Builder builder = ObjectState.builder();
- initializationInfos.forEach(
- appView,
- (field, initializationInfo) -> {
- // If the instance field is not written only in the instance initializer, then we can't
- // conclude that this field will have a constant value.
- //
- // We have special handling for library fields that satisfy the property that they are
- // only written in their corresponding instance initializers. This is needed since we
- // don't analyze these instance initializers in the Enqueuer, as they are in the library.
- if (!appView.appInfo().isInstanceFieldWrittenOnlyInInstanceInitializers(field)
- && !appView.dexItemFactory().enumMembers.isNameOrOrdinalField(field.getReference())) {
- return;
- }
- if (initializationInfo.isArgumentInitializationInfo()) {
- InstanceFieldArgumentInitializationInfo argumentInitializationInfo =
- initializationInfo.asArgumentInitializationInfo();
- Value argument =
- uniqueConstructorInvoke.getArgument(argumentInitializationInfo.getArgumentIndex());
- builder.recordFieldHasValue(field, argument.getAbstractValue(appView, context));
- } else if (initializationInfo.isSingleValue()) {
- builder.recordFieldHasValue(field, initializationInfo.asSingleValue());
- }
- });
- return builder.build();
+ // TODO(b/204159267): Move this logic into Instruction#getAbstractValue in NewInstance.
+ return ObjectStateAnalysis.computeObjectState(value, appView, context);
}
private boolean isEnumValuesArray(Value value) {
SingleFieldValue singleFieldValue = computeSingleEnumFieldValueForValuesArray(value);
- return singleFieldValue != null && singleFieldValue.getState().isEnumValuesObjectState();
+ return singleFieldValue != null && singleFieldValue.getObjectState().isEnumValuesObjectState();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
index 0b9a6cd..e57052f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
@@ -59,17 +59,18 @@
DexEncodedField staticField, AbstractValue value, DexItemFactory factory) {
if (factory.enumMembers.isValuesFieldCandidate(staticField, staticField.getHolderType())) {
if (value.isSingleFieldValue()
- && value.asSingleFieldValue().getState().isEnumValuesObjectState()) {
+ && value.asSingleFieldValue().getObjectState().isEnumValuesObjectState()) {
assert valuesCandidateAbstractValue == null
|| valuesCandidateAbstractValue.equals(value);
valuesCandidateAbstractValue = value;
enumObjectStateBuilder.put(
- staticField.getReference(), value.asSingleFieldValue().getState());
+ staticField.getReference(), value.asSingleFieldValue().getObjectState());
}
} else if (factory.enumMembers.isEnumField(staticField, staticField.getHolderType())) {
- if (value.isSingleFieldValue() && !value.asSingleFieldValue().getState().isEmpty()) {
+ if (value.isSingleFieldValue()
+ && !value.asSingleFieldValue().getObjectState().isEmpty()) {
enumObjectStateBuilder.put(
- staticField.getReference(), value.asSingleFieldValue().getState());
+ staticField.getReference(), value.asSingleFieldValue().getObjectState());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
index d69ca26..854ea6d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
public abstract class AbstractValue {
@@ -57,14 +58,32 @@
return null;
}
- public boolean isKnownLengthArrayValue() {
+ public boolean hasObjectState() {
return false;
}
- public KnownLengthArrayValue asKnownLengthArrayValue() {
+ public ObjectState getObjectState() {
+ throw new UnsupportedOperationException(
+ "Abstract value " + this + " does not have any object state.");
+ }
+
+ public boolean isStatefulObjectValue() {
+ return false;
+ }
+
+ public StatefulObjectValue asStatefulObjectValue() {
return null;
}
+ public boolean hasKnownArrayLength() {
+ return false;
+ }
+
+ public int getKnownArrayLength() {
+ throw new UnsupportedOperationException(
+ "Abstract value " + this + " does not have a known array length.");
+ }
+
public boolean isSingleConstValue() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java
index e1cdb6c..bc1a554 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.value.objectstate.KnownLengthArrayState;
import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import java.util.concurrent.ConcurrentHashMap;
@@ -19,15 +20,15 @@
private ConcurrentHashMap<Long, SingleNumberValue> singleNumberValues = new ConcurrentHashMap<>();
private ConcurrentHashMap<DexString, SingleStringValue> singleStringValues =
new ConcurrentHashMap<>();
- private ConcurrentHashMap<Integer, KnownLengthArrayValue> knownArrayLengthValues =
+ private ConcurrentHashMap<Integer, KnownLengthArrayState> knownArrayLengthStates =
new ConcurrentHashMap<>();
public SingleConstClassValue createSingleConstClassValue(DexType type) {
return singleConstClassValues.computeIfAbsent(type, SingleConstClassValue::new);
}
- public AbstractValue createKnownLengthArrayValue(int length) {
- return knownArrayLengthValues.computeIfAbsent(length, KnownLengthArrayValue::new);
+ public KnownLengthArrayState createKnownLengthArrayState(int length) {
+ return knownArrayLengthStates.computeIfAbsent(length, KnownLengthArrayState::new);
}
public SingleFieldValue createSingleFieldValue(DexField field, ObjectState state) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/KnownLengthArrayValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/KnownLengthArrayValue.java
deleted file mode 100644
index 9721031..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/KnownLengthArrayValue.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2021, 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.analysis.value;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-
-/** A KnownLengthArrayValue implicitly implies the value is non null. */
-public class KnownLengthArrayValue extends AbstractValue {
-
- private final int length;
-
- public KnownLengthArrayValue(int length) {
- this.length = length;
- }
-
- public int getLength() {
- return length;
- }
-
- @Override
- public boolean isKnownLengthArrayValue() {
- return true;
- }
-
- @Override
- public KnownLengthArrayValue asKnownLengthArrayValue() {
- return this;
- }
-
- @Override
- public boolean isNonTrivial() {
- return true;
- }
-
- @Override
- public AbstractValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
- return this;
- }
-
- @Override
- public boolean equals(Object o) {
- return this == o;
- }
-
- @Override
- public int hashCode() {
- return System.identityHashCode(this);
- }
-
- @Override
- public String toString() {
- return "KnownLengthArrayValue(len=" + length + ")";
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
index 71a6dbf..b499b81 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -48,7 +48,11 @@
return field.lookupOnClass(holder);
}
- public abstract ObjectState getState();
+ @Override
+ public abstract ObjectState getObjectState();
+
+ @Override
+ public abstract boolean hasObjectState();
public boolean mayHaveFinalizeMethodDirectlyOrIndirectly(AppView<AppInfoWithLiveness> appView) {
DexType fieldType = field.type;
@@ -140,6 +144,6 @@
}
}
return factory.createSingleFieldValue(
- lens.lookupField(field), getState().rewrittenWithLens(appView, lens));
+ lens.lookupField(field), getObjectState().rewrittenWithLens(appView, lens));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatefulFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatefulFieldValue.java
index d14f5a9..4191a9c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatefulFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatefulFieldValue.java
@@ -20,7 +20,22 @@
}
@Override
- public ObjectState getState() {
+ public boolean hasKnownArrayLength() {
+ return getObjectState().hasKnownArrayLength();
+ }
+
+ @Override
+ public int getKnownArrayLength() {
+ return getObjectState().getKnownArrayLength();
+ }
+
+ @Override
+ public boolean hasObjectState() {
+ return true;
+ }
+
+ @Override
+ public ObjectState getObjectState() {
return state;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatelessFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatelessFieldValue.java
index 809b365..3845594 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatelessFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatelessFieldValue.java
@@ -15,11 +15,16 @@
}
@Override
- public ObjectState getState() {
+ public ObjectState getObjectState() {
return ObjectState.empty();
}
@Override
+ public boolean hasObjectState() {
+ return false;
+ }
+
+ @Override
public String toString() {
return "SingleStatelessFieldValue(" + field.toSourceString() + ")";
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/StatefulObjectValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/StatefulObjectValue.java
new file mode 100644
index 0000000..39174da
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/StatefulObjectValue.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2021, 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.analysis.value;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+/** A KnownLengthArrayValue implicitly implies the value is non null. */
+public class StatefulObjectValue extends AbstractValue {
+
+ private final ObjectState state;
+
+ StatefulObjectValue(ObjectState state) {
+ assert !state.isEmpty();
+ this.state = state;
+ }
+
+ public static AbstractValue create(ObjectState objectState) {
+ return objectState.isEmpty()
+ ? UnknownValue.getInstance()
+ : new StatefulObjectValue(objectState);
+ }
+
+ @Override
+ public boolean isNonTrivial() {
+ return true;
+ }
+
+ @Override
+ public boolean isStatefulObjectValue() {
+ return true;
+ }
+
+ @Override
+ public StatefulObjectValue asStatefulObjectValue() {
+ return this;
+ }
+
+ @Override
+ public boolean hasKnownArrayLength() {
+ return getObjectState().hasKnownArrayLength();
+ }
+
+ @Override
+ public int getKnownArrayLength() {
+ return getObjectState().getKnownArrayLength();
+ }
+
+ @Override
+ public AbstractValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ return create(getObjectState().rewrittenWithLens(appView, lens));
+ }
+
+ @Override
+ public boolean hasObjectState() {
+ return true;
+ }
+
+ @Override
+ public ObjectState getObjectState() {
+ return state;
+ }
+
+ @Override
+ public String toString() {
+ return "StatefulValue";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (getClass() != o.getClass()) {
+ return false;
+ }
+ StatefulObjectValue statefulObjectValue = (StatefulObjectValue) o;
+ return state.equals(statefulObjectValue.state);
+ }
+
+ @Override
+ public int hashCode() {
+ return state.hashCode();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java
index 397e79f..817b166 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/EnumValuesObjectState.java
@@ -45,6 +45,16 @@
}
@Override
+ public boolean hasKnownArrayLength() {
+ return true;
+ }
+
+ @Override
+ public int getKnownArrayLength() {
+ return state.length;
+ }
+
+ @Override
public boolean isEnumValuesObjectState() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/KnownLengthArrayState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/KnownLengthArrayState.java
new file mode 100644
index 0000000..a379776
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/KnownLengthArrayState.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2021, 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.analysis.value.objectstate;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.UnknownValue;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.function.BiConsumer;
+
+public class KnownLengthArrayState extends ObjectState {
+
+ private final int length;
+
+ public KnownLengthArrayState(int length) {
+ this.length = length;
+ }
+
+ @Override
+ public void forEachAbstractFieldValue(BiConsumer<DexField, AbstractValue> consumer) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public AbstractValue getAbstractFieldValue(DexEncodedField field) {
+ return UnknownValue.getInstance();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public boolean hasKnownArrayLength() {
+ return true;
+ }
+
+ @Override
+ public int getKnownArrayLength() {
+ return length;
+ }
+
+ @Override
+ public ObjectState rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return this == o;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectState.java
index f709c59..b81fb32 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectState.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.analysis.value.objectstate;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
@@ -58,6 +59,15 @@
@Override
public abstract int hashCode();
+ public boolean hasKnownArrayLength() {
+ return false;
+ }
+
+ public int getKnownArrayLength() {
+ // Override this method if hasKnownArrayLength answers true.
+ throw new Unreachable();
+ }
+
public boolean isEnumValuesObjectState() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectStateAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectStateAnalysis.java
new file mode 100644
index 0000000..125acfa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectStateAnalysis.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2021, 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.analysis.value.objectstate;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldArgumentInitializationInfo;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public class ObjectStateAnalysis {
+
+ public static ObjectState computeObjectState(
+ Value value, AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
+ assert !value.hasAliasedValue();
+ if (value.isDefinedByInstructionSatisfying(
+ i -> i.isNewArrayEmpty() || i.isNewArrayFilledData() || i.isInvokeNewArray())) {
+ return computeNewArrayObjectState(value, appView, context);
+ }
+ if (value.isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
+ return computeNewInstanceObjectState(value, appView, context);
+ }
+ return ObjectState.empty();
+ }
+
+ private static ObjectState computeNewArrayObjectState(
+ Value value, AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
+ AbstractValue abstractValue = value.definition.getAbstractValue(appView, context);
+ if (abstractValue.isStatefulObjectValue()) {
+ // TODO(b/204272377): Avoid wrapping and unwrapping the object state.
+ return abstractValue.asStatefulObjectValue().getObjectState();
+ }
+ return ObjectState.empty();
+ }
+
+ private static ObjectState computeNewInstanceObjectState(
+ Value value, AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
+ NewInstance newInstance = value.definition.asNewInstance();
+ InvokeDirect uniqueConstructorInvoke =
+ newInstance.getUniqueConstructorInvoke(appView.dexItemFactory());
+ if (uniqueConstructorInvoke == null) {
+ return ObjectState.empty();
+ }
+
+ DexClassAndMethod singleTarget = uniqueConstructorInvoke.lookupSingleTarget(appView, context);
+ if (singleTarget == null) {
+ return ObjectState.empty();
+ }
+
+ InstanceFieldInitializationInfoCollection initializationInfos =
+ singleTarget
+ .getDefinition()
+ .getOptimizationInfo()
+ .getInstanceInitializerInfo(uniqueConstructorInvoke)
+ .fieldInitializationInfos();
+ if (initializationInfos.isEmpty()) {
+ return ObjectState.empty();
+ }
+
+ ObjectState.Builder builder = ObjectState.builder();
+ initializationInfos.forEach(
+ appView,
+ (field, initializationInfo) -> {
+ // If the instance field is not written only in the instance initializer, then we can't
+ // conclude that this field will have a constant value.
+ //
+ // We have special handling for library fields that satisfy the property that they are
+ // only written in their corresponding instance initializers. This is needed since we
+ // don't analyze these instance initializers in the Enqueuer, as they are in the library.
+ if (!appView.appInfo().isInstanceFieldWrittenOnlyInInstanceInitializers(field)
+ && !appView.dexItemFactory().enumMembers.isNameOrOrdinalField(field.getReference())) {
+ return;
+ }
+ if (initializationInfo.isArgumentInitializationInfo()) {
+ InstanceFieldArgumentInitializationInfo argumentInitializationInfo =
+ initializationInfo.asArgumentInitializationInfo();
+ Value argument =
+ uniqueConstructorInvoke.getArgument(argumentInitializationInfo.getArgumentIndex());
+ builder.recordFieldHasValue(field, argument.getAbstractValue(appView, context));
+ } else if (initializationInfo.isSingleValue()) {
+ builder.recordFieldHasValue(field, initializationInfo.asSingleValue());
+ }
+ });
+ return builder.build();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index 9aff428..e1bb5bf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -276,10 +276,10 @@
return true;
}
AbstractValue abstractValue = array().getAliasedValue().getAbstractValue(appView, context);
- if (!abstractValue.isKnownLengthArrayValue()) {
+ if (!abstractValue.hasKnownArrayLength()) {
return true;
}
- int newArraySize = abstractValue.asKnownLengthArrayValue().getLength();
+ int newArraySize = abstractValue.getKnownArrayLength();
int index = index().getConstInstruction().asConstNumber().getIntValue();
return newArraySize <= 0 || index < 0 || newArraySize <= index;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
index cad5e1d..636468c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -15,6 +15,9 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.StatefulObjectValue;
+import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -140,6 +143,17 @@
}
@Override
+ public AbstractValue getAbstractValue(
+ AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
+ if (!instructionMayHaveSideEffects(appView, context)) {
+ int size = inValues.size();
+ return StatefulObjectValue.create(
+ appView.abstractValueFactory().createKnownLengthArrayState(size));
+ }
+ return UnknownValue.getInstance();
+ }
+
+ @Override
public boolean instructionInstanceCanThrow(AppView<?> appView, ProgramMethod context) {
DexType baseType = type.isArrayType() ? type.toBaseType(appView.dexItemFactory()) : type;
if (baseType.isPrimitiveType()) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
index 3ddbcbd..3be3d3d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.StatefulObjectValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -90,9 +91,10 @@
AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
if (!instructionMayHaveSideEffects(appView, context) && size().getType().isInt()) {
assert !instructionInstanceCanThrow();
- return appView
- .abstractValueFactory()
- .createKnownLengthArrayValue(size().definition.asConstNumber().getIntValue());
+ return StatefulObjectValue.create(
+ appView
+ .abstractValueFactory()
+ .createKnownLengthArrayState(size().definition.asConstNumber().getIntValue()));
}
return UnknownValue.getInstance();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
index ce235d4..fb04f03 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.StatefulObjectValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -127,7 +128,8 @@
AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
if (!instructionMayHaveSideEffects(appView, context) && size <= Integer.MAX_VALUE) {
assert !instructionInstanceCanThrow();
- return appView.abstractValueFactory().createKnownLengthArrayValue((int) size);
+ return StatefulObjectValue.create(
+ appView.abstractValueFactory().createKnownLengthArrayState((int) size));
}
return UnknownValue.getInstance();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index bcd27e6..dae6f04 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -3451,7 +3451,7 @@
}
AbstractValue abstractValue = array.getAbstractValue(appView, code.context());
- if (!abstractValue.isKnownLengthArrayValue() && !array.isNeverNull()) {
+ if (!abstractValue.hasKnownArrayLength() && !array.isNeverNull()) {
continue;
}
Instruction arrayDefinition = array.getDefinition();
@@ -3468,15 +3468,13 @@
continue;
}
iterator.replaceCurrentInstructionWithConstInt(code, (int) size);
- } else if (abstractValue.isKnownLengthArrayValue()) {
- iterator.replaceCurrentInstructionWithConstInt(
- code, abstractValue.asKnownLengthArrayValue().getLength());
+ } else if (abstractValue.hasKnownArrayLength()) {
+ iterator.replaceCurrentInstructionWithConstInt(code, abstractValue.getKnownArrayLength());
} else {
continue;
}
phiUsers.forEach(Phi::removeTrivialPhi);
- // TODO(139489070): static-get of constant array
}
assert code.isConsistentSSA();
}
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 9e8d052..206c455 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
@@ -401,12 +401,8 @@
if (abstractValue.isUnknown() && !definition.isStatic()) {
AbstractValue abstractReceiverValue =
current.asInstanceGet().object().getAbstractValue(appView, code.context());
- if (abstractReceiverValue.isSingleFieldValue()) {
- abstractValue =
- abstractReceiverValue
- .asSingleFieldValue()
- .getState()
- .getAbstractFieldValue(definition);
+ if (abstractReceiverValue.hasObjectState()) {
+ abstractValue = abstractReceiverValue.getObjectState().getAbstractFieldValue(definition);
}
}
} else if (definition.isStatic()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index e42266b..290f25b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -421,7 +421,7 @@
SingleFieldValue singleFieldValue =
field.getDefinition().getOptimizationInfo().getAbstractValue().asSingleFieldValue();
if (singleFieldValue != null) {
- applyObjectState(staticGet.outValue(), singleFieldValue.getState());
+ applyObjectState(staticGet.outValue(), singleFieldValue.getObjectState());
}
}
}
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 487d677..82322c7 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
@@ -191,7 +191,7 @@
AbstractValue abstractValue = optimizationInfo.getAbstractValue();
objectState =
abstractValue.isSingleFieldValue()
- ? abstractValue.asSingleFieldValue().getState()
+ ? abstractValue.asSingleFieldValue().getObjectState()
: ObjectState.empty();
return EligibilityStatus.ELIGIBLE;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index f51dc69..ff79200 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -356,7 +356,7 @@
if (encodedField == null) {
return null;
}
- return abstractValue.asSingleFieldValue().getState().getAbstractFieldValue(encodedField);
+ return abstractValue.asSingleFieldValue().getObjectState().getAbstractFieldValue(encodedField);
}
private static final class EnumSwitchInfo {
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 15819c7..b5ced8c 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
@@ -65,6 +65,9 @@
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.StatefulObjectValue;
+import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
+import com.android.tools.r8.ir.analysis.value.objectstate.ObjectStateAnalysis;
import com.android.tools.r8.ir.code.AliasedValueConfiguration;
import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.AssumeAndCheckCastAliasedValueConfiguration;
@@ -209,6 +212,13 @@
checkCastAndInstanceOfMethodSpecialization.addCandidateForOptimization(
context, abstractReturnValue, methodProcessor);
}
+ } else if (returnValue.getType().isReferenceType()) {
+ // TODO(b/204159267): Move this logic into Instruction#getAbstractValue in NewInstance.
+ ObjectState objectState =
+ ObjectStateAnalysis.computeObjectState(aliasedValue, appView, context);
+ // TODO(b/204272377): Avoid wrapping and unwrapping the object state.
+ feedback.methodReturnsAbstractValue(
+ method, appView, StatefulObjectValue.create(objectState));
}
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java
index c9cc17b..b171525 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java
@@ -90,7 +90,7 @@
} else {
dead();
}
- if (Config.alwaysEmpty.length == 0) {
+ if (Config.alwaysEmpty.clone().length == 0) {
System.out.print(" world");
}
for (String str : Config.alwaysNonEmpty) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/StatePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/StatePropagationTest.java
new file mode 100644
index 0000000..f4b90ed
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/StatePropagationTest.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2021, 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.membervaluepropagation;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.optimize.membervaluepropagation.StatePropagationTest.TestClass.Data;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class StatePropagationTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public StatePropagationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(StatePropagationTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("1", "1.0", "2", "2.0");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject data = inspector.clazz(Data.class);
+ assertEquals(0, data.allInstanceFields().size());
+ }
+
+ static class TestClass {
+
+ @NeverClassInline
+ static class Data {
+ final int i;
+ final float j;
+
+ private Data(int i, float j) {
+ this.i = i;
+ this.j = j;
+ }
+ }
+
+ @NeverInline
+ public static Data getData1() {
+ return new Data(1, 1.0f);
+ }
+
+ @NeverInline
+ public static Data getData2() {
+ return new Data(2, 2.0f);
+ }
+
+ public static void main(String[] args) {
+ Data data1 = getData1();
+ Data data2 = getData2();
+ System.out.println(data1.i);
+ System.out.println(data1.j);
+ System.out.println(data2.i);
+ System.out.println(data2.j);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentSubtypeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentSubtypeTest.java
index c460e16..97b57f4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentSubtypeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentSubtypeTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.membervaluepropagation.fields;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -50,8 +51,7 @@
ClassSubject testClassSubject = inspector.clazz(TestClass.class);
assertThat(testClassSubject, isPresent());
assertThat(testClassSubject.uniqueMethodWithName("live"), isPresent());
- // TODO(b/147652121): Should be absent.
- assertThat(testClassSubject.uniqueMethodWithName("dead"), isPresent());
+ assertThat(testClassSubject.uniqueMethodWithName("dead"), isAbsent());
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayLengthRewriteTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayLengthRewriteTest.java
index 5a7be26..c38c66f 100644
--- a/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayLengthRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayLengthRewriteTest.java
@@ -68,7 +68,7 @@
.addProgramClasses(Main.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(expectedOutput)
- .inspect(this::inspect);
+ .inspect(i -> inspect(i, true));
}
@Test public void r8() throws Exception {
@@ -80,10 +80,10 @@
.enableInliningAnnotations()
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(expectedOutput)
- .inspect(this::inspect);
+ .inspect(i -> inspect(i, false));
}
- private void inspect(CodeInspector inspector) {
+ private void inspect(CodeInspector inspector, boolean d8) {
ClassSubject mainClass = inspector.clazz(Main.class);
assertTrue(mainClass.isPresent());
@@ -104,10 +104,10 @@
// TODO(139489070): these should be rewritten and result in 0 array-length bytecodes
MethodSubject staticConstants = mainClass.uniqueMethodWithName("staticConstants");
- assertArrayLengthCallCount(staticConstants, 3);
+ assertArrayLengthCallCount(staticConstants, (d8 || debugMode) ? 3 : 0);
MethodSubject staticNonConstants = mainClass.uniqueMethodWithName("staticNonConstants");
- assertArrayLengthCallCount(staticNonConstants, 2);
+ assertArrayLengthCallCount(staticNonConstants, (d8 || debugMode) ? 2 : 0);
}
private static void assertArrayLengthCallCount(MethodSubject subject, int expected) {
@@ -200,7 +200,7 @@
}
private static String[] mutable = { "one" };
- private static final String[] runtimeInit = { System.lineSeparator() };
+ private static final String[] runtimeInit = {"two"};
@NeverInline
private static void staticNonConstants() {