On Tue, Aug 4, 2026 at 3:00 AM <[email protected]> wrote:
>
> From: Kyrylo Tkachov <[email protected]>
>
> The rule folding (type) minmax ((wide_type) a, (wide_type) b) to
> minmax (a, b) is restricted to scalars. Extension is monotone, so it
> commutes with the comparison and the outer truncation is exact, and the
> argument is lanewise, so it holds for vectors unchanged.
>
> The vector case additionally needs the narrow operation to be available,
> otherwise vector lowering would scalarise what used to be a single wide
> instruction.
>
> typedef int v2si __attribute__((vector_size (8)));
> typedef long v2di __attribute__((vector_size (16)));
> v2si h (v2si a, v2si b)
> {
> v2di x = __builtin_convertvector (a, v2di);
> v2di y = __builtin_convertvector (b, v2di);
> return __builtin_convertvector (x < y ? x : y, v2si);
> }
>
> aarch64 -O3 before:
>
> sshll v0.2d, v0.2s, 0
> sshll v1.2d, v1.2s, 0
> cmgt v2.2d, v1.2d, v0.2d
> bsl v2.16b, v0.16b, v1.16b
> xtn v0.2s, v2.2d
>
> after:
>
> smin v0.2s, v0.2s, v1.2s
>
> Require the wide vector MIN or MAX to have one use. A shared wide
> result would otherwise gain a second narrow operation.
>
> Bootstrapped and tested on aarch64-none-linux-gnu.
> Ok for trunk?
> Thanks,
> Kyrill
>
> gcc/ChangeLog:
>
> * match.pd ((type) minmax ((wide_type) a, (wide_type) b)): Accept
> vector types, using element_precision and requiring the narrow
> operation to be supported.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/tree-ssa/vec-narrow-1.C: New test.
> * g++.dg/tree-ssa/vec-narrow-minmax-2.C: New test.
>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
> gcc/match.pd | 20 +++++++++++++------
> gcc/testsuite/g++.dg/tree-ssa/vec-narrow-1.C | 11 ++++++++++
> .../g++.dg/tree-ssa/vec-narrow-minmax-2.C | 18 +++++++++++++++++
> 3 files changed, 43 insertions(+), 6 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/tree-ssa/vec-narrow-1.C
> create mode 100644 gcc/testsuite/g++.dg/tree-ssa/vec-narrow-minmax-2.C
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index e0ec2626842..adbdda91b51 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -4649,17 +4649,25 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>
> /* (type) minmax ((wide_type) a, (wide_type) b) -> minmax (a, b)
> when type matches the type of a and b, and wide_type is a wider
> - type with the same signedness as type. */
> + type with the same signedness as type. Extension is monotone, so it
> + commutes with the comparison, and the truncation is then exact. The
> + same holds lanewise for a vector, where the narrow operation has to be
> + available so that lowering does not scalarise it. The wide operation
> + must become dead before a narrow vector operation is introduced. */
> (for minmax (min max)
> + MINMAX (MIN_EXPR MAX_EXPR)
> (simplify
> - (convert (minmax:c (convert@2 @0) (convert@3 @1)))
> - (if (INTEGRAL_TYPE_P (type)
> - && INTEGRAL_TYPE_P (TREE_TYPE (@2))
> + (convert (minmax:c@4 (convert@2 @0) (convert@3 @1)))
> + (if (ANY_INTEGRAL_TYPE_P (type)
> + && ANY_INTEGRAL_TYPE_P (TREE_TYPE (@2))
> && types_match (type, TREE_TYPE (@0))
> && types_match (type, TREE_TYPE (@1))
> && types_match (TREE_TYPE (@2), TREE_TYPE (@3))
> - && TYPE_PRECISION (TREE_TYPE (@2)) > TYPE_PRECISION (type)
> - && TYPE_UNSIGNED (TREE_TYPE (@2)) == TYPE_UNSIGNED (type))
> + && element_precision (TREE_TYPE (@2)) > element_precision (type)
> + && TYPE_UNSIGNED (TREE_TYPE (@2)) == TYPE_UNSIGNED (type)
> + && (!VECTOR_TYPE_P (type)
> + || (single_use (@4)
> + && target_supports_op_p (type, MINMAX, optab_vector))))
> (minmax @0 @1))))
>
> /* max (a, a + CST) -> a + CST where CST is positive. */
> diff --git a/gcc/testsuite/g++.dg/tree-ssa/vec-narrow-1.C
> b/gcc/testsuite/g++.dg/tree-ssa/vec-narrow-1.C
> new file mode 100644
> index 00000000000..de738e33c30
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/tree-ssa/vec-narrow-1.C
> @@ -0,0 +1,11 @@
> +// { dg-do compile }
> +// { dg-options "-O2 -fdump-tree-optimized" }
> +// Extension is monotone, so it commutes with the comparison and the outer
> +// truncation is exact. The argument is lanewise, so a widened vector
> +// MIN/MAX feeding a truncating conversion narrows.
> +typedef int v2si __attribute__((vector_size (8)));
> +typedef long long v2di __attribute__((vector_size (16)));
> +v2si f (v2si a, v2si b)
> +{ v2di x = __builtin_convertvector (a, v2di), y = __builtin_convertvector
> (b, v2di);
> + return __builtin_convertvector (x < y ? x : y, v2si); }
> +// { dg-final { scan-tree-dump-not "vector\\(2\\) long" "optimized" } }
This testcase fails on x86_64.
> diff --git a/gcc/testsuite/g++.dg/tree-ssa/vec-narrow-minmax-2.C
> b/gcc/testsuite/g++.dg/tree-ssa/vec-narrow-minmax-2.C
> new file mode 100644
> index 00000000000..d7fb7f06f16
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/tree-ssa/vec-narrow-minmax-2.C
> @@ -0,0 +1,18 @@
> +// { dg-do compile }
> +// { dg-options "-O2 -fdump-tree-optimized" }
> +
> +typedef int v2si __attribute__((vector_size (8)));
> +typedef long long v2di __attribute__((vector_size (16)));
> +
> +v2si
> +f (v2si a, v2si b, v2di *p)
> +{
> + v2di x = __builtin_convertvector (a, v2di);
> + v2di y = __builtin_convertvector (b, v2di);
> + v2di z = x < y ? x : y;
> + *p = z;
> + return __builtin_convertvector (z, v2si);
> +}
> +
> +// A shared wide MIN must not gain a second narrow MIN.
> +// { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "optimized" } }
> --
> 2.50.1 (Apple Git-155)
>