Re: [PATCH] match.pd: extend the MIN/MAX narrowing through a cast to vectors
Torbjorn SVENSSON <[email protected]>
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
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) >>