&& (!crtl->is_leaf || df_regs_ever_live_p (LR_REGNUM)))
return true;
+ /* Force a frame pointer for EH returns so the return address is at FP+8. */
+ if (crtl->calls_eh_return)
+ return true;
+
return false;
}
+ cfun->machine->frame.saved_varargs_size) != 0;
/* Emit a barrier to prevent loads from a deallocated stack. */
- if (final_adjust > crtl->outgoing_args_size || cfun->calls_alloca)
+ if (final_adjust > crtl->outgoing_args_size || cfun->calls_alloca
+ || crtl->calls_eh_return)
{
emit_insn (gen_stack_tie (stack_pointer_rtx, stack_pointer_rtx));
need_barrier_p = false;
emit_jump_insn (ret_rtx);
}
-/* Return the place to copy the exception unwinding return address to.
- This will probably be a stack slot, but could (in theory be the
- return register). */
-rtx
-aarch64_final_eh_return_addr (void)
-{
- HOST_WIDE_INT fp_offset;
-
- aarch64_layout_frame ();
+/* Implement EH_RETURN_HANDLER_RTX. EH returns need to either return
+ normally or return to a previous frame after unwinding.
- fp_offset = cfun->machine->frame.frame_size
- - cfun->machine->frame.hard_fp_offset;
+ An EH return uses a single shared return sequence. The epilogue is
+ exactly like a normal epilogue except that it has an extra input
+ register (EH_RETURN_STACKADJ_RTX) which contains the stack adjustment
+ that must be applied after the frame has been destroyed. An extra label
+ is inserted before the epilogue which initializes this register to zero,
+ and this is the entry point for a normal return.
- if (cfun->machine->frame.reg_offset[LR_REGNUM] < 0)
- return gen_rtx_REG (DImode, LR_REGNUM);
+ An actual EH return updates the return address, initializes the stack
+ adjustment and jumps directly into the epilogue (bypassing the zeroing
+ of the adjustment). Since the return address is typically saved on the
+ stack when a function makes a call, the saved LR must be updated outside
+ the epilogue.
- /* DSE and CSELIB do not detect an alias between sp+k1 and fp+k2. This can
- result in a store to save LR introduced by builtin_eh_return () being
- incorrectly deleted because the alias is not detected.
- So in the calculation of the address to copy the exception unwinding
- return address to, we note 2 cases.
- If FP is needed and the fp_offset is 0, it means that SP = FP and hence
- we return a SP-relative location since all the addresses are SP-relative
- in this case. This prevents the store from being optimized away.
- If the fp_offset is not 0, then the addresses will be FP-relative and
- therefore we return a FP-relative location. */
+ This poses problems as the store is generated well before the epilogue,
+ so the offset of LR is not known yet. Also optimizations will remove the
+ store as it appears dead, even after the epilogue is generated (as the
+ base or offset for loading LR is different in many cases).
- if (frame_pointer_needed)
- {
- if (fp_offset)
- return gen_frame_mem (DImode,
- plus_constant (Pmode, hard_frame_pointer_rtx, UNITS_PER_WORD));
- else
- return gen_frame_mem (DImode,
- plus_constant (Pmode, stack_pointer_rtx, UNITS_PER_WORD));
- }
-
- /* If FP is not needed, we calculate the location of LR, which would be
- at the top of the saved registers block. */
+ To avoid these problems this implementation forces the frame pointer
+ in eh_return functions so that the location of LR is fixed and known early.
+ It also marks the store volatile, so no optimization is permitted to
+ remove the store. */
+rtx
+aarch64_eh_return_handler_rtx (void)
+{
+ rtx tmp = gen_frame_mem (Pmode,
+ plus_constant (Pmode, hard_frame_pointer_rtx, UNITS_PER_WORD));
- return gen_frame_mem (DImode,
- plus_constant (Pmode,
- stack_pointer_rtx,
- fp_offset
- + cfun->machine->frame.saved_regs_size
- - 2 * UNITS_PER_WORD));
+ /* Mark the store volatile, so no optimization is permitted to remove it. */
+ MEM_VOLATILE_P (tmp) = true;
+ return tmp;
}
/* Output code to add DELTA to the first argument, and then jump
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-O2 -fno-inline" } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int val, test, failed;
+
+int main (void);
+
+void
+eh0 (void *p)
+{
+ val = (int)(long)p & 7;
+ if (val)
+ abort ();
+}
+
+void
+eh1 (void *p, int x)
+{
+ void *q = __builtin_alloca (x);
+ eh0 (q);
+ __builtin_eh_return (0, p);
+}
+
+void
+eh2a (int a,int b,int c,int d,int e,int f,int g,int h, void *p)
+{
+ val = a + b + c + d + e + f + g + h + (int)(long)p & 7;
+}
+
+void
+eh2 (void *p)
+{
+ eh2a (val, val, val, val, val, val, val, val, p);
+ __builtin_eh_return (0, p);
+}
+
+
+void
+continuation (void)
+{
+ test++;
+ main ();
+}
+
+void
+fail (void)
+{
+ failed = 1;
+ printf ("failed\n");
+ continuation ();
+}
+
+void
+do_test1 (void)
+{
+ if (!val)
+ eh1 (continuation, 100);
+ fail ();
+}
+
+void
+do_test2 (void)
+{
+ if (!val)
+ eh2 (continuation);
+ fail ();
+}
+
+int
+main (void)
+{
+ if (test == 0)
+ do_test1 ();
+ if (test == 1)
+ do_test2 ();
+ if (failed || test != 2)
+ exit (1);
+ exit (0);
+}