Fix incorrect field value propagation
Bug: b/307987907
Change-Id: I84e8b52e4e63b26da9220dd91b2ea983146b3ff3
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index 0f47d7e..559090b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -54,6 +54,7 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -80,6 +81,9 @@
private final Map<DexProgramClass, ProgramFieldMap<AbstractValue>>
abstractFinalInstanceFieldValues = new ConcurrentHashMap<>();
+ private final Set<DexProgramClass> classesWithPrunedInstanceInitializers =
+ ConcurrentHashMap.newKeySet();
+
FieldAssignmentTracker(AppView<AppInfoWithLiveness> appView) {
this.abstractValueFactory = appView.abstractValueFactory();
this.appView = appView;
@@ -356,41 +360,49 @@
private void recordAllInstanceFieldPutsProcessed(
ProgramField field, OptimizationFeedbackDelayed feedback) {
- if (appView.appInfo().isInstanceFieldWrittenOnlyInInstanceInitializers(field)) {
- AbstractValue abstractValue = BottomValue.getInstance();
- DexProgramClass clazz = field.getHolder();
- for (DexEncodedMethod method : clazz.directMethods(DexEncodedMethod::isInstanceInitializer)) {
- InstanceFieldInitializationInfo fieldInitializationInfo =
- method
- .getOptimizationInfo()
- .getContextInsensitiveInstanceInitializerInfo()
- .fieldInitializationInfos()
- .get(field);
- if (fieldInitializationInfo.isSingleValue()) {
- abstractValue =
- appView
- .getAbstractValueFieldJoiner()
- .join(abstractValue, fieldInitializationInfo.asSingleValue(), field);
- if (abstractValue.isUnknown()) {
- break;
- }
- } else if (fieldInitializationInfo.isTypeInitializationInfo()) {
- // TODO(b/149732532): Not handled, for now.
- abstractValue = UnknownValue.getInstance();
- break;
- } else {
- assert fieldInitializationInfo.isArgumentInitializationInfo()
- || fieldInitializationInfo.isUnknown();
- abstractValue = UnknownValue.getInstance();
+ if (!appView.appInfo().isInstanceFieldWrittenOnlyInInstanceInitializers(field)) {
+ return;
+ }
+ DexProgramClass clazz = field.getHolder();
+ if (classesWithPrunedInstanceInitializers.contains(clazz)) {
+ // The current method is analyzing the instance-puts to the field in the instance initializers
+ // of the holder class. Due to single caller inlining of instance initializers some of the
+ // methods needed for the analysis may have been pruned from the app, in which case the
+ // analysis result will not be conservative.
+ return;
+ }
+ AbstractValue abstractValue = BottomValue.getInstance();
+ for (DexEncodedMethod method : clazz.directMethods(DexEncodedMethod::isInstanceInitializer)) {
+ InstanceFieldInitializationInfo fieldInitializationInfo =
+ method
+ .getOptimizationInfo()
+ .getContextInsensitiveInstanceInitializerInfo()
+ .fieldInitializationInfos()
+ .get(field);
+ if (fieldInitializationInfo.isSingleValue()) {
+ abstractValue =
+ appView
+ .getAbstractValueFieldJoiner()
+ .join(abstractValue, fieldInitializationInfo.asSingleValue(), field);
+ if (abstractValue.isUnknown()) {
break;
}
+ } else if (fieldInitializationInfo.isTypeInitializationInfo()) {
+ // TODO(b/149732532): Not handled, for now.
+ abstractValue = UnknownValue.getInstance();
+ break;
+ } else {
+ assert fieldInitializationInfo.isArgumentInitializationInfo()
+ || fieldInitializationInfo.isUnknown();
+ abstractValue = UnknownValue.getInstance();
+ break;
}
+ }
- assert !abstractValue.isBottom();
+ assert !abstractValue.isBottom();
- if (!abstractValue.isUnknown()) {
- feedback.recordFieldHasAbstractValue(field.getDefinition(), appView, abstractValue);
- }
+ if (!abstractValue.isUnknown()) {
+ feedback.recordFieldHasAbstractValue(field.getDefinition(), appView, abstractValue);
}
}
@@ -417,6 +429,16 @@
});
}
+ public void onMethodPruned(ProgramMethod method) {
+ onMethodCodePruned(method);
+ }
+
+ public void onMethodCodePruned(ProgramMethod method) {
+ if (method.getDefinition().isInstanceInitializer()) {
+ classesWithPrunedInstanceInitializers.add(method.getHolder());
+ }
+ }
+
public void waveDone(ProgramMethodSet wave, OptimizationFeedbackDelayed feedback) {
// This relies on the instance initializer info in the method optimization feedback. It is
// therefore important that the optimization info has been flushed in advance.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index a7cd093..29fdae5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1169,6 +1169,7 @@
assert method.getHolder().lookupMethod(method.getReference()) == null;
appView.withArgumentPropagator(argumentPropagator -> argumentPropagator.onMethodPruned(method));
enumUnboxer.onMethodPruned(method);
+ fieldAccessAnalysis.fieldAssignmentTracker().onMethodPruned(method);
outliner.onMethodPruned(method);
if (inliner != null) {
inliner.onMethodPruned(method);
@@ -1186,6 +1187,7 @@
appView.withArgumentPropagator(
argumentPropagator -> argumentPropagator.onMethodCodePruned(method));
enumUnboxer.onMethodCodePruned(method);
+ fieldAccessAnalysis.fieldAssignmentTracker().onMethodCodePruned(method);
outliner.onMethodCodePruned(method);
if (inliner != null) {
inliner.onMethodCodePruned(method);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldsOnlyWrittenInInitializersWithSingleCallerPruningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldsOnlyWrittenInInitializersWithSingleCallerPruningTest.java
index 82aaa87..250dd0b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldsOnlyWrittenInInitializersWithSingleCallerPruningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldsOnlyWrittenInInitializersWithSingleCallerPruningTest.java
@@ -62,8 +62,7 @@
.enableInliningAnnotations()
.setMinApi(parameters)
.run(parameters.getRuntime(), Main.class)
- // TODO(b/307987907): Should succeed with "Hello, world!".
- .assertSuccessWithOutputLines("HelloHello");
+ .assertSuccessWithOutputLines("Hello, world!");
}
static class Main {