diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0e76adbcc02..027a53f7406 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,22 @@ +2003-07-16 Jakub Jelinek + + * unwind-dw2.c (MD_FROB_UPDATE_CONTEXT): Define. + (uw_update_context_1): Use it. + * config/rs6000/rs6000.c (insn_after_throw): Remove. + (rs6000_aix_emit_builtin_unwind_init): Save $r2 to its location + in parent frame if _Unwind_* called directly instead of through + .plt. + (rs6000_emit_eh_toc_restore): Remove. + (rs6000_emit_prologue): Update stack pointer before doing any saving + if current_function_calls_eh_return. Generate unwind info for $r2. + (rs6000_emit_epilogue): Restore stack pointer after doing all + restoring if current_function_calls_eh_return. Restore $r2. + * config/rs6000/rs6000-protos.h (rs6000_emit_eh_toc_restore): Remove. + * config/rs6000/rs6000.md (eh_return): Remove call to + rs6000_emit_eh_toc_restore. + * config/rs6000/linux64.h (MD_FROB_UPDATE_CONTEXT): Define. + * config/rs6000/aix.h (MD_FROB_UPDATE_CONTEXT): Define. + 2003-07-15 Jakub Jelinek * expr.c (emit_block_move): Don't move anything if size is const 0. diff --git a/gcc/config/rs6000/aix.h b/gcc/config/rs6000/aix.h index d40221513aa..ec6a350f157 100644 --- a/gcc/config/rs6000/aix.h +++ b/gcc/config/rs6000/aix.h @@ -209,6 +209,38 @@ So we have to squirrel it away with this. */ #define SETUP_FRAME_ADDRESSES() rs6000_aix_emit_builtin_unwind_init () +/* If the current unwind info (FS) does not contain explicit info + saving R2, then we have to do a minor amount of code reading to + figure out if it was saved. The big problem here is that the + code that does the save/restore is generated by the linker, so + we have no good way to determine at compile time what to do. */ + +#ifdef __powerpc64__ +#define MD_FROB_UPDATE_CONTEXT(CTX, FS) \ + do { \ + if ((FS)->regs.reg[2].how == REG_UNSAVED) \ + { \ + unsigned int *insn \ + = (unsigned int *) \ + _Unwind_GetGR ((CTX), LINK_REGISTER_REGNUM); \ + if (*insn == 0xE8410028) \ + _Unwind_SetGRPtr ((CTX), 2, (CTX)->cfa + 40); \ + } \ + } while (0) +#else +#define MD_FROB_UPDATE_CONTEXT(CTX, FS) \ + do { \ + if ((FS)->regs.reg[2].how == REG_UNSAVED) \ + { \ + unsigned int *insn \ + = (unsigned int *) \ + _Unwind_GetGR ((CTX), LINK_REGISTER_REGNUM); \ + if (*insn == 0x80410014) \ + _Unwind_SetGRPtr ((CTX), 2, (CTX)->cfa + 20); \ + } \ + } while (0) +#endif + #define PROFILE_HOOK(LABEL) output_profile_hook (LABEL) /* Print subsidiary information on the compiler version in use. */ diff --git a/gcc/config/rs6000/linux64.h b/gcc/config/rs6000/linux64.h index 4ef7382cee8..5d7c74df266 100644 --- a/gcc/config/rs6000/linux64.h +++ b/gcc/config/rs6000/linux64.h @@ -553,6 +553,24 @@ enum { SIGNAL_FRAMESIZE = 64 }; #ifdef __powerpc64__ +/* If the current unwind info (FS) does not contain explicit info + saving R2, then we have to do a minor amount of code reading to + figure out if it was saved. The big problem here is that the + code that does the save/restore is generated by the linker, so + we have no good way to determine at compile time what to do. */ + +#define MD_FROB_UPDATE_CONTEXT(CTX, FS) \ + do { \ + if ((FS)->regs.reg[2].how == REG_UNSAVED) \ + { \ + unsigned int *insn \ + = (unsigned int *) \ + _Unwind_GetGR ((CTX), LINK_REGISTER_REGNUM); \ + if (*insn == 0xE8410028) \ + _Unwind_SetGRPtr ((CTX), 2, (CTX)->cfa + 40); \ + } \ + } while (0) + #define MD_FALLBACK_FRAME_STATE_FOR(CONTEXT, FS, SUCCESS) \ do { \ unsigned char *pc_ = (CONTEXT)->ra; \ diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h index fc7e9cb96d8..bbdaa6ac6f0 100644 --- a/gcc/config/rs6000/rs6000-protos.h +++ b/gcc/config/rs6000/rs6000-protos.h @@ -126,7 +126,6 @@ extern int mfcr_operation PARAMS ((rtx, enum machine_mode)); extern int mtcrf_operation PARAMS ((rtx, enum machine_mode)); extern int lmw_operation PARAMS ((rtx, enum machine_mode)); extern struct rtx_def *create_TOC_reference PARAMS ((rtx)); -extern void rs6000_emit_eh_toc_restore PARAMS ((rtx)); extern void rs6000_split_altivec_in_gprs (rtx *); extern void rs6000_emit_move PARAMS ((rtx, rtx, enum machine_mode)); extern rtx rs6000_legitimize_address PARAMS ((rtx, rtx, enum machine_mode)); diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index fa296d5db94..8f268974aa5 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -10800,132 +10800,40 @@ create_TOC_reference (symbol) gen_rtx_SYMBOL_REF (Pmode, toc_label_name)))); } -/* __throw will restore its own return address to be the same as the - return address of the function that the throw is being made to. - This is unfortunate, because we want to check the original - return address to see if we need to restore the TOC. - So we have to squirrel it away here. - This is used only in compiling __throw and __rethrow. +/* If _Unwind_* has been called from within the same module, + toc register is not guaranteed to be saved to 40(1) on function + entry. Save it there in that case. */ - Most of this code should be removed by CSE. */ -static rtx insn_after_throw; - -/* This does the saving... */ void rs6000_aix_emit_builtin_unwind_init () { rtx mem; rtx stack_top = gen_reg_rtx (Pmode); rtx opcode_addr = gen_reg_rtx (Pmode); - - insn_after_throw = gen_reg_rtx (SImode); + rtx opcode = gen_reg_rtx (SImode); + rtx tocompare = gen_reg_rtx (SImode); + rtx no_toc_save_needed = gen_label_rtx (); mem = gen_rtx_MEM (Pmode, hard_frame_pointer_rtx); emit_move_insn (stack_top, mem); - mem = gen_rtx_MEM (Pmode, - gen_rtx_PLUS (Pmode, stack_top, - GEN_INT (2 * GET_MODE_SIZE (Pmode)))); - emit_move_insn (opcode_addr, mem); - emit_move_insn (insn_after_throw, gen_rtx_MEM (SImode, opcode_addr)); -} - -/* Emit insns to _restore_ the TOC register, at runtime (specifically - in _eh.o). Only used on AIX. - - The idea is that on AIX, function calls look like this: - bl somefunction-trampoline - lwz r2,20(sp) - - and later, - somefunction-trampoline: - stw r2,20(sp) - ... load function address in the count register ... - bctr - or like this, if the linker determines that this is not a cross-module call - and so the TOC need not be restored: - bl somefunction - nop - or like this, if the compiler could determine that this is not a - cross-module call: - bl somefunction - now, the tricky bit here is that register 2 is saved and restored - by the _linker_, so we can't readily generate debugging information - for it. So we need to go back up the call chain looking at the - insns at return addresses to see which calls saved the TOC register - and so see where it gets restored from. - - Oh, and all this gets done in RTL inside the eh_epilogue pattern, - just before the actual epilogue. - - On the bright side, this incurs no space or time overhead unless an - exception is thrown, except for the extra code in libgcc.a. - - The parameter STACKSIZE is a register containing (at runtime) - the amount to be popped off the stack in addition to the stack frame - of this routine (which will be __throw or __rethrow, and so is - guaranteed to have a stack frame). */ - -void -rs6000_emit_eh_toc_restore (stacksize) - rtx stacksize; -{ - rtx top_of_stack; - rtx bottom_of_stack = gen_reg_rtx (Pmode); - rtx tocompare = gen_reg_rtx (SImode); - rtx opcode = gen_reg_rtx (SImode); - rtx opcode_addr = gen_reg_rtx (Pmode); - rtx mem; - rtx loop_start = gen_label_rtx (); - rtx no_toc_restore_needed = gen_label_rtx (); - rtx loop_exit = gen_label_rtx (); - - mem = gen_rtx_MEM (Pmode, hard_frame_pointer_rtx); - set_mem_alias_set (mem, rs6000_sr_alias_set); - emit_move_insn (bottom_of_stack, mem); - - top_of_stack = expand_binop (Pmode, add_optab, - bottom_of_stack, stacksize, - NULL_RTX, 1, OPTAB_WIDEN); - - emit_move_insn (tocompare, gen_int_mode (TARGET_32BIT ? 0x80410014 - : 0xE8410028, SImode)); - - if (insn_after_throw == NULL_RTX) - abort (); - emit_move_insn (opcode, insn_after_throw); - - emit_note (NOTE_INSN_LOOP_BEG); - emit_label (loop_start); - - do_compare_rtx_and_jump (opcode, tocompare, NE, 1, - SImode, NULL_RTX, NULL_RTX, - no_toc_restore_needed); - - mem = gen_rtx_MEM (Pmode, - gen_rtx_PLUS (Pmode, bottom_of_stack, - GEN_INT (5 * GET_MODE_SIZE (Pmode)))); - emit_move_insn (gen_rtx_REG (Pmode, 2), mem); - - emit_label (no_toc_restore_needed); - do_compare_rtx_and_jump (top_of_stack, bottom_of_stack, EQ, 1, - Pmode, NULL_RTX, NULL_RTX, - loop_exit); - - mem = gen_rtx_MEM (Pmode, bottom_of_stack); - set_mem_alias_set (mem, rs6000_sr_alias_set); - emit_move_insn (bottom_of_stack, mem); - - mem = gen_rtx_MEM (Pmode, - gen_rtx_PLUS (Pmode, bottom_of_stack, + mem = gen_rtx_MEM (Pmode, + gen_rtx_PLUS (Pmode, stack_top, GEN_INT (2 * GET_MODE_SIZE (Pmode)))); emit_move_insn (opcode_addr, mem); emit_move_insn (opcode, gen_rtx_MEM (SImode, opcode_addr)); + emit_move_insn (tocompare, gen_int_mode (TARGET_32BIT ? 0x80410014 + : 0xE8410028, SImode)); - emit_note (NOTE_INSN_LOOP_CONT); - emit_jump (loop_start); - emit_note (NOTE_INSN_LOOP_END); - emit_label (loop_exit); + do_compare_rtx_and_jump (opcode, tocompare, EQ, 1, + SImode, NULL_RTX, NULL_RTX, + no_toc_save_needed); + + mem = gen_rtx_MEM (Pmode, + gen_rtx_PLUS (Pmode, stack_top, + GEN_INT (5 * GET_MODE_SIZE (Pmode)))); + emit_move_insn (mem, gen_rtx_REG (Pmode, 2)); + emit_label (no_toc_save_needed); } /* This ties together stack memory (MEM with an alias set of @@ -11347,7 +11255,8 @@ rs6000_emit_prologue () || FP_SAVE_INLINE (info->first_fp_reg_save)); /* For V.4, update stack before we do any saving and set back pointer. */ - if (info->push_p && DEFAULT_ABI == ABI_V4) + if (info->push_p + && (DEFAULT_ABI == ABI_V4 || current_function_calls_eh_return)) { if (info->total_size < 32767) sp_offset = info->total_size; @@ -11575,6 +11484,23 @@ rs6000_emit_prologue () { unsigned int i, regno; + /* In AIX ABI we need to pretend we save r2 here. */ + if (TARGET_AIX) + { + rtx addr, reg, mem; + + reg = gen_rtx_REG (reg_mode, 2); + addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (sp_offset + 5 * reg_size)); + mem = gen_rtx_MEM (reg_mode, addr); + set_mem_alias_set (mem, rs6000_sr_alias_set); + + insn = emit_move_insn (mem, reg); + rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, + NULL_RTX, NULL_RTX); + PATTERN (insn) = gen_blockage (); + } + for (i = 0; ; ++i) { regno = EH_RETURN_DATA_REGNO (i); @@ -11633,7 +11559,8 @@ rs6000_emit_prologue () /* Update stack and set back pointer unless this is V.4, for which it was done previously. */ - if (info->push_p && DEFAULT_ABI != ABI_V4) + if (info->push_p + && !(DEFAULT_ABI == ABI_V4 || current_function_calls_eh_return)) rs6000_emit_allocate_stack (info->total_size, FALSE); /* Set frame pointer, if needed. */ @@ -11812,7 +11739,8 @@ rs6000_emit_epilogue (sibcall) } else if (info->push_p) { - if (DEFAULT_ABI == ABI_V4) + if (DEFAULT_ABI == ABI_V4 + || current_function_calls_eh_return) sp_offset = info->total_size; else { @@ -11897,6 +11825,17 @@ rs6000_emit_epilogue (sibcall) { unsigned int i, regno; + if (TARGET_AIX) + { + rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (sp_offset + 5 * reg_size)); + rtx mem = gen_rtx_MEM (reg_mode, addr); + + set_mem_alias_set (mem, rs6000_sr_alias_set); + + emit_move_insn (gen_rtx_REG (reg_mode, 2), mem); + } + for (i = 0; ; ++i) { rtx mem; @@ -12048,7 +11987,8 @@ rs6000_emit_epilogue (sibcall) (which may not have any obvious dependency on the stack). This doesn't hurt performance, because there is no scheduling that can be done after this point. */ - if (DEFAULT_ABI == ABI_V4) + if (DEFAULT_ABI == ABI_V4 + || current_function_calls_eh_return) { if (frame_reg_rtx != sp_reg_rtx) rs6000_emit_stack_tie (); diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md index bf778e2e22c..f39e849628a 100644 --- a/gcc/config/rs6000/rs6000.md +++ b/gcc/config/rs6000/rs6000.md @@ -14637,8 +14637,6 @@ "" " { - if (TARGET_AIX) - rs6000_emit_eh_toc_restore (EH_RETURN_STACKADJ_RTX); if (TARGET_32BIT) emit_insn (gen_eh_set_lr_si (operands[0])); else diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index e6f3233c9ff..ab2deeb4fe6 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2003-07-16 Jakub Jelinek + + * gcc.dg/cleanup-8.c: New test. + * gcc.dg/cleanup-9.c: New test. + 2003-07-16 Danny Smith * g++.dg/ext/dll-MI1.h: New file. diff --git a/gcc/testsuite/gcc.dg/cleanup-8.c b/gcc/testsuite/gcc.dg/cleanup-8.c new file mode 100644 index 00000000000..91e387ce799 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cleanup-8.c @@ -0,0 +1,97 @@ +/* { dg-do run { target i?86-*-linux* x86_64-*-linux* ia64-*-linux* alpha*-*-linux* powerpc*-*-linux* s390*-*-linux* sparc*-*-linux* } } */ +/* { dg-options "-fasynchronous-unwind-tables -fexceptions -O2" } */ +/* Verify that cleanups work with exception handling through signal + frames. */ + +#include +#include +#include + +static _Unwind_Reason_Code +force_unwind_stop (int version, _Unwind_Action actions, + _Unwind_Exception_Class exc_class, + struct _Unwind_Exception *exc_obj, + struct _Unwind_Context *context, + void *stop_parameter) +{ + if (actions & _UA_END_OF_STACK) + abort (); + return _URC_NO_REASON; +} + +static void force_unwind () +{ + struct _Unwind_Exception *exc = malloc (sizeof (*exc)); + exc->exception_class = 0; + exc->exception_cleanup = 0; + +#ifndef __USING_SJLJ_EXCEPTIONS__ + _Unwind_ForcedUnwind (exc, force_unwind_stop, 0); +#else + _Unwind_SjLj_ForcedUnwind (exc, force_unwind_stop, 0); +#endif + + abort (); +} + +int count; +char *null; + +static void counter (void *p __attribute__((unused))) +{ + ++count; +} + +static void handler (void *p __attribute__((unused))) +{ + if (count != 2) + abort (); + exit (0); +} + +static int __attribute__((noinline)) fn5 () +{ + char dummy __attribute__((cleanup (counter))); + force_unwind (); + return 0; +} + +static void fn4 (int sig) +{ + char dummy __attribute__((cleanup (counter))); + fn5 (); + null = NULL; +} + +static void fn3 () +{ + abort (); +} + +static int __attribute__((noinline)) fn2 () +{ + *null = 0; + fn3 (); + return 0; +} + +static int __attribute__((noinline)) fn1 () +{ + signal (SIGSEGV, fn4); + fn2 (); + return 0; +} + +static int __attribute__((noinline)) fn0 () +{ + char dummy __attribute__((cleanup (handler))); + fn1 (); + null = 0; + return 0; +} + +int main() +{ + fn0 (); + abort (); +} diff --git a/gcc/testsuite/gcc.dg/cleanup-9.c b/gcc/testsuite/gcc.dg/cleanup-9.c new file mode 100644 index 00000000000..0c17f25f63d --- /dev/null +++ b/gcc/testsuite/gcc.dg/cleanup-9.c @@ -0,0 +1,101 @@ +/* { dg-do run { target i?86-*-linux* x86_64-*-linux* ia64-*-linux* alpha*-*-linux* powerpc*-*-linux* s390*-*-linux* sparc*-*-linux* } } */ +/* { dg-options "-fasynchronous-unwind-tables -fexceptions -O2" } */ +/* Verify that cleanups work with exception handling through realtime + signal frames. */ + +#include +#include +#include + +static _Unwind_Reason_Code +force_unwind_stop (int version, _Unwind_Action actions, + _Unwind_Exception_Class exc_class, + struct _Unwind_Exception *exc_obj, + struct _Unwind_Context *context, + void *stop_parameter) +{ + if (actions & _UA_END_OF_STACK) + abort (); + return _URC_NO_REASON; +} + +static void force_unwind () +{ + struct _Unwind_Exception *exc = malloc (sizeof (*exc)); + exc->exception_class = 0; + exc->exception_cleanup = 0; + +#ifndef __USING_SJLJ_EXCEPTIONS__ + _Unwind_ForcedUnwind (exc, force_unwind_stop, 0); +#else + _Unwind_SjLj_ForcedUnwind (exc, force_unwind_stop, 0); +#endif + + abort (); +} + +int count; +char *null; + +static void counter (void *p __attribute__((unused))) +{ + ++count; +} + +static void handler (void *p __attribute__((unused))) +{ + if (count != 2) + abort (); + exit (0); +} + +static int __attribute__((noinline)) fn5 () +{ + char dummy __attribute__((cleanup (counter))); + force_unwind (); + return 0; +} + +static void fn4 (int sig, siginfo_t *info, void *ctx) +{ + char dummy __attribute__((cleanup (counter))); + fn5 (); + null = NULL; +} + +static void fn3 () +{ + abort (); +} + +static int __attribute__((noinline)) fn2 () +{ + *null = 0; + fn3 (); + return 0; +} + +static int __attribute__((noinline)) fn1 () +{ + struct sigaction s; + sigemptyset (&s.sa_mask); + s.sa_sigaction = fn4; + s.sa_flags = SA_ONESHOT | SA_SIGINFO; + sigaction (SIGSEGV, &s, NULL); + fn2 (); + return 0; +} + +static int __attribute__((noinline)) fn0 () +{ + char dummy __attribute__((cleanup (handler))); + fn1 (); + null = 0; + return 0; +} + +int main() +{ + fn0 (); + abort (); +} diff --git a/gcc/unwind-dw2.c b/gcc/unwind-dw2.c index eadd88bba5a..3e58be6cb69 100644 --- a/gcc/unwind-dw2.c +++ b/gcc/unwind-dw2.c @@ -54,6 +54,11 @@ #define DWARF_REG_TO_UNWIND_COLUMN(REGNO) (REGNO) #endif +/* A target can do some update context frobbing. */ +#ifndef MD_FROB_UPDATE_CONTEXT +#define MD_FROB_UPDATE_CONTEXT(CTX, FS) do { } while (0) +#endif + /* This is the register and unwind state for a particular frame. This provides the information necessary to unwind up past a frame and return to its caller. */ @@ -1203,6 +1208,8 @@ uw_update_context_1 (struct _Unwind_Context *context, _Unwind_FrameState *fs) } break; } + + MD_FROB_UPDATE_CONTEXT (context, fs); } /* CONTEXT describes the unwind state for a frame, and FS describes the FDE