Bug 23522 - [3.4/4.0/4.1 Regression] fold_widened_comparison bug
Summary: [3.4/4.0/4.1 Regression] fold_widened_comparison bug
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: 4.0.1
: P2 normal
Target Milestone: 4.0.3
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2005-08-23 01:17 UTC by Alexey Starovoytov
Modified: 2005-10-18 03:20 UTC (History)
1 user (show)

See Also:
Host:
Target: 64bit targets
Build:
Known to work: 2.95
Known to fail: 4.0.0 4.1.0 3.4.0 3.0.4 3.3.3
Last reconfirmed: 2005-08-23 01:41:50


Attachments
a patch based on 4.0.1 (399 bytes, patch)
2005-09-09 02:14 UTC, Alexey Starovoytov
Details | Diff
proposed ChangeLog (271 bytes, text/plain)
2005-09-09 02:25 UTC, Alexey Starovoytov
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Alexey Starovoytov 2005-08-23 01:17:01 UTC
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.
Comment 1 Andrew Pinski 2005-08-23 01:41:50 UTC
Broken before fold_widended_comparison was added:
                && (t1 = get_unwidened (arg1, TREE_TYPE (tem))) != 0
	

So this is a regression from 2.95.
Comment 2 Alexey Starovoytov 2005-08-23 23:35:04 UTC
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.
Comment 3 Richard Biener 2005-09-07 09:26:47 UTC
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.
Comment 4 Alexey Starovoytov 2005-09-09 02:14:48 UTC
Created attachment 9695 [details]
a patch based on 4.0.1

I already have a copyright assignment on file with the FSF
Comment 5 Alexey Starovoytov 2005-09-09 02:25:20 UTC
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
Comment 6 GCC Commits 2005-10-18 03:16:27 UTC
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

Comment 7 GCC Commits 2005-10-18 03:19:19 UTC
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

Comment 8 Ian Lance Taylor 2005-10-18 03:20:01 UTC
Fixed on mainline and 4.0 branch.