From 885c9b5d3a53c531fca622b4a3b0ef03df92daed Mon Sep 17 00:00:00 2001 From: Eric Botcazou Date: Tue, 16 Nov 2010 22:13:52 +0000 Subject: [PATCH] re PR rtl-optimization/46315 (-O2 -fno-strict-overflow causes wrong code generation) PR rtl-optimization/46315 * rtl.h (remove_reg_equal_equiv_notes_for_regno): Declare. * rtlanal.c (remove_reg_equal_equiv_notes_for_regno): New function extracted from... * dce.c (delete_corresponding_reg_eq_notes): ...here. Rename into... (remove_reg_equal_equiv_notes_for_defs): ...this. (delete_unmarked_insns): Adjust to above renaming. * ifcvt.c (dead_or_predicable): Remove REG_EQUAL and REG_EQUIV notes referring to registers set in the insns being moved, if any. * df-core.c (df_ref_dump): New function extracted from... (df_refs_chain_dump): ...here. Call it. (df_regs_chain_dump): Likewise. * df-problems.c (df_chain_dump): Print 'e' for uses in notes. * df-scan.c (df_scan_start_dump): Likewise. Fix long line. From-SVN: r166827 --- gcc/ChangeLog | 18 +++++++++++ gcc/dce.c | 34 +++++--------------- gcc/df-core.c | 21 ++++++++----- gcc/df-problems.c | 7 +++-- gcc/df-scan.c | 8 +++-- gcc/ifcvt.c | 57 ++++++++++++++++++++++------------ gcc/rtl.h | 1 + gcc/rtlanal.c | 27 ++++++++++++++++ gcc/testsuite/ChangeLog | 4 +++ gcc/testsuite/gcc.dg/pr46315.c | 37 ++++++++++++++++++++++ 10 files changed, 155 insertions(+), 59 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/pr46315.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index a572780b53e..4e4f64a68d6 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,21 @@ +2010-11-16 Eric Botcazou + + PR rtl-optimization/46315 + * rtl.h (remove_reg_equal_equiv_notes_for_regno): Declare. + * rtlanal.c (remove_reg_equal_equiv_notes_for_regno): New function + extracted from... + * dce.c (delete_corresponding_reg_eq_notes): ...here. Rename into... + (remove_reg_equal_equiv_notes_for_defs): ...this. + (delete_unmarked_insns): Adjust to above renaming. + * ifcvt.c (dead_or_predicable): Remove REG_EQUAL and REG_EQUIV notes + referring to registers set in the insns being moved, if any. + + * df-core.c (df_ref_dump): New function extracted from... + (df_refs_chain_dump): ...here. Call it. + (df_regs_chain_dump): Likewise. + * df-problems.c (df_chain_dump): Print 'e' for uses in notes. + * df-scan.c (df_scan_start_dump): Likewise. Fix long line. + 2010-11-16 Andreas Schwab PR rtl-optimization/46395 diff --git a/gcc/dce.c b/gcc/dce.c index 2d4f3a61706..a7697f51e06 100644 --- a/gcc/dce.c +++ b/gcc/dce.c @@ -466,36 +466,16 @@ find_call_stack_args (rtx call_insn, bool do_mark, bool fast, } -/* Delete all REG_EQUAL notes of the registers INSN writes, to prevent - bad dangling REG_EQUAL notes. */ +/* Remove all REG_EQUAL and REG_EQUIV notes referring to the registers INSN + writes to. */ static void -delete_corresponding_reg_eq_notes (rtx insn) +remove_reg_equal_equiv_notes_for_defs (rtx insn) { df_ref *def_rec; - for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++) - { - df_ref def = *def_rec; - unsigned int regno = DF_REF_REGNO (def); - /* This loop is a little tricky. We cannot just go down the - chain because it is being modified by the actions in the - loop. So we just get the head. We plan to drain the list - anyway. */ - while (DF_REG_EQ_USE_CHAIN (regno)) - { - df_ref eq_use = DF_REG_EQ_USE_CHAIN (regno); - rtx noted_insn = DF_REF_INSN (eq_use); - rtx note = find_reg_note (noted_insn, REG_EQUAL, NULL_RTX); - if (!note) - note = find_reg_note (noted_insn, REG_EQUIV, NULL_RTX); - /* This assert is generally triggered when someone deletes a - REG_EQUAL or REG_EQUIV note by hacking the list manually - rather than calling remove_note. */ - gcc_assert (note); - remove_note (noted_insn, note); - } - } + for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++) + remove_reg_equal_equiv_notes_for_regno (DF_REF_REGNO (*def_rec)); } @@ -544,9 +524,9 @@ delete_unmarked_insns (void) if (dump_file) fprintf (dump_file, "DCE: Deleting insn %d\n", INSN_UID (insn)); - /* Before we delete the insn we have to delete REG_EQUAL notes + /* Before we delete the insn we have to remove the REG_EQUAL notes for the destination regs in order to avoid dangling notes. */ - delete_corresponding_reg_eq_notes (insn); + remove_reg_equal_equiv_notes_for_defs (insn); /* If a pure or const call is deleted, this may make the cfg have unreachable blocks. We rememeber this and call diff --git a/gcc/df-core.c b/gcc/df-core.c index 86ed23919a2..dacefc71aaf 100644 --- a/gcc/df-core.c +++ b/gcc/df-core.c @@ -2051,6 +2051,17 @@ df_dump_bottom (basic_block bb, FILE *file) } +static void +df_ref_dump (df_ref ref, FILE *file) +{ + fprintf (file, "%c%d(%d)", + DF_REF_REG_DEF_P (ref) + ? 'd' + : (DF_REF_FLAGS (ref) & DF_REF_IN_NOTE) ? 'e' : 'u', + DF_REF_ID (ref), + DF_REF_REGNO (ref)); +} + void df_refs_chain_dump (df_ref *ref_rec, bool follow_chain, FILE *file) { @@ -2058,10 +2069,7 @@ df_refs_chain_dump (df_ref *ref_rec, bool follow_chain, FILE *file) while (*ref_rec) { df_ref ref = *ref_rec; - fprintf (file, "%c%d(%d)", - DF_REF_REG_DEF_P (ref) ? 'd' : (DF_REF_FLAGS (ref) & DF_REF_IN_NOTE) ? 'e' : 'u', - DF_REF_ID (ref), - DF_REF_REGNO (ref)); + df_ref_dump (ref, file); if (follow_chain) df_chain_dump (DF_REF_CHAIN (ref), file); ref_rec++; @@ -2078,10 +2086,7 @@ df_regs_chain_dump (df_ref ref, FILE *file) fprintf (file, "{ "); while (ref) { - fprintf (file, "%c%d(%d) ", - DF_REF_REG_DEF_P (ref) ? 'd' : 'u', - DF_REF_ID (ref), - DF_REF_REGNO (ref)); + df_ref_dump (ref, file); ref = DF_REF_NEXT_REG (ref); } fprintf (file, "}"); diff --git a/gcc/df-problems.c b/gcc/df-problems.c index 3f5627fb8c0..559af918462 100644 --- a/gcc/df-problems.c +++ b/gcc/df-problems.c @@ -109,10 +109,13 @@ df_chain_dump (struct df_link *link, FILE *file) for (; link; link = link->next) { fprintf (file, "%c%d(bb %d insn %d) ", - DF_REF_REG_DEF_P (link->ref) ? 'd' : 'u', + DF_REF_REG_DEF_P (link->ref) + ? 'd' + : (DF_REF_FLAGS (link->ref) & DF_REF_IN_NOTE) ? 'e' : 'u', DF_REF_ID (link->ref), DF_REF_BBNO (link->ref), - DF_REF_IS_ARTIFICIAL (link->ref) ? -1 : DF_REF_INSN_UID (link->ref)); + DF_REF_IS_ARTIFICIAL (link->ref) + ? -1 : DF_REF_INSN_UID (link->ref)); } fprintf (file, "}"); } diff --git a/gcc/df-scan.c b/gcc/df-scan.c index 850f06702bb..5cda89730d3 100644 --- a/gcc/df-scan.c +++ b/gcc/df-scan.c @@ -445,7 +445,7 @@ df_scan_start_dump (FILE *file ATTRIBUTE_UNUSED) } if (DF_REG_EQ_USE_COUNT (i)) { - fprintf (file, "%s%dd", sep, DF_REG_EQ_USE_COUNT (i)); + fprintf (file, "%s%de", sep, DF_REG_EQ_USE_COUNT (i)); ecount += DF_REG_EQ_USE_COUNT (i); } fprintf (file, "} "); @@ -461,8 +461,10 @@ df_scan_start_dump (FILE *file ATTRIBUTE_UNUSED) icount++; } - fprintf (file, "\n;; total ref usage %d{%dd,%du,%de} in %d{%d regular + %d call} insns.\n", - dcount + ucount + ecount, dcount, ucount, ecount, icount + ccount, icount, ccount); + fprintf (file, "\n;; total ref usage %d{%dd,%du,%de}" + " in %d{%d regular + %d call} insns.\n", + dcount + ucount + ecount, dcount, ucount, ecount, + icount + ccount, icount, ccount); } /* Dump the bb_info for a given basic block. */ diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c index 58ee90c4b21..c91bbbfef29 100644 --- a/gcc/ifcvt.c +++ b/gcc/ifcvt.c @@ -3998,6 +3998,7 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, basic_block other_bb, basic_block new_dest, int reversep) { rtx head, end, jump, earliest = NULL_RTX, old_dest, new_label = NULL_RTX; + bitmap merge_set = NULL, merge_set_noclobber = NULL; /* Number of pending changes. */ int n_validated_changes = 0; @@ -4086,6 +4087,7 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, earliest = jump; } #endif + /* Try the NCE path if the CE path did not result in any changes. */ if (n_validated_changes == 0) { @@ -4094,9 +4096,8 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, that any registers modified are dead at the branch site. */ rtx insn, cond, prev; - bitmap merge_set, merge_set_noclobber, test_live, test_set; - unsigned i, fail = 0; - bitmap_iterator bi; + bitmap test_live, test_set; + bool intersect = false; /* Check for no calls or trapping operations. */ for (insn = head; ; insn = NEXT_INSN (insn)) @@ -4138,12 +4139,7 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, merge_set = BITMAP_ALLOC (®_obstack); merge_set_noclobber = BITMAP_ALLOC (®_obstack); - test_live = BITMAP_ALLOC (®_obstack); - test_set = BITMAP_ALLOC (®_obstack); - /* ??? bb->local_set is only valid during calculate_global_regs_live, - so we must recompute usage for MERGE_BB. Not so bad, I suppose, - since we've already asserted that MERGE_BB is small. */ /* If we allocated new pseudos (e.g. in the conditional move expander called from noce_emit_cmove), we must resize the array first. */ @@ -4164,17 +4160,22 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, if (! reload_completed && targetm.small_register_classes_for_mode_p (VOIDmode)) { + unsigned i; + bitmap_iterator bi; + EXECUTE_IF_SET_IN_BITMAP (merge_set_noclobber, 0, i, bi) { if (i < FIRST_PSEUDO_REGISTER && ! fixed_regs[i] && ! global_regs[i]) - fail = 1; + goto fail; } } /* For TEST, we're interested in a range of insns, not a whole block. Moreover, we're interested in the insns live from OTHER_BB. */ + test_live = BITMAP_ALLOC (®_obstack); + test_set = BITMAP_ALLOC (®_obstack); /* The loop below takes the set of live registers after JUMP, and calculates the live set before EARLIEST. */ @@ -4195,23 +4196,21 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, /* We can perform the transformation if MERGE_SET_NOCLOBBER & TEST_SET and - MERGE_SET & TEST_LIVE) + MERGE_SET & TEST_LIVE and TEST_SET & DF_LIVE_IN (merge_bb) are empty. */ - if (bitmap_intersect_p (test_set, merge_set_noclobber) - || bitmap_intersect_p (test_live, merge_set) + if (bitmap_intersect_p (merge_set_noclobber, test_set) + || bitmap_intersect_p (merge_set, test_live) || bitmap_intersect_p (test_set, df_get_live_in (merge_bb))) - fail = 1; + intersect = true; - BITMAP_FREE (merge_set_noclobber); - BITMAP_FREE (merge_set); BITMAP_FREE (test_live); BITMAP_FREE (test_set); - if (fail) - return FALSE; + if (intersect) + goto fail; } no_body: @@ -4261,8 +4260,8 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, if (end == BB_END (merge_bb)) BB_END (merge_bb) = PREV_INSN (head); - /* PR 21767: When moving insns above a conditional branch, REG_EQUAL - notes might become invalid. */ + /* PR 21767: when moving insns above a conditional branch, the REG_EQUAL + notes being moved might become invalid. */ insn = head; do { @@ -4279,6 +4278,20 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, remove_note (insn, note); } while (insn != end && (insn = NEXT_INSN (insn))); + /* PR46315: when moving insns above a conditional branch, the REG_EQUAL + notes referring to the registers being set might become invalid. */ + if (merge_set) + { + unsigned i; + bitmap_iterator bi; + + EXECUTE_IF_SET_IN_BITMAP (merge_set_noclobber, 0, i, bi) + remove_reg_equal_equiv_notes_for_regno (i); + + BITMAP_FREE (merge_set); + BITMAP_FREE (merge_set_noclobber); + } + reorder_insns (head, end, PREV_INSN (earliest)); } @@ -4295,6 +4308,12 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, cancel: cancel_changes (0); + fail: + if (merge_set) + { + BITMAP_FREE (merge_set); + BITMAP_FREE (merge_set_noclobber); + } return FALSE; } diff --git a/gcc/rtl.h b/gcc/rtl.h index 66751a67854..745d6f437ec 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -1892,6 +1892,7 @@ extern rtx alloc_reg_note (enum reg_note, rtx, rtx); extern void add_reg_note (rtx, enum reg_note, rtx); extern void remove_note (rtx, const_rtx); extern void remove_reg_equal_equiv_notes (rtx); +extern void remove_reg_equal_equiv_notes_for_regno (unsigned int); extern int side_effects_p (const_rtx); extern int volatile_refs_p (const_rtx); extern int volatile_insn_p (const_rtx); diff --git a/gcc/rtlanal.c b/gcc/rtlanal.c index 930828ac2eb..eb4d3ab9ad3 100644 --- a/gcc/rtlanal.c +++ b/gcc/rtlanal.c @@ -1941,6 +1941,33 @@ remove_reg_equal_equiv_notes (rtx insn) } } +/* Remove all REG_EQUAL and REG_EQUIV notes referring to REGNO. */ + +void +remove_reg_equal_equiv_notes_for_regno (unsigned int regno) +{ + df_ref eq_use; + + if (!df) + return; + + /* This loop is a little tricky. We cannot just go down the chain because + it is being modified by some actions in the loop. So we just iterate + over the head. We plan to drain the list anyway. */ + while ((eq_use = DF_REG_EQ_USE_CHAIN (regno)) != NULL) + { + rtx insn = DF_REF_INSN (eq_use); + rtx note = find_reg_equal_equiv_note (insn); + + /* This assert is generally triggered when someone deletes a REG_EQUAL + or REG_EQUIV note by hacking the list manually rather than calling + remove_note. */ + gcc_assert (note); + + remove_note (insn, note); + } +} + /* Search LISTP (an EXPR_LIST) for an entry whose first operand is NODE and return 1 if it is found. A simple equality test is used to determine if NODE matches. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index f6f96717707..afb99ff8f85 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2010-11-16 Eric Botcazou + + * gcc.target/rx/pack.c: New test. + 2010-11-16 Francois-Xavier Coudert Tobias Burnus diff --git a/gcc/testsuite/gcc.dg/pr46315.c b/gcc/testsuite/gcc.dg/pr46315.c new file mode 100644 index 00000000000..2349284e2e9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr46315.c @@ -0,0 +1,37 @@ +/* PR rtl-optimization/46315 */ +/* Reported by Magnus Granberg */ + +/* { dg-do run } */ +/* { dg-options "-O2 -fno-strict-overflow" } */ + +extern void abort (void); + +static char const * +parse_ranged (char const *s, int digits) +{ + int n = 0; + char const *lim = s + digits; + while (s < lim) + { + unsigned d = *s++ - '0'; + if (9 < d) + return 0; + n = 10 * n + d; + } + return s && 0 <= n && n <= 59 ? s : 0; +} + +int main(void) +{ + const char *s = "10092240"; + + s = parse_ranged (s, 2); + s = parse_ranged (s, 2); + s = parse_ranged (s, 2); + s = parse_ranged (s, 2); + + if (!s || *s != '\0') + abort(); + + return 0; +}