From 88e2c8107b5faaa2b2bee01ee6e6f51522b4e5dd Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Thu, 31 Jul 2008 20:08:36 +0200 Subject: [PATCH] re PR rtl-optimization/36419 (Wrong unwind info with -Os -fasynchronous-unwind-tables) PR rtl-optimization/36419 * dwarf2out.c (barrier_args_size): New variable. (compute_barrier_args_size, compute_barrier_args_size_1): New functions. (dwarf2out_stack_adjust): For BARRIERs call compute_barrier_args_size if not called yet in the current function, use barrier_args_size array to find the new args_size value. (dwarf2out_frame_debug): Free and clear barrier_args_size. * g++.dg/eh/async-unwind2.C: New test. From-SVN: r138427 --- gcc/ChangeLog | 11 + gcc/dwarf2out.c | 178 ++++++++++++++++- gcc/testsuite/ChangeLog | 3 + gcc/testsuite/g++.dg/eh/async-unwind2.C | 254 ++++++++++++++++++++++++ 4 files changed, 439 insertions(+), 7 deletions(-) create mode 100644 gcc/testsuite/g++.dg/eh/async-unwind2.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2b13a5ddf7a..28a956c443e 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,14 @@ +2008-07-31 Jakub Jelinek + + PR rtl-optimization/36419 + * dwarf2out.c (barrier_args_size): New variable. + (compute_barrier_args_size, compute_barrier_args_size_1): New + functions. + (dwarf2out_stack_adjust): For BARRIERs call compute_barrier_args_size + if not called yet in the current function, use barrier_args_size + array to find the new args_size value. + (dwarf2out_frame_debug): Free and clear barrier_args_size. + 2008-07-31 H.J. Lu PR debug/36980 diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index 2fc23a5206b..1219892f200 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -1156,6 +1156,162 @@ stack_adjust_offset (const_rtx pattern) return offset; } +/* Precomputed args_size for CODE_LABELs and BARRIERs preceeding them, + indexed by INSN_UID. */ + +static HOST_WIDE_INT *barrier_args_size; + +/* Helper function for compute_barrier_args_size. Handle one insn. */ + +static HOST_WIDE_INT +compute_barrier_args_size_1 (rtx insn, HOST_WIDE_INT cur_args_size, + VEC (rtx, heap) **next) +{ + HOST_WIDE_INT offset = 0; + int i; + + if (! RTX_FRAME_RELATED_P (insn)) + { + if (prologue_epilogue_contains (insn) + || sibcall_epilogue_contains (insn)) + /* Nothing */; + else if (GET_CODE (PATTERN (insn)) == SET) + offset = stack_adjust_offset (PATTERN (insn)); + else if (GET_CODE (PATTERN (insn)) == PARALLEL + || GET_CODE (PATTERN (insn)) == SEQUENCE) + { + /* There may be stack adjustments inside compound insns. Search + for them. */ + for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--) + if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET) + offset += stack_adjust_offset (XVECEXP (PATTERN (insn), 0, i)); + } + } + else + { + rtx expr = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX); + + if (expr) + { + expr = XEXP (expr, 0); + if (GET_CODE (expr) == PARALLEL + || GET_CODE (expr) == SEQUENCE) + for (i = 1; i < XVECLEN (expr, 0); i++) + { + rtx elem = XVECEXP (expr, 0, i); + + if (GET_CODE (elem) == SET && !RTX_FRAME_RELATED_P (elem)) + offset += stack_adjust_offset (elem); + } + } + } + +#ifndef STACK_GROWS_DOWNWARD + offset = -offset; +#endif + + cur_args_size += offset; + if (cur_args_size < 0) + cur_args_size = 0; + + if (JUMP_P (insn)) + { + rtx dest = JUMP_LABEL (insn); + + if (dest) + { + if (barrier_args_size [INSN_UID (dest)] < 0) + { + barrier_args_size [INSN_UID (dest)] = cur_args_size; + VEC_safe_push (rtx, heap, *next, dest); + } + else + gcc_assert (barrier_args_size[INSN_UID (dest)] + == cur_args_size); + } + } + + return cur_args_size; +} + +/* Walk the whole function and compute args_size on BARRIERs. */ + +static void +compute_barrier_args_size (void) +{ + int max_uid = get_max_uid (), i; + rtx insn; + VEC (rtx, heap) *worklist, *next, *tmp; + + barrier_args_size = XNEWVEC (HOST_WIDE_INT, max_uid); + for (i = 0; i < max_uid; i++) + barrier_args_size[i] = -1; + + worklist = VEC_alloc (rtx, heap, 20); + next = VEC_alloc (rtx, heap, 20); + insn = get_insns (); + barrier_args_size[INSN_UID (insn)] = 0; + VEC_quick_push (rtx, worklist, insn); + for (;;) + { + while (!VEC_empty (rtx, worklist)) + { + rtx prev, body; + HOST_WIDE_INT cur_args_size; + + insn = VEC_pop (rtx, worklist); + cur_args_size = barrier_args_size[INSN_UID (insn)]; + prev = prev_nonnote_insn (insn); + if (prev && BARRIER_P (prev)) + barrier_args_size[INSN_UID (prev)] = cur_args_size; + + for (; insn; insn = NEXT_INSN (insn)) + { + if (INSN_DELETED_P (insn) || NOTE_P (insn)) + continue; + if (BARRIER_P (insn)) + break; + + if (LABEL_P (insn)) + { + gcc_assert (barrier_args_size[INSN_UID (insn)] < 0 + || barrier_args_size[INSN_UID (insn)] + == cur_args_size); + continue; + } + + body = PATTERN (insn); + if (GET_CODE (body) == SEQUENCE) + { + for (i = 1; i < XVECLEN (body, 0); i++) + cur_args_size + = compute_barrier_args_size_1 (XVECEXP (body, 0, i), + cur_args_size, &next); + cur_args_size + = compute_barrier_args_size_1 (XVECEXP (body, 0, 0), + cur_args_size, &next); + } + else + cur_args_size + = compute_barrier_args_size_1 (insn, cur_args_size, &next); + } + } + + if (VEC_empty (rtx, next)) + break; + + /* Swap WORKLIST with NEXT and truncate NEXT for next iteration. */ + tmp = next; + next = worklist; + worklist = tmp; + VEC_truncate (rtx, next, 0); + } + + VEC_free (rtx, heap, worklist); + VEC_free (rtx, heap, next); +} + + /* Check INSN to see if it looks like a push or a stack adjustment, and make a note of it if it does. EH uses this information to find out how much extra space it needs to pop off the stack. */ @@ -1200,13 +1356,15 @@ dwarf2out_stack_adjust (rtx insn, bool after_p) } else if (BARRIER_P (insn)) { - /* When we see a BARRIER, we know to reset args_size to 0. Usually - the compiler will have already emitted a stack adjustment, but - doesn't bother for calls to noreturn functions. */ -#ifdef STACK_GROWS_DOWNWARD - offset = -args_size; -#else - offset = args_size; + if (barrier_args_size == NULL) + compute_barrier_args_size (); + offset = barrier_args_size[INSN_UID (insn)]; + if (offset < 0) + offset = 0; + + offset -= args_size; +#ifndef STACK_GROWS_DOWNWARD + offset = -offset; #endif } else if (GET_CODE (PATTERN (insn)) == SET) @@ -2160,6 +2318,12 @@ dwarf2out_frame_debug (rtx insn, bool after_p) regs_saved_in_regs[i].saved_in_reg = NULL_RTX; } num_regs_saved_in_regs = 0; + + if (barrier_args_size) + { + XDELETEVEC (barrier_args_size); + barrier_args_size = NULL; + } return; } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index f05f6276b1f..0d2ebbf6a7f 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,8 @@ 2008-07-31 Jakub Jelinek + PR rtl-optimization/36419 + * g++.dg/eh/async-unwind2.C: New test. + PR c++/36405 * g++.dg/rtti/typeid8.C: New test. diff --git a/gcc/testsuite/g++.dg/eh/async-unwind2.C b/gcc/testsuite/g++.dg/eh/async-unwind2.C new file mode 100644 index 00000000000..694fad6aca1 --- /dev/null +++ b/gcc/testsuite/g++.dg/eh/async-unwind2.C @@ -0,0 +1,254 @@ +// PR rtl-optimization/36419 +// { dg-do run { target { { i?86-*-* x86_64-*-* } && ilp32 } } } +// { dg-options "-Os -fasynchronous-unwind-tables -fpic -fno-inline" } + +#include + +extern "C" void abort (); + +extern "C" +{ + struct R { int r1; unsigned short r2[1]; }; + int bar1 (unsigned short *, int, short) throw (); + void bar2 (R *) throw (); + void bar3 (R **, const unsigned short *, int) throw (); + void bar4 (R **, const char *) throw (); + void bar5 (void *, const char *, ...); +} + +struct S +{ + R *s; + struct T { }; + S (R *x, T *) { s = x; } + ~S () { bar2 (s); } + S &operator= (const S &x); + S &operator+= (const S &x); + S sfn1 (const S &x) const; + friend S operator+ (const S &x1, const S &x2); + static S sfn2 (int i) + { + unsigned short q[33]; + R *p = 0; + bar3 (&p, q, bar1 (q, i, 10)); + return S (p, (T *) 0); + } + static S sfn3 (const char *x) + { + R *p = 0; + bar4 (&p, x); + return S (p, (T *) 0); + } +}; + +struct U { }; +template unsigned char operator >>= (const U &, C &); + +struct V; +struct W +{ + V *w; + unsigned char is () const; +}; + +template struct X : public W +{ + inline ~X (); + X (); + X (const W &); + T *operator -> () const; +}; + +struct E +{ + E (); + E (const S &, const X &); + E (E const &); + ~E (); + E &operator = (E const &); +}; + +struct V +{ + virtual void release () throw (); +}; + +template X ::~X () +{ + if (w) + w->release (); +} + +struct Y +{ + virtual U yfn1 (const S &); +}; + +struct Z; + +X baz1 (const S &) throw (E); +X baz2 (const X &) throw (E); + +template X::X () +{ + w = __null; +} + +template X::X (W const &) +{ + w = __null; +} + +U Y::yfn1 (const S &) +{ + throw 12; +} + +Y y; + +template T *X::operator -> () const +{ + return &y; +} + +X baz1 (const S &) throw (E) +{ + return X (); +} + +E::E () +{ +} + +E::~E () +{ +} + +X baz2 (const X &) throw (E) +{ + throw E (); +} + +int bar1 (unsigned short *, int, short) throw () +{ + asm volatile ("" : : : "memory"); + return 0; +} + +void bar2 (R *) throw () +{ + asm volatile ("" : : : "memory"); +} + +void bar3 (R **, const unsigned short *, int) throw () +{ + asm volatile ("" : : : "memory"); +} + +void bar4 (R **, const char *) throw () +{ + asm volatile ("" : : : "memory"); +} + +int events[2]; +void *sp; + +void bar5 (void *p, const char *s, ...) +{ + va_list ap; + va_start (ap, s); + if (p) + throw 19; + switch (*s) + { + case 't': + if (events[0] != va_arg (ap, int)) + abort (); + events[0]++; + break; + case 'f': + abort (); + case 'c': + if (events[1] != va_arg (ap, int)) + abort (); + events[1]++; + if (events[1] == 1) + sp = va_arg (ap, void *); + else if (sp != va_arg (ap, void *)) + abort (); + break; + } +} + +unsigned char W::is () const +{ + return 1; +} + +S &S::operator += (const S &) +{ + return *this; +} + +template unsigned char operator >>= (const U &, C &) +{ + throw 1; +} + +template X::X (); +template X::X (); +template unsigned char operator >>= (const U &, X &); +template X::X (W const &); + +template Y *X::operator-> () const; + +X foo () throw () +{ + X a; + X b; + try + { + b = X (baz1 (S::sfn3 ("defg"))); + } + catch (E &) + { + } + if (b.is ()) + { + for (int n = 0; n < 10; n++) + { + S c = S::sfn3 ("abcd"); + c += S::sfn2 (n); + X d; + try + { + bar5 ((void *) 0, "trying %d\n", n); + if ((b->yfn1 (c) >>= d)) + if (d.is ()) + { + bar5 ((void *) 0, "failure1 on %d\n", n); + a = baz2 (d); + if (a.is ()) + break; + } + bar5 ((void *) 0, "failure2 on %d\n", n); + } + catch (...) + { + void *p; + asm volatile ("movl %%esp, %0" : "=r" (p)); + bar5 ((void *) 0, "caught %d %p\n", n, p); + } + } + } + return a; +} + +int +main () +{ + foo (); + if (events[0] != 10 || events[1] != 10) + abort (); + return 0; +} -- 2.30.2