Ensure deterministic worklist processing
Fixes: b/338885156
Change-Id: Iab48b561857dc8a9362b59023b6aed87deeb36c2
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/CastAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/CastAbstractFunction.java
index ee147a5..660c42a 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/CastAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/CastAbstractFunction.java
@@ -38,6 +38,20 @@
}
@Override
+ public InFlowKind getKind() {
+ return InFlowKind.ABSTRACT_FUNCTION_CAST;
+ }
+
+ @Override
+ public int internalCompareToSameKind(InFlow other) {
+ CastAbstractFunction fn = other.asCastAbstractFunction();
+ if (inFlow != fn.inFlow) {
+ return inFlow.compareTo(fn.inFlow);
+ }
+ return type.compareTo(fn.type);
+ }
+
+ @Override
public boolean isCastAbstractFunction() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FieldValue.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FieldValue.java
index af8ec1a..0b9e152 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FieldValue.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/FieldValue.java
@@ -20,6 +20,16 @@
}
@Override
+ public InFlowKind getKind() {
+ return InFlowKind.FIELD;
+ }
+
+ @Override
+ public int internalCompareToSameKind(InFlow other) {
+ return field.compareTo(other.asFieldValue().getField());
+ }
+
+ @Override
public boolean isFieldValue() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/IdentityAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/IdentityAbstractFunction.java
index 1545a75..7b20e77 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/IdentityAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/IdentityAbstractFunction.java
@@ -36,6 +36,17 @@
}
@Override
+ public InFlowKind getKind() {
+ return InFlowKind.ABSTRACT_FUNCTION_IDENTITY;
+ }
+
+ @Override
+ public int internalCompareToSameKind(InFlow inFlow) {
+ assert this == inFlow;
+ return 0;
+ }
+
+ @Override
public boolean isIdentity() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InFlow.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InFlow.java
index 3c071be..e261214 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InFlow.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InFlow.java
@@ -7,7 +7,19 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.optimize.compose.UpdateChangedFlagsAbstractFunction;
-public interface InFlow {
+public interface InFlow extends Comparable<InFlow> {
+
+ @Override
+ default int compareTo(InFlow inFlow) {
+ if (getKind() == inFlow.getKind()) {
+ return internalCompareToSameKind(inFlow);
+ }
+ return getKind().ordinal() - inFlow.getKind().ordinal();
+ }
+
+ int internalCompareToSameKind(InFlow inFlow);
+
+ InFlowKind getKind();
default boolean isAbstractFunction() {
return false;
@@ -65,6 +77,10 @@
return null;
}
+ default OrAbstractFunction asOrAbstractFunction() {
+ return null;
+ }
+
default boolean isUnknownAbstractFunction() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InFlowKind.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InFlowKind.java
new file mode 100644
index 0000000..d516ee1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InFlowKind.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2024, 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.optimize.argumentpropagation.codescanner;
+
+public enum InFlowKind {
+ ABSTRACT_FUNCTION_CAST,
+ ABSTRACT_FUNCTION_IDENTITY,
+ ABSTRACT_FUNCTION_INSTANCE_FIELD_READ,
+ ABSTRACT_FUNCTION_OR,
+ ABSTRACT_FUNCTION_UNKNOWN,
+ ABSTRACT_FUNCTION_UPDATE_CHANGED_FLAGS,
+ FIELD,
+ METHOD_PARAMETER
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InstanceFieldReadAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InstanceFieldReadAbstractFunction.java
index 366dea4..560f00d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InstanceFieldReadAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/InstanceFieldReadAbstractFunction.java
@@ -63,11 +63,26 @@
}
@Override
+ public InFlowKind getKind() {
+ return InFlowKind.ABSTRACT_FUNCTION_INSTANCE_FIELD_READ;
+ }
+
+ @Override
public boolean hasSingleInFlow() {
return false;
}
@Override
+ public int internalCompareToSameKind(InFlow other) {
+ InstanceFieldReadAbstractFunction fn = other.asInstanceFieldReadAbstractFunction();
+ int result = receiver.compareTo(fn.receiver);
+ if (result == 0) {
+ result = field.compareTo(fn.field);
+ }
+ return result;
+ }
+
+ @Override
public boolean isInstanceFieldReadAbstractFunction() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java
index b8481e4..1c83bfa 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.BooleanUtils;
import java.util.Objects;
public class MethodParameter implements BaseInFlow {
@@ -29,6 +30,11 @@
return new MethodParameter(method, index, true);
}
+ @Override
+ public InFlowKind getKind() {
+ return InFlowKind.METHOD_PARAMETER;
+ }
+
public DexMethod getMethod() {
return method;
}
@@ -42,6 +48,21 @@
}
@Override
+ public int internalCompareToSameKind(InFlow other) {
+ MethodParameter methodParameter = other.asMethodParameter();
+ int result = method.compareTo(methodParameter.method);
+ if (result == 0) {
+ result = index - methodParameter.index;
+ }
+ if (result == 0) {
+ result =
+ BooleanUtils.intValue(isMethodStatic)
+ - BooleanUtils.intValue(methodParameter.isMethodStatic);
+ }
+ return result;
+ }
+
+ @Override
public boolean isMethodParameter() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/OrAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/OrAbstractFunction.java
index ca1223f..c5f9cc5 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/OrAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/OrAbstractFunction.java
@@ -27,6 +27,11 @@
}
@Override
+ public OrAbstractFunction asOrAbstractFunction() {
+ return this;
+ }
+
+ @Override
public ValueState apply(
AppView<AppInfoWithLiveness> appView,
FlowGraphStateProvider flowGraphStateProvider,
@@ -55,6 +60,21 @@
}
@Override
+ public InFlowKind getKind() {
+ return InFlowKind.ABSTRACT_FUNCTION_OR;
+ }
+
+ @Override
+ public int internalCompareToSameKind(InFlow other) {
+ OrAbstractFunction fn = other.asOrAbstractFunction();
+ int result = inFlow.compareTo(fn.inFlow);
+ if (result == 0) {
+ result = constant.getIntValue() - fn.constant.getIntValue();
+ }
+ return result;
+ }
+
+ @Override
@SuppressWarnings("EqualsGetClass")
public boolean equals(Object obj) {
if (this == obj) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java
index f9ffd98..4a4a665 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownAbstractFunction.java
@@ -36,6 +36,17 @@
}
@Override
+ public InFlowKind getKind() {
+ return InFlowKind.ABSTRACT_FUNCTION_UNKNOWN;
+ }
+
+ @Override
+ public int internalCompareToSameKind(InFlow inFlow) {
+ assert this == inFlow;
+ return 0;
+ }
+
+ @Override
public boolean isUnknownAbstractFunction() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraph.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraph.java
index e0e8d9f..58ebf78 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraph.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraph.java
@@ -18,29 +18,30 @@
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
import com.android.tools.r8.optimize.argumentpropagation.utils.BidirectedGraph;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.WorkList;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
-import java.util.Collection;
-import java.util.IdentityHashMap;
-import java.util.Map;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class FlowGraph extends BidirectedGraph<FlowGraphNode> implements FlowGraphStateProvider {
- private final Map<DexField, FlowGraphFieldNode> fieldNodes;
- private final Map<DexMethod, Int2ReferenceMap<FlowGraphParameterNode>> parameterNodes;
+ private final LinkedHashMap<DexField, FlowGraphFieldNode> fieldNodes;
+ private final LinkedHashMap<DexMethod, Int2ReferenceMap<FlowGraphParameterNode>> parameterNodes;
public FlowGraph(
- Map<DexField, FlowGraphFieldNode> fieldNodes,
- Map<DexMethod, Int2ReferenceMap<FlowGraphParameterNode>> parameterNodes) {
+ LinkedHashMap<DexField, FlowGraphFieldNode> fieldNodes,
+ LinkedHashMap<DexMethod, Int2ReferenceMap<FlowGraphParameterNode>> parameterNodes) {
this.fieldNodes = fieldNodes;
this.parameterNodes = parameterNodes;
}
- public FlowGraph(Collection<FlowGraphNode> nodes) {
- this(new IdentityHashMap<>(), new IdentityHashMap<>());
+ public FlowGraph(LinkedHashSet<FlowGraphNode> nodes) {
+ this(new LinkedHashMap<>(), new LinkedHashMap<>());
for (FlowGraphNode node : nodes) {
if (node.isFieldNode()) {
FlowGraphFieldNode fieldNode = node.asFieldNode();
@@ -63,6 +64,11 @@
return new FlowGraphBuilder(appView, converter, fieldStates, methodStates);
}
+ @Override
+ public Set<FlowGraphNode> computeStronglyConnectedComponent(FlowGraphNode node) {
+ return computeStronglyConnectedComponent(node, WorkList.newWorkList(new LinkedHashSet<>()));
+ }
+
public void forEachFieldNode(Consumer<? super FlowGraphFieldNode> consumer) {
fieldNodes.values().forEach(consumer);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphBuilder.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphBuilder.java
index 45ecb34..d564575 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphBuilder.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphBuilder.java
@@ -27,13 +27,14 @@
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.TraversalContinuation;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
-import java.util.IdentityHashMap;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
public class FlowGraphBuilder {
@@ -42,9 +43,9 @@
private final FieldStateCollection fieldStates;
private final MethodStateCollectionByReference methodStates;
- private final Map<DexField, FlowGraphFieldNode> fieldNodes = new IdentityHashMap<>();
- private final Map<DexMethod, Int2ReferenceMap<FlowGraphParameterNode>> parameterNodes =
- new IdentityHashMap<>();
+ private final LinkedHashMap<DexField, FlowGraphFieldNode> fieldNodes = new LinkedHashMap<>();
+ private final LinkedHashMap<DexMethod, Int2ReferenceMap<FlowGraphParameterNode>> parameterNodes =
+ new LinkedHashMap<>();
public FlowGraphBuilder(
AppView<AppInfoWithLiveness> appView,
@@ -58,7 +59,7 @@
}
public FlowGraphBuilder addClasses() {
- appView.appInfo().classes().forEach(this::add);
+ appView.appInfo().classesWithDeterministicOrder().forEach(this::add);
return this;
}
@@ -94,7 +95,9 @@
}
FlowGraphFieldNode node = getOrCreateFieldNode(field, concreteFieldState);
- for (InFlow inFlow : concreteFieldState.getInFlow()) {
+ List<InFlow> inFlowWithDeterministicOrder =
+ ListUtils.sort(concreteFieldState.getInFlow(), Comparator.naturalOrder());
+ for (InFlow inFlow : inFlowWithDeterministicOrder) {
if (addInFlow(inFlow, node).shouldBreak()) {
assert node.isUnknown();
break;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphNode.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphNode.java
index 7e9e448..7f9f0f0 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphNode.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FlowGraphNode.java
@@ -16,19 +16,18 @@
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Action;
-import com.google.common.collect.Sets;
import java.util.Deque;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
-import java.util.Map;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
public abstract class FlowGraphNode {
- private final Set<FlowGraphNode> predecessors = Sets.newIdentityHashSet();
- private final Map<FlowGraphNode, Set<AbstractFunction>> successors = new IdentityHashMap<>();
+ private final LinkedHashSet<FlowGraphNode> predecessors = new LinkedHashSet<>();
+ private final LinkedHashMap<FlowGraphNode, LinkedHashSet<AbstractFunction>> successors =
+ new LinkedHashMap<>();
@CheckDiscard private boolean debug = false;
@@ -76,7 +75,10 @@
}
void addPredecessor(FlowGraphNode predecessor, AbstractFunction abstractFunction) {
- predecessor.successors.computeIfAbsent(this, ignoreKey(HashSet::new)).add(abstractFunction);
+ predecessor
+ .successors
+ .computeIfAbsent(this, ignoreKey(LinkedHashSet::new))
+ .add(abstractFunction);
predecessors.add(predecessor);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
index 2cdcf24..796f991 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
@@ -34,6 +34,7 @@
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -98,7 +99,12 @@
.build();
List<Set<FlowGraphNode>> stronglyConnectedComponents =
flowGraph.computeStronglyConnectedComponents();
- return ListUtils.map(stronglyConnectedComponents, FlowGraph::new);
+ List<LinkedHashSet<FlowGraphNode>> stronglyConnectedComponentsWithDeterministicOrder =
+ ListUtils.map(
+ stronglyConnectedComponents,
+ stronglyConnectedComponent ->
+ (LinkedHashSet<FlowGraphNode>) stronglyConnectedComponent);
+ return ListUtils.map(stronglyConnectedComponentsWithDeterministicOrder, FlowGraph::new);
}
private Map<FlowGraph, Deque<FlowGraphNode>> includeDefaultValuesInFieldStates(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/BidirectedGraph.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/BidirectedGraph.java
index b7442b5..9427213 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/BidirectedGraph.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/BidirectedGraph.java
@@ -37,11 +37,16 @@
}
public Set<T> computeStronglyConnectedComponent(T node) {
- WorkList<T> worklist = WorkList.newEqualityWorkList(node);
+ return computeStronglyConnectedComponent(node, WorkList.newEqualityWorkList());
+ }
+
+ protected Set<T> computeStronglyConnectedComponent(T node, WorkList<T> worklist) {
+ assert worklist.isEmpty();
+ worklist.addIfNotSeen(node);
while (worklist.hasNext()) {
T current = worklist.next();
forEachNeighbor(current, worklist::addIfNotSeen);
}
- return worklist.getSeenSet();
+ return worklist.getMutableSeenSet();
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java b/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java
index 2d940e9..46c4d58 100644
--- a/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java
+++ b/src/main/java/com/android/tools/r8/optimize/compose/UpdateChangedFlagsAbstractFunction.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteValueState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.FlowGraphStateProvider;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.InFlow;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.InFlowKind;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.OrAbstractFunction;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -123,6 +124,16 @@
}
@Override
+ public InFlowKind getKind() {
+ return InFlowKind.ABSTRACT_FUNCTION_UPDATE_CHANGED_FLAGS;
+ }
+
+ @Override
+ public int internalCompareToSameKind(InFlow other) {
+ return inFlow.compareTo(other.asUpdateChangedFlagsAbstractFunction().inFlow);
+ }
+
+ @Override
public boolean isUpdateChangedFlagsAbstractFunction() {
return true;
}