The existing VEC_COND_EXPR min/max patterns require the comparison and result
constants to be equal so they do not recognize this canonicalized form.
Add patterns for the GE/LT forms produced from negative signed maximum
expressions and for the LE/GT forms produced from positive signed or unsigned
minimum expressions. Handle both normal and reversed conditional arms.
The off-by-one check is performed elementwise so the transform is not
restricted to uniform vector constants.
This also handles non-uniform vector constants such as:
X >= { -99, -98 } ? X : { -100, -99 }
which can be folded to:
MAX_EXPR <X, { -100, -99 }>
when each comparison element is exactly one greater than the corresponding
result element. Variable-length vector constants are handled when the two
constants have matching VECTOR_CST encodings.
gcc/ChangeLog:
PR tree-optimization/98602
* match.pd (vec_cond (cmp @0 VECTOR_CST@1) @0 VECTOR_CST@2):
New simplification.
(vec_cond (cmp @0 VECTOR_CST@1) VECTOR_CST@2 @0): Likewise.
gcc/testsuite/ChangeLog:
PR tree-optimization/98602
* g++.dg/tree-ssa/pr98602.C: New test.
* g++.target/aarch64/sve/max_1.C: Remove fixed XFAILs.
* g++.target/aarch64/sve/min_1.C: Likewise.
Signed-off-by: Naveen <[email protected]>
---
gcc/match.pd | 146 +++++++++++++++++++
gcc/testsuite/g++.dg/tree-ssa/pr98602.C | 37 +++++
gcc/testsuite/g++.target/aarch64/sve/max_1.C | 10 +-
gcc/testsuite/g++.target/aarch64/sve/min_1.C | 44 +++---
4 files changed, 210 insertions(+), 27 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/tree-ssa/pr98602.C
diff --git a/gcc/match.pd b/gcc/match.pd
index 017b1362b7e..1795963b0c6 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -6822,6 +6822,152 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
&& target_supports_op_p (type, MINMAX, optab_vector))
(minmax @0 @1))))
+/* Comparison canonicalization changes X > C to X >= C + 1 for
+ negative C and X < C to X <= C - 1 for positive C. */
+(for cmp (ge le)
+ minmax (max min)
+ MINMAX (MAX_EXPR MIN_EXPR)
+ (simplify
+ (vec_cond (cmp @0 VECTOR_CST@1) @0 VECTOR_CST@2)
+ (with
+ {
+ bool ok = true;
+
+ if (!known_eq (VECTOR_CST_NELTS (@1), VECTOR_CST_NELTS (@2)))
+ ok = false;
+ else
+ {
+ unsigned HOST_WIDE_INT nelts;
+ if (VECTOR_CST_NELTS (@1).is_constant (&nelts))
+ {
+ for (unsigned HOST_WIDE_INT i = 0; i < nelts; ++i)
+ {
+ tree cmp_cst = vector_cst_elt (@1, i);
+ tree res_cst = vector_cst_elt (@2, i);
+ if (TREE_CODE (cmp_cst) != INTEGER_CST
+ || TREE_CODE (res_cst) != INTEGER_CST
+ || tree_int_cst_sgn (res_cst)
+ != (minmax == MAX_EXPR ? -1 : 1)
+ || !wi::eq_p (wi::to_wide (cmp_cst),
+ wi::to_wide (res_cst)
+ + (minmax == MAX_EXPR ? 1 : -1)))
+ {
+ ok = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* For variable-length vectors, handle matching encodings and
+ check the encoded elements. */
+ if (VECTOR_CST_LOG2_NPATTERNS (@1)
+ != VECTOR_CST_LOG2_NPATTERNS (@2)
+ || VECTOR_CST_NELTS_PER_PATTERN (@1)
+ != VECTOR_CST_NELTS_PER_PATTERN (@2)
+ || vector_cst_encoded_nelts (@1)
+ != vector_cst_encoded_nelts (@2))
+ ok = false;
+ else
+ {
+ unsigned int encoded_nelts = vector_cst_encoded_nelts (@1);
+ for (unsigned int i = 0; i < encoded_nelts; ++i)
+ {
+ tree cmp_cst = VECTOR_CST_ENCODED_ELT (@1, i);
+ tree res_cst = VECTOR_CST_ENCODED_ELT (@2, i);
+ if (TREE_CODE (cmp_cst) != INTEGER_CST
+ || TREE_CODE (res_cst) != INTEGER_CST
+ || tree_int_cst_sgn (res_cst)
+ != (minmax == MAX_EXPR ? -1 : 1)
+ || !wi::eq_p (wi::to_wide (cmp_cst),
+ wi::to_wide (res_cst)
+ + (minmax == MAX_EXPR ? 1 : -1)))
+ {
+ ok = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ (if (VECTOR_INTEGER_TYPE_P (type)
+ && target_supports_op_p (type, MINMAX, optab_vector)
+ && ok)
+ (minmax @0 @2)))))
+
+(for cmp (lt gt)
+ minmax (max min)
+ MINMAX (MAX_EXPR MIN_EXPR)
+ (simplify
+ (vec_cond (cmp @0 VECTOR_CST@1) VECTOR_CST@2 @0)
+ (with
+ {
+ bool ok = true;
+
+ if (!known_eq (VECTOR_CST_NELTS (@1), VECTOR_CST_NELTS (@2)))
+ ok = false;
+ else
+ {
+ unsigned HOST_WIDE_INT nelts;
+ if (VECTOR_CST_NELTS (@1).is_constant (&nelts))
+ {
+ for (unsigned HOST_WIDE_INT i = 0; i < nelts; ++i)
+ {
+ tree cmp_cst = vector_cst_elt (@1, i);
+ tree res_cst = vector_cst_elt (@2, i);
+ if (TREE_CODE (cmp_cst) != INTEGER_CST
+ || TREE_CODE (res_cst) != INTEGER_CST
+ || tree_int_cst_sgn (res_cst)
+ != (minmax == MAX_EXPR ? -1 : 1)
+ || !wi::eq_p (wi::to_wide (cmp_cst),
+ wi::to_wide (res_cst)
+ + (minmax == MAX_EXPR ? 1 : -1)))
+ {
+ ok = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* For variable-length vectors, handle matching encodings and
+ check the encoded elements. */
+ if (VECTOR_CST_LOG2_NPATTERNS (@1)
+ != VECTOR_CST_LOG2_NPATTERNS (@2)
+ || VECTOR_CST_NELTS_PER_PATTERN (@1)
+ != VECTOR_CST_NELTS_PER_PATTERN (@2)
+ || vector_cst_encoded_nelts (@1)
+ != vector_cst_encoded_nelts (@2))
+ ok = false;
+ else
+ {
+ unsigned int encoded_nelts = vector_cst_encoded_nelts (@1);
+ for (unsigned int i = 0; i < encoded_nelts; ++i)
+ {
+ tree cmp_cst = VECTOR_CST_ENCODED_ELT (@1, i);
+ tree res_cst = VECTOR_CST_ENCODED_ELT (@2, i);
+ if (TREE_CODE (cmp_cst) != INTEGER_CST
+ || TREE_CODE (res_cst) != INTEGER_CST
+ || tree_int_cst_sgn (res_cst)
+ != (minmax == MAX_EXPR ? -1 : 1)
+ || !wi::eq_p (wi::to_wide (cmp_cst),
+ wi::to_wide (res_cst)
+ + (minmax == MAX_EXPR ? 1 : -1)))
+ {
+ ok = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ (if (VECTOR_INTEGER_TYPE_P (type)
+ && target_supports_op_p (type, MINMAX, optab_vector)
+ && ok)
+ (minmax @0 @2)))))
+
/* Try to optimize x < 0 ? -1 : 0 into (signed) x >> 31
and x < 0 ? 1 : 0 into (unsigned) x >> 31. */
(simplify
diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr98602.C
b/gcc/testsuite/g++.dg/tree-ssa/pr98602.C
new file mode 100644
index 00000000000..6a4a421747a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/pr98602.C
@@ -0,0 +1,37 @@
+/* { dg-do compile { target aarch64*-*-* } } */
+/* { dg-options "-O1 -fdump-tree-forwprop1-raw -Wno-psabi" } */
+
+typedef int v4si __attribute__ ((vector_size (16)));
+typedef unsigned int v4ui __attribute__ ((vector_size (16)));
+
+v4si
+smax_nonuniform (v4si x)
+{
+ return x >= (v4si) { -99, -98, -97, -96 }
+ ? x : (v4si) { -100, -99, -98, -97 };
+}
+
+v4si
+smax_nonuniform_rev (v4si x)
+{
+ return x < (v4si) { -99, -98, -97, -96 }
+ ? (v4si) { -100, -99, -98, -97 } : x;
+}
+
+v4si
+smin_nonuniform (v4si x)
+{
+ return x <= (v4si) { 99, 100, 101, 102 }
+ ? x : (v4si) { 100, 101, 102, 103 };
+}
+
+v4ui
+umin_nonuniform_rev (v4ui x)
+{
+ return x > (v4ui) { 1, 2, 3, 4 }
+ ? (v4ui) { 2, 3, 4, 5 } : x;
+}
+
+/* { dg-final { scan-tree-dump-times "max_expr, " 2 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "min_expr, " 2 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-not "vec_cond_expr, " "forwprop1" } } */
diff --git a/gcc/testsuite/g++.target/aarch64/sve/max_1.C
b/gcc/testsuite/g++.target/aarch64/sve/max_1.C
index caf9d7cd9bb..6328d5a1b38 100644
--- a/gcc/testsuite/g++.target/aarch64/sve/max_1.C
+++ b/gcc/testsuite/g++.target/aarch64/sve/max_1.C
@@ -39,11 +39,11 @@ TEST_TYPE (uint32_t, 128, 7, 255)
/* { dg-final { scan-assembler-times {\tumax\tz[0-9]+\.h, p[0-7]/m,
z[0-9]+\.h, z[0-9]+\.h\n} 2 } } */
/* { dg-final { scan-assembler-times {\tumax\tz[0-9]+\.s, p[0-7]/m,
z[0-9]+\.s, z[0-9]+\.s\n} 1 } } */
-/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.b, z[0-9]+\.b, #-100\n}
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.b, z[0-9]+\.b, #-110\n}
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.b, z[0-9]+\.b, #-120\n}
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.h, z[0-9]+\.h, #-128\n}
2 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.s, z[0-9]+\.s, #-128\n}
1 { xfail *-*-* } } } */
+/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.b, z[0-9]+\.b, #-100\n}
1 } } */
+/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.b, z[0-9]+\.b, #-110\n}
1 } } */
+/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.b, z[0-9]+\.b, #-120\n}
1 } } */
+/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.h, z[0-9]+\.h, #-128\n}
2 } } */
+/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.s, z[0-9]+\.s, #-128\n}
1 } } */
/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.b, z[0-9]+\.b, #50\n} 3
} } */
/* { dg-final { scan-assembler-times {\tsmax\tz[0-9]+\.h, z[0-9]+\.h, #50\n} 2
} } */
diff --git a/gcc/testsuite/g++.target/aarch64/sve/min_1.C
b/gcc/testsuite/g++.target/aarch64/sve/min_1.C
index 9c84690cd1b..172fa6a9da5 100644
--- a/gcc/testsuite/g++.target/aarch64/sve/min_1.C
+++ b/gcc/testsuite/g++.target/aarch64/sve/min_1.C
@@ -45,29 +45,29 @@ TEST_TYPE (uint32_t, 128, 7, 255)
/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.h, z[0-9]+\.h, #-128\n}
2 } } */
/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.s, z[0-9]+\.s, #-128\n}
1 } } */
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #50\n} 3
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.h, z[0-9]+\.h, #50\n} 2
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.s, z[0-9]+\.s, #50\n} 1
{ xfail *-*-* } } } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #50\n} 3
} } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.h, z[0-9]+\.h, #50\n} 2
} } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.s, z[0-9]+\.s, #50\n} 1
} } */
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #100\n}
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #110\n}
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #120\n}
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.h, z[0-9]+\.h, #127\n}
2 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.s, z[0-9]+\.s, #127\n}
1 { xfail *-*-* } } } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #100\n}
1 } } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #110\n}
1 } } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.b, z[0-9]+\.b, #120\n}
1 } } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.h, z[0-9]+\.h, #127\n}
2 } } */
+/* { dg-final { scan-assembler-times {\tsmin\tz[0-9]+\.s, z[0-9]+\.s, #127\n}
1 } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #2\n} 1
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #3\n} 1
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #4\n} 1
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #5\n} 1
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #6\n} 1
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.s, z[0-9]+\.s, #7\n} 1
{ xfail *-*-* } } } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #2\n} 1
} } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #3\n} 1
} } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #4\n} 1
} } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #5\n} 1
} } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #6\n} 1
} } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.s, z[0-9]+\.s, #7\n} 1
} } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #50\n} 3
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #50\n} 2
{ xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.s, z[0-9]+\.s, #50\n} 1
{ xfail *-*-* } } } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #50\n} 3
} } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #50\n} 2
} } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.s, z[0-9]+\.s, #50\n} 1
} } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #250\n}
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #251\n}
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #253\n}
1 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #255\n}
2 { xfail *-*-* } } } */
-/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.s, z[0-9]+\.s, #255\n}
1 { xfail *-*-* } } } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #250\n}
1 } } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #251\n}
1 } } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.b, z[0-9]+\.b, #253\n}
1 } } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.h, z[0-9]+\.h, #255\n}
2 } } */
+/* { dg-final { scan-assembler-times {\tumin\tz[0-9]+\.s, z[0-9]+\.s, #255\n}
1 } } */
--
2.34.1