Fix nondeterminism in argument propagation
Bug: b/364774724
Change-Id: Ic5e607d9bfc6835b8298ca261cd752bef2e52d2c
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
index d25c1b5..7e02b35 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -255,7 +255,7 @@
if (fieldAccessAnalysis != null && fieldAccessAnalysis.fieldAssignmentTracker() != null) {
fieldAccessAnalysis.fieldAssignmentTracker().waveDone(wave, delayedOptimizationFeedback);
}
- appView.withArgumentPropagator(ArgumentPropagator::publishDelayedReprocessingCriteria);
+ appView.withArgumentPropagator(ArgumentPropagator::waveDone);
if (appView.options().protoShrinking().enableRemoveProtoEnumSwitchMap()) {
appView.protoShrinker().protoEnumSwitchMapRemover.updateVisibleStaticFieldValues();
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
index 30b9398..063b69b 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -170,6 +170,13 @@
reprocessingCriteriaCollection.publishDelayedReprocessingCriteria();
}
+ public void waveDone() {
+ if (codeScanner != null) {
+ codeScanner.waveDone();
+ }
+ publishDelayedReprocessingCriteria();
+ }
+
public void transferArgumentInformation(ProgramMethod from, ProgramMethod to) {
assert codeScanner != null;
MethodStateCollectionByReference methodStates = codeScanner.getMethodStates();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index 2f6dff1..c4534d1 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -83,6 +83,7 @@
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.TraversalUtils;
import com.android.tools.r8.utils.WorkList;
+import com.android.tools.r8.utils.collections.ProgramFieldSet;
import com.android.tools.r8.utils.structural.StructuralItem;
import com.google.common.collect.Sets;
import java.io.IOException;
@@ -134,6 +135,9 @@
*/
private final FieldStateCollection fieldStates = FieldStateCollection.createConcurrent();
+ private final ProgramFieldSet newlyUnknownFieldsInCurrentWave =
+ ProgramFieldSet.createConcurrent();
+
/**
* The abstract program state for this optimization. Intuitively maps each parameter to its
* abstract value and dynamic type.
@@ -204,7 +208,9 @@
// Only allow early graph pruning when the two nodes have the same type. If the given field is
// unknown, but flows to a field or method parameter with a less precise type, we still want
// this type propagation to happen.
- return fieldStates.get(field).isUnknown() && field.getType().isIdenticalTo(staticType);
+ return fieldStates.get(field).isUnknown()
+ && field.getType().isIdenticalTo(staticType)
+ && !newlyUnknownFieldsInCurrentWave.contains(field);
}
protected boolean isMethodParameterAlreadyUnknown(
@@ -254,6 +260,10 @@
.scan(timing);
}
+ public void waveDone() {
+ newlyUnknownFieldsInCurrentWave.clear();
+ }
+
protected class CodeScanner {
protected final AbstractValueSupplier abstractValueSupplier;
@@ -315,7 +325,8 @@
StateCloner.getCloner(),
Action.empty());
return narrowFieldState(field, newFieldState);
- });
+ },
+ () -> newlyUnknownFieldsInCurrentWave.add(field));
timing.end();
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FieldStateCollection.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FieldStateCollection.java
index cf0dcc5..11c52ce 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FieldStateCollection.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FieldStateCollection.java
@@ -56,12 +56,25 @@
Supplier<NonEmptyValueState> fieldStateSupplier,
Timing timing,
BiFunction<ConcreteValueState, ConcreteValueState, NonEmptyValueState> joiner) {
+ return addTemporaryFieldState(field, fieldStateSupplier, timing, joiner, Action.empty());
+ }
+
+ public NonEmptyValueState addTemporaryFieldState(
+ ProgramField field,
+ Supplier<NonEmptyValueState> fieldStateSupplier,
+ Timing timing,
+ BiFunction<ConcreteValueState, ConcreteValueState, NonEmptyValueState> joiner,
+ Action unknownTransitionHandler) {
ValueState joinState =
fieldStates.compute(
field,
(f, existingFieldState) -> {
if (existingFieldState == null) {
- return fieldStateSupplier.get();
+ NonEmptyValueState result = fieldStateSupplier.get();
+ if (result.isUnknown()) {
+ unknownTransitionHandler.execute();
+ }
+ return result;
}
assert !existingFieldState.isBottom();
if (existingFieldState.isUnknown()) {
@@ -69,6 +82,7 @@
}
NonEmptyValueState fieldStateToAdd = fieldStateSupplier.get();
if (fieldStateToAdd.isUnknown()) {
+ unknownTransitionHandler.execute();
return fieldStateToAdd;
}
timing.begin("Join temporary field state");
@@ -76,6 +90,9 @@
ConcreteValueState concreteFieldStateToAdd = fieldStateToAdd.asConcrete();
NonEmptyValueState joinResult =
joiner.apply(existingConcreteFieldState, concreteFieldStateToAdd);
+ if (joinResult.isUnknown()) {
+ unknownTransitionHandler.execute();
+ }
timing.end();
return joinResult;
});