aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog14
-rw-r--r--gcc/config/aarch64/aarch64-protos.h2
-rw-r--r--gcc/config/aarch64/aarch64.c77
-rw-r--r--gcc/config/aarch64/aarch64.h6
-rw-r--r--gcc/config/aarch64/aarch64.md19
-rw-r--r--gcc/testsuite/ChangeLog4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/eh_return.c82
7 files changed, 139 insertions, 65 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 049fdb0..a8ba01c 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,17 @@
+2017-01-17 Wilco Dijkstra <wdijkstr@arm.com>
+
+ * config/aarch64/aarch64.md (eh_return): Remove pattern and splitter.
+ * config/aarch64/aarch64.h (AARCH64_EH_STACKADJ_REGNUM): Remove.
+ (EH_RETURN_HANDLER_RTX): New define.
+ * config/aarch64/aarch64.c (aarch64_frame_pointer_required):
+ Force frame pointer in EH return functions.
+ (aarch64_expand_epilogue): Add barrier for eh_return.
+ (aarch64_final_eh_return_addr): Remove.
+ (aarch64_eh_return_handler_rtx): New function.
+ * config/aarch64/aarch64-protos.h (aarch64_final_eh_return_addr):
+ Remove.
+ (aarch64_eh_return_handler_rtx): New prototype.
+
2017-01-17 Bill Schmidt <wschmidt@linux.vnet.ibm.com>
* config/rs6000/altivec.h (vec_rlmi): New #define.
diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h
index 29a3bd7..602f54f 100644
--- a/gcc/config/aarch64/aarch64-protos.h
+++ b/gcc/config/aarch64/aarch64-protos.h
@@ -358,7 +358,7 @@ int aarch64_hard_regno_mode_ok (unsigned, machine_mode);
int aarch64_hard_regno_nregs (unsigned, machine_mode);
int aarch64_uxt_size (int, HOST_WIDE_INT);
int aarch64_vec_fpconst_pow_of_2 (rtx);
-rtx aarch64_final_eh_return_addr (void);
+rtx aarch64_eh_return_handler_rtx (void);
rtx aarch64_mask_from_zextract_ops (rtx, rtx);
const char *aarch64_output_move_struct (rtx *operands);
rtx aarch64_return_addr (int, rtx);
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 67500c8..27f2dc9 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -2762,6 +2762,10 @@ aarch64_frame_pointer_required (void)
&& (!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;
}
@@ -3620,7 +3624,8 @@ aarch64_expand_epilogue (bool for_sibcall)
+ 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;
@@ -3688,52 +3693,40 @@ aarch64_expand_epilogue (bool for_sibcall)
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
diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h
index 7b0df99..f6c18e8 100644
--- a/gcc/config/aarch64/aarch64.h
+++ b/gcc/config/aarch64/aarch64.h
@@ -400,9 +400,9 @@ extern unsigned aarch64_architecture_version;
#define ASM_DECLARE_FUNCTION_NAME(STR, NAME, DECL) \
aarch64_declare_function_name (STR, NAME, DECL)
-/* The register that holds the return address in exception handlers. */
-#define AARCH64_EH_STACKADJ_REGNUM (R0_REGNUM + 4)
-#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, AARCH64_EH_STACKADJ_REGNUM)
+/* For EH returns X4 contains the stack adjustment. */
+#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, R4_REGNUM)
+#define EH_RETURN_HANDLER_RTX aarch64_eh_return_handler_rtx ()
/* Don't use __builtin_setjmp until we've defined it. */
#undef DONT_USE_BUILTIN_SETJMP
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index bde4231..b3be106 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -592,25 +592,6 @@
[(set_attr "type" "branch")]
)
-(define_insn "eh_return"
- [(unspec_volatile [(match_operand:DI 0 "register_operand" "r")]
- UNSPECV_EH_RETURN)]
- ""
- "#"
- [(set_attr "type" "branch")]
-
-)
-
-(define_split
- [(unspec_volatile [(match_operand:DI 0 "register_operand" "")]
- UNSPECV_EH_RETURN)]
- "reload_completed"
- [(set (match_dup 1) (match_dup 0))]
- {
- operands[1] = aarch64_final_eh_return_addr ();
- }
-)
-
(define_insn "*cb<optab><mode>1"
[(set (pc) (if_then_else (EQL (match_operand:GPI 0 "register_operand" "r")
(const_int 0))
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 7cdf31c..eb1e3cc 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2017-01-17 Wilco Dijkstra <wdijkstr@arm.com>
+
+ * gcc.target/aarch64/eh_return.c: New test.
+
2017-01-17 Bill Schmidt <wschmidt@linux.vnet.ibm.com>
* vec-rlmi-rlnm.c: New file.
diff --git a/gcc/testsuite/gcc.target/aarch64/eh_return.c b/gcc/testsuite/gcc.target/aarch64/eh_return.c
new file mode 100644
index 0000000..3217948
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/eh_return.c
@@ -0,0 +1,82 @@
+/* { 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);
+}