[llvm] [X86] Clear regcall arg aliases in dynamic CSR masks (PR #225262)

via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 21 18:46:20 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-x86

Author: Demetrios Chiuratto Agourakis (agourakis82)

<details>
<summary>Changes</summary>

### Summary
On x86-64 `x86_regcallcc`, dynamic call-preserved mask construction cleared only `subregs_inclusive` for argument registers. When an argument used a 32-bit subregister (e.g. `R14D`), the overlapping 64-bit superregister (`R14`) remained marked preserved. A live `i64` in that superregister could then be incorrectly kept across the call and read after the callee clobbered it.

### Fix
Use `MCRegAliasIterator` when clearing argument/return registers from dynamic CSR masks, matching callee-side `disableCalleeSavedRegister` alias handling.

### Test plan
- New test: `llvm/test/CodeGen/X86/regcall-subreg-argmask.ll`
- Manual repro from #<!-- -->225057:
  - before: `CustomRegMask` contained `$r14` while arg was `$r14d`; asm compared `%r14` after call
  - after: mask no longer contains `$r14`; value copied to a preserved reg (`%rbx`) before call

Fixes #<!-- -->225057

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


2 Files Affected:

- (modified) llvm/lib/Target/X86/X86ISelLoweringCall.cpp (+11-6) 
- (added) llvm/test/CodeGen/X86/regcall-subreg-argmask.ll (+37) 


``````````diff
diff --git a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
index 9a03da14ee10e..70f6a3222ebbe 100644
--- a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
+++ b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
@@ -1167,8 +1167,9 @@ SDValue X86TargetLowering::LowerCallResult(
     // In some calling conventions we need to remove the used registers
     // from the register mask.
     if (RegMask) {
-      for (MCPhysReg SubReg : TRI->subregs_inclusive(VA.getLocReg()))
-        RegMask[SubReg / 32] &= ~(1u << (SubReg % 32));
+      for (MCRegAliasIterator Alias(VA.getLocReg(), TRI, true); Alias.isValid();
+           ++Alias)
+        RegMask[*Alias / 32] &= ~(1u << (*Alias % 32));
     }
 
     // Report an error if there was an attempt to return FP values via XMM
@@ -2682,12 +2683,16 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
     unsigned RegMaskSize = MachineOperand::getRegMaskSize(TRI->getNumRegs());
     memcpy(RegMask, Mask, sizeof(RegMask[0]) * RegMaskSize);
 
-    // Make sure all sub registers of the argument registers are reset
-    // in the RegMask.
+    // Make sure all aliases of the argument registers are reset in the
+    // RegMask, including superregisters. Clearing only subregs_inclusive is
+    // insufficient: an i32 argument in R14D must also remove R14 from the
+    // preserved set, otherwise a live 64-bit value in R14 can be incorrectly
+    // kept across the call (see llvm/llvm-project#225057).
     if (ShouldDisableArgRegs) {
       for (auto const &RegPair : RegsToPass)
-        for (MCPhysReg SubReg : TRI->subregs_inclusive(RegPair.first))
-          RegMask[SubReg / 32] &= ~(1u << (SubReg % 32));
+        for (MCRegAliasIterator Alias(RegPair.first, TRI, true); Alias.isValid();
+             ++Alias)
+          RegMask[*Alias / 32] &= ~(1u << (*Alias % 32));
     }
 
     // Create the RegMask Operand according to our updated mask.
diff --git a/llvm/test/CodeGen/X86/regcall-subreg-argmask.ll b/llvm/test/CodeGen/X86/regcall-subreg-argmask.ll
new file mode 100644
index 0000000000000..52d354728e994
--- /dev/null
+++ b/llvm/test/CodeGen/X86/regcall-subreg-argmask.ll
@@ -0,0 +1,37 @@
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -O2 | FileCheck %s
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -O2 -stop-after=finalize-isel | FileCheck %s --check-prefix=MIR
+
+; When an x86_regcall argument uses a 32-bit subregister (R14D), the call
+; preserved mask must also clear the 64-bit superregister (R14). Otherwise a
+; live i64 in R14 can be incorrectly preserved across the call.
+;
+; Related to llvm/llvm-project#225057.
+
+target triple = "x86_64-unknown-linux-gnu"
+
+define x86_regcallcc void @callee(i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i32 %a9) noinline nounwind {
+  call void asm sideeffect "movq $$99, %r14", "~{r14}"()
+  ret void
+}
+
+define i32 @main() nounwind {
+; CHECK-LABEL: main:
+; CHECK:       # %bb.0:
+; CHECK:         movq $4, %r14
+; CHECK:         movq %r14, %[[SAVE:.*]]
+; CHECK:         callq
+; CHECK:         cmpq $4, %[[SAVE]]
+; CHECK-NOT:     cmpq $4, %r14
+entry:
+  %index = call i64 asm sideeffect "movq $$4, $0", "={r14}"()
+  %arg = trunc i64 %index to i32
+  call x86_regcallcc void @callee(i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, i32 %arg)
+  %wrong = icmp ne i64 %index, 4
+  %result = zext i1 %wrong to i32
+  ret i32 %result
+}
+
+; MIR-LABEL: name: main
+; MIR: CALL64m {{.*}} CustomRegMask(
+; MIR-NOT: $r14,
+; MIR-SAME: ), {{.*}} implicit $r14d

``````````

</details>


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


More information about the llvm-commits mailing list