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
>

Reply via email to