I believe there is a bug in fold_widened_comparison() present in all versions
of gcc including 4.1
The lines 6103 - 6105 of fold-const.c (gcc 4.0.1) looks like:
arg1_unw = get_unwidened (arg1, shorter_type);
if (!arg1_unw)
return NULL_TREE;
Notice that get_unwidened() never returns zero, so the if() may
look redundant, but actually it has to be arg1_unw == arg1 comparison
that will signify that get_unwidened() failed to adjust arg1
to shorter type.
The diff for fix looks like:
------- fold-const.c -------
*** fold-const.c_old Mon Aug 22 17:57:51 2005
--- fold-const.c Mon Aug 22 16:42:59 2005
***************
*** 6101,6107 ****
return NULL_TREE;
arg1_unw = get_unwidened (arg1, shorter_type);
! if (!arg1_unw)
return NULL_TREE;
/* If possible, express the comparison in the shorter mode. */
--- 6101,6107 ----
return NULL_TREE;
arg1_unw = get_unwidened (arg1, shorter_type);
! if (arg1_unw == arg1)
return NULL_TREE;
/* If possible, express the comparison in the shorter mode. */
Unfortunately I don't have a source example that demonstrates this bug,
but if you take the source from PR #21331 and run it on platform
with 64-bit longs the function foo():
unsigned long
foo ()
{ unsigned long retval;
retval = bar ();
if (retval == -1) return 0;
return 3; }
will contain an expression: (long long unsigned intD.6) D.1112 == -1.
<eq_expr fef90230
type <boolean_type fef92880 _Bool public unsigned QI
size <integer_cst fef84200 constant invariant 8>
unit size <integer_cst fef84220 constant invariant 1>
align 8 symtab 0 alias set -1 precision 1 min <integer_cst fef84740 0>
max <integer_cst fef84780 1>>
arg 0 <nop_expr fef84ea0
type <integer_type fef92700 long unsigned int sizes-gimplified public
unsigned DI
size <integer_cst fef845c0 constant invariant 64>
unit size <integer_cst fef845e0 constant invariant 8>
align 64 symtab 0 alias set -1 precision 64 min <integer_cst
fef84680 0> max <integer_cst fef84660 18446744073709551615>>
arg 0 <var_decl ff00eff0 D.1108 type <integer_type fef92580 int>
used ignored SI file simple.c line 8
size <integer_cst fef844a0 constant invariant 32>
unit size <integer_cst fef84160 constant invariant 4>
align 32 context <function_decl ff00ee58 foo> chain <var_decl
ff00f078 D.1109>>>
arg 1 <integer_cst fef84660 type <integer_type fef92700 long unsigned int>
constant invariant 18446744073709551615>
simple.c:9>
and if you try to apply fold() to this expression you'll receive an incorrect
result.
The fold(eq_expr above) will produce zero.
Let's take a look at fold_widened_comparison() to better understand the problem:
static tree
fold_widened_comparison (enum tree_code code, tree type, tree arg0, tree arg1)
{
tree arg0_unw = get_unwidened (arg0, NULL_TREE);
tree arg1_unw;
tree shorter_type, outer_type;
tree min, max;
bool above, below;
if (arg0_unw == arg0)
return NULL_TREE;
shorter_type = TREE_TYPE (arg0_unw);
if (TYPE_PRECISION (TREE_TYPE (arg0)) <= TYPE_PRECISION (shorter_type))
return NULL_TREE;
arg1_unw = get_unwidened (arg1, shorter_type);
if (arg1_unw == arg1) /* *********** line 6103. fixed */
return NULL_TREE;
The first call to get_unwidened(arg0 == nop_expr of var_decl) will remove
NOP_EXPR and
will return 32-bit VAR_DECL.
2nd call to get_unwidened(arg1 == 64-bit integer_cst) will return 64-bit integer
cst.
and incorrect check in line 6103 will fail to recognize type mismatch.
Further calls to fold_relational_const() will compare max 32-bit const with our
arg1
64-bit constant and incorrectly determine that such eq_expr is always zero.
The fix is trivial. If 2nd get_unwidened() returned the same tree, don't do any
further folding.
Alexey.
--
Summary: fold() bug
Product: gcc
Version: 4.0.1
Status: UNCONFIRMED
Severity: normal
Priority: P2
Component: tree-optimization
AssignedTo: unassigned at gcc dot gnu dot org
ReportedBy: alexey dot starovoytov at sun dot com
CC: gcc-bugs at gcc dot gnu dot org
GCC build triplet: x86_64-unknown-linux-gnu
GCC host triplet: x86_64-unknown-linux-gnu
GCC target triplet: x86_64-unknown-linux-gnu
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=23522