Leverage member value propagation for optimizing null-valued fields
Bug: 150269949
Change-Id: Id4ce1c4b419dbceb5bdc0e6d4979701fa4f12fec
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 73395b2..7a85dc8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -42,6 +42,10 @@
this.staticValue = staticValue;
}
+ public DexType type() {
+ return field.type;
+ }
+
public boolean isProgramField(DexDefinitionSupplier definitions) {
if (field.holder.isClassType()) {
DexClass clazz = definitions.definitionFor(field.holder);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 115afec..b8a5787 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -128,6 +128,11 @@
DexEncodedField encodedField =
appInfoWithLiveness.resolveField(getField()).getResolvedField();
assert encodedField != null : "NoSuchFieldError (resolution failure) should be caught.";
+
+ if (encodedField.type().isAlwaysNull(appViewWithLiveness)) {
+ return false;
+ }
+
return appInfoWithLiveness.isFieldRead(encodedField)
|| isStoringObjectWithFinalizer(appViewWithLiveness, encodedField);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 678a569..36b6520 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -125,6 +125,10 @@
return false;
}
+ if (encodedField.type().isAlwaysNull(appViewWithLiveness)) {
+ return false;
+ }
+
return appInfoWithLiveness.isFieldRead(encodedField)
|| isStoringObjectWithFinalizer(appViewWithLiveness, encodedField);
}
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 8222958..51c1c01 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
@@ -357,13 +357,10 @@
return;
}
- // Check if the field is pinned. In that case, it could be written by reflection.
- if (appView.appInfo().isPinned(target.field)) {
- return;
- }
-
AbstractValue abstractValue;
- if (appView.appInfo().isFieldWrittenByFieldPutInstruction(target)) {
+ if (field.type.isAlwaysNull(appView)) {
+ abstractValue = appView.abstractValueFactory().createSingleNumberValue(0);
+ } else if (appView.appInfo().isFieldWrittenByFieldPutInstruction(target)) {
abstractValue = target.getOptimizationInfo().getAbstractValue();
if (abstractValue.isUnknown() && !target.isStatic()) {
AbstractValue abstractReceiverValue =
@@ -485,12 +482,15 @@
private void replaceInstancePutByNullCheckIfNeverRead(
IRCode code, InstructionListIterator iterator, InstancePut current) {
- DexEncodedField target = appView.appInfo().resolveField(current.getField()).getResolvedField();
- if (target == null || appView.appInfo().isFieldRead(target)) {
+ DexEncodedField field = appView.appInfo().resolveField(current.getField()).getResolvedField();
+ if (field == null || field.isStatic()) {
return;
}
- if (target.isStatic()) {
+ // If the field is read, we can't remove the instance-put unless the value of the field is known
+ // to be null (in which case the instance-put is a no-op because it assigns the field the same
+ // value as its default value).
+ if (!field.type().isAlwaysNull(appView) && appView.appInfo().isFieldRead(field)) {
return;
}
@@ -500,11 +500,14 @@
private void replaceStaticPutByInitClassIfNeverRead(
IRCode code, InstructionListIterator iterator, StaticPut current) {
DexEncodedField field = appView.appInfo().resolveField(current.getField()).getResolvedField();
- if (field == null || appView.appInfo().isFieldRead(field)) {
+ if (field == null || !field.isStatic()) {
return;
}
- if (!field.isStatic()) {
+ // If the field is read, we can't remove the static-put unless the value of the field is known
+ // to be null (in which case the static-put is a no-op because it assigns the field the same
+ // value as its default value).
+ if (!field.type().isAlwaysNull(appView) && appView.appInfo().isFieldRead(field)) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 8d89c42..85f0274 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -9,9 +9,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
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.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -23,7 +21,6 @@
import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.RemovedArgumentInfo;
import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
-import com.android.tools.r8.ir.analysis.TypeChecker;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.FieldInstruction;
@@ -102,11 +99,9 @@
private static final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
private final AppView<AppInfoWithLiveness> appView;
- private final TypeChecker typeChecker;
public UninstantiatedTypeOptimization(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
- this.typeChecker = new TypeChecker(appView);
}
public UninstantiatedTypeOptimizationGraphLense run(
@@ -351,14 +346,7 @@
continue;
}
}
- if (instruction.isFieldInstruction()) {
- rewriteFieldInstruction(
- instruction.asFieldInstruction(),
- instructionIterator,
- code,
- assumeDynamicTypeRemover,
- valuesToNarrow);
- } else if (instruction.isInvokeMethod()) {
+ if (instruction.isInvokeMethod()) {
rewriteInvoke(
instruction.asInvokeMethod(),
blockIterator,
@@ -401,57 +389,6 @@
return true;
}
- // instance-{get|put} with a null receiver has already been rewritten to `throw null`.
- // At this point, field-instruction whose target field type is uninstantiated will be handled.
- private void rewriteFieldInstruction(
- FieldInstruction instruction,
- InstructionListIterator instructionIterator,
- IRCode code,
- AssumeDynamicTypeRemover assumeDynamicTypeRemover,
- Set<Value> affectedValues) {
- ProgramMethod context = code.context();
- DexField field = instruction.getField();
- DexType fieldType = field.type;
- if (fieldType.isAlwaysNull(appView)) {
- // TODO(b/123857022): Should be possible to use definitionFor().
- DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
- if (encodedField == null) {
- return;
- }
-
- boolean instructionCanBeRemoved = !instruction.instructionInstanceCanThrow(appView, context);
-
- BasicBlock block = instruction.getBlock();
- if (instruction.isFieldPut()) {
- if (!typeChecker.checkFieldPut(instruction)) {
- // Broken type hierarchy. See FieldTypeTest#test_brokenTypeHierarchy.
- assert appView.options().testing.allowTypeErrors;
- return;
- }
-
- // We know that the right-hand side must be null, so this is a no-op.
- if (instructionCanBeRemoved) {
- instructionIterator.removeOrReplaceByDebugLocalRead();
- }
- } else {
- if (instructionCanBeRemoved) {
- // Replace the field read by the constant null.
- assumeDynamicTypeRemover.markUsersForRemoval(instruction.outValue());
- affectedValues.addAll(instruction.outValue().affectedValues());
- instructionIterator.replaceCurrentInstruction(code.createConstNull());
- } else {
- replaceOutValueByNull(
- instruction, instructionIterator, code, assumeDynamicTypeRemover, affectedValues);
- }
- }
-
- if (block.hasCatchHandlers()) {
- // This block can no longer throw.
- block.getCatchHandlers().getUniqueTargets().forEach(BasicBlock::unlinkCatchHandler);
- }
- }
- }
-
// invoke instructions with a null receiver has already been rewritten to `throw null`.
// At this point, we attempt to explore non-null-param-or-throw optimization info and replace
// the invocation with `throw null` if an argument is known to be null and the method is going to