[llvm] [AMDGPU] Fix eliminateFrameIndex assertion when reusing the frame register. (PR #215182)

Vikash Gupta via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 10 02:37:49 PDT 2026


https://github.com/vg0204 updated https://github.com/llvm/llvm-project/pull/215182

>From a73e0cbd307e40472657138b4e3d476a4bf5b9da Mon Sep 17 00:00:00 2001
From: vg0204 <Vikash.Gupta at amd.com>
Date: Fri, 7 Aug 2026 16:50:12 +0530
Subject: [PATCH 1/7] [AMDGPU][NFC] Pre-commit test for eliminateFrameIndex SVS
 fallback with frame-register

On a flat-scratch target, a frame index used by a VALU instruction
reaches the generic SGPR-scavenging path in
`SIRegisterInfo::eliminateFrameIndex`. When the function has a frame
register but no SGPR is free for the scavenger, control enters the
branch that assumes no frame register exists (the flat-scratch SVS /
fatal-error fallback), whose guard `assert(!FrameReg && "there is a
frame register!")` then fires. This test captures described crash.
---
 ...me-index-flat-scratch-frame-reg-no-sgpr.ll | 66 +++++++++++++++++++
 1 file changed, 66 insertions(+)
 create mode 100644 llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll

diff --git a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
new file mode 100644
index 0000000000000..62891dc678da2
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
@@ -0,0 +1,66 @@
+; RUN: not --crash llc -O3 -mtriple=amdgcn-amd-amdhsa -mcpu=gfx942 < %s 2>&1 | FileCheck %s
+; REQUIRES: asserts
+
+; NOTE: SIRegisterInfo::eliminateFrameIndex currently asserts on a
+; flat-scratch target when a frame index used by a VALU instruction reaches the
+; generic SGPR scavenging path, the function has a frame register, and no SGPR
+; is free for the scavenger. Control then enters the branch that assumes no
+; frame register exists: it asserts !FrameReg and its SVS lowering materializes
+; the address from the offset alone, without a frame-register term.
+;
+; The reproducer needs all of following conditions:
+;   1. A VALU frame-index user (V_SUB_CO_U32_e32 from the addrspacecast).
+;   2. A frame register (non-entry function with a stack object).
+;   3. A non-zero frame offset (large stack arg area from the many arguments).
+;   4. No free SGPR for the scavenger (all pinned by the inline asm).
+;   5. A flat-scratch target (gfx942/gfx950).
+; The data dependency through %diff keeps every SGPR live across the subtract so
+; the scheduler cannot free one up.
+
+; CHECK: there is a frame register!
+
+target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128:128:48-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9"
+target triple = "amdgcn-amd-amdhsa"
+
+define fastcc i64 @no_scavengeable_sgpr_with_frame_register(
+    ptr %p0, i32 %i0, i64 %l0, i64 %l1, i64 %l2,
+    i64 %l3, i64 %l4, i16 %s0, i32 %i1, ptr %p1,
+    i64 %l5, i32 %i2, i32 %i3, i32 %i4,
+    double %d0, double %d1, double %d2,
+    i64 %l6, i64 %l7, i64 %l8, i64 %l9
+) #0 {
+entry:
+  %local = alloca i64, align 8, addrspace(5)
+
+  ; Fill all 16 allocatable SGPRs (8 pairs).
+  %asm = call {i64, i64, i64, i64, i64, i64, i64, i64}
+    asm sideeffect "; fill sgprs",
+    "={s[0:1]},={s[2:3]},={s[4:5]},={s[6:7]},={s[8:9]},={s[10:11]},={s[12:13]},={s[14:15]}"()
+
+  %s01 = extractvalue {i64, i64, i64, i64, i64, i64, i64, i64} %asm, 0
+  %s23 = extractvalue {i64, i64, i64, i64, i64, i64, i64, i64} %asm, 1
+  %s45 = extractvalue {i64, i64, i64, i64, i64, i64, i64, i64} %asm, 2
+  %s67 = extractvalue {i64, i64, i64, i64, i64, i64, i64, i64} %asm, 3
+  %s89 = extractvalue {i64, i64, i64, i64, i64, i64, i64, i64} %asm, 4
+  %s1011 = extractvalue {i64, i64, i64, i64, i64, i64, i64, i64} %asm, 5
+  %s1213 = extractvalue {i64, i64, i64, i64, i64, i64, i64, i64} %asm, 6
+  %s1415 = extractvalue {i64, i64, i64, i64, i64, i64, i64, i64} %asm, 7
+
+  ; The addrspacecast + subtract produces V_SUB_CO_U32_e32 with %stack.0.
+  %flat_ptr = addrspacecast ptr addrspace(5) %local to ptr
+  %ptrint = ptrtoint ptr %flat_ptr to i64
+  %loaded = load i64, ptr null, align 8
+  %diff = sub i64 %ptrint, %loaded
+
+  ; Use every SGPR value together with the subtract result so the "use all" asm
+  ; stays after the subtract, keeping all SGPRs live across it.
+  call void asm sideeffect "; use all",
+    "{s[0:1]},{s[2:3]},{s[4:5]},{s[6:7]},{s[8:9]},{s[10:11]},{s[12:13]},{s[14:15]},{v[0:1]}"(
+    i64 %s01, i64 %s23, i64 %s45, i64 %s67,
+    i64 %s89, i64 %s1011, i64 %s1213, i64 %s1415,
+    i64 %diff)
+
+  ret i64 0
+}
+
+attributes #0 = { "amdgpu-num-sgpr"="16" }
\ No newline at end of file

>From bfd9b3f03406fd4c53a3daa36cc377f70271aca6 Mon Sep 17 00:00:00 2001
From: vg0204 <Vikash.Gupta at amd.com>
Date: Mon, 10 Aug 2026 11:48:29 +0530
Subject: [PATCH 2/7] Removed unnecessary details from the test.

---
 .../eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll    | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
index 62891dc678da2..e46b9483f4e6b 100644
--- a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
+++ b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
@@ -19,9 +19,6 @@
 
 ; CHECK: there is a frame register!
 
-target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128:128:48-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9"
-target triple = "amdgcn-amd-amdhsa"
-
 define fastcc i64 @no_scavengeable_sgpr_with_frame_register(
     ptr %p0, i32 %i0, i64 %l0, i64 %l1, i64 %l2,
     i64 %l3, i64 %l4, i16 %s0, i32 %i1, ptr %p1,

>From 1ea11ff472821cb84d8811a7a90c1621512b13be Mon Sep 17 00:00:00 2001
From: Vikash Gupta <Vikash.Gupta at amd.com>
Date: Mon, 10 Aug 2026 12:07:26 +0530
Subject: [PATCH 3/7] Update
 llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll

Co-authored-by: Christudasan Devadasan <christudasan.devadasan at amd.com>
---
 .../eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
index e46b9483f4e6b..b2695f15ab350 100644
--- a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
+++ b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
@@ -1,4 +1,4 @@
-; RUN: not --crash llc -O3 -mtriple=amdgcn-amd-amdhsa -mcpu=gfx942 < %s 2>&1 | FileCheck %s
+; RUN: not --crash llc -O3 -mtriple=amdgpu9.42-amd-amdhsa < %s 2>&1 | FileCheck %s
 ; REQUIRES: asserts
 
 ; NOTE: SIRegisterInfo::eliminateFrameIndex currently asserts on a

>From 752470622ca7921a0ce0cd693f0662e683c3035e Mon Sep 17 00:00:00 2001
From: vg0204 <Vikash.Gupta at amd.com>
Date: Mon, 10 Aug 2026 12:09:34 +0530
Subject: [PATCH 4/7] Addressed reviewed changes in test.

---
 .../eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
index b2695f15ab350..96aedbbd28ac0 100644
--- a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
+++ b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
@@ -60,4 +60,4 @@ entry:
   ret i64 0
 }
 
-attributes #0 = { "amdgpu-num-sgpr"="16" }
\ No newline at end of file
+attributes #0 = { "amdgpu-num-sgpr"="16" }

>From 5c86e012a0c24f75e1c1ad87910bde8b6dbc7fbd Mon Sep 17 00:00:00 2001
From: vg0204 <Vikash.Gupta at amd.com>
Date: Mon, 10 Aug 2026 14:42:22 +0530
Subject: [PATCH 5/7] Addressed the final set of reviews.

---
 .../eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll  | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
index 96aedbbd28ac0..5795317447d34 100644
--- a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
+++ b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
@@ -28,8 +28,6 @@ define fastcc i64 @no_scavengeable_sgpr_with_frame_register(
 ) #0 {
 entry:
   %local = alloca i64, align 8, addrspace(5)
-
-  ; Fill all 16 allocatable SGPRs (8 pairs).
   %asm = call {i64, i64, i64, i64, i64, i64, i64, i64}
     asm sideeffect "; fill sgprs",
     "={s[0:1]},={s[2:3]},={s[4:5]},={s[6:7]},={s[8:9]},={s[10:11]},={s[12:13]},={s[14:15]}"()
@@ -43,14 +41,11 @@ entry:
   %s1213 = extractvalue {i64, i64, i64, i64, i64, i64, i64, i64} %asm, 6
   %s1415 = extractvalue {i64, i64, i64, i64, i64, i64, i64, i64} %asm, 7
 
-  ; The addrspacecast + subtract produces V_SUB_CO_U32_e32 with %stack.0.
   %flat_ptr = addrspacecast ptr addrspace(5) %local to ptr
   %ptrint = ptrtoint ptr %flat_ptr to i64
   %loaded = load i64, ptr null, align 8
   %diff = sub i64 %ptrint, %loaded
 
-  ; Use every SGPR value together with the subtract result so the "use all" asm
-  ; stays after the subtract, keeping all SGPRs live across it.
   call void asm sideeffect "; use all",
     "{s[0:1]},{s[2:3]},{s[4:5]},{s[6:7]},{s[8:9]},{s[10:11]},{s[12:13]},{s[14:15]},{v[0:1]}"(
     i64 %s01, i64 %s23, i64 %s45, i64 %s67,

>From b8091e62183ebe41fff333fb27690e9cd790b2b1 Mon Sep 17 00:00:00 2001
From: vg0204 <Vikash.Gupta at amd.com>
Date: Fri, 7 Aug 2026 17:21:32 +0530
Subject: [PATCH 6/7] [AMDGPU] Fix eliminateFrameIndex assertion when reusing
 the frame register

On a flat-scratch target, a frame index used by a VALU instruction with
a live frame register but no scavengeable SGPR entered the branch that
assumes no frame register exists, tripping `assert(!FrameReg && "there
is a frame register!")`.

Restore the `!FrameReg` term in the guard so this case falls through to
the existing path that reuses the frame register as the temporary (offset
folded in, then restored). The `!FrameReg` assertion is moved into the
SVS fallback, where it actually holds.

This resolves the pre-commit testcase in the cdcd
---
 llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp     | 12 ++++++++++--
 ...me-index-flat-scratch-frame-reg-no-sgpr.ll | 19 +++++++++++++------
 2 files changed, 23 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp b/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp
index a3d6509628d50..c661787b301c0 100644
--- a/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp
@@ -3260,10 +3260,18 @@ bool SIRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator MI,
                   : RS->scavengeRegisterBackwards(AMDGPU::SReg_32_XM0RegClass,
                                                   MI, false, 0, !UseSGPR);
 
-      if (!TmpSReg || (!TmpReg && !UseSGPR)) {
-        assert(!FrameReg && "there is a frame register!");
+      // If no SGPR was scavenged but a frame register is available, fall
+      // through to reuse it as the temporary (computed in place, restored
+      // after). Only bail out when there is no frame register, or a VGPR
+      // operand is needed but none could be scavenged.
+      if ((!TmpSReg && !FrameReg) || (!TmpReg && !UseSGPR)) {
         int SVOpcode = AMDGPU::getFlatScratchInstSVfromSS(MI->getOpcode());
         if (ST.hasFlatScratchSVSMode() && SVOpcode != -1) {
+          // SV form encodes only the offset in vaddr; an SS-form scratch op
+          // keeps its FI in the SGPR saddr, so this is only reached with no
+          // frame register.
+          assert(!FrameReg &&
+                 "SV-form fallback cannot encode a frame register");
           Register TmpVGPR = RS->scavengeRegisterBackwards(
               AMDGPU::VGPR_32RegClass, MI, false, 0, /*AllowSpill=*/true);
 
diff --git a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
index 5795317447d34..1634c5602ef5a 100644
--- a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
+++ b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
@@ -1,12 +1,13 @@
 ; RUN: not --crash llc -O3 -mtriple=amdgpu9.42-amd-amdhsa < %s 2>&1 | FileCheck %s
 ; REQUIRES: asserts
 
-; NOTE: SIRegisterInfo::eliminateFrameIndex currently asserts on a
-; flat-scratch target when a frame index used by a VALU instruction reaches the
-; generic SGPR scavenging path, the function has a frame register, and no SGPR
-; is free for the scavenger. Control then enters the branch that assumes no
-; frame register exists: it asserts !FrameReg and its SVS lowering materializes
-; the address from the offset alone, without a frame-register term.
+; Verifies SIRegisterInfo::eliminateFrameIndex on a flat-scratch target: a frame
+; index used by a VALU instruction reaches the generic SGPR scavenging path.
+; When the function has a frame register but no SGPR is free for the scavenger,
+; the fix reuses the frame register as the temporary instead of taking the SVS
+; fallback (which assumes no frame register and previously asserted here). The
+; offset is folded into the frame register in place, the subtract reads it
+; directly as an SGPR source, and the frame register is restored afterwards.
 ;
 ; The reproducer needs all of following conditions:
 ;   1. A VALU frame-index user (V_SUB_CO_U32_e32 from the addrspacecast).
@@ -20,6 +21,12 @@
 ; CHECK: there is a frame register!
 
 define fastcc i64 @no_scavengeable_sgpr_with_frame_register(
+; CHECK-LABEL: no_scavengeable_sgpr_with_frame_register:
+; Fold the frame-index offset into the frame register in place, use it directly
+; as the SGPR source of the subtract, then restore the frame register.
+; CHECK:         s_add_i32 s32, s32, 24
+; CHECK:         v_sub_co_u32_e32 v0, vcc, s32, v0
+; CHECK:         s_addk_i32 s32, 0xffe8
     ptr %p0, i32 %i0, i64 %l0, i64 %l1, i64 %l2,
     i64 %l3, i64 %l4, i16 %s0, i32 %i1, ptr %p1,
     i64 %l5, i32 %i2, i32 %i3, i32 %i4,

>From cf745781a0ca7280e4f42f45ab5773da6e14345f Mon Sep 17 00:00:00 2001
From: vg0204 <Vikash.Gupta at amd.com>
Date: Mon, 10 Aug 2026 15:07:07 +0530
Subject: [PATCH 7/7] updated the crashing test that got fixed.

---
 .../eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll  | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
index 1634c5602ef5a..b36f5cef756c5 100644
--- a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
+++ b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-flat-scratch-frame-reg-no-sgpr.ll
@@ -1,5 +1,4 @@
-; RUN: not --crash llc -O3 -mtriple=amdgpu9.42-amd-amdhsa < %s 2>&1 | FileCheck %s
-; REQUIRES: asserts
+; RUN: llc -O3 -mtriple=amdgpu9.42-amd-amdhsa < %s | FileCheck %s
 
 ; Verifies SIRegisterInfo::eliminateFrameIndex on a flat-scratch target: a frame
 ; index used by a VALU instruction reaches the generic SGPR scavenging path.
@@ -18,8 +17,6 @@
 ; The data dependency through %diff keeps every SGPR live across the subtract so
 ; the scheduler cannot free one up.
 
-; CHECK: there is a frame register!
-
 define fastcc i64 @no_scavengeable_sgpr_with_frame_register(
 ; CHECK-LABEL: no_scavengeable_sgpr_with_frame_register:
 ; Fold the frame-index offset into the frame register in place, use it directly



More information about the llvm-commits mailing list