diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 10 | ||||
-rw-r--r-- | gcc/cfgcleanup.c | 19 | ||||
-rw-r--r-- | gcc/expr.c | 236 | ||||
-rw-r--r-- | gcc/rtl.h | 1 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/pr50132.c | 10 |
5 files changed, 163 insertions, 113 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1da50b4..df92093 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2011-08-25 Richard Henderson <rth@redhat.com> + + PR 50132 + PR 49864 + * cfgcleanup.c (old_insns_match_p): Don't allow cross-jump for + non-constant stack adjutment. + * expr.c (find_args_size_adjust): Break out from ... + (fixup_args_size_notes): ... here. + * rtl.h (find_args_size_adjust): Declare. + 2011-08-25 Uros Bizjak <ubizjak@gmail.com> * config/i386/i386.md (isa): Add sse2, sse2_noavx, sse3, diff --git a/gcc/cfgcleanup.c b/gcc/cfgcleanup.c index 7173013..396057c 100644 --- a/gcc/cfgcleanup.c +++ b/gcc/cfgcleanup.c @@ -1081,11 +1081,20 @@ old_insns_match_p (int mode ATTRIBUTE_UNUSED, rtx i1, rtx i2) /* ??? Do not allow cross-jumping between different stack levels. */ p1 = find_reg_note (i1, REG_ARGS_SIZE, NULL); p2 = find_reg_note (i2, REG_ARGS_SIZE, NULL); - if (p1) - p1 = XEXP (p1, 0); - if (p2) - p2 = XEXP (p2, 0); - if (!rtx_equal_p (p1, p2)) + if (p1 && p2) + { + p1 = XEXP (p1, 0); + p2 = XEXP (p2, 0); + if (!rtx_equal_p (p1, p2)) + return dir_none; + + /* ??? Worse, this adjustment had better be constant lest we + have differing incoming stack levels. */ + if (!frame_pointer_needed + && find_args_size_adjust (i1) == HOST_WIDE_INT_MIN) + return dir_none; + } + else if (p1 || p2) return dir_none; p1 = PATTERN (i1); @@ -3548,131 +3548,151 @@ mem_autoinc_base (rtx mem) verified, via immediate operand or auto-inc. If the adjustment cannot be trivially extracted, the return value is INT_MIN. */ -int -fixup_args_size_notes (rtx prev, rtx last, int end_args_size) +HOST_WIDE_INT +find_args_size_adjust (rtx insn) { - int args_size = end_args_size; - bool saw_unknown = false; - rtx insn; + rtx dest, set, pat; + int i; - for (insn = last; insn != prev; insn = PREV_INSN (insn)) - { - rtx dest, set, pat; - HOST_WIDE_INT this_delta = 0; - int i; + pat = PATTERN (insn); + set = NULL; - if (!NONDEBUG_INSN_P (insn)) - continue; - pat = PATTERN (insn); - set = NULL; + /* Look for a call_pop pattern. */ + if (CALL_P (insn)) + { + /* We have to allow non-call_pop patterns for the case + of emit_single_push_insn of a TLS address. */ + if (GET_CODE (pat) != PARALLEL) + return 0; - /* Look for a call_pop pattern. */ - if (CALL_P (insn)) + /* All call_pop have a stack pointer adjust in the parallel. + The call itself is always first, and the stack adjust is + usually last, so search from the end. */ + for (i = XVECLEN (pat, 0) - 1; i > 0; --i) { - /* We have to allow non-call_pop patterns for the case - of emit_single_push_insn of a TLS address. */ - if (GET_CODE (pat) != PARALLEL) - continue; - - /* All call_pop have a stack pointer adjust in the parallel. - The call itself is always first, and the stack adjust is - usually last, so search from the end. */ - for (i = XVECLEN (pat, 0) - 1; i > 0; --i) - { - set = XVECEXP (pat, 0, i); - if (GET_CODE (set) != SET) - continue; - dest = SET_DEST (set); - if (dest == stack_pointer_rtx) - break; - } - /* We'd better have found the stack pointer adjust. */ - if (i == 0) + set = XVECEXP (pat, 0, i); + if (GET_CODE (set) != SET) continue; - /* Fall through to process the extracted SET and DEST - as if it was a standalone insn. */ + dest = SET_DEST (set); + if (dest == stack_pointer_rtx) + break; } - else if (GET_CODE (pat) == SET) - set = pat; - else if ((set = single_set (insn)) != NULL) - ; - else if (GET_CODE (pat) == PARALLEL) + /* We'd better have found the stack pointer adjust. */ + if (i == 0) + return 0; + /* Fall through to process the extracted SET and DEST + as if it was a standalone insn. */ + } + else if (GET_CODE (pat) == SET) + set = pat; + else if ((set = single_set (insn)) != NULL) + ; + else if (GET_CODE (pat) == PARALLEL) + { + /* ??? Some older ports use a parallel with a stack adjust + and a store for a PUSH_ROUNDING pattern, rather than a + PRE/POST_MODIFY rtx. Don't force them to update yet... */ + /* ??? See h8300 and m68k, pushqi1. */ + for (i = XVECLEN (pat, 0) - 1; i >= 0; --i) { - /* ??? Some older ports use a parallel with a stack adjust - and a store for a PUSH_ROUNDING pattern, rather than a - PRE/POST_MODIFY rtx. Don't force them to update yet... */ - /* ??? See h8300 and m68k, pushqi1. */ - for (i = XVECLEN (pat, 0) - 1; i >= 0; --i) - { - set = XVECEXP (pat, 0, i); - if (GET_CODE (set) != SET) - continue; - dest = SET_DEST (set); - if (dest == stack_pointer_rtx) - break; - - /* We do not expect an auto-inc of the sp in the parallel. */ - gcc_checking_assert (mem_autoinc_base (dest) - != stack_pointer_rtx); - gcc_checking_assert (mem_autoinc_base (SET_SRC (set)) - != stack_pointer_rtx); - } - if (i < 0) + set = XVECEXP (pat, 0, i); + if (GET_CODE (set) != SET) continue; + dest = SET_DEST (set); + if (dest == stack_pointer_rtx) + break; + + /* We do not expect an auto-inc of the sp in the parallel. */ + gcc_checking_assert (mem_autoinc_base (dest) != stack_pointer_rtx); + gcc_checking_assert (mem_autoinc_base (SET_SRC (set)) + != stack_pointer_rtx); } + if (i < 0) + return 0; + } + else + return 0; + + dest = SET_DEST (set); + + /* Look for direct modifications of the stack pointer. */ + if (REG_P (dest) && REGNO (dest) == STACK_POINTER_REGNUM) + { + /* Look for a trivial adjustment, otherwise assume nothing. */ + /* Note that the SPU restore_stack_block pattern refers to + the stack pointer in V4SImode. Consider that non-trivial. */ + if (SCALAR_INT_MODE_P (GET_MODE (dest)) + && GET_CODE (SET_SRC (set)) == PLUS + && XEXP (SET_SRC (set), 0) == stack_pointer_rtx + && CONST_INT_P (XEXP (SET_SRC (set), 1))) + return INTVAL (XEXP (SET_SRC (set), 1)); + /* ??? Reload can generate no-op moves, which will be cleaned + up later. Recognize it and continue searching. */ + else if (rtx_equal_p (dest, SET_SRC (set))) + return 0; else - continue; - dest = SET_DEST (set); - - /* Look for direct modifications of the stack pointer. */ - if (REG_P (dest) && REGNO (dest) == STACK_POINTER_REGNUM) - { - gcc_assert (!saw_unknown); - /* Look for a trivial adjustment, otherwise assume nothing. */ - /* Note that the SPU restore_stack_block pattern refers to - the stack pointer in V4SImode. Consider that non-trivial. */ - if (SCALAR_INT_MODE_P (GET_MODE (dest)) - && GET_CODE (SET_SRC (set)) == PLUS - && XEXP (SET_SRC (set), 0) == stack_pointer_rtx - && CONST_INT_P (XEXP (SET_SRC (set), 1))) - this_delta = INTVAL (XEXP (SET_SRC (set), 1)); - /* ??? Reload can generate no-op moves, which will be cleaned - up later. Recognize it and continue searching. */ - else if (rtx_equal_p (dest, SET_SRC (set))) - this_delta = 0; - else - saw_unknown = true; - } + return HOST_WIDE_INT_MIN; + } + else + { + rtx mem, addr; + /* Otherwise only think about autoinc patterns. */ - else if (mem_autoinc_base (dest) == stack_pointer_rtx) + if (mem_autoinc_base (dest) == stack_pointer_rtx) { - rtx addr = XEXP (dest, 0); - gcc_assert (!saw_unknown); - switch (GET_CODE (addr)) - { - case PRE_INC: - case POST_INC: - this_delta = GET_MODE_SIZE (GET_MODE (dest)); - break; - case PRE_DEC: - case POST_DEC: - this_delta = -GET_MODE_SIZE (GET_MODE (dest)); - break; - case PRE_MODIFY: - case POST_MODIFY: - addr = XEXP (addr, 1); - gcc_assert (GET_CODE (addr) == PLUS); - gcc_assert (XEXP (addr, 0) == stack_pointer_rtx); - gcc_assert (CONST_INT_P (XEXP (addr, 1))); - this_delta = INTVAL (XEXP (addr, 1)); - break; - default: - gcc_unreachable (); - } + mem = dest; + gcc_checking_assert (mem_autoinc_base (SET_SRC (set)) + != stack_pointer_rtx); } + else if (mem_autoinc_base (SET_SRC (set)) == stack_pointer_rtx) + mem = SET_SRC (set); else + return 0; + + addr = XEXP (mem, 0); + switch (GET_CODE (addr)) + { + case PRE_INC: + case POST_INC: + return GET_MODE_SIZE (GET_MODE (mem)); + case PRE_DEC: + case POST_DEC: + return -GET_MODE_SIZE (GET_MODE (mem)); + case PRE_MODIFY: + case POST_MODIFY: + addr = XEXP (addr, 1); + gcc_assert (GET_CODE (addr) == PLUS); + gcc_assert (XEXP (addr, 0) == stack_pointer_rtx); + gcc_assert (CONST_INT_P (XEXP (addr, 1))); + return INTVAL (XEXP (addr, 1)); + default: + gcc_unreachable (); + } + } +} + +int +fixup_args_size_notes (rtx prev, rtx last, int end_args_size) +{ + int args_size = end_args_size; + bool saw_unknown = false; + rtx insn; + + for (insn = last; insn != prev; insn = PREV_INSN (insn)) + { + HOST_WIDE_INT this_delta; + + if (!NONDEBUG_INSN_P (insn)) + continue; + + this_delta = find_args_size_adjust (insn); + if (this_delta == 0) continue; + gcc_assert (!saw_unknown); + if (this_delta == HOST_WIDE_INT_MIN) + saw_unknown = true; + add_reg_note (insn, REG_ARGS_SIZE, GEN_INT (args_size)); #ifdef STACK_GROWS_DOWNWARD this_delta = -this_delta; @@ -2508,6 +2508,7 @@ extern void emit_jump (rtx); /* In expr.c */ extern rtx move_by_pieces (rtx, rtx, unsigned HOST_WIDE_INT, unsigned int, int); +extern HOST_WIDE_INT find_args_size_adjust (rtx); extern int fixup_args_size_notes (rtx, rtx, int); /* In cfgrtl.c */ diff --git a/gcc/testsuite/gcc.dg/pr50132.c b/gcc/testsuite/gcc.dg/pr50132.c new file mode 100644 index 0000000..84a9c73 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr50132.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -fno-asynchronous-unwind-tables -g" } */ + +void bar (long double n); + +void foo (int c) +{ + if (c) + bar (0); +} |