diff mbox series

[v2] tree-optimization/116024 - match.pd: add 4 int-compare simplifications

Message ID 20240821162547.25246-1-artemiy@synopsys.com
State New
Headers show
Series [v2] tree-optimization/116024 - match.pd: add 4 int-compare simplifications | expand

Commit Message

Artemiy Volkov Aug. 21, 2024, 4:25 p.m. UTC
Hi,

sending a v2 of
https://gcc.gnu.org/pipermail/gcc-patches/2024-August/659851.html after
changing variable types in all new testcases from standard to fixed-width.

Could anyone please assist with reviewing and/or pushing to trunk/14 since I
don't have commit access?

Many thanks,
Artemiy

---------- 8< ------------

This patch implements match.pd patterns for the following transformations:

(1) (UB-on-overflow types) C1 - X cmp C2 -> X cmp C1 - C2

(2) (unsigned types) C1 - X cmp C2 ->
    (a) X cmp C1 - C2, when cmp is !=, ==
    (b) X - (C1 - C2) cmp C2, when cmp is <=, >
    (c) X - (C1 - C2 + 1) cmp C2, when cmp is <, >=,

(3) (signed wrapping types) C1 - X cmp C2
    (a) X cmp C1 - C2, when cmp is !=, ==
    (b) X - (C1 + 1) rcmp -(C2 + 1), otherwise

(4) (all wrapping types) X + C1 cmp C2 ->
    (a) X cmp C2 - C1, when cmp is !=, ==
    (b) X cmp -C1, when cmp is <=, > and C2 - C1 == max
    (c) X cmp -C1, when cmp is <, >= and C2 - C1 == min

Included along are testcases for all the aforementioned changes.  This
patch has been bootstrapped and regtested on aarch64, x86_64, and i386,
and additionally regtested on riscv32.  Existing tests were adjusted
where necessary.

gcc/ChangeLog:

	PR tree-optimization/116024
        * match.pd: New transformations around integer comparison.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/pr116024.c: New test.
        * gcc.dg/tree-ssa/pr116024-1.c: Ditto.
        * gcc.dg/tree-ssa/pr116024-1-fwrapv.c: Ditto.
        * gcc.dg/tree-ssa/pr116024-2.c: Ditto.
        * gcc.dg/tree-ssa/pr116024-2-fwrapv.c: Ditto.
        * gcc.dg/pr67089-6.c: Adjust.
        * gcc.target/aarch64/gtu_to_ltu_cmp_1.c: Ditto.

Signed-off-by: Artemiy Volkov <artemiy@synopsys.com>
---
 gcc/match.pd                                  | 75 ++++++++++++++++++-
 gcc/testsuite/gcc.dg/pr67089-6.c              |  4 +-
 .../gcc.dg/tree-ssa/pr116024-1-fwrapv.c       | 74 ++++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c    | 74 ++++++++++++++++++
 .../gcc.dg/tree-ssa/pr116024-2-fwrapv.c       | 38 ++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c    | 39 ++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr116024.c      | 74 ++++++++++++++++++
 .../gcc.target/aarch64/gtu_to_ltu_cmp_1.c     |  2 +-
 8 files changed, 376 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024.c

Comments

Richard Biener Aug. 22, 2024, 1:15 p.m. UTC | #1
On Wed, 21 Aug 2024, Artemiy Volkov wrote:

> Hi,
> 
> sending a v2 of
> https://gcc.gnu.org/pipermail/gcc-patches/2024-August/659851.html after
> changing variable types in all new testcases from standard to fixed-width.
> 
> Could anyone please assist with reviewing and/or pushing to trunk/14 since I
> don't have commit access?
> 
> Many thanks,
> Artemiy
> 
> ---------- 8< ------------
> 
> This patch implements match.pd patterns for the following transformations:
> 
> (1) (UB-on-overflow types) C1 - X cmp C2 -> X cmp C1 - C2
> 
> (2) (unsigned types) C1 - X cmp C2 ->
>     (a) X cmp C1 - C2, when cmp is !=, ==
>     (b) X - (C1 - C2) cmp C2, when cmp is <=, >
>     (c) X - (C1 - C2 + 1) cmp C2, when cmp is <, >=,
> 
> (3) (signed wrapping types) C1 - X cmp C2
>     (a) X cmp C1 - C2, when cmp is !=, ==
>     (b) X - (C1 + 1) rcmp -(C2 + 1), otherwise
> 
> (4) (all wrapping types) X + C1 cmp C2 ->
>     (a) X cmp C2 - C1, when cmp is !=, ==
>     (b) X cmp -C1, when cmp is <=, > and C2 - C1 == max
>     (c) X cmp -C1, when cmp is <, >= and C2 - C1 == min

It would be easier to review if separating those, it wasn't all
clear what of (1) .. (4) corresponds to which hunk below.

> Included along are testcases for all the aforementioned changes.  This
> patch has been bootstrapped and regtested on aarch64, x86_64, and i386,
> and additionally regtested on riscv32.  Existing tests were adjusted
> where necessary.
> 
> gcc/ChangeLog:
> 
> 	PR tree-optimization/116024
>         * match.pd: New transformations around integer comparison.
> 
> gcc/testsuite/ChangeLog:
> 
>         * gcc.dg/tree-ssa/pr116024.c: New test.
>         * gcc.dg/tree-ssa/pr116024-1.c: Ditto.
>         * gcc.dg/tree-ssa/pr116024-1-fwrapv.c: Ditto.
>         * gcc.dg/tree-ssa/pr116024-2.c: Ditto.
>         * gcc.dg/tree-ssa/pr116024-2-fwrapv.c: Ditto.
>         * gcc.dg/pr67089-6.c: Adjust.
>         * gcc.target/aarch64/gtu_to_ltu_cmp_1.c: Ditto.
> 
> Signed-off-by: Artemiy Volkov <artemiy@synopsys.com>
> ---
>  gcc/match.pd                                  | 75 ++++++++++++++++++-
>  gcc/testsuite/gcc.dg/pr67089-6.c              |  4 +-
>  .../gcc.dg/tree-ssa/pr116024-1-fwrapv.c       | 74 ++++++++++++++++++
>  gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c    | 74 ++++++++++++++++++
>  .../gcc.dg/tree-ssa/pr116024-2-fwrapv.c       | 38 ++++++++++
>  gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c    | 39 ++++++++++
>  gcc/testsuite/gcc.dg/tree-ssa/pr116024.c      | 74 ++++++++++++++++++
>  .../gcc.target/aarch64/gtu_to_ltu_cmp_1.c     |  2 +-
>  8 files changed, 376 insertions(+), 4 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024.c
> 
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 65a3aae2243..bf3ccef7437 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -8800,6 +8800,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>         (cmp @0 { TREE_OVERFLOW (res)
>  		 ? drop_tree_overflow (res) : res; }))))))))
>  (for cmp (lt le gt ge)
> +     rcmp (gt ge lt le)
>   (for op (plus minus)
>        rop (minus plus)
>    (simplify
> @@ -8827,7 +8828,79 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>  				  "X cmp C2 -+ C1"),
>  				 WARN_STRICT_OVERFLOW_COMPARISON);
>  	}
> -	(cmp @0 { res; })))))))))
> +	(cmp @0 { res; })))))
> +/* For wrapping types, simplify X + C1 CMP C2 to X CMP -C1 when possible.  */

it's more like X +- C1 CMP C2 to X CMP' C2 -+ C1 when C2 -+ C1 == +-INF?

I have to think hard whether this requires an unsigned type
(naturally TYPE_OVERFLOW_WRAPS) or if it also works for signed
types (with -fwrapv).

Can you explain?

> +   (if (TYPE_OVERFLOW_WRAPS (TREE_TYPE (@0)))
> +     (with
> +       {
> +	wide_int max = wi::max_value (TREE_TYPE (@0));
> +	wide_int min = wi::min_value (TREE_TYPE (@0));
> +
> +	wide_int c2 = rop == PLUS_EXPR
> +			      ? wi::add (wi::to_wide (@2), wi::to_wide (@1))
> +			      : wi::sub (wi::to_wide (@2), wi::to_wide (@1));
> +	}
> +	(if (((cmp == LE_EXPR || cmp == GT_EXPR) && wi::eq_p (c2, max))
> +	    || ((cmp == LT_EXPR || cmp == GE_EXPR) && wi::eq_p (c2, min)))
> +	  (with
> +	   {
> +	     wide_int c1 = rop == PLUS_EXPR
> +				   ? wi::add (min, wi::to_wide (@1))
> +				   : wi::sub (min, wi::to_wide (@1));
> +	     tree c1_cst = build_uniform_cst (TREE_TYPE (@0),
> +				wide_int_to_tree (TREE_TYPE (@0), c1));
> +	   }
> +	   (rcmp @0 { c1_cst; })))))))))
> +
> +/* Invert sign of X in comparisons of the form C1 - X CMP C2.  */
> +
> +(for cmp (lt le gt ge eq ne)
> +     rcmp (gt ge lt le eq ne)
> +  (simplify
> +   (cmp (minus INTEGER_CST@0 @1) INTEGER_CST@2)
> +   (if (!TREE_OVERFLOW (@0) && !TREE_OVERFLOW (@2)
> +       && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@1)))
> +     (with { tree res = int_const_binop (MINUS_EXPR, @0, @2); }
> +      (if (TREE_OVERFLOW (res))
> +       (with
> +	{
> +	  fold_overflow_warning (("assuming signed overflow does not occur "
> +				  "when simplifying conditional to constant"),
> +				  WARN_STRICT_OVERFLOW_CONDITIONAL);
> +	}
> +	(switch
> +	 (if (cmp == NE_EXPR)
> +	  { constant_boolean_node (true, type); })
> +	 (if (cmp == EQ_EXPR)
> +	  { constant_boolean_node (false, type); })
> +	 {
> +	   bool less = cmp == LE_EXPR || cmp == LT_EXPR;
> +	   bool ovf_high = wi::lt_p (wi::to_wide (@0), 0,
> +				     TYPE_SIGN (TREE_TYPE (@0)));
> +	   constant_boolean_node (less == ovf_high, type);
> +	 }))
> +       (with
> +	{
> +	  fold_overflow_warning (("assuming signed overflow does not occur "
> +				  "when changing C1 - X cmp C2 to "
> +				  "X cmp C1 - C2"),
> +				  WARN_STRICT_OVERFLOW_COMPARISON);

please don't add more fold_overflow_warning

> +	}
> +	(rcmp @1 { res; }))))
> +   (if (TYPE_UNSIGNED (TREE_TYPE (@1)))
> +     (switch
> +       (if (cmp == EQ_EXPR || cmp == NE_EXPR)
> +	   (cmp @1 (minus @0 @2)))
> +       (if (cmp == LE_EXPR || cmp == GT_EXPR)
> +	   (cmp (plus @1 (minus @2 @0)) @2))

Why's that an improvement?

> +       (if (cmp == LT_EXPR || cmp == GE_EXPR)
> +	   (cmp (plus @1 (minus @2
> +			(plus @0 { build_int_cst (TREE_TYPE (@1), 1); }))) @2)))

Likewise?  Can you add comments describing the transform before the
respective transforms?

> +   (if (TYPE_OVERFLOW_WRAPS (TREE_TYPE (@1)))
> +     (if (cmp == EQ_EXPR || cmp == NE_EXPR)
> +	 (cmp @1 (minus @0 @2))
> +     (rcmp (minus @1 (plus @0 { build_int_cst (TREE_TYPE (@1), 1); }))
> +		(negate @2))))))))

why is the latter valid for all compares but the TYPE_UNSIGNED case is
not?  I suppose this is for signed compares but without undefined
overflow?

More comments would clearly help so that people later understand what
the different cases are.

>  
>  /* Canonicalizations of BIT_FIELD_REFs.  */
>  
> diff --git a/gcc/testsuite/gcc.dg/pr67089-6.c b/gcc/testsuite/gcc.dg/pr67089-6.c
> index b59d75b2318..80a33c3f3e2 100644
> --- a/gcc/testsuite/gcc.dg/pr67089-6.c
> +++ b/gcc/testsuite/gcc.dg/pr67089-6.c
> @@ -57,5 +57,5 @@ T (25, unsigned short, 2U - x, if (r > 2U) foo (0))
>  T (26, unsigned char, 2U - x, if (r <= 2U) foo (0))
>  
>  /* { dg-final { scan-tree-dump-times "ADD_OVERFLOW" 16 "widening_mul" { target { i?86-*-* x86_64-*-* } } } } */
> -/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 11 "widening_mul" { target { { i?86-*-* x86_64-*-* } && { ! ia32 } } } } } */
> -/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 9 "widening_mul" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */
> +/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 9 "widening_mul" { target { { i?86-*-* x86_64-*-* } && { ! ia32 } } } } } */
> +/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 7 "widening_mul" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c
> new file mode 100644
> index 00000000000..1fe8c821bec
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c
> @@ -0,0 +1,74 @@
> +/* PR tree-optimization/116024 */
> +/* { dg-do compile } */
> +/* { dg-options "-O1 -fdump-tree-forwprop1-details -fwrapv" } */
> +
> +#include <stdint.h>
> +#include <limits.h>
> +
> +uint32_t f(void);
> +
> +int32_t i2(void)
> +{
> +  int32_t l = 2;
> +  l = 10 - (int32_t)f();
> +  return l <= 20; // f() + 10 + INT_MIN <= 20 
> +}
> +
> +int32_t i2a(void)
> +{
> +  int32_t l = 2;
> +  l = 10 - (int32_t)f();
> +  return l < 30; // f() + 19 + INT_MIN <= 29 
> +}
> +
> +int32_t i2b(void)
> +{
> +  int32_t l = 2;
> +  l = 200 - (int32_t)f();
> +  return l <= 100; // f() - 100 + INT_MIN <= 100 
> +}
> +
> +int32_t i2c(void)
> +{
> +  int32_t l = 2;
> +  l = 300 - (int32_t)f();
> +  return l < 100; // f() - 201 + INT_MIN <= 99 
> +}
> +
> +int32_t i2d(void)
> +{
> +  int32_t l = 2;
> +  l = 1000 - (int32_t)f();
> +  return l >= 2000; // f() + 999 + INT_MIN > 1999
> +}
> +
> +int32_t i2e(void)
> +{
> +  int32_t l = 2;
> +  l = 1000 - (int32_t)f();
> +  return l > 3000; // f() + 2000 + INT_MIN > 3000
> +}
> +
> +int32_t i2f(void)
> +{
> +  int32_t l = 2;
> +  l = 20000 - (int32_t)f();
> +  return l >= 10000; // f() - 10001 + INT_MIN > 9999
> +}
> +
> +int32_t i2g(void)
> +{
> +  int32_t l = 2;
> +  l = 30000 - (int32_t)f();
> +  return l > 10000; // f() - 20000 + INT_MIN > 10000
> +}
> +
> +/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*?- _" 8 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -11.*\n.*>= -20" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -11.*\n.*>= -29" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -201.*\n.*>= -100" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -301.*\n.*>= -99" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -1001.*\n.*< -1999" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -1001.*\n.*< -3000" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -20001.*\n.*< -9999" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -30001.*\n.*< -10000" 1 "forwprop1" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c
> new file mode 100644
> index 00000000000..ecafb41fa09
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c
> @@ -0,0 +1,74 @@
> +/* PR tree-optimization/116024 */
> +/* { dg-do compile } */
> +/* { dg-options "-O1 -fdump-tree-forwprop1-details" } */
> +
> +#include <stdint.h>
> +#include <limits.h>
> +
> +uint32_t f(void);
> +
> +int32_t i2(void)
> +{
> +  uint32_t l = 2;
> +  l = 10 - (uint32_t)f();
> +  return l <= 20; // f() + 10 <= 20 
> +}
> +
> +int32_t i2a(void)
> +{
> +  uint32_t l = 2;
> +  l = 10 - (uint32_t)f();
> +  return l < 30; // f() + 19 <= 29 
> +}
> +
> +int32_t i2b(void)
> +{
> +  uint32_t l = 2;
> +  l = 200 - (uint32_t)f();
> +  return l <= 100; // f() - 100 <= 100 
> +}
> +
> +int32_t i2c(void)
> +{
> +  uint32_t l = 2;
> +  l = 300 - (uint32_t)f();
> +  return l < 100; // f() - 201 <= 99 
> +}
> +
> +int32_t i2d(void)
> +{
> +  uint32_t l = 2;
> +  l = 1000 - (uint32_t)f();
> +  return l >= 2000; // f() + 999 > 1999
> +}
> +
> +int32_t i2e(void)
> +{
> +  uint32_t l = 2;
> +  l = 1000 - (uint32_t)f();
> +  return l > 3000; // f() + 2000 > 3000
> +}
> +
> +int32_t i2f(void)
> +{
> +  uint32_t l = 2;
> +  l = 20000 - (uint32_t)f();
> +  return l >= 10000; // f() - 10001 > 9999
> +}
> +
> +int32_t i2g(void)
> +{
> +  uint32_t l = 2;
> +  l = 30000 - (uint32_t)f();
> +  return l > 10000; // f() - 20000 > 10000
> +}
> +
> +/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*?- _" 8 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 10.*\n.*<= 20" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 19.*\n.*<= 29" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 4294967196.*\n.*<= 100" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 4294967095.*\n.*<= 99" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 999.*\n.*> 1999" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 2000.*\n.*> 3000" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 4294957295.*\n.*> 9999" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 4294947296.*\n.*> 10000" 1 "forwprop1" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c
> new file mode 100644
> index 00000000000..ff8f097e647
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c
> @@ -0,0 +1,38 @@
> +/* PR tree-optimization/116024 */
> +/* { dg-do compile } */
> +/* { dg-options "-O1 -fdump-tree-forwprop1-details -fwrapv" } */
> +
> +#include <stdint.h>
> +#include <limits.h>
> +
> +uint32_t f(void);
> +
> +int32_t i3(void)
> +{
> +  int32_t l = -10 + (int32_t)f();
> +  return l <= INT_MAX - 10; // f() > INT_MIN + 9
> +}
> +
> +int32_t i3a(void)
> +{
> +  int32_t l = -21 + (int32_t)f();
> +  return l < INT_MAX - 20; // f() > INT_MIN + 20
> +}
> +
> +int32_t i3b(void)
> +{
> +  int32_t l = 30 + (int32_t)f();
> +  return l >= INT_MIN + 30; // f() <= INT_MAX - 30
> +}
> +
> +int32_t i3c(void)
> +{
> +  int32_t l = 40 + (int32_t)f();
> +  return l > INT_MIN + 39; // f() <= INT_MAX - 39
> +}
> +
> +/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*? \\+" 4 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* >= -2147483638" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* >= -2147483627" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 2147483618" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 2147483608" 1 "forwprop1" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c
> new file mode 100644
> index 00000000000..f7269765fcb
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c
> @@ -0,0 +1,39 @@
> +/* PR tree-optimization/116024 */
> +/* { dg-do compile } */
> +/* { dg-options "-O1 -fdump-tree-forwprop1-details" } */
> +
> +#include <stdint.h>
> +#include <limits.h>
> +
> +uint32_t f(void);
> +
> +int32_t i3(void)
> +{
> +  uint32_t l = 2;
> +  l = UINT_MAX - 9 + (uint32_t)f();
> +  return l <= UINT_MAX - 10; // f() > 9
> +}
> +
> +int32_t i3a(void)
> +{
> +  uint32_t l = UINT_MAX - 20 + (uint32_t)f();
> +  return l < UINT_MAX - 20; // f() > 20
> +}
> +
> +int32_t i3b(void)
> +{
> +  uint32_t l = 30 + (uint32_t)f();
> +  return l >= 30; // f() < -30u
> +}
> +
> +int32_t i3c(void)
> +{
> +  uint32_t l = 40 + (uint32_t)f();
> +  return l > 39; // f() < -39u
> +}
> +
> +/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*? \\+" 4 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 9" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 20" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 4294967265" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 4294967255" 1 "forwprop1" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024.c b/gcc/testsuite/gcc.dg/tree-ssa/pr116024.c
> new file mode 100644
> index 00000000000..22b08057b2b
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024.c
> @@ -0,0 +1,74 @@
> +/* PR tree-optimization/116024 */
> +/* { dg-do compile } */
> +/* { dg-options "-O1 -fdump-tree-forwprop1-details" } */
> +
> +#include <stdint.h>
> +#include <limits.h>
> +
> +uint32_t f(void);
> +
> +int32_t i1(void)
> +{
> +  int32_t l = 2;
> +  l = 10 - (int32_t)f();
> +  return l <= 9; // f() > 0
> +}
> +
> +int32_t i1a(void)
> +{
> +  int32_t l = 2;
> +  l = 20 - (int32_t)f();
> +  return l <= INT_MIN; // return 0
> +}
> +
> +int32_t i1b(void)
> +{
> +  int32_t l = 2;
> +  l = 30 - (int32_t)f();
> +  return l <= INT_MIN + 31; // f() == INT_MAX
> +}
> +
> +int32_t i1c(void)
> +{
> +  int32_t l = 2;
> +  l = INT_MAX - 40 - (int32_t)f();
> +  return l <= -38; // f() > INT_MAX - 3
> +}
> +
> +int32_t i1d(void)
> +{
> +  int32_t l = 2;
> +  l = INT_MAX - 50 - (int32_t)f();
> +  return l <= INT_MAX - 1; // f() != -50
> +}
> +
> +int32_t i1e(void)
> +{
> +  int32_t l = 2;
> +  l = INT_MAX - 60 - (int32_t)f();
> +  return l != INT_MAX - 90; // f() != 30
> +}
> +
> +int32_t i1f(void)
> +{
> +  int32_t l = 2;
> +  l = INT_MIN + 70 - (int32_t)f();
> +  return l <= INT_MAX - 2; // return 0
> +}
> +
> +int32_t i1g(void)
> +{
> +  int32_t l = 2;
> +  l = INT_MAX/2 + 30 - (int32_t)f();
> +  return l <= INT_MIN/2 - 30; // return 1
> +}
> +
> +
> +/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*?- _" 5 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "return 0" 2 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "return 1" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 0" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* == 2147483647" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 2147483644" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* != 4294967246" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* != 30" 1 "forwprop1" } } */
> diff --git a/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c b/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c
> index 81c536c90af..bfcec6719de 100644
> --- a/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c
> +++ b/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c
> @@ -10,4 +10,4 @@ f1 (int x, int t)
>    return t;
>  }
>  
> -/* { dg-final { scan-assembler-times "cmn\\tw\[0-9\]+, #2" 1 } } */
> +/* { dg-final { scan-assembler-times "cmn\\tw\[0-9\]+, #3" 1 } } */
>
Artemiy Volkov Aug. 23, 2024, 2:09 p.m. UTC | #2
On 8/22/2024 3:15 PM, Richard Biener wrote:
 > On Wed, 21 Aug 2024, Artemiy Volkov wrote:
 >
 >> Hi,
 >>
 >> sending a v2 of
 >> https://gcc.gnu.org/pipermail/gcc-patches/2024-August/659851.html after
 >> changing variable types in all new testcases from standard to 
fixed-width.
 >>
 >> Could anyone please assist with reviewing and/or pushing to trunk/14 
since I
 >> don't have commit access?
 >>
 >> Many thanks,
 >> Artemiy
 >>
 >> ---------- 8< ------------
 >>
 >> This patch implements match.pd patterns for the following 
transformations:
 >>
 >> (1) (UB-on-overflow types) C1 - X cmp C2 -> X cmp C1 - C2
 >>
 >> (2) (unsigned types) C1 - X cmp C2 ->
 >>      (a) X cmp C1 - C2, when cmp is !=, ==
 >>      (b) X - (C1 - C2) cmp C2, when cmp is <=, >
 >>      (c) X - (C1 - C2 + 1) cmp C2, when cmp is <, >=,
 >>
 >> (3) (signed wrapping types) C1 - X cmp C2
 >>      (a) X cmp C1 - C2, when cmp is !=, ==
 >>      (b) X - (C1 + 1) rcmp -(C2 + 1), otherwise
 >>
 >> (4) (all wrapping types) X + C1 cmp C2 ->
 >>      (a) X cmp C2 - C1, when cmp is !=, ==
 >>      (b) X cmp -C1, when cmp is <=, > and C2 - C1 == max
 >>      (c) X cmp -C1, when cmp is <, >= and C2 - C1 == min
 >
 > It would be easier to review if separating those, it wasn't all
 > clear what of (1) .. (4) corresponds to which hunk below.

OK, I will split this patch into 4 in v3.

 >
 >> Included along are testcases for all the aforementioned changes.  This
 >> patch has been bootstrapped and regtested on aarch64, x86_64, and i386,
 >> and additionally regtested on riscv32.  Existing tests were adjusted
 >> where necessary.
 >>
 >> gcc/ChangeLog:
 >>
 >> 	PR tree-optimization/116024
 >>          * match.pd: New transformations around integer comparison.
 >>
 >> gcc/testsuite/ChangeLog:
 >>
 >>          * gcc.dg/tree-ssa/pr116024.c: New test.
 >>          * gcc.dg/tree-ssa/pr116024-1.c: Ditto.
 >>          * gcc.dg/tree-ssa/pr116024-1-fwrapv.c: Ditto.
 >>          * gcc.dg/tree-ssa/pr116024-2.c: Ditto.
 >>          * gcc.dg/tree-ssa/pr116024-2-fwrapv.c: Ditto.
 >>          * gcc.dg/pr67089-6.c: Adjust.
 >>          * gcc.target/aarch64/gtu_to_ltu_cmp_1.c: Ditto.
 >>
 >> Signed-off-by: Artemiy Volkov <artemiy@synopsys.com>
 >> ---
 >>   gcc/match.pd                                  | 75 ++++++++++++++++++-
 >>   gcc/testsuite/gcc.dg/pr67089-6.c              |  4 +-
 >>   .../gcc.dg/tree-ssa/pr116024-1-fwrapv.c       | 74 ++++++++++++++++++
 >>   gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c    | 74 ++++++++++++++++++
 >>   .../gcc.dg/tree-ssa/pr116024-2-fwrapv.c       | 38 ++++++++++
 >>   gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c    | 39 ++++++++++
 >>   gcc/testsuite/gcc.dg/tree-ssa/pr116024.c      | 74 ++++++++++++++++++
 >>   .../gcc.target/aarch64/gtu_to_ltu_cmp_1.c     |  2 +-
 >>   8 files changed, 376 insertions(+), 4 deletions(-)
 >>   create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c
 >>   create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c
 >>   create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c
 >>   create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c
 >>   create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024.c
 >>
 >> diff --git a/gcc/match.pd b/gcc/match.pd
 >> index 65a3aae2243..bf3ccef7437 100644
 >> --- a/gcc/match.pd
 >> +++ b/gcc/match.pd
 >> @@ -8800,6 +8800,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 >>          (cmp @0 { TREE_OVERFLOW (res)
 >>   		 ? drop_tree_overflow (res) : res; }))))))))
 >>   (for cmp (lt le gt ge)
 >> +     rcmp (gt ge lt le)
 >>    (for op (plus minus)
 >>         rop (minus plus)
 >>     (simplify
 >> @@ -8827,7 +8828,79 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 >>   				  "X cmp C2 -+ C1"),
 >>   				 WARN_STRICT_OVERFLOW_COMPARISON);
 >>   	}
 >> -	(cmp @0 { res; })))))))))
 >> +	(cmp @0 { res; })))))
 >> +/* For wrapping types, simplify X + C1 CMP C2 to X CMP -C1 when 
possible.  */
 >
 > it's more like X +- C1 CMP C2 to X CMP' C2 -+ C1 when C2 -+ C1 == +-INF?
 >
 > I have to think hard whether this requires an unsigned type
 > (naturally TYPE_OVERFLOW_WRAPS) or if it also works for signed
 > types (with -fwrapv).
 >
 > Can you explain?

If CMP is <= and C2 -+ C1 == +INF, we can add -+C1 to both sides of the 
comparison (and use the fact that -INF <= X+-C1) to obtain (-INF-+C1 <=) 
X <= C2-+C1. The right part always holds, so we are left with -INF-+C1 
<= X. (> is the negation of this, so -INF-+C1 > X).

Similarly, for >= (and <), using the fact that X+-C1 <= +INF, we can 
eliminate C2 and simplify to X <= +INF-+C1.

 >
 >> +   (if (TYPE_OVERFLOW_WRAPS (TREE_TYPE (@0)))
 >> +     (with
 >> +       {
 >> +	wide_int max = wi::max_value (TREE_TYPE (@0));
 >> +	wide_int min = wi::min_value (TREE_TYPE (@0));
 >> +
 >> +	wide_int c2 = rop == PLUS_EXPR
 >> +			      ? wi::add (wi::to_wide (@2), wi::to_wide (@1))
 >> +			      : wi::sub (wi::to_wide (@2), wi::to_wide (@1));
 >> +	}
 >> +	(if (((cmp == LE_EXPR || cmp == GT_EXPR) && wi::eq_p (c2, max))
 >> +	    || ((cmp == LT_EXPR || cmp == GE_EXPR) && wi::eq_p (c2, min)))
 >> +	  (with
 >> +	   {
 >> +	     wide_int c1 = rop == PLUS_EXPR
 >> +				   ? wi::add (min, wi::to_wide (@1))
 >> +				   : wi::sub (min, wi::to_wide (@1));

BTW, in accordance with the above, this should clearly be wi::{add,sub} 
(c2, wi::to_wide (@1)). This error did not cause any of the newly added 
tests to fail only because something had replaced < to <= and >= to > 
prior to the forwprop1 pass. I will correct this in v3.

 >> +	     tree c1_cst = build_uniform_cst (TREE_TYPE (@0),
 >> +				wide_int_to_tree (TREE_TYPE (@0), c1));
 >> +	   }
 >> +	   (rcmp @0 { c1_cst; })))))))))
 >> +
 >> +/* Invert sign of X in comparisons of the form C1 - X CMP C2.  */
 >> +
 >> +(for cmp (lt le gt ge eq ne)
 >> +     rcmp (gt ge lt le eq ne)
 >> +  (simplify
 >> +   (cmp (minus INTEGER_CST@0 @1) INTEGER_CST@2)
 >> +   (if (!TREE_OVERFLOW (@0) && !TREE_OVERFLOW (@2)
 >> +       && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@1)))
 >> +     (with { tree res = int_const_binop (MINUS_EXPR, @0, @2); }
 >> +      (if (TREE_OVERFLOW (res))
 >> +       (with
 >> +	{
 >> +	  fold_overflow_warning (("assuming signed overflow does not occur "
 >> +				  "when simplifying conditional to constant"),
 >> +				  WARN_STRICT_OVERFLOW_CONDITIONAL);
 >> +	}
 >> +	(switch
 >> +	 (if (cmp == NE_EXPR)
 >> +	  { constant_boolean_node (true, type); })
 >> +	 (if (cmp == EQ_EXPR)
 >> +	  { constant_boolean_node (false, type); })
 >> +	 {
 >> +	   bool less = cmp == LE_EXPR || cmp == LT_EXPR;
 >> +	   bool ovf_high = wi::lt_p (wi::to_wide (@0), 0,
 >> +				     TYPE_SIGN (TREE_TYPE (@0)));
 >> +	   constant_boolean_node (less == ovf_high, type);
 >> +	 }))
 >> +       (with
 >> +	{
 >> +	  fold_overflow_warning (("assuming signed overflow does not occur "
 >> +				  "when changing C1 - X cmp C2 to "
 >> +				  "X cmp C1 - C2"),
 >> +				  WARN_STRICT_OVERFLOW_COMPARISON);
 >
 > please don't add more fold_overflow_warning

OK, I will remove both warnings.

 >
 >> +	}
 >> +	(rcmp @1 { res; }))))
 >> +   (if (TYPE_UNSIGNED (TREE_TYPE (@1)))
 >> +     (switch
 >> +       (if (cmp == EQ_EXPR || cmp == NE_EXPR)
 >> +	   (cmp @1 (minus @0 @2)))
 >> +       (if (cmp == LE_EXPR || cmp == GT_EXPR)
 >> +	   (cmp (plus @1 (minus @2 @0)) @2))
 >
 > Why's that an improvement?
 >
 >> +       (if (cmp == LT_EXPR || cmp == GE_EXPR)
 >> +	   (cmp (plus @1 (minus @2
 >> +			(plus @0 { build_int_cst (TREE_TYPE (@1), 1); }))) @2)))
 >
 > Likewise?  Can you add comments describing the transform before the
 > respective transforms?

The rest of the cases (including these two above) are an improvement 
because the variable (@1) now appears with a plus instead of a minus 
sign, potentially saving sub instructions in generated code. E.g., 
currently on 32-bit RISC-V the expression "10 - (unsigned int) f() < 30" 
is computed using 3 instructions:
     li      a5,10
     sub     a0,a5,a0
     sltiu   a0,a0,30
With my patch applied, it becomes 2:
     addi    a0,a0,19
     sltiu   a0,a0,30

This was the motivating example in PR116024.

 >
 >> +   (if (TYPE_OVERFLOW_WRAPS (TREE_TYPE (@1)))
 >> +     (if (cmp == EQ_EXPR || cmp == NE_EXPR)
 >> +	 (cmp @1 (minus @0 @2))
 >> +     (rcmp (minus @1 (plus @0 { build_int_cst (TREE_TYPE (@1), 1); }))
 >> +		(negate @2))))))))
 >
 > why is the latter valid for all compares but the TYPE_UNSIGNED case is
 > not?  I suppose this is for signed compares but without undefined
 > overflow?
 >
 > More comments would clearly help so that people later understand what
 > the different cases are.

Yes, this is for !TYPE_UNSIGNED () && TYPE_OVERFLOW_WRAPS (), thus for 
signed integers under -fwrapv. The primary reason the approaches for 
unsigned and wrapping signed integers are different is that for the 
latter I can just negate C2 and invert the comparison, and for unsigned 
types it's easier to replace C1 - X CMP C2 with X + (C2 - C1) CMP C2 
(with additional adjustments in both cases).

I will make sure to add more comments describing all of this to match.pd 
in v3.

Thanks for taking a look,
Artemiy

 >
 >>
 >>   /* Canonicalizations of BIT_FIELD_REFs.  */
 >>
 >> diff --git a/gcc/testsuite/gcc.dg/pr67089-6.c 
b/gcc/testsuite/gcc.dg/pr67089-6.c
 >> index b59d75b2318..80a33c3f3e2 100644
 >> --- a/gcc/testsuite/gcc.dg/pr67089-6.c
 >> +++ b/gcc/testsuite/gcc.dg/pr67089-6.c
 >> @@ -57,5 +57,5 @@ T (25, unsigned short, 2U - x, if (r > 2U) foo (0))
 >>   T (26, unsigned char, 2U - x, if (r <= 2U) foo (0))
 >>
 >>   /* { dg-final { scan-tree-dump-times "ADD_OVERFLOW" 16 
"widening_mul" { target { i?86-*-* x86_64-*-* } } } } */
 >> -/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 11 
"widening_mul" { target { { i?86-*-* x86_64-*-* } && { ! ia32 } } } } } */
 >> -/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 9 
"widening_mul" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */
 >> +/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 9 
"widening_mul" { target { { i?86-*-* x86_64-*-* } && { ! ia32 } } } } } */
 >> +/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 7 
"widening_mul" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */
 >> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c
 >> new file mode 100644
 >> index 00000000000..1fe8c821bec
 >> --- /dev/null
 >> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c
 >> @@ -0,0 +1,74 @@
 >> +/* PR tree-optimization/116024 */
 >> +/* { dg-do compile } */
 >> +/* { dg-options "-O1 -fdump-tree-forwprop1-details -fwrapv" } */
 >> +
 >> +#include <stdint.h>
 >> +#include <limits.h>
 >> +
 >> +uint32_t f(void);
 >> +
 >> +int32_t i2(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = 10 - (int32_t)f();
 >> +  return l <= 20; // f() + 10 + INT_MIN <= 20
 >> +}
 >> +
 >> +int32_t i2a(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = 10 - (int32_t)f();
 >> +  return l < 30; // f() + 19 + INT_MIN <= 29
 >> +}
 >> +
 >> +int32_t i2b(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = 200 - (int32_t)f();
 >> +  return l <= 100; // f() - 100 + INT_MIN <= 100
 >> +}
 >> +
 >> +int32_t i2c(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = 300 - (int32_t)f();
 >> +  return l < 100; // f() - 201 + INT_MIN <= 99
 >> +}
 >> +
 >> +int32_t i2d(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = 1000 - (int32_t)f();
 >> +  return l >= 2000; // f() + 999 + INT_MIN > 1999
 >> +}
 >> +
 >> +int32_t i2e(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = 1000 - (int32_t)f();
 >> +  return l > 3000; // f() + 2000 + INT_MIN > 3000
 >> +}
 >> +
 >> +int32_t i2f(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = 20000 - (int32_t)f();
 >> +  return l >= 10000; // f() - 10001 + INT_MIN > 9999
 >> +}
 >> +
 >> +int32_t i2g(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = 30000 - (int32_t)f();
 >> +  return l > 10000; // f() - 20000 + INT_MIN > 10000
 >> +}
 >> +
 >> +/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*?- _" 8 
"forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
-11.*\n.*>= -20" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
-11.*\n.*>= -29" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
-201.*\n.*>= -100" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
-301.*\n.*>= -99" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
-1001.*\n.*< -1999" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
-1001.*\n.*< -3000" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
-20001.*\n.*< -9999" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
-30001.*\n.*< -10000" 1 "forwprop1" } } */
 >> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c
 >> new file mode 100644
 >> index 00000000000..ecafb41fa09
 >> --- /dev/null
 >> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c
 >> @@ -0,0 +1,74 @@
 >> +/* PR tree-optimization/116024 */
 >> +/* { dg-do compile } */
 >> +/* { dg-options "-O1 -fdump-tree-forwprop1-details" } */
 >> +
 >> +#include <stdint.h>
 >> +#include <limits.h>
 >> +
 >> +uint32_t f(void);
 >> +
 >> +int32_t i2(void)
 >> +{
 >> +  uint32_t l = 2;
 >> +  l = 10 - (uint32_t)f();
 >> +  return l <= 20; // f() + 10 <= 20
 >> +}
 >> +
 >> +int32_t i2a(void)
 >> +{
 >> +  uint32_t l = 2;
 >> +  l = 10 - (uint32_t)f();
 >> +  return l < 30; // f() + 19 <= 29
 >> +}
 >> +
 >> +int32_t i2b(void)
 >> +{
 >> +  uint32_t l = 2;
 >> +  l = 200 - (uint32_t)f();
 >> +  return l <= 100; // f() - 100 <= 100
 >> +}
 >> +
 >> +int32_t i2c(void)
 >> +{
 >> +  uint32_t l = 2;
 >> +  l = 300 - (uint32_t)f();
 >> +  return l < 100; // f() - 201 <= 99
 >> +}
 >> +
 >> +int32_t i2d(void)
 >> +{
 >> +  uint32_t l = 2;
 >> +  l = 1000 - (uint32_t)f();
 >> +  return l >= 2000; // f() + 999 > 1999
 >> +}
 >> +
 >> +int32_t i2e(void)
 >> +{
 >> +  uint32_t l = 2;
 >> +  l = 1000 - (uint32_t)f();
 >> +  return l > 3000; // f() + 2000 > 3000
 >> +}
 >> +
 >> +int32_t i2f(void)
 >> +{
 >> +  uint32_t l = 2;
 >> +  l = 20000 - (uint32_t)f();
 >> +  return l >= 10000; // f() - 10001 > 9999
 >> +}
 >> +
 >> +int32_t i2g(void)
 >> +{
 >> +  uint32_t l = 2;
 >> +  l = 30000 - (uint32_t)f();
 >> +  return l > 10000; // f() - 20000 > 10000
 >> +}
 >> +
 >> +/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*?- _" 8 
"forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
10.*\n.*<= 20" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
19.*\n.*<= 29" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
4294967196.*\n.*<= 100" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
4294967095.*\n.*<= 99" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
999.*\n.*> 1999" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
2000.*\n.*> 3000" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
4294957295.*\n.*> 9999" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
4294947296.*\n.*> 10000" 1 "forwprop1" } } */
 >> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c
 >> new file mode 100644
 >> index 00000000000..ff8f097e647
 >> --- /dev/null
 >> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c
 >> @@ -0,0 +1,38 @@
 >> +/* PR tree-optimization/116024 */
 >> +/* { dg-do compile } */
 >> +/* { dg-options "-O1 -fdump-tree-forwprop1-details -fwrapv" } */
 >> +
 >> +#include <stdint.h>
 >> +#include <limits.h>
 >> +
 >> +uint32_t f(void);
 >> +
 >> +int32_t i3(void)
 >> +{
 >> +  int32_t l = -10 + (int32_t)f();
 >> +  return l <= INT_MAX - 10; // f() > INT_MIN + 9
 >> +}
 >> +
 >> +int32_t i3a(void)
 >> +{
 >> +  int32_t l = -21 + (int32_t)f();
 >> +  return l < INT_MAX - 20; // f() > INT_MIN + 20
 >> +}
 >> +
 >> +int32_t i3b(void)
 >> +{
 >> +  int32_t l = 30 + (int32_t)f();
 >> +  return l >= INT_MIN + 30; // f() <= INT_MAX - 30
 >> +}
 >> +
 >> +int32_t i3c(void)
 >> +{
 >> +  int32_t l = 40 + (int32_t)f();
 >> +  return l > INT_MIN + 39; // f() <= INT_MAX - 39
 >> +}
 >> +
 >> +/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*? \\+" 4 
"forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* >= 
-2147483638" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* >= 
-2147483627" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 
2147483618" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 
2147483608" 1 "forwprop1" } } */
 >> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c
 >> new file mode 100644
 >> index 00000000000..f7269765fcb
 >> --- /dev/null
 >> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c
 >> @@ -0,0 +1,39 @@
 >> +/* PR tree-optimization/116024 */
 >> +/* { dg-do compile } */
 >> +/* { dg-options "-O1 -fdump-tree-forwprop1-details" } */
 >> +
 >> +#include <stdint.h>
 >> +#include <limits.h>
 >> +
 >> +uint32_t f(void);
 >> +
 >> +int32_t i3(void)
 >> +{
 >> +  uint32_t l = 2;
 >> +  l = UINT_MAX - 9 + (uint32_t)f();
 >> +  return l <= UINT_MAX - 10; // f() > 9
 >> +}
 >> +
 >> +int32_t i3a(void)
 >> +{
 >> +  uint32_t l = UINT_MAX - 20 + (uint32_t)f();
 >> +  return l < UINT_MAX - 20; // f() > 20
 >> +}
 >> +
 >> +int32_t i3b(void)
 >> +{
 >> +  uint32_t l = 30 + (uint32_t)f();
 >> +  return l >= 30; // f() < -30u
 >> +}
 >> +
 >> +int32_t i3c(void)
 >> +{
 >> +  uint32_t l = 40 + (uint32_t)f();
 >> +  return l > 39; // f() < -39u
 >> +}
 >> +
 >> +/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*? \\+" 4 
"forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 9" 1 
"forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 20" 
1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 
4294967265" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 
4294967255" 1 "forwprop1" } } */
 >> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr116024.c
 >> new file mode 100644
 >> index 00000000000..22b08057b2b
 >> --- /dev/null
 >> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024.c
 >> @@ -0,0 +1,74 @@
 >> +/* PR tree-optimization/116024 */
 >> +/* { dg-do compile } */
 >> +/* { dg-options "-O1 -fdump-tree-forwprop1-details" } */
 >> +
 >> +#include <stdint.h>
 >> +#include <limits.h>
 >> +
 >> +uint32_t f(void);
 >> +
 >> +int32_t i1(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = 10 - (int32_t)f();
 >> +  return l <= 9; // f() > 0
 >> +}
 >> +
 >> +int32_t i1a(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = 20 - (int32_t)f();
 >> +  return l <= INT_MIN; // return 0
 >> +}
 >> +
 >> +int32_t i1b(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = 30 - (int32_t)f();
 >> +  return l <= INT_MIN + 31; // f() == INT_MAX
 >> +}
 >> +
 >> +int32_t i1c(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = INT_MAX - 40 - (int32_t)f();
 >> +  return l <= -38; // f() > INT_MAX - 3
 >> +}
 >> +
 >> +int32_t i1d(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = INT_MAX - 50 - (int32_t)f();
 >> +  return l <= INT_MAX - 1; // f() != -50
 >> +}
 >> +
 >> +int32_t i1e(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = INT_MAX - 60 - (int32_t)f();
 >> +  return l != INT_MAX - 90; // f() != 30
 >> +}
 >> +
 >> +int32_t i1f(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = INT_MIN + 70 - (int32_t)f();
 >> +  return l <= INT_MAX - 2; // return 0
 >> +}
 >> +
 >> +int32_t i1g(void)
 >> +{
 >> +  int32_t l = 2;
 >> +  l = INT_MAX/2 + 30 - (int32_t)f();
 >> +  return l <= INT_MIN/2 - 30; // return 1
 >> +}
 >> +
 >> +
 >> +/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*?- _" 5 
"forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "return 0" 2 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "return 1" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 0" 1 
"forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* == 
2147483647" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 
2147483644" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* != 
4294967246" 1 "forwprop1" } } */
 >> +/* { dg-final { scan-tree-dump-times "gimple_simplified to.* != 30" 
1 "forwprop1" } } */
 >> diff --git a/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c 
b/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c
 >> index 81c536c90af..bfcec6719de 100644
 >> --- a/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c
 >> +++ b/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c
 >> @@ -10,4 +10,4 @@ f1 (int x, int t)
 >>     return t;
 >>   }
 >>
 >> -/* { dg-final { scan-assembler-times "cmn\\tw\[0-9\]+, #2" 1 } } */
 >> +/* { dg-final { scan-assembler-times "cmn\\tw\[0-9\]+, #3" 1 } } */
 >>
 >
diff mbox series

Patch

diff --git a/gcc/match.pd b/gcc/match.pd
index 65a3aae2243..bf3ccef7437 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -8800,6 +8800,7 @@  DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        (cmp @0 { TREE_OVERFLOW (res)
 		 ? drop_tree_overflow (res) : res; }))))))))
 (for cmp (lt le gt ge)
+     rcmp (gt ge lt le)
  (for op (plus minus)
       rop (minus plus)
   (simplify
@@ -8827,7 +8828,79 @@  DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 				  "X cmp C2 -+ C1"),
 				 WARN_STRICT_OVERFLOW_COMPARISON);
 	}
-	(cmp @0 { res; })))))))))
+	(cmp @0 { res; })))))
+/* For wrapping types, simplify X + C1 CMP C2 to X CMP -C1 when possible.  */
+   (if (TYPE_OVERFLOW_WRAPS (TREE_TYPE (@0)))
+     (with
+       {
+	wide_int max = wi::max_value (TREE_TYPE (@0));
+	wide_int min = wi::min_value (TREE_TYPE (@0));
+
+	wide_int c2 = rop == PLUS_EXPR
+			      ? wi::add (wi::to_wide (@2), wi::to_wide (@1))
+			      : wi::sub (wi::to_wide (@2), wi::to_wide (@1));
+	}
+	(if (((cmp == LE_EXPR || cmp == GT_EXPR) && wi::eq_p (c2, max))
+	    || ((cmp == LT_EXPR || cmp == GE_EXPR) && wi::eq_p (c2, min)))
+	  (with
+	   {
+	     wide_int c1 = rop == PLUS_EXPR
+				   ? wi::add (min, wi::to_wide (@1))
+				   : wi::sub (min, wi::to_wide (@1));
+	     tree c1_cst = build_uniform_cst (TREE_TYPE (@0),
+				wide_int_to_tree (TREE_TYPE (@0), c1));
+	   }
+	   (rcmp @0 { c1_cst; })))))))))
+
+/* Invert sign of X in comparisons of the form C1 - X CMP C2.  */
+
+(for cmp (lt le gt ge eq ne)
+     rcmp (gt ge lt le eq ne)
+  (simplify
+   (cmp (minus INTEGER_CST@0 @1) INTEGER_CST@2)
+   (if (!TREE_OVERFLOW (@0) && !TREE_OVERFLOW (@2)
+       && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@1)))
+     (with { tree res = int_const_binop (MINUS_EXPR, @0, @2); }
+      (if (TREE_OVERFLOW (res))
+       (with
+	{
+	  fold_overflow_warning (("assuming signed overflow does not occur "
+				  "when simplifying conditional to constant"),
+				  WARN_STRICT_OVERFLOW_CONDITIONAL);
+	}
+	(switch
+	 (if (cmp == NE_EXPR)
+	  { constant_boolean_node (true, type); })
+	 (if (cmp == EQ_EXPR)
+	  { constant_boolean_node (false, type); })
+	 {
+	   bool less = cmp == LE_EXPR || cmp == LT_EXPR;
+	   bool ovf_high = wi::lt_p (wi::to_wide (@0), 0,
+				     TYPE_SIGN (TREE_TYPE (@0)));
+	   constant_boolean_node (less == ovf_high, type);
+	 }))
+       (with
+	{
+	  fold_overflow_warning (("assuming signed overflow does not occur "
+				  "when changing C1 - X cmp C2 to "
+				  "X cmp C1 - C2"),
+				  WARN_STRICT_OVERFLOW_COMPARISON);
+	}
+	(rcmp @1 { res; }))))
+   (if (TYPE_UNSIGNED (TREE_TYPE (@1)))
+     (switch
+       (if (cmp == EQ_EXPR || cmp == NE_EXPR)
+	   (cmp @1 (minus @0 @2)))
+       (if (cmp == LE_EXPR || cmp == GT_EXPR)
+	   (cmp (plus @1 (minus @2 @0)) @2))
+       (if (cmp == LT_EXPR || cmp == GE_EXPR)
+	   (cmp (plus @1 (minus @2
+			(plus @0 { build_int_cst (TREE_TYPE (@1), 1); }))) @2)))
+   (if (TYPE_OVERFLOW_WRAPS (TREE_TYPE (@1)))
+     (if (cmp == EQ_EXPR || cmp == NE_EXPR)
+	 (cmp @1 (minus @0 @2))
+     (rcmp (minus @1 (plus @0 { build_int_cst (TREE_TYPE (@1), 1); }))
+		(negate @2))))))))
 
 /* Canonicalizations of BIT_FIELD_REFs.  */
 
diff --git a/gcc/testsuite/gcc.dg/pr67089-6.c b/gcc/testsuite/gcc.dg/pr67089-6.c
index b59d75b2318..80a33c3f3e2 100644
--- a/gcc/testsuite/gcc.dg/pr67089-6.c
+++ b/gcc/testsuite/gcc.dg/pr67089-6.c
@@ -57,5 +57,5 @@  T (25, unsigned short, 2U - x, if (r > 2U) foo (0))
 T (26, unsigned char, 2U - x, if (r <= 2U) foo (0))
 
 /* { dg-final { scan-tree-dump-times "ADD_OVERFLOW" 16 "widening_mul" { target { i?86-*-* x86_64-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 11 "widening_mul" { target { { i?86-*-* x86_64-*-* } && { ! ia32 } } } } } */
-/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 9 "widening_mul" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */
+/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 9 "widening_mul" { target { { i?86-*-* x86_64-*-* } && { ! ia32 } } } } } */
+/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 7 "widening_mul" { target { { i?86-*-* x86_64-*-* } && ia32 } } } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c
new file mode 100644
index 00000000000..1fe8c821bec
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c
@@ -0,0 +1,74 @@ 
+/* PR tree-optimization/116024 */
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-forwprop1-details -fwrapv" } */
+
+#include <stdint.h>
+#include <limits.h>
+
+uint32_t f(void);
+
+int32_t i2(void)
+{
+  int32_t l = 2;
+  l = 10 - (int32_t)f();
+  return l <= 20; // f() + 10 + INT_MIN <= 20 
+}
+
+int32_t i2a(void)
+{
+  int32_t l = 2;
+  l = 10 - (int32_t)f();
+  return l < 30; // f() + 19 + INT_MIN <= 29 
+}
+
+int32_t i2b(void)
+{
+  int32_t l = 2;
+  l = 200 - (int32_t)f();
+  return l <= 100; // f() - 100 + INT_MIN <= 100 
+}
+
+int32_t i2c(void)
+{
+  int32_t l = 2;
+  l = 300 - (int32_t)f();
+  return l < 100; // f() - 201 + INT_MIN <= 99 
+}
+
+int32_t i2d(void)
+{
+  int32_t l = 2;
+  l = 1000 - (int32_t)f();
+  return l >= 2000; // f() + 999 + INT_MIN > 1999
+}
+
+int32_t i2e(void)
+{
+  int32_t l = 2;
+  l = 1000 - (int32_t)f();
+  return l > 3000; // f() + 2000 + INT_MIN > 3000
+}
+
+int32_t i2f(void)
+{
+  int32_t l = 2;
+  l = 20000 - (int32_t)f();
+  return l >= 10000; // f() - 10001 + INT_MIN > 9999
+}
+
+int32_t i2g(void)
+{
+  int32_t l = 2;
+  l = 30000 - (int32_t)f();
+  return l > 10000; // f() - 20000 + INT_MIN > 10000
+}
+
+/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*?- _" 8 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -11.*\n.*>= -20" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -11.*\n.*>= -29" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -201.*\n.*>= -100" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -301.*\n.*>= -99" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -1001.*\n.*< -1999" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -1001.*\n.*< -3000" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -20001.*\n.*< -9999" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -30001.*\n.*< -10000" 1 "forwprop1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c
new file mode 100644
index 00000000000..ecafb41fa09
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c
@@ -0,0 +1,74 @@ 
+/* PR tree-optimization/116024 */
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-forwprop1-details" } */
+
+#include <stdint.h>
+#include <limits.h>
+
+uint32_t f(void);
+
+int32_t i2(void)
+{
+  uint32_t l = 2;
+  l = 10 - (uint32_t)f();
+  return l <= 20; // f() + 10 <= 20 
+}
+
+int32_t i2a(void)
+{
+  uint32_t l = 2;
+  l = 10 - (uint32_t)f();
+  return l < 30; // f() + 19 <= 29 
+}
+
+int32_t i2b(void)
+{
+  uint32_t l = 2;
+  l = 200 - (uint32_t)f();
+  return l <= 100; // f() - 100 <= 100 
+}
+
+int32_t i2c(void)
+{
+  uint32_t l = 2;
+  l = 300 - (uint32_t)f();
+  return l < 100; // f() - 201 <= 99 
+}
+
+int32_t i2d(void)
+{
+  uint32_t l = 2;
+  l = 1000 - (uint32_t)f();
+  return l >= 2000; // f() + 999 > 1999
+}
+
+int32_t i2e(void)
+{
+  uint32_t l = 2;
+  l = 1000 - (uint32_t)f();
+  return l > 3000; // f() + 2000 > 3000
+}
+
+int32_t i2f(void)
+{
+  uint32_t l = 2;
+  l = 20000 - (uint32_t)f();
+  return l >= 10000; // f() - 10001 > 9999
+}
+
+int32_t i2g(void)
+{
+  uint32_t l = 2;
+  l = 30000 - (uint32_t)f();
+  return l > 10000; // f() - 20000 > 10000
+}
+
+/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*?- _" 8 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 10.*\n.*<= 20" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 19.*\n.*<= 29" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 4294967196.*\n.*<= 100" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 4294967095.*\n.*<= 99" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 999.*\n.*> 1999" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 2000.*\n.*> 3000" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 4294957295.*\n.*> 9999" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 4294947296.*\n.*> 10000" 1 "forwprop1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c
new file mode 100644
index 00000000000..ff8f097e647
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c
@@ -0,0 +1,38 @@ 
+/* PR tree-optimization/116024 */
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-forwprop1-details -fwrapv" } */
+
+#include <stdint.h>
+#include <limits.h>
+
+uint32_t f(void);
+
+int32_t i3(void)
+{
+  int32_t l = -10 + (int32_t)f();
+  return l <= INT_MAX - 10; // f() > INT_MIN + 9
+}
+
+int32_t i3a(void)
+{
+  int32_t l = -21 + (int32_t)f();
+  return l < INT_MAX - 20; // f() > INT_MIN + 20
+}
+
+int32_t i3b(void)
+{
+  int32_t l = 30 + (int32_t)f();
+  return l >= INT_MIN + 30; // f() <= INT_MAX - 30
+}
+
+int32_t i3c(void)
+{
+  int32_t l = 40 + (int32_t)f();
+  return l > INT_MIN + 39; // f() <= INT_MAX - 39
+}
+
+/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*? \\+" 4 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* >= -2147483638" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* >= -2147483627" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 2147483618" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 2147483608" 1 "forwprop1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c
new file mode 100644
index 00000000000..f7269765fcb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c
@@ -0,0 +1,39 @@ 
+/* PR tree-optimization/116024 */
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-forwprop1-details" } */
+
+#include <stdint.h>
+#include <limits.h>
+
+uint32_t f(void);
+
+int32_t i3(void)
+{
+  uint32_t l = 2;
+  l = UINT_MAX - 9 + (uint32_t)f();
+  return l <= UINT_MAX - 10; // f() > 9
+}
+
+int32_t i3a(void)
+{
+  uint32_t l = UINT_MAX - 20 + (uint32_t)f();
+  return l < UINT_MAX - 20; // f() > 20
+}
+
+int32_t i3b(void)
+{
+  uint32_t l = 30 + (uint32_t)f();
+  return l >= 30; // f() < -30u
+}
+
+int32_t i3c(void)
+{
+  uint32_t l = 40 + (uint32_t)f();
+  return l > 39; // f() < -39u
+}
+
+/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*? \\+" 4 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 9" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 20" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 4294967265" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 4294967255" 1 "forwprop1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024.c b/gcc/testsuite/gcc.dg/tree-ssa/pr116024.c
new file mode 100644
index 00000000000..22b08057b2b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024.c
@@ -0,0 +1,74 @@ 
+/* PR tree-optimization/116024 */
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-forwprop1-details" } */
+
+#include <stdint.h>
+#include <limits.h>
+
+uint32_t f(void);
+
+int32_t i1(void)
+{
+  int32_t l = 2;
+  l = 10 - (int32_t)f();
+  return l <= 9; // f() > 0
+}
+
+int32_t i1a(void)
+{
+  int32_t l = 2;
+  l = 20 - (int32_t)f();
+  return l <= INT_MIN; // return 0
+}
+
+int32_t i1b(void)
+{
+  int32_t l = 2;
+  l = 30 - (int32_t)f();
+  return l <= INT_MIN + 31; // f() == INT_MAX
+}
+
+int32_t i1c(void)
+{
+  int32_t l = 2;
+  l = INT_MAX - 40 - (int32_t)f();
+  return l <= -38; // f() > INT_MAX - 3
+}
+
+int32_t i1d(void)
+{
+  int32_t l = 2;
+  l = INT_MAX - 50 - (int32_t)f();
+  return l <= INT_MAX - 1; // f() != -50
+}
+
+int32_t i1e(void)
+{
+  int32_t l = 2;
+  l = INT_MAX - 60 - (int32_t)f();
+  return l != INT_MAX - 90; // f() != 30
+}
+
+int32_t i1f(void)
+{
+  int32_t l = 2;
+  l = INT_MIN + 70 - (int32_t)f();
+  return l <= INT_MAX - 2; // return 0
+}
+
+int32_t i1g(void)
+{
+  int32_t l = 2;
+  l = INT_MAX/2 + 30 - (int32_t)f();
+  return l <= INT_MIN/2 - 30; // return 1
+}
+
+
+/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*?- _" 5 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "return 0" 2 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "return 1" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 0" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* == 2147483647" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 2147483644" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* != 4294967246" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* != 30" 1 "forwprop1" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c b/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c
index 81c536c90af..bfcec6719de 100644
--- a/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c
@@ -10,4 +10,4 @@  f1 (int x, int t)
   return t;
 }
 
-/* { dg-final { scan-assembler-times "cmn\\tw\[0-9\]+, #2" 1 } } */
+/* { dg-final { scan-assembler-times "cmn\\tw\[0-9\]+, #3" 1 } } */