Replace all occurrences of SingleEnumValue by SingleFieldValue
Change-Id: If73c8ebda320fa61ace737c65709757c6abcdf4c
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 0d5c50d..4e3c645 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -129,6 +129,10 @@
return this;
}
+ public boolean isEnum() {
+ return accessFlags.isEnum();
+ }
+
public boolean isFinal() {
return accessFlags.isFinal();
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
index f417d65..f68aa3a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -109,8 +110,13 @@
if (isConstantArrayThroughoutMethod(root, assumedNotToDependOnEnvironment)) {
return false;
}
- if (root.getAbstractValue(appView, context).isSingleEnumValue()) {
- return false;
+ AbstractValue abstractValue = root.getAbstractValue(appView, context);
+ if (abstractValue.isSingleFieldValue()) {
+ DexEncodedField field =
+ appView.definitionFor(abstractValue.asSingleFieldValue().getField());
+ if (field != null && field.isEnum()) {
+ return false;
+ }
}
if (isNewInstanceWithoutEnvironmentDependentFields(root, assumedNotToDependOnEnvironment)) {
return false;
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 e2ab677..596cd06 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
@@ -4,27 +4,17 @@
package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
-import static com.android.tools.r8.ir.code.Opcodes.*;
-
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
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.AbstractValueFactory;
-import com.android.tools.r8.ir.analysis.value.SingleEnumValue;
-import com.android.tools.r8.ir.analysis.value.UnknownValue;
-import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
-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.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
@@ -100,7 +90,7 @@
void updateFieldOptimizationInfo(DexEncodedField field, FieldInstruction fieldPut, Value value) {
// Abstract value.
Value root = value.getAliasedValue();
- AbstractValue abstractValue = computeAbstractValue(root);
+ AbstractValue abstractValue = root.getAbstractValue(appView, clazz.type);
if (abstractValue.isUnknown()) {
feedback.recordFieldHasAbstractValue(
field, appView, appView.abstractValueFactory().createSingleFieldValue(field.field));
@@ -123,113 +113,4 @@
feedback.markFieldHasDynamicLowerBoundType(field, dynamicLowerBoundType);
}
}
-
- private AbstractValue computeAbstractValue(Value value) {
- assert !value.hasAliasedValue();
- if (clazz.isEnum()) {
- SingleEnumValue singleEnumValue = getSingleEnumValue(value);
- if (singleEnumValue != null) {
- return singleEnumValue;
- }
- }
- if (!value.isPhi()) {
- return value.definition.getAbstractValue(appView, clazz.type);
- }
- return UnknownValue.getInstance();
- }
-
- /**
- * If {@param value} is defined by a new-instance instruction that instantiates the enclosing enum
- * class, and the value is assigned into exactly one static enum field on the enclosing enum
- * class, then returns a {@link SingleEnumValue} instance. Otherwise, returns {@code null}.
- *
- * <p>Note that enum constructors also store the newly instantiated enums in the {@code $VALUES}
- * array field on the enum. Therefore, this code also allows {@param value} to be stored into an
- * array as long as the array is identified as being the {@code $VALUES} array.
- */
- private SingleEnumValue getSingleEnumValue(Value value) {
- assert clazz.isEnum();
- assert !value.hasAliasedValue();
- if (value.isPhi() || !value.definition.isNewInstance()) {
- return null;
- }
-
- NewInstance newInstance = value.definition.asNewInstance();
- if (newInstance.clazz != clazz.type) {
- return null;
- }
-
- if (value.hasDebugUsers() || value.hasPhiUsers()) {
- return null;
- }
-
- DexEncodedField enumField = null;
- for (Instruction user : value.uniqueUsers()) {
- switch (user.opcode()) {
- case ARRAY_PUT:
- // Check that this is assigning the enum into the enum values array.
- ArrayPut arrayPut = user.asArrayPut();
- if (arrayPut.value().getAliasedValue() != value || !isEnumValuesArray(arrayPut.array())) {
- return null;
- }
- break;
-
- case INVOKE_DIRECT:
- // Check that this is the corresponding constructor call.
- InvokeDirect invoke = user.asInvokeDirect();
- if (!appView.dexItemFactory().isConstructor(invoke.getInvokedMethod())
- || invoke.getReceiver() != value) {
- return null;
- }
- break;
-
- case STATIC_PUT:
- DexEncodedField field = clazz.lookupStaticField(user.asStaticPut().getField());
- if (field != null && field.accessFlags.isEnum()) {
- if (enumField != null) {
- return null;
- }
- enumField = field;
- }
- break;
-
- default:
- return null;
- }
- }
-
- if (enumField == null) {
- return null;
- }
-
- return appView.abstractValueFactory().createSingleEnumValue(enumField.field);
- }
-
- private boolean isEnumValuesArray(Value value) {
- assert clazz.isEnum();
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- DexField valuesField =
- dexItemFactory.createField(
- clazz.type,
- clazz.type.toArrayType(1, dexItemFactory),
- dexItemFactory.enumValuesFieldName);
-
- Value root = value.getAliasedValue();
- if (root.isPhi()) {
- return false;
- }
-
- Instruction definition = root.definition;
- if (definition.isNewArrayEmpty()) {
- for (Instruction user : root.aliasedUsers()) {
- if (user.isStaticPut() && user.asStaticPut().getField() == valuesField) {
- return true;
- }
- }
- } else if (definition.isStaticGet()) {
- return definition.asStaticGet().getField() == valuesField;
- }
-
- return false;
- }
}
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 404faff..9939980 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
@@ -44,14 +44,6 @@
return null;
}
- public boolean isSingleEnumValue() {
- return false;
- }
-
- public SingleEnumValue asSingleEnumValue() {
- return null;
- }
-
public boolean isSingleFieldValue() {
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 80ca063..bb467a5 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
@@ -13,7 +13,6 @@
private ConcurrentHashMap<DexType, SingleConstClassValue> singleConstClassValues =
new ConcurrentHashMap<>();
- private ConcurrentHashMap<DexField, SingleEnumValue> singleEnumValues = new ConcurrentHashMap<>();
private ConcurrentHashMap<DexField, SingleFieldValue> singleFieldValues =
new ConcurrentHashMap<>();
private ConcurrentHashMap<Long, SingleNumberValue> singleNumberValues = new ConcurrentHashMap<>();
@@ -24,10 +23,6 @@
return singleConstClassValues.computeIfAbsent(type, SingleConstClassValue::new);
}
- public SingleEnumValue createSingleEnumValue(DexField field) {
- return singleEnumValues.computeIfAbsent(field, SingleEnumValue::new);
- }
-
public SingleFieldValue createSingleFieldValue(DexField field) {
return singleFieldValues.computeIfAbsent(field, SingleFieldValue::new);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleEnumValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleEnumValue.java
deleted file mode 100644
index 4beb525..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleEnumValue.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.analysis.value;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
-import com.android.tools.r8.graph.GraphLense;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-
-public class SingleEnumValue extends SingleFieldValue {
-
- /** Intentionally package private, use {@link AbstractValueFactory} instead. */
- SingleEnumValue(DexField field) {
- super(field);
- }
-
- @Override
- public boolean isSingleEnumValue() {
- return true;
- }
-
- @Override
- public SingleEnumValue asSingleEnumValue() {
- return this;
- }
-
- @Override
- public String toString() {
- return "SingleEnumValue(" + getField().toSourceString() + ")";
- }
-
- @Override
- public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
- DexField field = getField();
- EnumValueInfoMap unboxedEnumInfo = appView.unboxedEnums().getEnumValueInfoMap(field.type);
- if (unboxedEnumInfo != null) {
- // Return the ordinal of the unboxed enum.
- assert unboxedEnumInfo.hasEnumValueInfo(field);
- return appView
- .abstractValueFactory()
- .createSingleNumberValue(unboxedEnumInfo.getEnumValueInfo(field).convertToInt());
- }
- return appView.abstractValueFactory().createSingleEnumValue(lens.lookupField(field));
- }
-}
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 855694a..bc51a08 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
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -108,9 +109,14 @@
@Override
public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
- DexField rewrittenField = lens.lookupField(field);
- assert !appView.unboxedEnums().containsEnum(field.holder)
- || !appView.appInfo().resolveField(rewrittenField).accessFlags.isEnum();
- return appView.abstractValueFactory().createSingleFieldValue(rewrittenField);
+ EnumValueInfoMap unboxedEnumInfo = appView.unboxedEnums().getEnumValueInfoMap(field.type);
+ if (unboxedEnumInfo != null) {
+ // Return the ordinal of the unboxed enum.
+ assert unboxedEnumInfo.hasEnumValueInfo(field);
+ return appView
+ .abstractValueFactory()
+ .createSingleNumberValue(unboxedEnumInfo.getEnumValueInfo(field).convertToInt());
+ }
+ return appView.abstractValueFactory().createSingleFieldValue(lens.lookupField(field));
}
}
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 eb36e39..3ab21cf 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
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexItemFactory.ThrowableMethods;
@@ -27,6 +28,8 @@
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
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.SingleConstClassValue;
+import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
import com.android.tools.r8.ir.code.AlwaysMaterializingNop;
import com.android.tools.r8.ir.code.ArrayLength;
import com.android.tools.r8.ir.code.ArrayPut;
@@ -75,6 +78,7 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.optimize.controlflow.SwitchCaseAnalyzer;
import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOutputMode;
import com.android.tools.r8.utils.LongInterval;
@@ -2499,12 +2503,37 @@
} else {
DexType context = code.method.holder();
AbstractValue abstractValue = lhs.getAbstractValue(appView, context);
- if (abstractValue.isSingleConstClassValue() || abstractValue.isSingleFieldValue()) {
+ if (abstractValue.isSingleConstClassValue()) {
AbstractValue otherAbstractValue = rhs.getAbstractValue(appView, context);
- if (abstractValue == otherAbstractValue) {
- simplifyIfWithKnownCondition(code, block, theIf, 0);
- } else if (otherAbstractValue.isSingleEnumValue()) {
- simplifyIfWithKnownCondition(code, block, theIf, 1);
+ if (otherAbstractValue.isSingleConstClassValue()) {
+ SingleConstClassValue singleConstClassValue =
+ abstractValue.asSingleConstClassValue();
+ SingleConstClassValue otherSingleConstClassValue =
+ otherAbstractValue.asSingleConstClassValue();
+ simplifyIfWithKnownCondition(
+ code,
+ block,
+ theIf,
+ BooleanUtils.intValue(
+ singleConstClassValue.getType() != otherSingleConstClassValue.getType()));
+ }
+ } else if (abstractValue.isSingleFieldValue()) {
+ AbstractValue otherAbstractValue = rhs.getAbstractValue(appView, context);
+ if (otherAbstractValue.isSingleFieldValue()) {
+ SingleFieldValue singleFieldValue = abstractValue.asSingleFieldValue();
+ SingleFieldValue otherSingleFieldValue = otherAbstractValue.asSingleFieldValue();
+ if (singleFieldValue.getField() == otherSingleFieldValue.getField()) {
+ simplifyIfWithKnownCondition(code, block, theIf, 0);
+ } else {
+ DexEncodedField field = appView.definitionFor(singleFieldValue.getField());
+ if (field != null && field.isEnum()) {
+ DexEncodedField otherField =
+ appView.definitionFor(otherSingleFieldValue.getField());
+ if (otherField != null && otherField.isEnum()) {
+ simplifyIfWithKnownCondition(code, block, theIf, 1);
+ }
+ }
+ }
}
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ifs/ConstClassComparisonTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ifs/ConstClassComparisonTest.java
new file mode 100644
index 0000000..6b8cb68
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ifs/ConstClassComparisonTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.ifs;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+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.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 ConstClassComparisonTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ConstClassComparisonTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ConstClassComparisonTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("true", "false", "false", "true");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(classSubject, isPresent());
+
+ MethodSubject mainMethod = classSubject.mainMethod();
+ assertThat(mainMethod, isPresent());
+ assertTrue(mainMethod.streamInstructions().noneMatch(InstructionSubject::isConstClass));
+
+ assertThat(inspector.clazz(A.class), not(isPresent()));
+ assertThat(inspector.clazz(B.class), not(isPresent()));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println(getA() == getA());
+ System.out.println(getA() != getA());
+ System.out.println(getA() == getB());
+ System.out.println(getA() != getB());
+ }
+
+ static Class<?> getA() {
+ return A.class;
+ }
+
+ static Class<?> getB() {
+ return B.class;
+ }
+ }
+
+ static class A {}
+
+ static class B {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ifs/EnumAliasComparisonTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ifs/EnumAliasComparisonTest.java
index 5c0f75b..08fdca2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ifs/EnumAliasComparisonTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ifs/EnumAliasComparisonTest.java
@@ -4,18 +4,10 @@
package com.android.tools.r8.ir.optimize.ifs;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
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.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -42,40 +34,27 @@
.addKeepMainRule(TestClass.class)
.setMinApi(parameters.getApiLevel())
.compile()
- .inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("true");
- }
-
- private void inspect(CodeInspector inspector) {
- ClassSubject classSubject = inspector.clazz(TestClass.class);
- assertThat(classSubject, isPresent());
-
- MethodSubject mainMethod = classSubject.mainMethod();
- assertThat(mainMethod, isPresent());
- assertTrue(
- mainMethod
- .streamInstructions()
- .filter(InstructionSubject::isStaticGet)
- .map(InstructionSubject::getField)
- .map(field -> field.name.toSourceString())
- .allMatch("out"::equals));
-
- assertThat(inspector.clazz(MyEnum.class), not(isPresent()));
+ .assertSuccessWithOutputLines("true", "false", "false", "true", "true", "false");
}
static class TestClass {
public static void main(String[] args) {
- // Should print "true" since MyEnum.B is an alias of MyEnum.A.
+ System.out.println(MyEnum.A == MyEnum.A);
+ System.out.println(MyEnum.A != MyEnum.A);
System.out.println(MyEnum.A == MyEnum.B);
+ System.out.println(MyEnum.A != MyEnum.B);
+ System.out.println(MyEnum.A == MyEnum.C);
+ System.out.println(MyEnum.A != MyEnum.C);
}
}
enum MyEnum {
- A;
+ A,
+ B;
// Introduce an alias of MyEnum.A.
- static MyEnum B = A;
+ static MyEnum C = A;
}
}