[llvm] AMDGPU: Don't save FP/BP for noreturn functions (PR #187668)
Diana Picus via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 30 01:45:50 PDT 2026
https://github.com/rovka updated https://github.com/llvm/llvm-project/pull/187668
>From 46a338dd24a480a4e7399bfb6c6b4e1f79e82639 Mon Sep 17 00:00:00 2001
From: Diana Picus <diana-magda.picus at amd.com>
Date: Thu, 19 Mar 2026 10:50:49 +0100
Subject: [PATCH 1/4] Add test with noreturn function
---
.../test/CodeGen/AMDGPU/callee-frame-setup.ll | 55 +++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/llvm/test/CodeGen/AMDGPU/callee-frame-setup.ll b/llvm/test/CodeGen/AMDGPU/callee-frame-setup.ll
index e7254eb5c3465..54004f974b50a 100644
--- a/llvm/test/CodeGen/AMDGPU/callee-frame-setup.ll
+++ b/llvm/test/CodeGen/AMDGPU/callee-frame-setup.ll
@@ -1918,7 +1918,62 @@ define void @spill_fp_to_memory_scratch_reg_needed_mubuf_offset(ptr addrspace(5)
ret void
}
+define void @dont_save_fp_bp_for_noreturn_funcs() #4 {
+; MUBUF-LABEL: dont_save_fp_bp_for_noreturn_funcs:
+; MUBUF: ; %bb.0:
+; MUBUF-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; MUBUF-NEXT: s_mov_b32 s16, s33
+; MUBUF-NEXT: s_add_i32 s33, s32, 0x1fc0
+; MUBUF-NEXT: s_and_b32 s33, s33, 0xffffe000
+; MUBUF-NEXT: s_or_saveexec_b64 s[18:19], -1
+; MUBUF-NEXT: buffer_store_dword v40, off, s[0:3], s33 offset:4 ; 4-byte Folded Spill
+; MUBUF-NEXT: s_mov_b64 exec, s[18:19]
+; MUBUF-NEXT: v_writelane_b32 v40, s16, 2
+; MUBUF-NEXT: v_writelane_b32 v40, s34, 3
+; MUBUF-NEXT: s_mov_b32 s34, s32
+; MUBUF-NEXT: s_addk_i32 s32, 0x4000
+; MUBUF-NEXT: s_getpc_b64 s[16:17]
+; MUBUF-NEXT: s_add_u32 s16, s16, dont_save_fp_bp_for_noreturn_funcs at gotpcrel32@lo+4
+; MUBUF-NEXT: s_addc_u32 s17, s17, dont_save_fp_bp_for_noreturn_funcs at gotpcrel32@hi+12
+; MUBUF-NEXT: s_load_dwordx2 s[16:17], s[16:17], 0x0
+; MUBUF-NEXT: v_writelane_b32 v40, s30, 0
+; MUBUF-NEXT: v_mov_b32_e32 v0, 0
+; MUBUF-NEXT: v_writelane_b32 v40, s31, 1
+; MUBUF-NEXT: buffer_store_dword v0, off, s[0:3], s33
+; MUBUF-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0)
+; MUBUF-NEXT: s_swappc_b64 s[30:31], s[16:17]
+;
+; FLATSCR-LABEL: dont_save_fp_bp_for_noreturn_funcs:
+; FLATSCR: ; %bb.0:
+; FLATSCR-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; FLATSCR-NEXT: s_mov_b32 s0, s33
+; FLATSCR-NEXT: s_add_i32 s33, s32, 0x7f
+; FLATSCR-NEXT: s_and_b32 s33, s33, 0xffffff80
+; FLATSCR-NEXT: s_or_saveexec_b64 s[2:3], -1
+; FLATSCR-NEXT: scratch_store_dword off, v40, s33 offset:4 ; 4-byte Folded Spill
+; FLATSCR-NEXT: s_mov_b64 exec, s[2:3]
+; FLATSCR-NEXT: v_writelane_b32 v40, s0, 2
+; FLATSCR-NEXT: v_writelane_b32 v40, s34, 3
+; FLATSCR-NEXT: s_mov_b32 s34, s32
+; FLATSCR-NEXT: s_addk_i32 s32, 0x100
+; FLATSCR-NEXT: s_getpc_b64 s[0:1]
+; FLATSCR-NEXT: s_add_u32 s0, s0, dont_save_fp_bp_for_noreturn_funcs at gotpcrel32@lo+4
+; FLATSCR-NEXT: s_addc_u32 s1, s1, dont_save_fp_bp_for_noreturn_funcs at gotpcrel32@hi+12
+; FLATSCR-NEXT: s_load_dwordx2 s[0:1], s[0:1], 0x0
+; FLATSCR-NEXT: v_writelane_b32 v40, s30, 0
+; FLATSCR-NEXT: v_mov_b32_e32 v0, 0
+; FLATSCR-NEXT: v_writelane_b32 v40, s31, 1
+; FLATSCR-NEXT: scratch_store_dword off, v0, s33
+; FLATSCR-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0)
+; FLATSCR-NEXT: s_swappc_b64 s[30:31], s[0:1]
+ %alloca = alloca i32, align 128, addrspace(5)
+ store volatile i32 0, ptr addrspace(5) %alloca
+ call void @dont_save_fp_bp_for_noreturn_funcs()
+ unreachable
+}
+
attributes #0 = { nounwind }
attributes #1 = { nounwind "frame-pointer"="all" }
attributes #2 = { nounwind "frame-pointer"="non-leaf" }
attributes #3 = { nounwind "frame-pointer"="all" "amdgpu-waves-per-eu"="6,6" }
+attributes #4 = { nounwind noreturn }
>From 45c05e1893b09dc0bc710d61251ac1626a702df7 Mon Sep 17 00:00:00 2001
From: Diana Picus <diana-magda.picus at amd.com>
Date: Thu, 19 Mar 2026 11:06:56 +0100
Subject: [PATCH 2/4] AMDGPU: Don't save FP/BP for noreturn functions
As suggested here:
https://github.com/llvm/llvm-project/pull/184616#discussion_r2889401998
We could probably skip saving other regs too, but that can be a future
patch.
Assisted-by: Sline with Claude Opus (only the SIMachineFunction bits).
---
llvm/lib/Target/AMDGPU/SIFrameLowering.cpp | 9 ++++-----
llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.cpp | 4 +++-
llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.h | 6 ++++++
llvm/test/CodeGen/AMDGPU/callee-frame-setup.ll | 14 ++++----------
4 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp b/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
index 9ee9e63fdab26..dca6f10ba8145 100644
--- a/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
@@ -1221,9 +1221,8 @@ void SIFrameLowering::emitPrologue(MachineFunction &MF,
uint32_t NumBytes = MFI.getStackSize();
uint32_t RoundedSize = NumBytes;
- // Chain functions never return, so there's no need to save and restore the FP
- // or BP.
- bool SavesStackRegs = !FuncInfo->isChainFunction();
+ // Functions that never return don't need to save and restore the FP or BP.
+ bool SavesStackRegs = !FuncInfo->doesNotReturn();
if (TRI.hasStackRealignment(MF))
HasFP = true;
@@ -1649,9 +1648,9 @@ void SIFrameLowering::determinePrologEpilogSGPRSaves(
MFI->setSGPRForEXECCopy(AMDGPU::NoRegister);
}
- // Chain functions don't return to the caller, so they don't need to preserve
+ // Functions that don't return to the caller don't need to preserve
// the FP and BP.
- if (MFI->isChainFunction())
+ if (MFI->doesNotReturn())
return;
// hasFP only knows about stack objects that already exist. We're now
diff --git a/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.cpp b/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.cpp
index 2aec8aaf9b613..3dae6ea715de8 100644
--- a/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.cpp
@@ -56,7 +56,9 @@ SIMachineFunctionInfo::SIMachineFunctionInfo(const Function &F,
WorkItemIDX(false), WorkItemIDY(false), WorkItemIDZ(false),
ImplicitArgPtr(false), GITPtrHigh(0xffffffff), HighBitsOf32BitAddress(0),
IsWholeWaveFunction(F.getCallingConv() ==
- CallingConv::AMDGPU_Gfx_WholeWave) {
+ CallingConv::AMDGPU_Gfx_WholeWave),
+ DoesNotReturn(F.hasFnAttribute(Attribute::NoReturn) ||
+ AMDGPU::isChainCC(F.getCallingConv())) {
const GCNSubtarget &ST = *STI;
FlatWorkGroupSizes = ST.getFlatWorkGroupSizes(F);
WavesPerEU = ST.getWavesPerEU(F);
diff --git a/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.h b/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.h
index 50e92424e570f..5061c28e7ac67 100644
--- a/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.h
+++ b/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.h
@@ -586,6 +586,9 @@ class SIMachineFunctionInfo final : public AMDGPUMachineFunctionInfo,
bool IsWholeWaveFunction = false;
+ // True if the function does not return to the caller.
+ bool DoesNotReturn = false;
+
using PrologEpilogSGPRSpill =
std::pair<Register, PrologEpilogSGPRSaveRestoreInfo>;
// To track the SGPR spill method used for a CSR SGPR register during
@@ -1225,6 +1228,9 @@ class SIMachineFunctionInfo final : public AMDGPUMachineFunctionInfo,
unsigned getMaxNumWorkGroupsZ() const { return MaxNumWorkGroups[2]; }
AMDGPU::ClusterDimsAttr getClusterDims() const { return ClusterDims; }
+
+ /// \returns true if the function does not return to the caller.
+ bool doesNotReturn() const { return DoesNotReturn; }
};
} // end namespace llvm
diff --git a/llvm/test/CodeGen/AMDGPU/callee-frame-setup.ll b/llvm/test/CodeGen/AMDGPU/callee-frame-setup.ll
index 54004f974b50a..f81ab372b7cdf 100644
--- a/llvm/test/CodeGen/AMDGPU/callee-frame-setup.ll
+++ b/llvm/test/CodeGen/AMDGPU/callee-frame-setup.ll
@@ -1922,14 +1922,11 @@ define void @dont_save_fp_bp_for_noreturn_funcs() #4 {
; MUBUF-LABEL: dont_save_fp_bp_for_noreturn_funcs:
; MUBUF: ; %bb.0:
; MUBUF-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; MUBUF-NEXT: s_mov_b32 s16, s33
; MUBUF-NEXT: s_add_i32 s33, s32, 0x1fc0
; MUBUF-NEXT: s_and_b32 s33, s33, 0xffffe000
-; MUBUF-NEXT: s_or_saveexec_b64 s[18:19], -1
+; MUBUF-NEXT: s_or_saveexec_b64 s[16:17], -1
; MUBUF-NEXT: buffer_store_dword v40, off, s[0:3], s33 offset:4 ; 4-byte Folded Spill
-; MUBUF-NEXT: s_mov_b64 exec, s[18:19]
-; MUBUF-NEXT: v_writelane_b32 v40, s16, 2
-; MUBUF-NEXT: v_writelane_b32 v40, s34, 3
+; MUBUF-NEXT: s_mov_b64 exec, s[16:17]
; MUBUF-NEXT: s_mov_b32 s34, s32
; MUBUF-NEXT: s_addk_i32 s32, 0x4000
; MUBUF-NEXT: s_getpc_b64 s[16:17]
@@ -1946,14 +1943,11 @@ define void @dont_save_fp_bp_for_noreturn_funcs() #4 {
; FLATSCR-LABEL: dont_save_fp_bp_for_noreturn_funcs:
; FLATSCR: ; %bb.0:
; FLATSCR-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; FLATSCR-NEXT: s_mov_b32 s0, s33
; FLATSCR-NEXT: s_add_i32 s33, s32, 0x7f
; FLATSCR-NEXT: s_and_b32 s33, s33, 0xffffff80
-; FLATSCR-NEXT: s_or_saveexec_b64 s[2:3], -1
+; FLATSCR-NEXT: s_or_saveexec_b64 s[0:1], -1
; FLATSCR-NEXT: scratch_store_dword off, v40, s33 offset:4 ; 4-byte Folded Spill
-; FLATSCR-NEXT: s_mov_b64 exec, s[2:3]
-; FLATSCR-NEXT: v_writelane_b32 v40, s0, 2
-; FLATSCR-NEXT: v_writelane_b32 v40, s34, 3
+; FLATSCR-NEXT: s_mov_b64 exec, s[0:1]
; FLATSCR-NEXT: s_mov_b32 s34, s32
; FLATSCR-NEXT: s_addk_i32 s32, 0x100
; FLATSCR-NEXT: s_getpc_b64 s[0:1]
>From 1eac09c39bdf9a1ca256e5d4812e6750490480e9 Mon Sep 17 00:00:00 2001
From: Diana Picus <diana-magda.picus at amd.com>
Date: Fri, 27 Mar 2026 13:24:53 +0100
Subject: [PATCH 3/4] Don't add a new FuncInfo member
---
llvm/lib/Target/AMDGPU/SIFrameLowering.cpp | 8 ++++++--
llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.cpp | 4 +---
llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.h | 6 ------
3 files changed, 7 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp b/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
index dca6f10ba8145..7a283cd32bdd1 100644
--- a/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
@@ -1222,7 +1222,9 @@ void SIFrameLowering::emitPrologue(MachineFunction &MF,
uint32_t RoundedSize = NumBytes;
// Functions that never return don't need to save and restore the FP or BP.
- bool SavesStackRegs = !FuncInfo->doesNotReturn();
+ const Function &F = MF.getFunction();
+ bool SavesStackRegs = !(F.hasFnAttribute(Attribute::NoReturn) ||
+ AMDGPU::isChainCC(F.getCallingConv()));
if (TRI.hasStackRealignment(MF))
HasFP = true;
@@ -1650,7 +1652,9 @@ void SIFrameLowering::determinePrologEpilogSGPRSaves(
// Functions that don't return to the caller don't need to preserve
// the FP and BP.
- if (MFI->doesNotReturn())
+ const Function &F = MF.getFunction();
+ if (F.hasFnAttribute(Attribute::NoReturn) ||
+ AMDGPU::isChainCC(F.getCallingConv()))
return;
// hasFP only knows about stack objects that already exist. We're now
diff --git a/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.cpp b/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.cpp
index 3dae6ea715de8..2aec8aaf9b613 100644
--- a/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.cpp
@@ -56,9 +56,7 @@ SIMachineFunctionInfo::SIMachineFunctionInfo(const Function &F,
WorkItemIDX(false), WorkItemIDY(false), WorkItemIDZ(false),
ImplicitArgPtr(false), GITPtrHigh(0xffffffff), HighBitsOf32BitAddress(0),
IsWholeWaveFunction(F.getCallingConv() ==
- CallingConv::AMDGPU_Gfx_WholeWave),
- DoesNotReturn(F.hasFnAttribute(Attribute::NoReturn) ||
- AMDGPU::isChainCC(F.getCallingConv())) {
+ CallingConv::AMDGPU_Gfx_WholeWave) {
const GCNSubtarget &ST = *STI;
FlatWorkGroupSizes = ST.getFlatWorkGroupSizes(F);
WavesPerEU = ST.getWavesPerEU(F);
diff --git a/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.h b/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.h
index 5061c28e7ac67..50e92424e570f 100644
--- a/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.h
+++ b/llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.h
@@ -586,9 +586,6 @@ class SIMachineFunctionInfo final : public AMDGPUMachineFunctionInfo,
bool IsWholeWaveFunction = false;
- // True if the function does not return to the caller.
- bool DoesNotReturn = false;
-
using PrologEpilogSGPRSpill =
std::pair<Register, PrologEpilogSGPRSaveRestoreInfo>;
// To track the SGPR spill method used for a CSR SGPR register during
@@ -1228,9 +1225,6 @@ class SIMachineFunctionInfo final : public AMDGPUMachineFunctionInfo,
unsigned getMaxNumWorkGroupsZ() const { return MaxNumWorkGroups[2]; }
AMDGPU::ClusterDimsAttr getClusterDims() const { return ClusterDims; }
-
- /// \returns true if the function does not return to the caller.
- bool doesNotReturn() const { return DoesNotReturn; }
};
} // end namespace llvm
>From cee5f665ee87ee4a3e0baa6c18f22a985993f229 Mon Sep 17 00:00:00 2001
From: Diana Picus <diana-magda.picus at amd.com>
Date: Mon, 30 Mar 2026 10:45:29 +0200
Subject: [PATCH 4/4] DeMorganize
---
llvm/lib/Target/AMDGPU/SIFrameLowering.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp b/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
index 7a283cd32bdd1..4a5fd501235f0 100644
--- a/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
@@ -1223,8 +1223,8 @@ void SIFrameLowering::emitPrologue(MachineFunction &MF,
// Functions that never return don't need to save and restore the FP or BP.
const Function &F = MF.getFunction();
- bool SavesStackRegs = !(F.hasFnAttribute(Attribute::NoReturn) ||
- AMDGPU::isChainCC(F.getCallingConv()));
+ bool SavesStackRegs =
+ !F.hasFnAttribute(Attribute::NoReturn) && !FuncInfo->isChainFunction();
if (TRI.hasStackRealignment(MF))
HasFP = true;
More information about the llvm-commits
mailing list