[llvm] [RegAllocFast] Give an undef tied use the register of its tied def (PR #222249)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 8 23:16:36 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-x86

Author: Fangrui Song (MaskRay)

<details>
<summary>Changes</summary>

X86TargetLowering::emitSetJmpShadowStackFix zeroes a register by
building an XOR whose two uses are undef reads of its own def (per
MachineOperand.h "... reading the same dont-care value"). Def processing
frees a def whose tied use is undef before the uses are allocated, so
allocVirtRegUndef() no longer finds the virtual register and takes the
head of the allocation order instead. -verify-machineinstrs would fail:

```
renamable $rcx = XOR64rr undef renamable $rax(tied-def 0), undef renamable $rax, implicit-def dead $eflags
*** Bad machine code: Tied physical registers must match. ***
```

Take the register from the tie, and rewrite the instruction's other
reads of the value with it so that they keep agreeing on one register.

Aided by Opus 5


---
Full diff: https://github.com/llvm/llvm-project/pull/222249.diff


2 Files Affected:

- (modified) llvm/lib/CodeGen/RegAllocFast.cpp (+22) 
- (added) llvm/test/CodeGen/X86/regallocfast-undef-tied-use.mir (+21) 


``````````diff
diff --git a/llvm/lib/CodeGen/RegAllocFast.cpp b/llvm/lib/CodeGen/RegAllocFast.cpp
index 3db118c62fb09..a084faf3cc108 100644
--- a/llvm/lib/CodeGen/RegAllocFast.cpp
+++ b/llvm/lib/CodeGen/RegAllocFast.cpp
@@ -1031,6 +1031,28 @@ void RegAllocFastImpl::allocVirtRegUndef(MachineOperand &MO) {
   if (!shouldAllocateRegister(VirtReg))
     return;
 
+  // The def is freed before the uses are allocated, so a tie is the only
+  // record left of its register. Rewrite every read of VirtReg so they agree.
+  MachineInstr &MI = *MO.getParent();
+  for (const MachineOperand &Tied : MI.all_uses()) {
+    if (!Tied.isTied() || Tied.getReg() != VirtReg)
+      continue;
+    unsigned TiedIdx = MI.findTiedOperandIdx(MI.getOperandNo(&Tied));
+    MCRegister DefReg = MI.getOperand(TiedIdx).getReg().asMCReg();
+    for (MachineOperand &O : MI.all_uses()) {
+      if (O.getReg() != VirtReg)
+        continue;
+      // A tie needs the register itself, not the subregister it names.
+      MCRegister Reg = DefReg;
+      if (unsigned SubIdx = O.getSubReg(); SubIdx && !O.isTied())
+        Reg = TRI->getSubReg(DefReg, SubIdx);
+      O.setSubReg(0);
+      O.setReg(Reg);
+      O.setIsRenamable(!MRI->isReserved(Reg));
+    }
+    return;
+  }
+
   LiveRegMap::iterator LRI = findLiveVirtReg(VirtReg);
   MCRegister PhysReg;
   bool IsRenamable = true;
diff --git a/llvm/test/CodeGen/X86/regallocfast-undef-tied-use.mir b/llvm/test/CodeGen/X86/regallocfast-undef-tied-use.mir
new file mode 100644
index 0000000000000..b9cef97eca525
--- /dev/null
+++ b/llvm/test/CodeGen/X86/regallocfast-undef-tied-use.mir
@@ -0,0 +1,21 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=x86_64-linux -run-pass=regallocfast -verify-machineinstrs %s -o - | FileCheck %s
+
+## X86 builds this zeroing XOR for __builtin_setjmp with a shadow stack. The
+## copy hint puts the def in $rcx, which the undef reads have to follow.
+
+--- |
+  define i64 @undef_tied_use() { ret i64 0 }
+...
+---
+name: undef_tied_use
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: name: undef_tied_use
+    ; CHECK: renamable $rcx = XOR64rr undef renamable $rcx, undef renamable $rcx, implicit-def dead $eflags
+    ; CHECK-NEXT: RET64 implicit killed $rcx
+    %0:gr64 = XOR64rr undef %0, undef %0, implicit-def dead $eflags
+    $rcx = COPY %0
+    RET64 implicit $rcx
+...

``````````

</details>


https://github.com/llvm/llvm-project/pull/222249


More information about the llvm-commits mailing list