[gcc r17-2917] RISC-V: Avoid unused frame-pointer references in stack ties

Ma Jin via Gcc-cvs <[email protected]> Tue, 4 Aug 2026 06:50:03 +0000 (GMT)
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:2db2cf62f1ad2788365b1898aefdba2fa76aab87

commit r17-2917-g2db2cf62f1ad2788365b1898aefdba2fa76aab87
Author: Jin Ma <[email protected]>
Date:   Wed Jul 15 22:26:18 2026 +0800

    RISC-V: Avoid unused frame-pointer references in stack ties
    
    Epilogue stack ties referenced s0 without a frame pointer, making the
    unsaved register ever live.  Regrename could then select it and
    corrupt callee-saved state during exception unwinding.
    
    The simpler fix of allowing equal operands in the existing pattern is
    unsafe: copy propagation can fold stack_tie (sp, t3) used by stack
    probing into stack_tie (sp, sp).  This removes t3 = sp while CFI
    still names t3 as the CFA.
    
    Keep the two-register pattern distinct and add an SP-only form for
    epilogue barriers.
    
    gcc/ChangeLog:
    
            * config/riscv/riscv-sr.cc (riscv_sr_match_epilogue): Accept
            SP-only stack ties.
            * config/riscv/riscv.cc (riscv_emit_stack_tie): Use parameterized
            generators and emit SP-only ties.
            (riscv_expand_epilogue): Use SP ties without a frame pointer.
            * config/riscv/riscv.md (@stack_tie<mode>): Use a parameterized name.
            (@stack_tie_sp<mode>): New pattern.
    
    gcc/testsuite/ChangeLog:
    
            * g++.target/riscv/stack-tie-unwind.C: New test.
            * gcc.target/riscv/stack-tie-no-fp.c: New test.
    
    Signed-off-by: Jin Ma <[email protected]>

Diff:
---
 gcc/config/riscv/riscv-sr.cc                      |  6 ++--
 gcc/config/riscv/riscv.cc                         | 22 +++++++++---
 gcc/config/riscv/riscv.md                         | 13 ++++++-
 gcc/testsuite/g++.target/riscv/stack-tie-unwind.C | 41 +++++++++++++++++++++++
 gcc/testsuite/gcc.target/riscv/stack-tie-no-fp.c  | 14 ++++++++
 5 files changed, 88 insertions(+), 8 deletions(-)

diff --git a/gcc/config/riscv/riscv-sr.cc b/gcc/config/riscv/riscv-sr.cc
index e1b2dafc30b9..84dc9504d05f 100644
--- a/gcc/config/riscv/riscv-sr.cc
+++ b/gcc/config/riscv/riscv-sr.cc
@@ -144,7 +144,7 @@ riscv_sr_match_epilogue (void)
      standard epilogue (of the form we expect to handle) consists of the
      following instructions:
 
-     1. A stack_tiesi or stack_tiedi (for RV32 and RV64 respectively),
+     1. A stack tie instruction,
 
      2. An optional use instruction for the register holding the return
         value.  This will be missing in functions with no return value,
@@ -154,7 +154,9 @@ riscv_sr_match_epilogue (void)
      4. A jump instruction of type gpr_restore_return.  */
   start = insn;
   if (INSN_CODE (insn) != CODE_FOR_stack_tiesi
-      && INSN_CODE (insn) != CODE_FOR_stack_tiedi)
+      && INSN_CODE (insn) != CODE_FOR_stack_tiedi
+      && INSN_CODE (insn) != CODE_FOR_stack_tie_spsi
+      && INSN_CODE (insn) != CODE_FOR_stack_tie_spdi)
     return NULL;
 
   insn = NEXT_INSN (insn);
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index d813f03b549f..83f552c5e468 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -9876,10 +9876,17 @@ riscv_adjust_multi_push_cfi_prologue (int saved_size)
 static void
 riscv_emit_stack_tie (rtx reg)
 {
-  if (Pmode == SImode)
-    emit_insn (gen_stack_tiesi (stack_pointer_rtx, reg));
+  /* A frame-pointer tie requires a saved frame pointer.  */
+  if (REG_P (reg)
+      && REGNO (reg) == HARD_FRAME_POINTER_REGNUM)
+    gcc_assert (frame_pointer_needed
+		&& (cfun->machine->frame.mask
+		    & (1U << HARD_FRAME_POINTER_REGNUM)));
+
+  if (rtx_equal_p (reg, stack_pointer_rtx))
+    emit_insn (gen_stack_tie_sp (Pmode, reg));
   else
-    emit_insn (gen_stack_tiedi (stack_pointer_rtx, reg));
+    emit_insn (gen_stack_tie (Pmode, stack_pointer_rtx, reg));
 }
 
 /*zcmp multi push and pop code_for_push_pop function ptr array  */
@@ -10508,6 +10515,11 @@ riscv_expand_epilogue (int style)
   unsigned th_int_mask = 0;
   rtx insn;
 
+  /* Avoid referencing an unused frame pointer.  */
+  rtx stack_tie_reg = frame_pointer_needed
+		      ? hard_frame_pointer_rtx
+		      : stack_pointer_rtx;
+
   /* We need to add memory barrier to prevent read from deallocated stack.  */
   bool need_barrier_p = known_ne (get_frame_size ()
 				  + cfun->machine->frame.arg_pointer_offset, 0);
@@ -10628,7 +10640,7 @@ riscv_expand_epilogue (int style)
   if (known_gt (step1, 0))
     {
       /* Emit a barrier to prevent loads from a deallocated stack.  */
-      riscv_emit_stack_tie (hard_frame_pointer_rtx);
+      riscv_emit_stack_tie (stack_tie_reg);
       need_barrier_p = false;
 
       /* Restore the scalable frame which is assigned in prologue.  */
@@ -10729,7 +10741,7 @@ riscv_expand_epilogue (int style)
     frame->mask = mask; /* Undo the above fib.  */
 
   if (need_barrier_p)
-    riscv_emit_stack_tie (hard_frame_pointer_rtx);
+    riscv_emit_stack_tie (stack_tie_reg);
 
   /* Deallocate the final bit of the frame.  */
   if (step2.to_constant () > 0)
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 9140a0ce6fe7..6f66f983d876 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -4471,7 +4471,7 @@
   "mnret"
   [(set_attr "type" "ret")])
 
-(define_insn "stack_tie<mode>"
+(define_insn "@stack_tie<mode>"
   [(set (mem:BLK (scratch))
 	(unspec:BLK [(match_operand:X 0 "register_operand" "r")
 		     (match_operand:X 1 "register_operand" "r")]
@@ -4482,6 +4482,17 @@
    (set_attr "length" "0")]
 )
 
+;; Keep stack loads before an SP adjustment without a second register.
+(define_insn "@stack_tie_sp<mode>"
+  [(set (mem:BLK (scratch))
+	(unspec:BLK [(match_operand:X 0 "register_operand" "r")]
+		    UNSPEC_TIE))]
+  "rtx_equal_p (operands[0], stack_pointer_rtx)"
+  ""
+  [(set_attr "type" "ghost")
+   (set_attr "length" "0")]
+)
+
 (define_expand "save_stack_nonlocal"
   [(set (match_operand 0 "memory_operand")
 	(match_operand 1 "register_operand"))]
diff --git a/gcc/testsuite/g++.target/riscv/stack-tie-unwind.C b/gcc/testsuite/g++.target/riscv/stack-tie-unwind.C
new file mode 100644
index 000000000000..f9d99d5d71bb
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/stack-tie-unwind.C
@@ -0,0 +1,41 @@
+// { dg-do run }
+// { dg-options "-O2 -frename-registers -fprofile-generate -fomit-frame-pointer" }
+// { dg-require-profiling "-fprofile-generate" }
+// { dg-require-effective-target exceptions_enabled }
+// { dg-final { cleanup-coverage-files } }
+
+/* Verify that regrename cannot use an unsaved frame pointer.  */
+
+extern "C" void abort (void);
+
+struct MyException {};
+struct Data {
+    int nr;
+    Data() : nr(66) {}
+};
+
+Data __attribute__((noinline,noclone))
+getData (int i)
+{
+  if (i)
+    throw MyException ();
+  Data data;
+  data.nr = i;
+  return data;
+}
+
+int
+main (int, char **)
+{
+  Data data;
+  try
+    {
+      data = getData (1);
+    }
+  catch (MyException &)
+    {
+      if (data.nr != 66)
+	abort ();
+    }
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/stack-tie-no-fp.c b/gcc/testsuite/gcc.target/riscv/stack-tie-no-fp.c
new file mode 100644
index 000000000000..a1333b927769
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-tie-no-fp.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fomit-frame-pointer -fdump-rtl-pro_and_epilogue" } */
+
+/* Keep the epilogue stack tie without a frame pointer.  */
+
+int
+foo (int x)
+{
+  volatile int data[8];
+  data[0] = x;
+  return data[0];
+}
+
+/* { dg-final { scan-rtl-dump "UNSPEC_TIE" "pro_and_epilogue" } } */