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.
---
 gcc/config/riscv/riscv-sr.cc                  |  6 ++-
 gcc/config/riscv/riscv.cc                     | 22 +++++++---
 gcc/config/riscv/riscv.md                     | 13 +++++-
 .../g++.target/riscv/stack-tie-unwind.C       | 41 +++++++++++++++++++
 .../gcc.target/riscv/stack-tie-no-fp.c        | 14 +++++++
 5 files changed, 88 insertions(+), 8 deletions(-)
 create mode 100644 gcc/testsuite/g++.target/riscv/stack-tie-unwind.C
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-tie-no-fp.c

diff --git a/gcc/config/riscv/riscv-sr.cc b/gcc/config/riscv/riscv-sr.cc
index e1b2dafc30b..84dc9504d05 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 873defc5a5b..7846ff9e998 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -9856,10 +9856,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  */
@@ -10488,6 +10495,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);
@@ -10608,7 +10620,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.  */
@@ -10709,7 +10721,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 f10a08460ac..d7f70e422b4 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -4434,7 +4434,7 @@ (define_insn "riscv_mnret"
   "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")]
@@ -4445,6 +4445,17 @@ (define_insn "stack_tie<mode>"
    (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 00000000000..f9d99d5d71b
--- /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 00000000000..a1333b92776
--- /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" } } */
-- 
2.52.0

Reply via email to