Account for invoke/range where all operands are defined later in the IR

Fixes: b/382441994
Change-Id: I45cf93bf205fc5b8ec5f25d97a09ebd56ebe2322
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 7c8e08e..f85e068 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -1484,68 +1484,76 @@
             split -> split.isInvokeRangeIntervals() && !split.hasRegister());
     timing.end();
     timing.begin("Process splits");
-    for (LiveIntervals split : invokeRangeIntervals) {
-      timing.begin("Extract list");
-      Invoke invoke = split.getIsInvokeRangeIntervals();
-      List<LiveIntervals> intervalsList =
-          ListUtils.map(
-              invoke.arguments(),
-              invokeArgument -> {
-                LiveIntervals overlappingInvokeArgumentIntervals =
-                    invokeArgument.getLiveIntervals().getSplitCovering(invoke);
-                assert !overlappingInvokeArgumentIntervals.hasRegister();
-                assert overlappingInvokeArgumentIntervals.getStart() == invoke.getNumber() - 1;
-                assert overlappingInvokeArgumentIntervals.getEnd() == invoke.getNumber()
-                    || overlappingInvokeArgumentIntervals.getEnd() == invoke.getNumber() + 1;
-                return overlappingInvokeArgumentIntervals;
-              });
-      timing.end();
-      timing.begin("Prelude");
-
-      // Save the current register allocation state so we can restore it at the end.
-      timing.begin("Copy free registers");
-      IntSortedSet savedFreeRegisters = new IntRBTreeSet(freeRegisters);
-      int savedMaxRegisterNumber = maxRegisterNumber;
-      timing.end();
-
-      // Simulate adding all the active intervals to the inactive set by blocking their register if
-      // they overlap with any of the invoke/range intervals.
-      timing.begin("Overlaps active");
-      for (LiveIntervals active : active) {
-        // We could allow the use of all the currently active registers for the ranged invoke (by
-        // adding the registers for all the active intervals to freeRegisters here). That could lead
-        // to lower register pressure. However, it would also often mean that we cannot allocate the
-        // right argument register to the current unhandled interval. Size measurements on GMSCore
-        // indicate that blocking the current active registers works the best for code size.
-        if (Iterables.any(intervalsList, active::overlaps)) {
-          excludeRegistersForInterval(active);
-        } else if (active.isArgumentInterval()) {
-          // Allow the ranged invoke to use argument registers if free. This improves register
-          // allocation for bridge methods that forwards all of their arguments after check-cast
-          // checks on their types.
-          freeOccupiedRegistersForIntervals(active);
-        }
-      }
-      timing.end();
-
-      timing.begin("Remove intervals from unhandled");
-      intervalsList.forEach(LiveIntervals::setHandled);
-      timing.end();
-      timing.end();
-      timing.begin("Allocate");
-      allocateLinkedIntervals(intervalsList, invoke);
-      timing.end();
-      timing.begin("Postlude");
-      // Restore the register allocation state.
-      freeRegisters = savedFreeRegisters;
-      // In case maxRegisterNumber has changed, update freeRegisters.
-      for (int i = savedMaxRegisterNumber + 1; i <= maxRegisterNumber; i++) {
-        freeRegisters.add(i);
-      }
-      // Move all the argument intervals to the inactive set.
-      inactive.addAll(intervalsList);
-      timing.end();
+    if (unhandledIntervals.isSplitParent() && unhandledIntervals.isInvokeRangeIntervals()) {
+      allocateRegistersForInvokeRangeSplit(unhandledIntervals);
     }
+    for (LiveIntervals split : invokeRangeIntervals) {
+      allocateRegistersForInvokeRangeSplit(split);
+    }
+    timing.end();
+  }
+
+  private void allocateRegistersForInvokeRangeSplit(LiveIntervals split) {
+
+    timing.begin("Extract list");
+    Invoke invoke = split.getIsInvokeRangeIntervals();
+    List<LiveIntervals> intervalsList =
+        ListUtils.map(
+            invoke.arguments(),
+            invokeArgument -> {
+              LiveIntervals overlappingInvokeArgumentIntervals =
+                  invokeArgument.getLiveIntervals().getSplitCovering(invoke);
+              assert !overlappingInvokeArgumentIntervals.hasRegister();
+              assert overlappingInvokeArgumentIntervals.getStart() == invoke.getNumber() - 1;
+              assert overlappingInvokeArgumentIntervals.getEnd() == invoke.getNumber()
+                  || overlappingInvokeArgumentIntervals.getEnd() == invoke.getNumber() + 1;
+              return overlappingInvokeArgumentIntervals;
+            });
+    timing.end();
+    timing.begin("Prelude");
+
+    // Save the current register allocation state so we can restore it at the end.
+    timing.begin("Copy free registers");
+    IntSortedSet savedFreeRegisters = new IntRBTreeSet(freeRegisters);
+    int savedMaxRegisterNumber = maxRegisterNumber;
+    timing.end();
+
+    // Simulate adding all the active intervals to the inactive set by blocking their register if
+    // they overlap with any of the invoke/range intervals.
+    timing.begin("Overlaps active");
+    for (LiveIntervals active : active) {
+      // We could allow the use of all the currently active registers for the ranged invoke (by
+      // adding the registers for all the active intervals to freeRegisters here). That could lead
+      // to lower register pressure. However, it would also often mean that we cannot allocate the
+      // right argument register to the current unhandled interval. Size measurements on GMSCore
+      // indicate that blocking the current active registers works the best for code size.
+      if (Iterables.any(intervalsList, active::overlaps)) {
+        excludeRegistersForInterval(active);
+      } else if (active.isArgumentInterval()) {
+        // Allow the ranged invoke to use argument registers if free. This improves register
+        // allocation for bridge methods that forwards all of their arguments after check-cast
+        // checks on their types.
+        freeOccupiedRegistersForIntervals(active);
+      }
+    }
+    timing.end();
+
+    timing.begin("Remove intervals from unhandled");
+    intervalsList.forEach(LiveIntervals::setHandled);
+    timing.end();
+    timing.end();
+    timing.begin("Allocate");
+    allocateLinkedIntervals(intervalsList, invoke);
+    timing.end();
+    timing.begin("Postlude");
+    // Restore the register allocation state.
+    freeRegisters = savedFreeRegisters;
+    // In case maxRegisterNumber has changed, update freeRegisters.
+    for (int i = savedMaxRegisterNumber + 1; i <= maxRegisterNumber; i++) {
+      freeRegisters.add(i);
+    }
+    // Move all the argument intervals to the inactive set.
+    inactive.addAll(intervalsList);
     timing.end();
   }