Bug 30267

Summary: folding (~ -x) >= (-2147483647-1) to x != -2147483648
Product: gcc Reporter: Bruno Haible <bruno>
Component: middle-endAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED FIXED    
Severity: minor CC: gabravier, gcc-bugs, rguenth
Priority: P3 Keywords: missed-optimization
Version: 4.1.1   
Target Milestone: 7.0   
Host: Target: i686-pc-linux-gnu
Build: Known to work: 10.3.0, 11.1.0, 4.0.3, 6.5.0, 7.5.0, 8.4.0, 9.3.0
Known to fail: 4.1.2, 4.3.0, 4.8.5, 4.9.4, 5.5.0 Last reconfirmed: 2006-12-27 16:48:37

Description Bruno Haible 2006-12-20 14:35:40 UTC
This program shows that some range propagation became worse between
gcc 4.0.2 and gcc 4.1.1.

=========================== foo.c ========================
int notneg (int x)
{
  return (~ -x) >= (-2147483647-1);
}
int negnot (int x)
{
  return (- ~x) <= 2147483647;
}
==========================================================


# With gcc 4.0.2 on i686-pc-linux-gnu the code is fully optimized:

$ gcc -O2 -fomit-frame-pointer -S foo.c && cat foo.s
        .file   "foo.c"
        .text
        .p2align 4,,15
.globl notneg
        .type   notneg, @function
notneg:
        movl    $1, %eax
        ret
        .size   notneg, .-notneg
        .p2align 4,,15
.globl negnot
        .type   negnot, @function
negnot:
        movl    $1, %eax
        ret
        .size   negnot, .-negnot
        .ident  "GCC: (GNU) 4.0.2"
        .section        .note.GNU-stack,"",@progbits


# With gcc 4.1.1 on i686-pc-linux-gnu the code is fully optimized with -fwrapv
# but not without -fwrapv:

$ gcc -O2 -fomit-frame-pointer -S foo.c && cat foo.s
        .file   "foo.c"
        .text
        .p2align 4,,15
.globl notneg
        .type   notneg, @function
notneg:
        xorl    %eax, %eax
        cmpl    $-2147483648, 4(%esp)
        setne   %al
        ret
        .size   notneg, .-notneg
        .p2align 4,,15
.globl negnot
        .type   negnot, @function
negnot:
        xorl    %eax, %eax
        cmpl    $2147483647, 4(%esp)
        setne   %al
        ret
        .size   negnot, .-negnot
        .ident  "GCC: (GNU) 4.1.1"
        .section        .note.GNU-stack,"",@progbits

$ gcc -O2 -fomit-frame-pointer -fwrapv -S foo.c && cat foo.s
        .file   "foo.c"
        .text
        .p2align 4,,15
.globl notneg
        .type   notneg, @function
notneg:
        movl    $1, %eax
        ret
        .size   notneg, .-notneg
        .p2align 4,,15
.globl negnot
        .type   negnot, @function
negnot:
        movl    $1, %eax
        ret
        .size   negnot, .-negnot
        .ident  "GCC: (GNU) 4.1.1"
        .section        .note.GNU-stack,"",@progbits

So somehow this seems to be linked to flag_wrapv. But regardless which
value is the result after signed overflow, any int >= INT_MIN and
any int <= INT_MAX should evaluate to 1 unconditionally.
Comment 1 Richard Biener 2006-12-27 16:48:36 UTC
This is folded to

;; Function notneg (notneg)
;; enabled by -tree-original


{
  return x != -2147483648;
}



;; Function negnot (negnot)
;; enabled by -tree-original

{
  return x != 2147483647;
}

via

      /* Convert - (~A) to A + 1.  */
      if (INTEGRAL_TYPE_P (type) && TREE_CODE (arg0) == BIT_NOT_EXPR)
        return fold_build2 (PLUS_EXPR, type, TREE_OPERAND (arg0, 0),
                            build_int_cst (type, 1));

and

      /* Convert ~ (-A) to A - 1.  */
      else if (INTEGRAL_TYPE_P (type) && TREE_CODE (arg0) == NEGATE_EXPR)
        return fold_build2 (MINUS_EXPR, type, TREE_OPERAND (arg0, 0),
                            build_int_cst (type, 1));

and

      /* Transform comparisons of the form X +- C1 CMP C2 to X CMP C2 +- C1.  */      if ((TREE_CODE (arg0) == PLUS_EXPR || TREE_CODE (arg0) == MINUS_EXPR)
          && (TREE_CODE (TREE_OPERAND (arg0, 1)) == INTEGER_CST
              && !TREE_OVERFLOW (TREE_OPERAND (arg0, 1))
              && !TYPE_UNSIGNED (TREE_TYPE (arg1))
              && !(flag_wrapv || flag_trapv))
          && (TREE_CODE (arg1) == INTEGER_CST
              && !TREE_OVERFLOW (arg1)))
        {
          tree const1 = TREE_OPERAND (arg0, 1);
          tree const2 = arg1;
          tree variable = TREE_OPERAND (arg0, 0);
          tree lhs;
          int lhs_add;
          lhs_add = TREE_CODE (arg0) != PLUS_EXPR;

          lhs = fold_build2 (lhs_add ? PLUS_EXPR : MINUS_EXPR,
                             TREE_TYPE (arg1), const2, const1);
          if (TREE_CODE (lhs) == TREE_CODE (arg1)
              && (TREE_CODE (lhs) != INTEGER_CST
                  || !TREE_OVERFLOW (lhs)))
            return fold_build2 (code, type, variable, lhs);
        }

(citing from the 4.1 branch)

Note this is actually wrong-code.
Comment 2 Andrew Pinski 2006-12-27 17:01:14 UTC
(In reply to comment #1)
> Note this is actually wrong-code.
No, it is not.

In notneg, if x is -2147483647-1, it is obviously, we have an overflow as -(-2147483647-1) is overflowed.

In negnot, if x is 2147483647, then ~ 2147483647 == -2147483647-1, and then we have an overflow.
Comment 3 Andrew Pinski 2021-06-08 08:47:07 UTC
Fixed with at least GCC 7.
It was still broken in GCC 4.8.5 though.
Comment 4 Bruno Haible 2021-06-08 11:09:28 UTC
(In reply to Andrew Pinski from comment #3)
> Fixed with at least GCC 7.
> It was still broken in GCC 4.8.5 though.

Indeed. Here's the status with GCC versions since 4.0.x:

4.0.4 optimized
4.1.2 missed
4.2.4 missed
4.3.6 missed
4.4.7 missed
4.5.4 missed
4.6.4 missed
4.7.3 missed
4.8.5 missed
4.9.4 missed
5.5.0 missed
6.5.0 optimized
7.5.0 optimized
8.4.0 optimized
9.3.0 optimized
10.3.0 optimized
11.1.0 optimized