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