[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