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