[v4] tree-optimization/94899: Remove "+ 0x80000000" in int comparisons
Commit Message
Expressions of the form "X + CST < Y + CST" where:
* CST is an unsigned integer constant with only the MSB set, and
* X and Y's types have integer conversion ranks <= CST's
can be simplified to "(signed) X < (signed) Y".
This is because, assuming a 32-bit signed numbers,
(unsigned) INT_MIN + 0x80000000 is 0, and
(unsigned) INT_MAX + 0x80000000 is UINT_MAX.
i.e. the result increases monotonically with signed input.
This means:
((signed) X < (signed) Y) iff (X + 0x80000000 < Y + 0x80000000)
gcc/
* match.pd (X + C < Y + C -> (signed) X < (signed) Y, if C is
0x80000000): New simplification.
gcc/testsuite/
* gcc.dg/pr94899.c: New test.
---
gcc/match.pd | 13 +++++++++
gcc/testsuite/gcc.dg/pr94899.c | 49 ++++++++++++++++++++++++++++++++++
2 files changed, 62 insertions(+)
create mode 100644 gcc/testsuite/gcc.dg/pr94899.c
---
v3: https://gcc.gnu.org/pipermail/gcc-patches/2022-June/596785.html
Notes on v4, based on Richard and Jakub's review comments:
Richard wrote:
> It might be possible to test for zero + or - operations instead?
OK. That seems more fool-proof. I've made the change.
Jakub wrote:
> Can't one just omit the INTEGER_CST part on the second @0?
I hadn't thought of that. Done!
> As a follow-up, it might be useful to make it work for vector integral types
> too,
> typedef unsigned V __attribute__((vector_size (4 * sizeof (int))));
> #define M __INT_MAX__ + 1U
> V foo (V x, V y)
> {
> return x + (V) { M, M, M, M } < y + (V) { M, M, M, M };
> }
> using uniform_integer_cst_p.
OK. This syntax is unfamiliar to me. I'll read a bit and then try to work on
a follow-up. Thanks!
Comments
On Mon, Jun 20, 2022 at 4:23 PM Arjun Shankar <arjun@redhat.com> wrote:
>
> Expressions of the form "X + CST < Y + CST" where:
>
> * CST is an unsigned integer constant with only the MSB set, and
> * X and Y's types have integer conversion ranks <= CST's
>
> can be simplified to "(signed) X < (signed) Y".
>
> This is because, assuming a 32-bit signed numbers,
> (unsigned) INT_MIN + 0x80000000 is 0, and
> (unsigned) INT_MAX + 0x80000000 is UINT_MAX.
>
> i.e. the result increases monotonically with signed input.
>
> This means:
> ((signed) X < (signed) Y) iff (X + 0x80000000 < Y + 0x80000000)
>
> gcc/
> * match.pd (X + C < Y + C -> (signed) X < (signed) Y, if C is
> 0x80000000): New simplification.
> gcc/testsuite/
> * gcc.dg/pr94899.c: New test.
> ---
> gcc/match.pd | 13 +++++++++
> gcc/testsuite/gcc.dg/pr94899.c | 49 ++++++++++++++++++++++++++++++++++
> 2 files changed, 62 insertions(+)
> create mode 100644 gcc/testsuite/gcc.dg/pr94899.c
> ---
> v3: https://gcc.gnu.org/pipermail/gcc-patches/2022-June/596785.html
>
> Notes on v4, based on Richard and Jakub's review comments:
>
> Richard wrote:
>
> > It might be possible to test for zero + or - operations instead?
>
> OK. That seems more fool-proof. I've made the change.
>
> Jakub wrote:
>
> > Can't one just omit the INTEGER_CST part on the second @0?
>
> I hadn't thought of that. Done!
>
> > As a follow-up, it might be useful to make it work for vector integral types
> > too,
> > typedef unsigned V __attribute__((vector_size (4 * sizeof (int))));
> > #define M __INT_MAX__ + 1U
> > V foo (V x, V y)
> > {
> > return x + (V) { M, M, M, M } < y + (V) { M, M, M, M };
> > }
> > using uniform_integer_cst_p.
>
> OK. This syntax is unfamiliar to me. I'll read a bit and then try to work on
> a follow-up. Thanks!
This variant is OK. Let's do the vector case as followup.
Richard.
> diff --git a/gcc/match.pd b/gcc/match.pd
> index a63b649841b..4a570894b2e 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -2089,6 +2089,19 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> (if (ANY_INTEGRAL_TYPE_P (TREE_TYPE (@0))
> && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0)))
> (op @0 @1))))
> +
> +/* As a special case, X + C < Y + C is the same as (signed) X < (signed) Y
> + when C is an unsigned integer constant with only the MSB set, and X and
> + Y have types of equal or lower integer conversion rank than C's. */
> +(for op (lt le ge gt)
> + (simplify
> + (op (plus @1 INTEGER_CST@0) (plus @2 @0))
> + (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
> + && TYPE_UNSIGNED (TREE_TYPE (@0))
> + && wi::only_sign_bit_p (wi::to_wide (@0)))
> + (with { tree stype = signed_type_for (TREE_TYPE (@0)); }
> + (op (convert:stype @1) (convert:stype @2))))))
> +
> /* For equality and subtraction, this is also true with wrapping overflow. */
> (for op (eq ne minus)
> (simplify
> diff --git a/gcc/testsuite/gcc.dg/pr94899.c b/gcc/testsuite/gcc.dg/pr94899.c
> new file mode 100644
> index 00000000000..2fc7009a2e7
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/pr94899.c
> @@ -0,0 +1,49 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +typedef __INT16_TYPE__ int16_t;
> +typedef __INT32_TYPE__ int32_t;
> +typedef __UINT16_TYPE__ uint16_t;
> +typedef __UINT32_TYPE__ uint32_t;
> +
> +#define MAGIC (~ (uint32_t) 0 / 2 + 1)
> +
> +int
> +f_i16_i16 (int16_t x, int16_t y)
> +{
> + return x + MAGIC < y + MAGIC;
> +}
> +
> +int
> +f_i16_i32 (int16_t x, int32_t y)
> +{
> + return x + MAGIC < y + MAGIC;
> +}
> +
> +int
> +f_i32_i32 (int32_t x, int32_t y)
> +{
> + return x + MAGIC < y + MAGIC;
> +}
> +
> +int
> +f_u32_i32 (uint32_t x, int32_t y)
> +{
> + return x + MAGIC < y + MAGIC;
> +}
> +
> +int
> +f_u32_u32 (uint32_t x, uint32_t y)
> +{
> + return x + MAGIC < y + MAGIC;
> +}
> +
> +int
> +f_i32_i32_sub (int32_t x, int32_t y)
> +{
> + return x - MAGIC < y - MAGIC;
> +}
> +
> +/* The addition/subtraction of constants should be optimized away. */
> +/* { dg-final { scan-tree-dump-not "\\+" "optimized"} } */
> +/* { dg-final { scan-tree-dump-not "\\-" "optimized"} } */
> --
> 2.35.3
>
@@ -2089,6 +2089,19 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
(if (ANY_INTEGRAL_TYPE_P (TREE_TYPE (@0))
&& TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0)))
(op @0 @1))))
+
+/* As a special case, X + C < Y + C is the same as (signed) X < (signed) Y
+ when C is an unsigned integer constant with only the MSB set, and X and
+ Y have types of equal or lower integer conversion rank than C's. */
+(for op (lt le ge gt)
+ (simplify
+ (op (plus @1 INTEGER_CST@0) (plus @2 @0))
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
+ && TYPE_UNSIGNED (TREE_TYPE (@0))
+ && wi::only_sign_bit_p (wi::to_wide (@0)))
+ (with { tree stype = signed_type_for (TREE_TYPE (@0)); }
+ (op (convert:stype @1) (convert:stype @2))))))
+
/* For equality and subtraction, this is also true with wrapping overflow. */
(for op (eq ne minus)
(simplify
new file mode 100644
@@ -0,0 +1,49 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+typedef __INT16_TYPE__ int16_t;
+typedef __INT32_TYPE__ int32_t;
+typedef __UINT16_TYPE__ uint16_t;
+typedef __UINT32_TYPE__ uint32_t;
+
+#define MAGIC (~ (uint32_t) 0 / 2 + 1)
+
+int
+f_i16_i16 (int16_t x, int16_t y)
+{
+ return x + MAGIC < y + MAGIC;
+}
+
+int
+f_i16_i32 (int16_t x, int32_t y)
+{
+ return x + MAGIC < y + MAGIC;
+}
+
+int
+f_i32_i32 (int32_t x, int32_t y)
+{
+ return x + MAGIC < y + MAGIC;
+}
+
+int
+f_u32_i32 (uint32_t x, int32_t y)
+{
+ return x + MAGIC < y + MAGIC;
+}
+
+int
+f_u32_u32 (uint32_t x, uint32_t y)
+{
+ return x + MAGIC < y + MAGIC;
+}
+
+int
+f_i32_i32_sub (int32_t x, int32_t y)
+{
+ return x - MAGIC < y - MAGIC;
+}
+
+/* The addition/subtraction of constants should be optimized away. */
+/* { dg-final { scan-tree-dump-not "\\+" "optimized"} } */
+/* { dg-final { scan-tree-dump-not "\\-" "optimized"} } */