[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