The regrename pass runs after prologue/epilogue generation and may
rename temporaries to callee-saved registers that the prologue did
not save. This corrupts the caller's register values on return or
exception unwind.
The IRA callee-save cost hooks added by r15-8547-gb191e8bdecf changed
the cost model so that IRA leaves more callee-saved registers unused.
The epilogue's stack_tie references s0 (hard frame pointer), which
makes df_regs_ever_live_p(s0) true after the prologue is generated.
regrename's check_new_reg_p relies on df_regs_ever_live_p as a proxy
for "saved by prologue", so it treats s0 as available even though it
was not saved.
Fix by checking frame.mask/frame.fmask/frame.vmask in
HARD_REGNO_RENAME_OK instead.
gcc/ChangeLog:
* config/riscv/riscv.cc (riscv_hard_regno_rename_ok): Reject
callee-saved registers not present in frame.mask, frame.fmask,
or frame.vmask.
gcc/testsuite/ChangeLog:
* g++.target/riscv/regrename-unwind.C: New test.
---
gcc/config/riscv/riscv.cc | 19 +++++++-
.../g++.target/riscv/regrename-unwind.C | 45 +++++++++++++++++++
2 files changed, 63 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/g++.target/riscv/regrename-unwind.C
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 873defc5a5b..2ba24559aa3 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -12952,7 +12952,24 @@ riscv_hard_regno_rename_ok (unsigned from_regno
ATTRIBUTE_UNUSED,
/* Interrupt functions can only use registers that have already been
saved by the prologue, even if they would normally be
call-clobbered. */
- return !cfun->machine->interrupt_handler_p || df_regs_ever_live_p (to_regno);
+ if (cfun->machine->interrupt_handler_p)
+ return df_regs_ever_live_p (to_regno);
+
+ /* Registers not saved by the prologue must not be renamed into.
+ Check the frame save masks directly, since df_regs_ever_live_p
+ can look live merely because of a stack_tie in the epilogue,
+ without the register having actually been saved. */
+ if (crtl->abi->clobbers_full_reg_p (to_regno))
+ return true;
+
+ if (GP_REG_P (to_regno))
+ return (cfun->machine->frame.mask >> to_regno) & 1;
+ if (FP_REG_P (to_regno))
+ return (cfun->machine->frame.fmask >> (to_regno - FP_REG_FIRST)) & 1;
+ if (V_REG_P (to_regno))
+ return (cfun->machine->frame.vmask >> (to_regno - V_REG_FIRST)) & 1;
+
+ return true;
}
/* Implement TARGET_NEW_ADDRESS_PROFITABLE_P. */
diff --git a/gcc/testsuite/g++.target/riscv/regrename-unwind.C
b/gcc/testsuite/g++.target/riscv/regrename-unwind.C
new file mode 100644
index 00000000000..0b133d72640
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/regrename-unwind.C
@@ -0,0 +1,45 @@
+// { dg-do run }
+// { dg-options "-O2 -frename-registers -fprofile-generate" }
+// { dg-require-profiling "-fprofile-generate" }
+// { dg-require-effective-target exceptions_enabled }
+
+/* Verify that regrename does not rename temporaries to callee-saved
+ registers that were not saved by the prologue. With
+ -fprofile-generate the gcov instrumentation in the throw path
+ provides enough temporaries for regrename to pick s0, which the
+ prologue did not save. Exception unwinding then fails to restore
+ s0 and the caller sees a corrupted value. */
+
+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;
+}
--
2.52.0