[llvm] fix preserve_none in X86 backend (PR #192300)
Jerry Dang via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 15 19:59:57 PDT 2026
https://github.com/kuroyukiasuna updated https://github.com/llvm/llvm-project/pull/192300
>From 8b19cda235970b22daf4308247dcf4908f18be60 Mon Sep 17 00:00:00 2001
From: Jerry Dang <kuroyukiasuna at gmail.com>
Date: Wed, 15 Apr 2026 11:57:36 -0400
Subject: [PATCH 1/2] fix preserve_none
---
llvm/lib/Target/X86/X86ISelLoweringCall.cpp | 9 +++++++++
llvm/test/CodeGen/X86/preserve_nonecc_call.ll | 18 ++++++++++++++++++
2 files changed, 27 insertions(+)
diff --git a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
index 37c80e27f4bd2..f1a0a0047a4fd 100644
--- a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
+++ b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
@@ -2953,6 +2953,15 @@ bool X86TargetLowering::isEligibleForSiblingCallOpt(
if (!mayTailCallThisCC(CalleeCC))
return false;
+ // preserve_nonecc uses a distinct caller/callee-saved register convention.
+ // Be conservative and avoid sibling tail calls when byval arguments are
+ // present, as they require stack argument copying with stricter layout
+ // constraints.
+ if (CalleeCC == CallingConv::PreserveNone)
+ for (const auto &Out : Outs)
+ if (Out.Flags.isByVal())
+ return false;
+
// If -tailcallopt is specified, make fastcc functions tail-callable.
MachineFunction &MF = DAG.getMachineFunction();
X86MachineFunctionInfo *FuncInfo = MF.getInfo<X86MachineFunctionInfo>();
diff --git a/llvm/test/CodeGen/X86/preserve_nonecc_call.ll b/llvm/test/CodeGen/X86/preserve_nonecc_call.ll
index 500ebb139811a..df1ef74b86ae3 100644
--- a/llvm/test/CodeGen/X86/preserve_nonecc_call.ll
+++ b/llvm/test/CodeGen/X86/preserve_nonecc_call.ll
@@ -55,6 +55,24 @@ define preserve_nonecc void @caller2(ptr %a) {
ret void
}
+%S = type { [3 x i64] }
+declare preserve_nonecc void @callee_byval(ptr byval(%S) align 8)
+
+; Preserve_none caller with a byval argument should not be lowered as a
+; sibling tail call on X86.
+define preserve_nonecc void @caller_byval(ptr %a) {
+; CHECK-LABEL: caller_byval:
+; CHECK: # %bb.0:
+; CHECK-NEXT: pushq %rax
+; CHECK-NEXT: .cfi_def_cfa_offset 16
+; CHECK-NEXT: callq callee_byval at PLT
+; CHECK-NEXT: popq %rax
+; CHECK-NEXT: .cfi_def_cfa_offset 8
+; CHECK-NEXT: retq
+ tail call preserve_nonecc void @callee_byval(ptr byval(%S) align 8 %a)
+ ret void
+}
+
; Preserve_none function can use more registers to pass parameters.
declare preserve_nonecc i64 @callee_with_many_param2(i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i64 %a9, i64 %a10, i64 %a11)
define preserve_nonecc i64 @callee_with_many_param(i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i64 %a9, i64 %a10, i64 %a11, i64 %a12) {
>From b60d65c477787b45f7a0a51fa25587624f9d3603 Mon Sep 17 00:00:00 2001
From: Jerry Dang <kuroyukiasuna at gmail.com>
Date: Wed, 15 Apr 2026 21:44:44 -0400
Subject: [PATCH 2/2] fix byval handling when argument is register-located
---
llvm/lib/Target/X86/X86ISelLoweringCall.cpp | 20 ++++++-------
llvm/test/CodeGen/X86/preserve_nonecc_call.ll | 28 +++++++++++++------
2 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
index f1a0a0047a4fd..8b08158e34431 100644
--- a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
+++ b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
@@ -2953,15 +2953,6 @@ bool X86TargetLowering::isEligibleForSiblingCallOpt(
if (!mayTailCallThisCC(CalleeCC))
return false;
- // preserve_nonecc uses a distinct caller/callee-saved register convention.
- // Be conservative and avoid sibling tail calls when byval arguments are
- // present, as they require stack argument copying with stricter layout
- // constraints.
- if (CalleeCC == CallingConv::PreserveNone)
- for (const auto &Out : Outs)
- if (Out.Flags.isByVal())
- return false;
-
// If -tailcallopt is specified, make fastcc functions tail-callable.
MachineFunction &MF = DAG.getMachineFunction();
X86MachineFunctionInfo *FuncInfo = MF.getInfo<X86MachineFunctionInfo>();
@@ -3079,7 +3070,7 @@ bool X86TargetLowering::isEligibleForSiblingCallOpt(
// If the callee takes no arguments then go on to check the results of the
// call.
if (!Outs.empty()) {
- if (StackArgsSize > 0) {
+ if (ArgLocs.size() > 0) {
// Check if the arguments are already laid out in the right way as
// the caller's fixed stack objects.
MachineFrameInfo &MFI = MF.getFrameInfo();
@@ -3091,7 +3082,14 @@ bool X86TargetLowering::isEligibleForSiblingCallOpt(
ISD::ArgFlagsTy Flags = Outs[I].Flags;
if (VA.getLocInfo() == CCValAssign::Indirect)
return false;
- if (!VA.isRegLoc()) {
+ // Byval arguments require the caller to make a hidden copy of the
+ // pointee. If the argument is passed in a register (e.g.
+ // preserve_none), the copy may not have been materialized, so sibling
+ // call optimization is not safe.
+ if (VA.isRegLoc() && Flags.isByVal())
+ return false;
+
+ if (!VA.isRegLoc() && StackArgsSize > 0) {
if (!MatchingStackOffset(Arg, VA.getLocMemOffset(), Flags, MFI, MRI,
TII, VA))
return false;
diff --git a/llvm/test/CodeGen/X86/preserve_nonecc_call.ll b/llvm/test/CodeGen/X86/preserve_nonecc_call.ll
index df1ef74b86ae3..80337492ded65 100644
--- a/llvm/test/CodeGen/X86/preserve_nonecc_call.ll
+++ b/llvm/test/CodeGen/X86/preserve_nonecc_call.ll
@@ -58,18 +58,30 @@ define preserve_nonecc void @caller2(ptr %a) {
%S = type { [3 x i64] }
declare preserve_nonecc void @callee_byval(ptr byval(%S) align 8)
-; Preserve_none caller with a byval argument should not be lowered as a
-; sibling tail call on X86.
-define preserve_nonecc void @caller_byval(ptr %a) {
-; CHECK-LABEL: caller_byval:
+; Preserve_none caller with a byval argument from a local alloca should not
+; be lowered as a sibling tail call, as this would corrupt the stack frame
+; containing the byval copy.
+define preserve_nonecc void @caller_byval_alloca() {
+; CHECK-LABEL: caller_byval_alloca:
; CHECK: # %bb.0:
-; CHECK-NEXT: pushq %rax
-; CHECK-NEXT: .cfi_def_cfa_offset 16
+; CHECK-NEXT: subq $24, %rsp
+; CHECK-NEXT: .cfi_def_cfa_offset 32
+; CHECK-NEXT: xorps %xmm0, %xmm0
+; CHECK-NEXT: movups %xmm0, (%rsp)
+; CHECK-NEXT: movq $0, {{[0-9]+}}(%rsp)
+; CHECK-NEXT: movq %rsp, %r12
; CHECK-NEXT: callq callee_byval at PLT
-; CHECK-NEXT: popq %rax
+; CHECK-NEXT: addq $24, %rsp
; CHECK-NEXT: .cfi_def_cfa_offset 8
; CHECK-NEXT: retq
- tail call preserve_nonecc void @callee_byval(ptr byval(%S) align 8 %a)
+ %s = alloca %S, align 8
+ %a0 = getelementptr inbounds %S, ptr %s, i32 0, i32 0, i32 0
+ store i64 0, ptr %a0, align 8
+ %a1 = getelementptr inbounds %S, ptr %s, i32 0, i32 0, i32 1
+ store i64 0, ptr %a1, align 8
+ %a2 = getelementptr inbounds %S, ptr %s, i32 0, i32 0, i32 2
+ store i64 0, ptr %a2, align 8
+ tail call preserve_nonecc void @callee_byval(ptr byval(%S) align 8 %s)
ret void
}
More information about the llvm-commits
mailing list