On 2026-08-09 00:18, Andrew Pinski wrote:
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.
This test case fails for arm-none-eabi target
thumb/arch=armv7ve+simd/tune=cortex-a7/float-abi=hard/fpu=auto (even after
Jakubs tweaks in r17-3353-ge9d5aaf572e8d1):
Testing tree-ssa/vec-narrow-1.C, -std=gnu++20
doing compile
Executing on host: /build/r17-3152-gbc8c49094cdfbb/bin/arm-none-eabi-g++
/build/gcc_src/gcc/testsuite/g++.dg/tree-ssa/vec-narrow-1.C -mthumb
-march=armv7ve+simd -mtune=cortex-a7 -mfloat-abi=hard -mfpu=auto
-fdiagnostics-plain-output -fmessage-length=0 -std=gnu++20 -O2
-fdump-tree-optimized -S -o vec-narrow-1.s (timeout = 800)
spawn -ignore SIGHUP /build/r17-3152-gbc8c49094cdfbb/bin/arm-none-eabi-g++
/build/gcc_src/gcc/testsuite/g++.dg/tree-ssa/vec-narrow-1.C -mthumb
-march=armv7ve+simd -mtune=cortex-a7 -mfloat-abi=hard -mfpu=auto
-fdiagnostics-plain-output -fmessage-length=0 -std=gnu++20 -O2
-fdump-tree-optimized -S -o vec-narrow-1.s
pid is 185 -185
pid is -1
output is status 0
PASS: g++.dg/tree-ssa/vec-narrow-1.C -std=gnu++20 (test for excess errors)
FAIL: g++.dg/tree-ssa/vec-narrow-1.C -std=gnu++20 scan-tree-dump-not optimized
"vector\\(2\\) long"
Content of vec-narrow-1.C.272t.optimized is:
;; Function f (_Z1fDv2_iS_, funcdef_no=0, decl_uid=8035, cgraph_uid=1,
symbol_order=0)
v2si f (v2si a, v2si b)
{
vector(2) long long int _1;
int _8;
long long int _9;
int _10;
long long int _11;
int _12;
long long int _13;
int _14;
long long int _15;
long long int _16;
long long int _17;
vector(4) int _18;
vector(2) int _19;
<bb 2> [local count: 1073741824]:
_8 = BIT_FIELD_REF <a_2(D), 32, 0>;
_9 = (long long int) _8;
_10 = BIT_FIELD_REF <a_2(D), 32, 32>;
_11 = (long long int) _10;
_12 = BIT_FIELD_REF <b_4(D), 32, 0>;
_13 = (long long int) _12;
_14 = BIT_FIELD_REF <b_4(D), 32, 32>;
_15 = (long long int) _14;
_16 = MIN_EXPR <_9, _13>;
_17 = MIN_EXPR <_11, _15>;
_1 = {_16, _17};
_18 = VEC_PACK_TRUNC_EXPR <_1, { 0, 0 }>;
_19 = BIT_FIELD_REF <_18, 64, 0>;
return _19;
}
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" } }
And this testcase fails for all arm-none-eabi targets I test (even after Jakubs
tweaks in r17-3353-ge9d5aaf572e8d1):
Testing tree-ssa/vec-narrow-minmax-2.C, -std=gnu++20
doing compile
Executing on host: /build/r17-3152-gbc8c49094cdfbb/bin/arm-none-eabi-g++
/build/gcc_src/gcc/testsuite/g++.dg/tree-ssa/vec-narrow-minmax-2.C -mthumb
-march=armv7ve -mtune=cortex-a7 -mfloat-abi=soft -mfpu=auto
-fdiagnostics-plain-output -fmessage-length=0 -std=gnu++20 -O2
-fdump-tree-optimized -Wno-psabi -S -o vec-narrow-minmax-2.s (timeout =
800)
spawn -ignore SIGHUP /build/r17-3152-gbc8c49094cdfbb/bin/arm-none-eabi-g++
/build/gcc_src/gcc/testsuite/g++.dg/tree-ssa/vec-narrow-minmax-2.C -mthumb
-march=armv7ve -mtune=cortex-a7 -mfloat-abi=soft -mfpu=auto
-fdiagnostics-plain-output -fmessage-length=0 -std=gnu++20 -O2
-fdump-tree-optimized -Wno-psabi -S -o vec-narrow-minmax-2.s
pid is 185 -185
pid is -1
output is status 0
PASS: g++.dg/tree-ssa/vec-narrow-minmax-2.C -std=gnu++20 (test for excess
errors)
g++.dg/tree-ssa/vec-narrow-minmax-2.C -std=gnu++20 : pattern found 4 times
FAIL: g++.dg/tree-ssa/vec-narrow-minmax-2.C -std=gnu++20 scan-tree-dump-times optimized
"MIN_EXPR" 1
Content of vec-narrow-minmax-2.C.272t.optimized is:
;; Function f (_Z1fDv2_iS_PDv2_x, funcdef_no=0, decl_uid=4948, cgraph_uid=1,
symbol_order=0)
v2si f (v2si a, v2si b, v2di * p)
{
v2di z;
v2si _9;
int _10;
long long int _11;
int _12;
long long int _13;
int _14;
long long int _15;
int _16;
long long int _17;
long long int _18;
long long int _19;
int _20;
int _21;
<bb 2> [local count: 1073741824]:
_10 = BIT_FIELD_REF <a_1(D), 32, 0>;
_11 = (long long int) _10;
_12 = BIT_FIELD_REF <a_1(D), 32, 32>;
_13 = (long long int) _12;
_14 = BIT_FIELD_REF <b_3(D), 32, 0>;
_15 = (long long int) _14;
_16 = BIT_FIELD_REF <b_3(D), 32, 32>;
_17 = (long long int) _16;
_18 = MIN_EXPR <_11, _15>;
_19 = MIN_EXPR <_13, _17>;
z_5 = {_18, _19};
*p_7(D) = z_5;
_20 = MIN_EXPR <_10, _14>;
_21 = MIN_EXPR <_12, _16>;
_9 = {_20, _21};
return _9;
}
Kind regards,
Torbjörn
--
2.50.1 (Apple Git-155)