ira_setup_eliminable_regset calls df_set_regs_ever_live for the hard frame
pointer when it decides frame_pointer_needed. LRA can reach that decision
later instead, in setup_can_eliminate, when it finds the frame pointer to
stack pointer elimination is not possible after all, but it does not mark the
register live there.
A target whose prologue decides which registers to save from
df_regs_ever_live_p then sets up the frame pointer without saving the caller's
value. On alpha this miscompiles pge while bootstrapping the Modula-2 front
end (PR117184): alpha_compute_frame_layout leaves $15 out of the save mask, so
alpha_expand_prologue emits the "mov $30,$15" that clobbers it but no matching
store, while alpha_expand_epilogue restores the register whenever
frame_pointer_needed, from an fp_offset that stayed 0 -- the return address
slot. The caller gets its call-saved $15 back as a code address, which shows
up much later as a NULL dereference, and the Modula-2 runtime turns the
resulting SIGSEGV into an unhandled exception:
terminate called after throwing an instance of 'unsigned int'
The testcase needs the VLA to reach the caller by inlining: a caller with its
own VLA has cfun->calls_alloca set, so IRA already knows a frame pointer is
needed and marks $15 live itself.
Do what IRA does, so the two paths agree.
gcc/
* lra-eliminations.cc (setup_can_eliminate): Mark the hard frame
pointer live when setting frame_pointer_needed.
gcc/testsuite/
* gcc.target/alpha/frame-pointer-save-1.c: New test.
---
gcc/lra-eliminations.cc | 12 +++++-
.../gcc.target/alpha/frame-pointer-save-1.c | 37 +++++++++++++++++++
2 files changed, 48 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/gcc.target/alpha/frame-pointer-save-1.c
diff --git ./gcc/lra-eliminations.cc ./gcc/lra-eliminations.cc
index 40f05563566..2aa83e2aa42 100644
--- ./gcc/lra-eliminations.cc
+++ ./gcc/lra-eliminations.cc
@@ -147,7 +147,17 @@ setup_can_eliminate (class lra_elim_table *ep, bool value)
ep->can_eliminate = ep->prev_can_eliminate = value;
if (! value
&& ep->from == FRAME_POINTER_REGNUM && ep->to == STACK_POINTER_REGNUM)
- frame_pointer_needed = 1;
+ {
+ frame_pointer_needed = 1;
+ /* ira_setup_eliminable_regset marks the hard frame pointer live when
+ it decides a frame pointer is needed. When we make that decision
+ here instead, we have to do the same, otherwise a target whose
+ prologue keys the register save off df_regs_ever_live_p would
+ clobber the caller's hard frame pointer without saving it. */
+ int fp_reg_count = hard_regno_nregs (HARD_FRAME_POINTER_REGNUM, Pmode);
+ for (int i = 0; i < fp_reg_count; i++)
+ df_set_regs_ever_live (HARD_FRAME_POINTER_REGNUM + i, true);
+ }
if (!frame_pointer_needed)
REGNO_POINTER_ALIGN (HARD_FRAME_POINTER_REGNUM) = 0;
}
diff --git ./gcc/testsuite/gcc.target/alpha/frame-pointer-save-1.c
./gcc/testsuite/gcc.target/alpha/frame-pointer-save-1.c
new file mode 100644
index 00000000000..97fa7c474e6
--- /dev/null
+++ ./gcc/testsuite/gcc.target/alpha/frame-pointer-save-1.c
@@ -0,0 +1,37 @@
+/* $15 is call-saved, and a function that needs a frame pointer clobbers it.
+ The VLA lives in a static helper that is inlined into the caller, so the
+ caller acquires dynamic stack allocation without frame_pointer_needed
+ being known when IRA runs; LRA only decides a frame pointer is needed
+ later, while marking the elimination not possible. The prologue must
+ still save $15 -- the epilogue restores it unconditionally, and used to
+ read it back from the return address slot. */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+#include <string.h>
+
+extern void use (const char *, unsigned);
+extern void sink (char);
+extern unsigned Indent;
+
+static void
+ind (const char *s_, unsigned n)
+{
+ char a[n + 1];
+
+ memcpy (a, s_, n + 1);
+ for (unsigned i = 0; i < Indent; i++)
+ sink (' ');
+ use (a, n);
+}
+
+void
+caller (unsigned name)
+{
+ ind ("", 0);
+ use ("x", name);
+}
+
+/* If the frame pointer is set up from the stack pointer, $15 must have been
+ saved first. */
+/* { dg-final { scan-assembler "stq\[ \t\]+\\\$15" } } */
--
2.54.0