]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
RISC-V: Avoid unused frame-pointer references in stack ties
authorJin Ma <jinma@linux.alibaba.com>
Wed, 15 Jul 2026 14:26:18 +0000 (22:26 +0800)
committerJin Ma <jinma@linux.alibaba.com>
Tue, 4 Aug 2026 06:48:51 +0000 (14:48 +0800)
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 <jinma@linux.alibaba.com>
gcc/config/riscv/riscv-sr.cc
gcc/config/riscv/riscv.cc
gcc/config/riscv/riscv.md
gcc/testsuite/g++.target/riscv/stack-tie-unwind.C [new file with mode: 0644]
gcc/testsuite/gcc.target/riscv/stack-tie-no-fp.c [new file with mode: 0644]

index e1b2dafc30b923e82edbe78d1147b55c28683a92..84dc9504d05fe697578e982e4d61147de6260da4 100644 (file)
@@ -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);
index d813f03b549f90eb23850b61759e7fd9352e55ee..83f552c5e468c733eabcf86fb2d3d53dc526752c 100644 (file)
@@ -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)
index 9140a0ce6fe70e8090a2312055c474640bb577ef..6f66f983d876f9b47a8de196c177a7e53a1c9c82 100644 (file)
   "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")]
    (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 (file)
index 0000000..f9d99d5
--- /dev/null
@@ -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 (file)
index 0000000..a1333b9
--- /dev/null
@@ -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" } } */