[llvm] [RegisterCoalescer] Fix compile-time blowup with partially-reserved physregs (PR #181728)

Brian Cain via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 16 11:25:10 PST 2026


https://github.com/androm3da created https://github.com/llvm/llvm-project/pull/181728

canJoinPhys checks isReserved on the super-register but joinReservedPhysReg additionally requires every register-unit root to be reserved.  When a target reserves only part of a super-register (e.g. Hexagon -ffixed-r19 reserves R19 and D9, but not R18), the mismatch causes joinReservedPhysReg to fail, reMaterializeDef to fail, and the copy to be unconditionally retried (Again = true) every worklist round.  In large functions this O(N^2) retry behaviour leads to multi-hour compile times.

Add the same regunit-root-reserved check to canJoinPhys so that it returns false early, routing through the !canJoinPhys path where Again is only set when IsDefCopy is true.

>From 89741d54892359d09a6a2cf5395e4a13b18d32a7 Mon Sep 17 00:00:00 2001
From: Brian Cain <brian.cain at oss.qualcomm.com>
Date: Mon, 16 Feb 2026 11:21:16 -0800
Subject: [PATCH] [RegisterCoalescer] Fix compile-time blowup with
 partially-reserved physregs

canJoinPhys checks isReserved on the super-register but
joinReservedPhysReg additionally requires every register-unit root to
be reserved.  When a target reserves only part of a super-register
(e.g. Hexagon -ffixed-r19 reserves R19 and D9, but not R18), the
mismatch causes joinReservedPhysReg to fail, reMaterializeDef to fail,
and the copy to be unconditionally retried (Again = true) every
worklist round.  In large functions this O(N^2) retry behaviour leads
to multi-hour compile times.

Add the same regunit-root-reserved check to canJoinPhys so that it
returns false early, routing through the !canJoinPhys path where Again
is only set when IsDefCopy is true.
---
 llvm/lib/CodeGen/RegisterCoalescer.cpp        | 14 +++++++++++++
 .../coalescer-partial-reserved-physreg.mir    | 20 +++++++++++++++++++
 2 files changed, 34 insertions(+)
 create mode 100644 llvm/test/CodeGen/Hexagon/coalescer-partial-reserved-physreg.mir

diff --git a/llvm/lib/CodeGen/RegisterCoalescer.cpp b/llvm/lib/CodeGen/RegisterCoalescer.cpp
index 586c27b7e3baf..6db602ee5fb2a 100644
--- a/llvm/lib/CodeGen/RegisterCoalescer.cpp
+++ b/llvm/lib/CodeGen/RegisterCoalescer.cpp
@@ -1982,6 +1982,20 @@ bool RegisterCoalescer::canJoinPhys(const CoalescerPair &CP) {
     return false;
   }
 
+  // Verify all register unit roots are reserved. joinReservedPhysReg requires
+  // this, so reject early to route the copy through the !canJoinPhys path
+  // where Again is only set when IsDefCopy is true.
+  if (!MRI->isConstantPhysReg(CP.getDstReg())) {
+    for (MCRegUnit Unit : TRI->regunits(CP.getDstReg())) {
+      for (MCRegUnitRootIterator RI(Unit, TRI); RI.isValid(); ++RI) {
+        if (!MRI->isReserved(*RI)) {
+          LLVM_DEBUG(dbgs() << "\tNot all register unit roots reserved.\n");
+          return false;
+        }
+      }
+    }
+  }
+
   LiveInterval &JoinVInt = LIS->getInterval(CP.getSrcReg());
   if (JoinVInt.containsOneValue())
     return true;
diff --git a/llvm/test/CodeGen/Hexagon/coalescer-partial-reserved-physreg.mir b/llvm/test/CodeGen/Hexagon/coalescer-partial-reserved-physreg.mir
new file mode 100644
index 0000000000000..1b3c2f306edec
--- /dev/null
+++ b/llvm/test/CodeGen/Hexagon/coalescer-partial-reserved-physreg.mir
@@ -0,0 +1,20 @@
+# RUN: llc -mtriple=hexagon -mattr=+reserved-r19 \
+# RUN:   -run-pass=register-coalescer -o - %s | FileCheck %s
+#
+# Regression test for a compile-time blowup in the register coalescer with
+# partially-reserved physical registers.
+#
+# CHECK-LABEL: name: test
+# CHECK: bb.0:
+---
+name: test
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $r31
+
+    %0:doubleregs = A2_tfrpi 1
+    $r18 = COPY %0.isub_lo:doubleregs
+    J2_jumpr $r31, implicit-def dead $pc, implicit $r18
+
+...



More information about the llvm-commits mailing list