Re: [PATCH] tree-optimization: Simplify ((X CMP1 Y) - (X CMP2 Y)) REL 0

Andrea Pinski <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <CALvbMcDo-sitGQU8jHJ7UhG+VXSGJFwyuT2_FGea5pN0MdFmfg@mail.gmail.com>
On Mon, Jul 27, 2026 at 2:12 AM Tudor-Stefan Magirescu
<[email protected]> wrote:
>
> Add a new match.pd pattern to fold sign-of-difference expressions into
> a single comparison.  The pattern matches differences of two comparisons
> of the same operands, where the comparison pair is one of {gt,lt} or
> {ge,le}.
>
> The pattern cannot be merged into the generic (X - Y) CMP 0 rule
> above it, which is restricted to eq/ne.  Extending that rule to
> lt/le/gt/ge would defeat ABS_EXPR synthesis in
> fold_cond_expr_with_comparison for expressions of the form
> (X - Y) < 0 ? (Y - X) : (X - Y).  Matching the full comparison
> structure avoids this regression.
>
> Bootstrapped and tested on x86_64-linux-gnu.
>
> gcc/ChangeLog:
>
>         * match.pd (((X CMP1 Y) - (X CMP2 Y)) REL 0): New pattern.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.c-torture/execute/subcmp-1.c: New test.
>         * gcc.dg/tree-ssa/subcmp-1.c: New test.
>
> Signed-off-by: Tudor-Stefan Magirescu <[email protected]>
> ---
>  gcc/match.pd                                  |  34 +++++
>  .../gcc.c-torture/execute/subcmp-1.c          |  55 ++++++++
>  gcc/testsuite/gcc.dg/tree-ssa/subcmp-1.c      | 131 ++++++++++++++++++
>  3 files changed, 220 insertions(+)
>  create mode 100644 gcc/testsuite/gcc.c-torture/execute/subcmp-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/subcmp-1.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index cf03333fdc6..a6ae87fb27d 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -7314,6 +7314,40 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>     (if (single_use (@2))
>      (cmp @0 @1)))))
>
> +/* Fold ((X CMP1 Y) - (X CMP2 Y)) REL 0 into a single comparison of X and Y,
> +   e.g., ((X > Y) - (X < Y)) < 0 to X < Y.  This cannot be merged into the
> +   generic X - Y CMP 0 rule above as it would prevent ABS_EXPR synthesis in
> +   fold_cond_expr_with_comparison.  */
> +(for rel (lt le gt ge)
> + (simplify
> +  (rel (minus@2 (convert? (gt @0 @1)) (convert? (lt @0 @1))) integer_zerop)
> +   (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
> +       && !TYPE_UNSIGNED (TREE_TYPE (@2))
> +       && TYPE_PRECISION (TREE_TYPE (@2)) > 1
> +       && single_use (@2))
> +       (rel @0 @1)))
> + (simplify
> +  (rel (minus@2 (convert? (ge @0 @1)) (convert? (le @0 @1))) integer_zerop)
> +   (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
> +       && !TYPE_UNSIGNED (TREE_TYPE (@2))
> +       && TYPE_PRECISION (TREE_TYPE (@2)) > 1
> +       && single_use (@2))
> +       (rel @0 @1)))
> + (simplify
> +  (rel (minus@2 (convert? (lt @0 @1)) (convert? (gt @0 @1))) integer_zerop)
> +   (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
> +       && !TYPE_UNSIGNED (TREE_TYPE (@2))
> +       && TYPE_PRECISION (TREE_TYPE (@2)) > 1
> +       && single_use (@2))
> +       (rel @1 @0)))
> + (simplify
> +  (rel (minus@2 (convert? (le @0 @1)) (convert? (ge @0 @1))) integer_zerop)
> +   (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
> +       && !TYPE_UNSIGNED (TREE_TYPE (@2))
> +       && TYPE_PRECISION (TREE_TYPE (@2)) > 1
> +       && single_use (@2))
> +       (rel @1 @0))))

I think it would be a good idea to combine this into one pattern using
the for loop feature better.
Something like:
(for rel (lt le gt ge)
 (for cmp1 (gt ge)
        cmp2 (lt le)
  (rel (minus@2 (convert? (cmp1:c @0 @1)) (convert? (cmp2:c @0 @1)))
integer_zerop)

I am not sure we need/want the single use here. Yes in theory it could
extend @0/@1 lifetime, but the expression is much simpler and will be
folded into a conditional.
Note the `:c` here for gt/ge will match lt/le with swapped operands.
that is (lt @1 @0) will match for (gt @0 @1) (likewise for ge).

Thanks,
Andrea

> +
>  /* Simplify (x < 0) ^ (y < 0) to (x ^ y) < 0 and
>     (x >= 0) ^ (y >= 0) to (x ^ y) < 0.  */
>  (for cmp (lt ge)
> diff --git a/gcc/testsuite/gcc.c-torture/execute/subcmp-1.c b/gcc/testsuite/gcc.c-torture/execute/subcmp-1.c
> new file mode 100644
> index 00000000000..4682246c355
> --- /dev/null
> +++ b/gcc/testsuite/gcc.c-torture/execute/subcmp-1.c
> @@ -0,0 +1,55 @@
> +#define func(vol, op1, op2, op3)       \
> +_Bool op1##_##op2##_##op3##_##vol (int a, int b)       \
> +{                                      \
> + vol _Bool x = op_##op1(a, b);         \
> + vol _Bool y = op_##op2(a, b);         \
> + return op_##op3(x - y, 0);            \
> +}
> +
> +#define op_lt(a, b) ((a) < (b))
> +#define op_le(a, b) ((a) <= (b))
> +#define op_gt(a, b) ((a) > (b))
> +#define op_ge(a, b) ((a) >= (b))
> +
> +#define funcs(a) \
> + a(gt,lt,lt) \
> + a(gt,lt,le) \
> + a(gt,lt,gt) \
> + a(gt,lt,ge) \
> +  \
> + a(ge,le,lt) \
> + a(ge,le,le) \
> + a(ge,le,gt) \
> + a(ge,le,ge) \
> +  \
> + a(lt,gt,lt) \
> + a(lt,gt,le) \
> + a(lt,gt,gt) \
> + a(lt,gt,ge) \
> +  \
> + a(le,ge,lt) \
> + a(le,ge,le) \
> + a(le,ge,gt) \
> + a(le,ge,ge) \
> +
> +#define funcs1(a,b,c) \
> +func(,a,b,c) \
> +func(volatile,a,b,c)
> +
> +funcs(funcs1)
> +
> +#define test(op1,op2,op3)                      \
> +do {                                           \
> +  if (op1##_##op2##_##op3##_(x,y)              \
> +      != op1##_##op2##_##op3##_volatile(x,y))  \
> +    __builtin_abort();                         \
> +} while(0);
> +
> +int main()
> +{
> +  for(int x = -10; x < 10; x++)
> +    for(int y = -10; y < 10; y++)
> +    {
> +        funcs(test)
> +    }
> +}
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/subcmp-1.c b/gcc/testsuite/gcc.dg/tree-ssa/subcmp-1.c
> new file mode 100644
> index 00000000000..489cf4c1f40
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/subcmp-1.c
> @@ -0,0 +1,131 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +_Bool subgtlt_lt(int a00, int b00)
> +{
> +  _Bool x00 = a00 > b00;
> +  _Bool y00 = a00 < b00;
> +  return x00 - y00 < 0;
> +}
> +/* { dg-final { scan-tree-dump "a00_\[0-9\]+.D. < b00_\[0-9\]+.D.|b00_\[0-9\]+.D. > a00_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool subgtlt_le(int a01, int b01)
> +{
> +  _Bool x01 = a01 > b01;
> +  _Bool y01 = a01 < b01;
> +  return x01 - y01 <= 0;
> +}
> +/* { dg-final { scan-tree-dump "a01_\[0-9\]+.D. <= b01_\[0-9\]+.D.|b01_\[0-9\]+.D. >= a01_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool subgtlt_gt(int a02, int b02)
> +{
> +  _Bool x02 = a02 > b02;
> +  _Bool y02 = a02 < b02;
> +  return x02 - y02 > 0;
> +}
> +/* { dg-final { scan-tree-dump "a02_\[0-9\]+.D. > b02_\[0-9\]+.D.|b02_\[0-9\]+.D. < a02_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool subgtlt_ge(int a03, int b03)
> +{
> +  _Bool x03 = a03 > b03;
> +  _Bool y03 = a03 < b03;
> +  return x03 - y03 >= 0;
> +}
> +/* { dg-final { scan-tree-dump "a03_\[0-9\]+.D. >= b03_\[0-9\]+.D.|b03_\[0-9\]+.D. <= a03_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool subgele_lt(int a04, int b04)
> +{
> +  _Bool x04 = a04 >= b04;
> +  _Bool y04 = a04 <= b04;
> +  return x04 - y04 < 0;
> +}
> +/* { dg-final { scan-tree-dump "a04_\[0-9\]+.D. < b04_\[0-9\]+.D.|b04_\[0-9\]+.D. > a04_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool subgele_le(int a05, int b05)
> +{
> +  _Bool x05 = a05 >= b05;
> +  _Bool y05 = a05 <= b05;
> +  return x05 - y05 <= 0;
> +}
> +/* { dg-final { scan-tree-dump "a05_\[0-9\]+.D. <= b05_\[0-9\]+.D.|b05_\[0-9\]+.D. >= a05_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool subgele_gt(int a06, int b06)
> +{
> +  _Bool x06 = a06 >= b06;
> +  _Bool y06 = a06 <= b06;
> +  return x06 - y06 > 0;
> +}
> +/* { dg-final { scan-tree-dump "a06_\[0-9\]+.D. > b06_\[0-9\]+.D.|b06_\[0-9\]+.D. < a06_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool subgele_ge(int a07, int b07)
> +{
> +  _Bool x07 = a07 >= b07;
> +  _Bool y07 = a07 <= b07;
> +  return x07 - y07 >= 0;
> +}
> +/* { dg-final { scan-tree-dump "a07_\[0-9\]+.D. >= b07_\[0-9\]+.D.|b07_\[0-9\]+.D. <= a07_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool subltgt_lt(int a08, int b08)
> +{
> +  _Bool x08 = a08 < b08;
> +  _Bool y08 = a08 > b08;
> +  return x08 - y08 < 0;
> +}
> +/* { dg-final { scan-tree-dump "a08_\[0-9\]+.D. > b08_\[0-9\]+.D.|b08_\[0-9\]+.D. < a08_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool subltgt_le(int a09, int b09)
> +{
> +  _Bool x09 = a09 < b09;
> +  _Bool y09 = a09 > b09;
> +  return x09 - y09 <= 0;
> +}
> +/* { dg-final { scan-tree-dump "a09_\[0-9\]+.D. >= b09_\[0-9\]+.D.|b09_\[0-9\]+.D. <= a09_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool subltgt_gt(int a10, int b10)
> +{
> +  _Bool x10 = a10 < b10;
> +  _Bool y10 = a10 > b10;
> +  return x10 - y10 > 0;
> +}
> +/* { dg-final { scan-tree-dump "a10_\[0-9\]+.D. < b10_\[0-9\]+.D.|b10_\[0-9\]+.D. > a10_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool subltgt_ge(int a11, int b11)
> +{
> +  _Bool x11 = a11 < b11;
> +  _Bool y11 = a11 > b11;
> +  return x11 - y11 >= 0;
> +}
> +/* { dg-final { scan-tree-dump "a11_\[0-9\]+.D. <= b11_\[0-9\]+.D.|b11_\[0-9\]+.D. >= a11_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool sublege_lt(int a12, int b12)
> +{
> +  _Bool x12 = a12 <= b12;
> +  _Bool y12 = a12 >= b12;
> +  return x12 - y12 < 0;
> +}
> +/* { dg-final { scan-tree-dump "a12_\[0-9\]+.D. > b12_\[0-9\]+.D.|b12_\[0-9\]+.D. < a12_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool sublege_le(int a13, int b13)
> +{
> +  _Bool x13 = a13 <= b13;
> +  _Bool y13 = a13 >= b13;
> +  return x13 - y13 <= 0;
> +}
> +/* { dg-final { scan-tree-dump "a13_\[0-9\]+.D. >= b13_\[0-9\]+.D.|b13_\[0-9\]+.D. <= a13_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool sublege_gt(int a14, int b14)
> +{
> +  _Bool x14 = a14 <= b14;
> +  _Bool y14 = a14 >= b14;
> +  return x14 - y14 > 0;
> +}
> +/* { dg-final { scan-tree-dump "a14_\[0-9\]+.D. < b14_\[0-9\]+.D.|b14_\[0-9\]+.D. > a14_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool sublege_ge(int a15, int b15)
> +{
> +  _Bool x15 = a15 <= b15;
> +  _Bool y15 = a15 >= b15;
> +  return x15 - y15 >= 0;
> +}
> +/* { dg-final { scan-tree-dump "a15_\[0-9\]+.D. <= b15_\[0-9\]+.D.|b15_\[0-9\]+.D. >= a15_\[0-9\]+.D." "optimized" } } */
> +/* { dg-final { scan-tree-dump-not "_\[0-9\]+ - _\[0-9\]+" "optimized" } } */
> --
> 2.43.0
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.