mirror of git://gcc.gnu.org/git/gcc.git
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
This commit is contained in:
parent
2b12962772
commit
885c9b5d3a
|
@ -1,3 +1,21 @@
|
||||||
|
2010-11-16 Eric Botcazou <ebotcazou@adacore.com>
|
||||||
|
|
||||||
|
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 <schwab@linux-m68k.org>
|
2010-11-16 Andreas Schwab <schwab@linux-m68k.org>
|
||||||
|
|
||||||
PR rtl-optimization/46395
|
PR rtl-optimization/46395
|
||||||
|
|
34
gcc/dce.c
34
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
|
/* Remove all REG_EQUAL and REG_EQUIV notes referring to the registers INSN
|
||||||
bad dangling REG_EQUAL notes. */
|
writes to. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
delete_corresponding_reg_eq_notes (rtx insn)
|
remove_reg_equal_equiv_notes_for_defs (rtx insn)
|
||||||
{
|
{
|
||||||
df_ref *def_rec;
|
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
|
for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
|
||||||
REG_EQUAL or REG_EQUIV note by hacking the list manually
|
remove_reg_equal_equiv_notes_for_regno (DF_REF_REGNO (*def_rec));
|
||||||
rather than calling remove_note. */
|
|
||||||
gcc_assert (note);
|
|
||||||
remove_note (noted_insn, note);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -544,9 +524,9 @@ delete_unmarked_insns (void)
|
||||||
if (dump_file)
|
if (dump_file)
|
||||||
fprintf (dump_file, "DCE: Deleting insn %d\n", INSN_UID (insn));
|
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. */
|
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
|
/* If a pure or const call is deleted, this may make the cfg
|
||||||
have unreachable blocks. We rememeber this and call
|
have unreachable blocks. We rememeber this and call
|
||||||
|
|
|
@ -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
|
void
|
||||||
df_refs_chain_dump (df_ref *ref_rec, bool follow_chain, FILE *file)
|
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)
|
while (*ref_rec)
|
||||||
{
|
{
|
||||||
df_ref ref = *ref_rec;
|
df_ref ref = *ref_rec;
|
||||||
fprintf (file, "%c%d(%d)",
|
df_ref_dump (ref, file);
|
||||||
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));
|
|
||||||
if (follow_chain)
|
if (follow_chain)
|
||||||
df_chain_dump (DF_REF_CHAIN (ref), file);
|
df_chain_dump (DF_REF_CHAIN (ref), file);
|
||||||
ref_rec++;
|
ref_rec++;
|
||||||
|
@ -2078,10 +2086,7 @@ df_regs_chain_dump (df_ref ref, FILE *file)
|
||||||
fprintf (file, "{ ");
|
fprintf (file, "{ ");
|
||||||
while (ref)
|
while (ref)
|
||||||
{
|
{
|
||||||
fprintf (file, "%c%d(%d) ",
|
df_ref_dump (ref, file);
|
||||||
DF_REF_REG_DEF_P (ref) ? 'd' : 'u',
|
|
||||||
DF_REF_ID (ref),
|
|
||||||
DF_REF_REGNO (ref));
|
|
||||||
ref = DF_REF_NEXT_REG (ref);
|
ref = DF_REF_NEXT_REG (ref);
|
||||||
}
|
}
|
||||||
fprintf (file, "}");
|
fprintf (file, "}");
|
||||||
|
|
|
@ -109,10 +109,13 @@ df_chain_dump (struct df_link *link, FILE *file)
|
||||||
for (; link; link = link->next)
|
for (; link; link = link->next)
|
||||||
{
|
{
|
||||||
fprintf (file, "%c%d(bb %d insn %d) ",
|
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_ID (link->ref),
|
||||||
DF_REF_BBNO (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, "}");
|
fprintf (file, "}");
|
||||||
}
|
}
|
||||||
|
|
|
@ -445,7 +445,7 @@ df_scan_start_dump (FILE *file ATTRIBUTE_UNUSED)
|
||||||
}
|
}
|
||||||
if (DF_REG_EQ_USE_COUNT (i))
|
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);
|
ecount += DF_REG_EQ_USE_COUNT (i);
|
||||||
}
|
}
|
||||||
fprintf (file, "} ");
|
fprintf (file, "} ");
|
||||||
|
@ -461,8 +461,10 @@ df_scan_start_dump (FILE *file ATTRIBUTE_UNUSED)
|
||||||
icount++;
|
icount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf (file, "\n;; total ref usage %d{%dd,%du,%de} in %d{%d regular + %d call} insns.\n",
|
fprintf (file, "\n;; total ref usage %d{%dd,%du,%de}"
|
||||||
dcount + ucount + ecount, dcount, ucount, ecount, icount + ccount, icount, ccount);
|
" 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. */
|
/* Dump the bb_info for a given basic block. */
|
||||||
|
|
57
gcc/ifcvt.c
57
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)
|
basic_block other_bb, basic_block new_dest, int reversep)
|
||||||
{
|
{
|
||||||
rtx head, end, jump, earliest = NULL_RTX, old_dest, new_label = NULL_RTX;
|
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. */
|
/* Number of pending changes. */
|
||||||
int n_validated_changes = 0;
|
int n_validated_changes = 0;
|
||||||
|
|
||||||
|
@ -4086,6 +4087,7 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
||||||
earliest = jump;
|
earliest = jump;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Try the NCE path if the CE path did not result in any changes. */
|
/* Try the NCE path if the CE path did not result in any changes. */
|
||||||
if (n_validated_changes == 0)
|
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. */
|
that any registers modified are dead at the branch site. */
|
||||||
|
|
||||||
rtx insn, cond, prev;
|
rtx insn, cond, prev;
|
||||||
bitmap merge_set, merge_set_noclobber, test_live, test_set;
|
bitmap test_live, test_set;
|
||||||
unsigned i, fail = 0;
|
bool intersect = false;
|
||||||
bitmap_iterator bi;
|
|
||||||
|
|
||||||
/* Check for no calls or trapping operations. */
|
/* Check for no calls or trapping operations. */
|
||||||
for (insn = head; ; insn = NEXT_INSN (insn))
|
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 = BITMAP_ALLOC (®_obstack);
|
||||||
merge_set_noclobber = 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
|
/* If we allocated new pseudos (e.g. in the conditional move
|
||||||
expander called from noce_emit_cmove), we must resize the
|
expander called from noce_emit_cmove), we must resize the
|
||||||
array first. */
|
array first. */
|
||||||
|
@ -4164,17 +4160,22 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
||||||
if (! reload_completed
|
if (! reload_completed
|
||||||
&& targetm.small_register_classes_for_mode_p (VOIDmode))
|
&& targetm.small_register_classes_for_mode_p (VOIDmode))
|
||||||
{
|
{
|
||||||
|
unsigned i;
|
||||||
|
bitmap_iterator bi;
|
||||||
|
|
||||||
EXECUTE_IF_SET_IN_BITMAP (merge_set_noclobber, 0, i, bi)
|
EXECUTE_IF_SET_IN_BITMAP (merge_set_noclobber, 0, i, bi)
|
||||||
{
|
{
|
||||||
if (i < FIRST_PSEUDO_REGISTER
|
if (i < FIRST_PSEUDO_REGISTER
|
||||||
&& ! fixed_regs[i]
|
&& ! fixed_regs[i]
|
||||||
&& ! global_regs[i])
|
&& ! global_regs[i])
|
||||||
fail = 1;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For TEST, we're interested in a range of insns, not a whole block.
|
/* 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. */
|
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
|
/* The loop below takes the set of live registers
|
||||||
after JUMP, and calculates the live set before EARLIEST. */
|
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
|
/* We can perform the transformation if
|
||||||
MERGE_SET_NOCLOBBER & TEST_SET
|
MERGE_SET_NOCLOBBER & TEST_SET
|
||||||
and
|
and
|
||||||
MERGE_SET & TEST_LIVE)
|
MERGE_SET & TEST_LIVE
|
||||||
and
|
and
|
||||||
TEST_SET & DF_LIVE_IN (merge_bb)
|
TEST_SET & DF_LIVE_IN (merge_bb)
|
||||||
are empty. */
|
are empty. */
|
||||||
|
|
||||||
if (bitmap_intersect_p (test_set, merge_set_noclobber)
|
if (bitmap_intersect_p (merge_set_noclobber, test_set)
|
||||||
|| bitmap_intersect_p (test_live, merge_set)
|
|| bitmap_intersect_p (merge_set, test_live)
|
||||||
|| bitmap_intersect_p (test_set, df_get_live_in (merge_bb)))
|
|| 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_live);
|
||||||
BITMAP_FREE (test_set);
|
BITMAP_FREE (test_set);
|
||||||
|
|
||||||
if (fail)
|
if (intersect)
|
||||||
return FALSE;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
no_body:
|
no_body:
|
||||||
|
@ -4261,8 +4260,8 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
||||||
if (end == BB_END (merge_bb))
|
if (end == BB_END (merge_bb))
|
||||||
BB_END (merge_bb) = PREV_INSN (head);
|
BB_END (merge_bb) = PREV_INSN (head);
|
||||||
|
|
||||||
/* PR 21767: When moving insns above a conditional branch, REG_EQUAL
|
/* PR 21767: when moving insns above a conditional branch, the REG_EQUAL
|
||||||
notes might become invalid. */
|
notes being moved might become invalid. */
|
||||||
insn = head;
|
insn = head;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
@ -4279,6 +4278,20 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
||||||
remove_note (insn, note);
|
remove_note (insn, note);
|
||||||
} while (insn != end && (insn = NEXT_INSN (insn)));
|
} 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));
|
reorder_insns (head, end, PREV_INSN (earliest));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4295,6 +4308,12 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
|
||||||
|
|
||||||
cancel:
|
cancel:
|
||||||
cancel_changes (0);
|
cancel_changes (0);
|
||||||
|
fail:
|
||||||
|
if (merge_set)
|
||||||
|
{
|
||||||
|
BITMAP_FREE (merge_set);
|
||||||
|
BITMAP_FREE (merge_set_noclobber);
|
||||||
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 add_reg_note (rtx, enum reg_note, rtx);
|
||||||
extern void remove_note (rtx, const_rtx);
|
extern void remove_note (rtx, const_rtx);
|
||||||
extern void remove_reg_equal_equiv_notes (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 side_effects_p (const_rtx);
|
||||||
extern int volatile_refs_p (const_rtx);
|
extern int volatile_refs_p (const_rtx);
|
||||||
extern int volatile_insn_p (const_rtx);
|
extern int volatile_insn_p (const_rtx);
|
||||||
|
|
|
@ -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
|
/* 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
|
return 1 if it is found. A simple equality test is used to determine if
|
||||||
NODE matches. */
|
NODE matches. */
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
2010-11-16 Eric Botcazou <ebotcazou@adacore.com>
|
||||||
|
|
||||||
|
* gcc.target/rx/pack.c: New test.
|
||||||
|
|
||||||
2010-11-16 Francois-Xavier Coudert <fxcoudert@gcc.gnu.org>
|
2010-11-16 Francois-Xavier Coudert <fxcoudert@gcc.gnu.org>
|
||||||
Tobias Burnus <burnus@net-b.de>
|
Tobias Burnus <burnus@net-b.de>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/* PR rtl-optimization/46315 */
|
||||||
|
/* Reported by Magnus Granberg <zorry@gentoo.org> */
|
||||||
|
|
||||||
|
/* { 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;
|
||||||
|
}
|
Loading…
Reference in New Issue