Do not copy callees in cycle eliminator
Bug: 131860173
Change-Id: Ibb659ad2b68dae0ec0d28873989d68d81e40254f
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
index 4d46084..d4ed94b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -75,8 +75,12 @@
}
}
- public Node[] getCalleesWithDeterministicOrder() {
- return callees.toArray(Node.EMPTY_ARRAY);
+ public Set<Node> getCallersWithDeterministicOrder() {
+ return callers;
+ }
+
+ public Set<Node> getCalleesWithDeterministicOrder() {
+ return callees;
}
public int getNumberOfCallSites() {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilder.java
index 699cd86..7606901 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilder.java
@@ -26,7 +26,6 @@
import com.google.common.collect.Sets;
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
@@ -345,20 +344,30 @@
// The callees must be sorted before calling traverse recursively. This ensures that cycles
// are broken the same way across multiple compilations.
- Node[] callees = node.getCalleesWithDeterministicOrder();
+ Collection<Node> callees = node.getCalleesWithDeterministicOrder();
if (options.testing.nondeterministicCycleElimination) {
- reorderNodes(Arrays.asList(callees));
+ callees = reorderNodes(new ArrayList<>(callees));
}
- for (Node callee : callees) {
+ Iterator<Node> calleeIterator = callees.iterator();
+ while (calleeIterator.hasNext()) {
+ Node callee = calleeIterator.next();
if (stackSet.contains(callee)) {
// Found a cycle that needs to be eliminated.
numberOfCycles++;
if (edgeRemovalIsSafe(node, callee)) {
// Break the cycle by removing the edge node->callee.
- callee.removeCaller(node);
+ if (options.testing.nondeterministicCycleElimination) {
+ callee.removeCaller(node);
+ } else {
+ // Need to remove `callee` from `node.callees` using the iterator to prevent a
+ // ConcurrentModificationException. This is not needed when nondeterministic cycle
+ // elimination is enabled, because we iterate a copy of `node.callees` in that case.
+ calleeIterator.remove();
+ callee.getCallersWithDeterministicOrder().remove(node);
+ }
if (Log.ENABLED) {
Log.info(