From e8c89d29703051af683fe65dc055089b61acde2f Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Fri, 19 Dec 2003 15:00:53 +0100 Subject: [PATCH] unwind-ia64.c (ia64_copy_rbs): New function. * config/ia64/unwind-ia64.c (ia64_copy_rbs): New function. (unw_access_gr): Only call ia64_rse_rnat_addr if addr is above regstk_top. (uw_frame_state_for): Handle locations inside bundles. (uw_init_context_1): Initialize context->rnat. Set context->regstk_top to lowest rbs address which has nat collection in context->rnat. (uw_install_context): Fix rnat restoring. Restore ar.rsc to previous state. * config/ia64/linux.h (MD_FALLBACK_FRAME_STATE_FOR, MD_HANDLE_UNWABI): Handle unwinding through SA_ONSTACK frames. * gcc.dg/cleanup-10.c: New test. * gcc.dg/cleanup-11.c: New test. From-SVN: r74835 --- gcc/ChangeLog | 14 ++++ gcc/config/ia64/linux.h | 34 ++++++++- gcc/config/ia64/unwind-ia64.c | 119 ++++++++++++++++++++++++++---- gcc/testsuite/ChangeLog | 5 ++ gcc/testsuite/gcc.dg/cleanup-10.c | 114 ++++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/cleanup-11.c | 114 ++++++++++++++++++++++++++++ 6 files changed, 385 insertions(+), 15 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/cleanup-10.c create mode 100644 gcc/testsuite/gcc.dg/cleanup-11.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index dec8a807a4b..ac10544a42d 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,17 @@ +2003-12-19 Jakub Jelinek + + * config/ia64/unwind-ia64.c (ia64_copy_rbs): New function. + (unw_access_gr): Only call ia64_rse_rnat_addr if addr is above + regstk_top. + (uw_frame_state_for): Handle locations inside bundles. + (uw_init_context_1): Initialize context->rnat. + Set context->regstk_top to lowest rbs address which has nat collection + in context->rnat. + (uw_install_context): Fix rnat restoring. + Restore ar.rsc to previous state. + * config/ia64/linux.h (MD_FALLBACK_FRAME_STATE_FOR, + MD_HANDLE_UNWABI): Handle unwinding through SA_ONSTACK frames. + 2003-12-19 Jakub Jelinek PR c++/13239 diff --git a/gcc/config/ia64/linux.h b/gcc/config/ia64/linux.h index d456ea5f52e..fc7df808188 100644 --- a/gcc/config/ia64/linux.h +++ b/gcc/config/ia64/linux.h @@ -97,7 +97,6 @@ do { \ (CONTEXT)->br_loc[0] = &(sc_->sc_br[0]); \ (CONTEXT)->br_loc[6] = &(sc_->sc_br[6]); \ (CONTEXT)->br_loc[7] = &(sc_->sc_br[7]); \ - (CONTEXT)->bsp = sc_->sc_ar_bsp; \ (CONTEXT)->pr = sc_->sc_pr; \ (CONTEXT)->psp = sc_->sc_gr[12]; \ (CONTEXT)->gp = sc_->sc_gr[1]; \ @@ -105,6 +104,22 @@ do { \ other than what we adjust for below. */ \ (FS) -> no_reg_stack_frame = 1; \ \ + if (sc_->sc_rbs_base) \ + { \ + /* Need to switch from alternate register backing store. */ \ + long ndirty, loadrs = sc_->sc_loadrs >> 16; \ + unsigned long alt_bspstore = (CONTEXT)->bsp - loadrs; \ + unsigned long bspstore; \ + unsigned long *ar_bsp = (unsigned long *)(sc_->sc_ar_bsp); \ + \ + ndirty = ia64_rse_num_regs ((unsigned long *) alt_bspstore, \ + (unsigned long *) (CONTEXT)->bsp);\ + bspstore = (unsigned long) \ + ia64_rse_skip_regs (ar_bsp, -ndirty); \ + ia64_copy_rbs ((CONTEXT), bspstore, alt_bspstore, loadrs, \ + sc_->sc_ar_rnat); \ + } \ + \ /* Don't touch the branch registers o.t. b0, b6 and b7. \ The kernel doesn't pass the preserved branch registers \ in the sigcontext but leaves them intact, so there's no \ @@ -154,13 +169,28 @@ do { \ (CONTEXT)->br_loc[0] = &(sc_->sc_br[0]); \ (CONTEXT)->br_loc[6] = &(sc_->sc_br[6]); \ (CONTEXT)->br_loc[7] = &(sc_->sc_br[7]); \ - (CONTEXT)->bsp = sc_->sc_ar_bsp; \ (CONTEXT)->pr = sc_->sc_pr; \ (CONTEXT)->gp = sc_->sc_gr[1]; \ /* Signal frame doesn't have an associated reg. stack frame \ other than what we adjust for below. */ \ (FS) -> no_reg_stack_frame = 1; \ \ + if (sc_->sc_rbs_base) \ + { \ + /* Need to switch from alternate register backing store. */ \ + long ndirty, loadrs = sc_->sc_loadrs >> 16; \ + unsigned long alt_bspstore = (CONTEXT)->bsp - loadrs; \ + unsigned long bspstore; \ + unsigned long *ar_bsp = (unsigned long *)(sc_->sc_ar_bsp); \ + \ + ndirty = ia64_rse_num_regs ((unsigned long *) alt_bspstore, \ + (unsigned long *) (CONTEXT)->bsp);\ + bspstore = (unsigned long) \ + ia64_rse_skip_regs (ar_bsp, -ndirty); \ + ia64_copy_rbs ((CONTEXT), bspstore, alt_bspstore, loadrs, \ + sc_->sc_ar_rnat); \ + } \ + \ /* Don't touch the branch registers o.t. b0, b6 and b7. \ The kernel doesn't pass the preserved branch registers \ in the sigcontext but leaves them intact, so there's no \ diff --git a/gcc/config/ia64/unwind-ia64.c b/gcc/config/ia64/unwind-ia64.c index 4de32638367..29c63fa13a7 100644 --- a/gcc/config/ia64/unwind-ia64.c +++ b/gcc/config/ia64/unwind-ia64.c @@ -184,7 +184,8 @@ struct _Unwind_Context { /* Initial frame info. */ unsigned long rnat; /* rse nat collection */ - unsigned long regstk_top; /* bsp for first frame */ + unsigned long regstk_top; /* lowest address of rbs stored register + which uses context->rnat collection */ /* Current frame info. */ unsigned long bsp; /* backing store pointer value @@ -1492,6 +1493,80 @@ ia64_rse_skip_regs (unsigned long *addr, long num_regs) } +/* Copy register backing store from SRC to DST, LEN words + (which include both saved registers and nat collections). + DST_RNAT is a partial nat collection for DST. SRC and DST + don't have to be equal modulo 64 slots, so it cannot be + done with a simple memcpy as the nat collections will be + at different relative offsets and need to be combined together. */ +static void +ia64_copy_rbs (struct _Unwind_Context *info, unsigned long dst, + unsigned long src, long len, unsigned long dst_rnat) +{ + long count; + unsigned long src_rnat; + unsigned long shift1, shift2; + + len <<= 3; + dst_rnat &= (1UL << ((dst >> 3) & 0x3f)) - 1; + src_rnat = src >= info->regstk_top + ? info->rnat : *(unsigned long *) (src | 0x1f8); + src_rnat &= ~((1UL << ((src >> 3) & 0x3f)) - 1); + /* Just to make sure. */ + src_rnat &= ~(1UL << 63); + shift1 = ((dst - src) >> 3) & 0x3f; + if ((dst & 0x1f8) < (src & 0x1f8)) + shift1--; + shift2 = 0x3f - shift1; + if ((dst & 0x1f8) >= (src & 0x1f8)) + { + count = ~dst & 0x1f8; + goto first; + } + count = ~src & 0x1f8; + goto second; + while (len > 0) + { + src_rnat = src >= info->regstk_top + ? info->rnat : *(unsigned long *) (src | 0x1f8); + /* Just to make sure. */ + src_rnat &= ~(1UL << 63); + count = shift2 << 3; +first: + if (count > len) + count = len; + memcpy ((char *) dst, (char *) src, count); + dst += count; + src += count; + len -= count; + dst_rnat |= (src_rnat << shift1) & ~(1UL << 63); + if (len <= 0) + break; + *(long *) dst = dst_rnat; + dst += 8; + dst_rnat = 0; + count = shift1 << 3; +second: + if (count > len) + count = len; + memcpy ((char *) dst, (char *) src, count); + dst += count; + src += count + 8; + len -= count + 8; + dst_rnat |= (src_rnat >> shift2); + } + if ((dst & 0x1f8) == 0x1f8) + { + *(long *) dst = dst_rnat; + dst += 8; + dst_rnat = 0; + } + /* Set info->regstk_top to lowest rbs address which will use + info->rnat collection. */ + info->regstk_top = dst & ~0x1ffUL; + info->rnat = dst_rnat; +} + /* Unwind accessors. */ static void @@ -1551,9 +1626,10 @@ unw_access_gr (struct _Unwind_Context *info, int regnum, break; case UNW_NAT_REGSTK: - nat_addr = ia64_rse_rnat_addr (addr); - if ((unsigned long) nat_addr >= info->regstk_top) + if ((unsigned long) addr >= info->regstk_top) nat_addr = &info->rnat; + else + nat_addr = ia64_rse_rnat_addr (addr); nat_mask = 1UL << ia64_rse_slot_num (addr); break; } @@ -1563,9 +1639,10 @@ unw_access_gr (struct _Unwind_Context *info, int regnum, { /* Access a stacked register. */ addr = ia64_rse_skip_regs ((unsigned long *) info->bsp, regnum - 32); - nat_addr = ia64_rse_rnat_addr (addr); - if ((unsigned long) nat_addr >= info->regstk_top) + if ((unsigned long) addr >= info->regstk_top) nat_addr = &info->rnat; + else + nat_addr = ia64_rse_rnat_addr (addr); nat_mask = 1UL << ia64_rse_slot_num (addr); } @@ -1724,7 +1801,8 @@ uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs) } context->region_start = ent->start_offset + segment_base; - fs->when_target = (context->rp - context->region_start) / 16 * 3; + fs->when_target = ((context->rp & -16) - context->region_start) / 16 * 3 + + (context->rp & 15); unw = (unsigned long *) (ent->info_offset + segment_base); header = *unw; @@ -1998,18 +2076,31 @@ uw_init_context_1 (struct _Unwind_Context *context, void *bsp) /* Set psp to the caller's stack pointer. */ void *psp = __builtin_dwarf_cfa () - 16; _Unwind_FrameState fs; + unsigned long rnat, tmp1, tmp2; - /* Flush the register stack to memory so that we can access it. */ - __builtin_ia64_flushrs (); + /* Flush the register stack to memory so that we can access it. + Get rse nat collection for the last incomplete rbs chunk of + registers at the same time. For this RSE needs to be turned + into the mandatory only mode. */ + asm ("mov.m %1 = ar.rsc;;\n\t" + "and %2 = 0x1c, %1;;\n\t" + "mov.m ar.rsc = %2;;\n\t" + "flushrs;;\n\t" + "mov.m %0 = ar.rnat;;\n\t" + "mov.m ar.rsc = %1\n\t" + : "=r" (rnat), "=r" (tmp1), "=r" (tmp2)); memset (context, 0, sizeof (struct _Unwind_Context)); - context->bsp = context->regstk_top = (unsigned long) bsp; + context->bsp = (unsigned long) bsp; + /* Set context->regstk_top to lowest rbs address which will use + context->rnat collection. */ + context->regstk_top = context->bsp & ~0x1ffULL; + context->rnat = rnat; context->psp = (unsigned long) psp; context->rp = (unsigned long) rp; asm ("mov %0 = sp" : "=r" (context->sp)); asm ("mov %0 = pr" : "=r" (context->pr)); context->pri_unat_loc = &context->initial_unat; /* ??? */ - /* ??? Get rnat. Don't we have to turn off the rse for that? */ if (uw_frame_state_for (context, &fs) != _URC_NO_REASON) abort (); @@ -2050,6 +2141,9 @@ uw_install_context (struct _Unwind_Context *current __attribute__((unused)), ia64_rse_skip_regs ((unsigned long *)target->bsp, (*target->pfs_loc >> 7) & 0x7f); + if (target->bsp < target->regstk_top) + target->rnat = *ia64_rse_rnat_addr ((unsigned long *) target->bsp); + /* Provide assembly with the offsets into the _Unwind_Context. */ asm volatile ("uc_rnat = %0" : : "i"(offsetof (struct _Unwind_Context, rnat))); @@ -2251,10 +2345,10 @@ uw_install_context (struct _Unwind_Context *current __attribute__((unused)), "mov.m r25 = ar.rsc \n\t" "(p6) mov.m ar.fpsr = r30 \n\t" ";; \n\t" - "and r25 = 0x1c, r25 \n\t" + "and r29 = 0x1c, r25 \n\t" "mov b0 = r26 \n\t" ";; \n\t" - "mov.m ar.rsc = r25 \n\t" + "mov.m ar.rsc = r29 \n\t" ";; \n\t" /* This must be done before setting AR.BSPSTORE, otherwise AR.BSP will be initialized with a random displacement @@ -2265,7 +2359,6 @@ uw_install_context (struct _Unwind_Context *current __attribute__((unused)), ";; \n\t" "mov.m ar.bspstore = r23 \n\t" ";; \n\t" - "or r25 = 0x3, r25 \n\t" "mov.m ar.rnat = r22 \n\t" ";; \n\t" "mov.m ar.rsc = r25 \n\t" diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 3010412fce1..33e006ec386 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2003-12-19 Jakub Jelinek + + * gcc.dg/cleanup-10.c: New test. + * gcc.dg/cleanup-11.c: New test. + 2003-12-19 Jakub Jelinek PR c++/13239 diff --git a/gcc/testsuite/gcc.dg/cleanup-10.c b/gcc/testsuite/gcc.dg/cleanup-10.c new file mode 100644 index 00000000000..39923fdde15 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cleanup-10.c @@ -0,0 +1,114 @@ +/* { dg-do run { target i?86-*-linux* x86_64-*-linux* ia64-*-linux* alpha*-*-linux* powerpc*-*-linux* s390*-*-linux* sparc*-*-linux* mips*-*-linux* } } */ +/* { dg-options "-fasynchronous-unwind-tables -fexceptions -O2" } */ +/* Verify that cleanups work with exception handling through signal frames + on alternate stack. */ + +#include +#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 () +{ + stack_t ss; + struct sigaction s; + + ss.ss_size = 4 * sysconf (_SC_PAGESIZE); + if (ss.ss_size < SIGSTKSZ) + ss.ss_size = SIGSTKSZ; + ss.ss_sp = malloc (ss.ss_size); + if (ss.ss_sp == NULL) + exit (1); + ss.ss_flags = 0; + if (sigaltstack (&ss, NULL) < 0) + exit (1); + + sigemptyset (&s.sa_mask); + s.sa_sigaction = fn4; + s.sa_flags = SA_ONESHOT | SA_ONSTACK; + 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/testsuite/gcc.dg/cleanup-11.c b/gcc/testsuite/gcc.dg/cleanup-11.c new file mode 100644 index 00000000000..9c47f6cf658 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cleanup-11.c @@ -0,0 +1,114 @@ +/* { dg-do run { target i?86-*-linux* x86_64-*-linux* ia64-*-linux* alpha*-*-linux* powerpc*-*-linux* s390*-*-linux* sparc*-*-linux* mips*-*-linux* } } */ +/* { dg-options "-fasynchronous-unwind-tables -fexceptions -O2" } */ +/* Verify that cleanups work with exception handling through realtime signal + frames on alternate stack. */ + +#include +#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 () +{ + stack_t ss; + struct sigaction s; + + ss.ss_size = 4 * sysconf (_SC_PAGESIZE); + if (ss.ss_size < SIGSTKSZ) + ss.ss_size = SIGSTKSZ; + ss.ss_sp = malloc (ss.ss_size); + if (ss.ss_sp == NULL) + exit (1); + ss.ss_flags = 0; + if (sigaltstack (&ss, NULL) < 0) + exit (1); + + sigemptyset (&s.sa_mask); + s.sa_sigaction = fn4; + s.sa_flags = SA_ONESHOT | SA_ONSTACK | 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 (); +}