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.
Broken before fold_widended_comparison was added: && (t1 = get_unwidened (arg1, TREE_TYPE (tem))) != 0 So this is a regression from 2.95.
Actually I think 3.x versions are fine. Here is the snipper from 3.4.3: else if (TREE_CODE (TREE_TYPE (arg0)) == INTEGER_TYPE && TREE_CODE (arg0) == NOP_EXPR && (tem = get_unwidened (arg0, NULL_TREE)) != arg0 && (code == EQ_EXPR || code == NE_EXPR || TREE_UNSIGNED (TREE_TYPE (arg0)) == TREE_UNSIGNED (TREE_TYPE (tem))) && (t1 = get_unwidened (arg1, TREE_TYPE (tem))) != 0 && (TREE_TYPE (t1) == TREE_TYPE (tem) || (TREE_CODE (t1) == INTEGER_CST && int_fits_type_p (t1, TREE_TYPE (tem))))) return fold (build (code, type, tem, fold_convert (TREE_TYPE (tem), t1))); the (t1 = get_unwidened (arg1, TREE_TYPE (tem))) != 0 line may look suspicious, but it's actually just redundant. If it's changed to t1 != arg1, then probably int_fits_type_p() condition will never be called. The real problem is in 4.0 and 4.1. The snipper from 4.0.1: if (TREE_CODE (arg1_unw) != INTEGER_CST) return NULL_TREE; /* If we are comparing with the integer that does not fit into the range of the shorter type, the result is known. */ outer_type = TREE_TYPE (arg1_unw); min = lower_bound_in_type (outer_type, shorter_type); max = upper_bound_in_type (outer_type, shorter_type); above = integer_nonzerop (fold_relational_const (LT_EXPR, type, max, arg1_unw)); below = integer_nonzerop (fold_relational_const (LT_EXPR, type, arg1_unw, min)); switch (code) { case EQ_EXPR: if (above || below) return omit_one_operand (type, integer_zero_node, arg0); break; This optimization is incorrect if unwidening of arg1 didn't happen. So I think the proper new fix would be to remove lines 6104,6105 and add the check that unwidening happened to line 6115: ------- fold-const.c ------- *** /tmp/geta16407 Tue Aug 23 16:21:18 2005 --- /tmp/getb16407 Tue Aug 23 16:21:18 2005 *************** *** 6101,6108 **** 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. */ if ((code == EQ_EXPR || code == NE_EXPR --- 6101,6106 ---- *************** *** 6114,6120 **** return fold (build (code, type, arg0_unw, fold_convert (shorter_type, arg1_unw))); ! if (TREE_CODE (arg1_unw) != INTEGER_CST) return NULL_TREE; /* If we are comparing with the integer that does not fit into the range --- 6112,6118 ---- return fold (build (code, type, arg0_unw, fold_convert (shorter_type, arg1_unw))); ! if (arg1_unw == arg1 || TREE_CODE (arg1_unw) != INTEGER_CST) return NULL_TREE; /* If we are comparing with the integer that does not fit into the range My previous suggested fix accidentally disabled vectorization in gcc.dg/vect/vect-87.c and gcc.dg/vect/vect-88.c on 64-bit targets. New fix seems to be fine. Alex.
I agree with the analysis and the fix. Care to submit the patch to gcc-patches? Do you have a copyright assignment on file with the FSF? If not the patch may be simple enough to be accepted regardless of this.
Created attachment 9695 [details] a patch based on 4.0.1 I already have a copyright assignment on file with the FSF
Created attachment 9696 [details] proposed ChangeLog I think all patches require ChangeLog part, so here it is. I don't have a CVS write access. No regressions or changes in testsuite 4.0.1
Subject: Bug 23522 CVSROOT: /cvs/gcc Module name: gcc Changes by: ian@gcc.gnu.org 2005-10-18 03:16:21 Modified files: gcc : ChangeLog fold-const.c Log message: PR middle-end/23522 * fold-const.c (fold_widened_comparison): Do not allow range based constant folding when right operand cannot be unwidened. Patches: http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/ChangeLog.diff?cvsroot=gcc&r1=2.10178&r2=2.10179 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/fold-const.c.diff?cvsroot=gcc&r1=1.627&r2=1.628
Subject: Bug 23522 CVSROOT: /cvs/gcc Module name: gcc Branch: gcc-4_0-branch Changes by: ian@gcc.gnu.org 2005-10-18 03:19:14 Modified files: gcc : ChangeLog fold-const.c Log message: PR middle-end/23522 * fold-const.c (fold_widened_comparison): Do not allow range based constant folding when right operand cannot be unwidened. Patches: http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/ChangeLog.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=2.7592.2.468&r2=2.7592.2.469 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/fold-const.c.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=1.517.2.16&r2=1.517.2.17
Fixed on mainline and 4.0 branch.