https://gcc.gnu.org/g:4b9034d6b240aa2bac3152703c3961b76e30a187
commit r17-4671-g4b9034d6b240aa2bac3152703c3961b76e30a187 Author: Dominic P <[email protected]> Date: Thu Sep 10 15:33:43 2026 +0100 aarch64: express sqxtun and friends as us_truncate of smax The sqxtun patterns are written with more complex RTL than the instruction needs: us_truncate of an smax against zero is what they should match. Rewrite them that way, so that the shape the vectoriser produces for MAX_EXPR <0, x> fed into IFN_SAT_TRUNC is matched by the sqxtun pattern itself. The rewrite covers more than the two sqxtun patterns. sqxtun2, sqshrun, sqshrun2, sqrshrun and sqrshrun2 wrap their results in the same truncate/smin/smax clamp against half_mask, so converting only the sqxtun pair would have left two spellings of "saturate into the unsigned narrow mode" in one file; they are converted too. The named expanders build the us_truncate form directly, which is what keeps the vqmovun_*, vqshrun_n_* and vqrshrun_n_* intrinsics working: they reach the patterns through gen_* rather than through recog on a combination. aarch64_simd_umax_half_mode and aarch64_simd_umax_quarter_mode lose their last users and are removed. The scalar sqshrun/sqrshrun define_insns are unchanged: they compute in the wide mode and the expander takes a lowpart, so there is no truncate for the clamp to fold into. The vqmovun/vqshrun_n/vqrshrun_n tests in gcc.target/aarch64/advsimd-intrinsics compare against expected values and run under qemu, so the family already has execution coverage; the new test only checks that the vectoriser's form reaches sqxtun. Bootstrapped and regression tested on x86_64-pc-linux-gnu, and cross-tested on aarch64-linux-gnu under qemu (aarch64.exp, simd.exp, advsimd-intrinsics.exp, vect.exp). Assisted-by: Claude Opus 5 (Anthropic) gcc/ChangeLog: * config/aarch64/predicates.md (aarch64_simd_umax_half_mode) (aarch64_simd_umax_quarter_mode): Remove. * config/aarch64/aarch64-simd.md (aarch64_sqmovun<mode>) (*aarch64_sqmovun<mode>_insn<vczle><vczbe>) (aarch64_sqxtun2<mode>_le, aarch64_sqxtun2<mode>_be) (aarch64_sqxtun2<mode>) (*aarch64_sqshrun_n<mode>_insn<vczle><vczbe>) (aarch64_sqshrun_n<mode>) (*aarch64_sqrshrun_n<mode>_insn<vczle><vczbe>) (aarch64_sqrshrun_n<mode>, aarch64_sqshrun2_n<mode>_insn_le) (aarch64_sqshrun2_n<mode>_insn_be, aarch64_sqshrun2_n<mode>) (aarch64_sqrshrun2_n<mode>_insn_le) (aarch64_sqrshrun2_n<mode>_insn_be, aarch64_sqrshrun2_n<mode>): Match an us_truncate of an smax instead of a truncate of an smin of an smax. gcc/testsuite/ChangeLog: * gcc.target/aarch64/vect-sat-trunc-3.c: New test. Signed-off-by: Dominic P <[email protected]> Diff: --- gcc/config/aarch64/aarch64-simd.md | 211 ++++++++------------- gcc/config/aarch64/predicates.md | 15 -- .../gcc.target/aarch64/vect-sat-trunc-3.c | 41 ++++ 3 files changed, 125 insertions(+), 142 deletions(-) diff --git a/gcc/config/aarch64/aarch64-simd.md b/gcc/config/aarch64/aarch64-simd.md index fe6cb4250f6a..9d1a929576c7 100644 --- a/gcc/config/aarch64/aarch64-simd.md +++ b/gcc/config/aarch64/aarch64-simd.md @@ -6282,12 +6282,10 @@ (define_insn "aarch64_sqmovun<mode>" [(set (match_operand:<VNARROWQ> 0 "register_operand" "=w") - (truncate:<VNARROWQ> - (smin:SD_HSDI - (smax:SD_HSDI - (match_operand:SD_HSDI 1 "register_operand" "w") - (const_int 0)) - (const_int <half_mask>))))] + (us_truncate:<VNARROWQ> + (smax:SD_HSDI + (match_operand:SD_HSDI 1 "register_operand" "w") + (const_int 0))))] "TARGET_SIMD" "sqxtun\\t%<vn2>0<Vmntype>, %<v>1<Vmtype>" [(set_attr "type" "neon_sat_shift_imm_narrow_q")] @@ -6295,11 +6293,9 @@ (define_insn "*aarch64_sqmovun<mode>_insn<vczle><vczbe>" [(set (match_operand:<VNARROWQ> 0 "register_operand" "=w") - (truncate:<VNARROWQ> - (smin:VQN - (smax:VQN (match_operand:VQN 1 "register_operand" "w") - (match_operand:VQN 2 "aarch64_simd_or_scalar_imm_zero")) - (match_operand:VQN 3 "aarch64_simd_umax_half_mode"))))] + (us_truncate:<VNARROWQ> + (smax:VQN (match_operand:VQN 1 "register_operand" "w") + (match_operand:VQN 2 "aarch64_simd_or_scalar_imm_zero"))))] "TARGET_SIMD" "sqxtun\\t%<vn2>0<Vmntype>, %<v>1<Vmtype>" [(set_attr "type" "neon_sat_shift_imm_narrow_q")] @@ -6307,17 +6303,12 @@ (define_expand "aarch64_sqmovun<mode>" [(set (match_operand:<VNARROWQ> 0 "register_operand" "=w") - (truncate:<VNARROWQ> - (smin:VQN - (smax:VQN (match_operand:VQN 1 "register_operand" "w") - (match_dup 2)) - (match_dup 3))))] + (us_truncate:<VNARROWQ> + (smax:VQN (match_operand:VQN 1 "register_operand" "w") + (match_dup 2))))] "TARGET_SIMD" { operands[2] = CONST0_RTX (<MODE>mode); - operands[3] - = aarch64_simd_gen_const_vector_dup (<MODE>mode, - GET_MODE_MASK (GET_MODE_INNER (<VNARROWQ>mode))); } ) @@ -6325,12 +6316,10 @@ [(set (match_operand:<VNARROWQ2> 0 "register_operand" "=w") (vec_concat:<VNARROWQ2> (match_operand:<VNARROWQ> 1 "register_operand" "0") - (truncate:<VNARROWQ> - (smin:VQN - (smax:VQN - (match_operand:VQN 2 "register_operand" "w") - (match_operand:VQN 3 "aarch64_simd_or_scalar_imm_zero")) - (match_operand:VQN 4 "aarch64_simd_umax_half_mode")))))] + (us_truncate:<VNARROWQ> + (smax:VQN + (match_operand:VQN 2 "register_operand" "w") + (match_operand:VQN 3 "aarch64_simd_or_scalar_imm_zero")))))] "TARGET_SIMD && !BYTES_BIG_ENDIAN" "sqxtun2\\t%0.<V2ntype>, %2.<Vtype>" [(set_attr "type" "neon_sat_shift_imm_narrow_q")] @@ -6339,12 +6328,10 @@ (define_insn "aarch64_sqxtun2<mode>_be" [(set (match_operand:<VNARROWQ2> 0 "register_operand" "=w") (vec_concat:<VNARROWQ2> - (truncate:<VNARROWQ> - (smin:VQN - (smax:VQN - (match_operand:VQN 2 "register_operand" "w") - (match_operand:VQN 3 "aarch64_simd_or_scalar_imm_zero")) - (match_operand:VQN 4 "aarch64_simd_umax_half_mode"))) + (us_truncate:<VNARROWQ> + (smax:VQN + (match_operand:VQN 2 "register_operand" "w") + (match_operand:VQN 3 "aarch64_simd_or_scalar_imm_zero"))) (match_operand:<VNARROWQ> 1 "register_operand" "0")))] "TARGET_SIMD && BYTES_BIG_ENDIAN" "sqxtun2\\t%0.<V2ntype>, %2.<Vtype>" @@ -6358,14 +6345,12 @@ "TARGET_SIMD" { rtx zeros = CONST0_RTX (<MODE>mode); - rtx half_umax = aarch64_simd_gen_const_vector_dup (<MODE>mode, - GET_MODE_MASK (GET_MODE_INNER (<VNARROWQ>mode))); if (BYTES_BIG_ENDIAN) emit_insn (gen_aarch64_sqxtun2<mode>_be (operands[0], operands[1], - operands[2], zeros, half_umax)); + operands[2], zeros)); else emit_insn (gen_aarch64_sqxtun2<mode>_le (operands[0], operands[1], - operands[2], zeros, half_umax)); + operands[2], zeros)); DONE; } ) @@ -7626,14 +7611,12 @@ (define_insn "*aarch64_sqshrun_n<mode>_insn<vczle><vczbe>" [(set (match_operand:<VNARROWQ> 0 "register_operand" "=w") - (truncate:<VNARROWQ> - (smin:VQN - (smax:VQN - (ashiftrt:VQN - (match_operand:VQN 1 "register_operand" "w") - (match_operand:VQN 2 "aarch64_simd_shift_imm_vec_<vn_mode>")) - (match_operand:VQN 3 "aarch64_simd_imm_zero")) - (match_operand:VQN 4 "aarch64_simd_umax_half_mode"))))] + (us_truncate:<VNARROWQ> + (smax:VQN + (ashiftrt:VQN + (match_operand:VQN 1 "register_operand" "w") + (match_operand:VQN 2 "aarch64_simd_shift_imm_vec_<vn_mode>")) + (match_operand:VQN 3 "aarch64_simd_imm_zero"))))] "TARGET_SIMD" "sqshrun\t%<vn2>0<Vmntype>, %<v>1<Vmtype>, %2" [(set_attr "type" "neon_shift_imm_narrow_q")] @@ -7669,38 +7652,31 @@ (define_expand "aarch64_sqshrun_n<mode>" [(set (match_operand:<VNARROWQ> 0 "register_operand") - (truncate:<VNARROWQ> - (smin:VQN - (smax:VQN - (ashiftrt:VQN - (match_operand:VQN 1 "register_operand") - (match_operand:SI 2 "aarch64_simd_shift_imm_offset_<vn_mode>")) - (match_dup 3)) - (match_dup 4))))] + (us_truncate:<VNARROWQ> + (smax:VQN + (ashiftrt:VQN + (match_operand:VQN 1 "register_operand") + (match_operand:SI 2 "aarch64_simd_shift_imm_offset_<vn_mode>")) + (match_dup 3))))] "TARGET_SIMD" { operands[2] = aarch64_simd_gen_const_vector_dup (<MODE>mode, INTVAL (operands[2])); operands[3] = CONST0_RTX (<MODE>mode); - operands[4] - = aarch64_simd_gen_const_vector_dup (<MODE>mode, - GET_MODE_MASK (GET_MODE_INNER (<VNARROWQ>mode))); } ) (define_insn "*aarch64_sqrshrun_n<mode>_insn<vczle><vczbe>" [(set (match_operand:<VNARROWQ> 0 "register_operand" "=w") - (truncate:<VNARROWQ> - (smin:<V2XWIDE> - (smax:<V2XWIDE> - (ashiftrt:<V2XWIDE> - (plus:<V2XWIDE> - (sign_extend:<V2XWIDE> - (match_operand:VQN 1 "register_operand" "w")) - (match_operand:<V2XWIDE> 3 "aarch64_int_rnd_operand")) - (match_operand:VQN 2 "aarch64_simd_shift_imm_vec_<vn_mode>")) - (match_operand:<V2XWIDE> 4 "aarch64_simd_imm_zero")) - (match_operand:<V2XWIDE> 5 "aarch64_simd_umax_quarter_mode"))))] + (us_truncate:<VNARROWQ> + (smax:<V2XWIDE> + (ashiftrt:<V2XWIDE> + (plus:<V2XWIDE> + (sign_extend:<V2XWIDE> + (match_operand:VQN 1 "register_operand" "w")) + (match_operand:<V2XWIDE> 3 "aarch64_int_rnd_operand")) + (match_operand:VQN 2 "aarch64_simd_shift_imm_vec_<vn_mode>")) + (match_operand:<V2XWIDE> 4 "aarch64_simd_imm_zero"))))] "TARGET_SIMD && aarch64_const_vec_rnd_cst_p (operands[3], operands[2])" "sqrshrun\t%<vn2>0<Vmntype>, %<v>1<Vmtype>, %2" @@ -7743,17 +7719,15 @@ (define_expand "aarch64_sqrshrun_n<mode>" [(set (match_operand:<VNARROWQ> 0 "register_operand") - (truncate:<VNARROWQ> - (smin:<V2XWIDE> - (smax:<V2XWIDE> - (ashiftrt:<V2XWIDE> - (plus:<V2XWIDE> - (sign_extend:<V2XWIDE> - (match_operand:VQN 1 "register_operand")) - (match_dup 3)) - (match_operand:SI 2 "aarch64_simd_shift_imm_offset_<vn_mode>")) - (match_dup 4)) - (match_dup 5))))] + (us_truncate:<VNARROWQ> + (smax:<V2XWIDE> + (ashiftrt:<V2XWIDE> + (plus:<V2XWIDE> + (sign_extend:<V2XWIDE> + (match_operand:VQN 1 "register_operand")) + (match_dup 3)) + (match_operand:SI 2 "aarch64_simd_shift_imm_offset_<vn_mode>")) + (match_dup 4))))] "TARGET_SIMD" { int prec = GET_MODE_UNIT_PRECISION (<V2XWIDE>mode); @@ -7762,9 +7736,6 @@ operands[3] = gen_const_vec_duplicate (<V2XWIDE>mode, operands[3]); operands[2] = gen_const_vec_duplicate (<MODE>mode, operands[2]); operands[4] = CONST0_RTX (<V2XWIDE>mode); - operands[5] - = gen_int_mode (GET_MODE_MASK (GET_MODE_INNER (<VNARROWQ>mode)), DImode); - operands[5] = gen_const_vec_duplicate (<V2XWIDE>mode, operands[5]); } ) @@ -7894,14 +7865,12 @@ [(set (match_operand:<VNARROWQ2> 0 "register_operand" "=w") (vec_concat:<VNARROWQ2> (match_operand:<VNARROWQ> 1 "register_operand" "0") - (truncate:<VNARROWQ> - (smin:VQN - (smax:VQN - (ashiftrt:VQN - (match_operand:VQN 2 "register_operand" "w") - (match_operand:VQN 3 "aarch64_simd_shift_imm_vec_<vn_mode>")) - (match_operand:VQN 4 "aarch64_simd_imm_zero")) - (match_operand:VQN 5 "aarch64_simd_umax_half_mode")))))] + (us_truncate:<VNARROWQ> + (smax:VQN + (ashiftrt:VQN + (match_operand:VQN 2 "register_operand" "w") + (match_operand:VQN 3 "aarch64_simd_shift_imm_vec_<vn_mode>")) + (match_operand:VQN 4 "aarch64_simd_imm_zero")))))] "TARGET_SIMD && !BYTES_BIG_ENDIAN" "sqshrun2\t%<vn2>0.<V2ntype>, %<v>2.<Vtype>, %3" [(set_attr "type" "neon_shift_imm_narrow_q")] @@ -7910,14 +7879,12 @@ (define_insn "aarch64_sqshrun2_n<mode>_insn_be" [(set (match_operand:<VNARROWQ2> 0 "register_operand" "=w") (vec_concat:<VNARROWQ2> - (truncate:<VNARROWQ> - (smin:VQN - (smax:VQN - (ashiftrt:VQN - (match_operand:VQN 2 "register_operand" "w") - (match_operand:VQN 3 "aarch64_simd_shift_imm_vec_<vn_mode>")) - (match_operand:VQN 4 "aarch64_simd_imm_zero")) - (match_operand:VQN 5 "aarch64_simd_umax_half_mode"))) + (us_truncate:<VNARROWQ> + (smax:VQN + (ashiftrt:VQN + (match_operand:VQN 2 "register_operand" "w") + (match_operand:VQN 3 "aarch64_simd_shift_imm_vec_<vn_mode>")) + (match_operand:VQN 4 "aarch64_simd_imm_zero"))) (match_operand:<VNARROWQ> 1 "register_operand" "0")))] "TARGET_SIMD && BYTES_BIG_ENDIAN" "sqshrun2\t%<vn2>0.<V2ntype>, %<v>2.<Vtype>, %3" @@ -7934,17 +7901,14 @@ operands[3] = aarch64_simd_gen_const_vector_dup (<MODE>mode, INTVAL (operands[3])); rtx zeros = CONST0_RTX (<MODE>mode); - rtx half_umax - = aarch64_simd_gen_const_vector_dup (<MODE>mode, - GET_MODE_MASK (GET_MODE_INNER (<VNARROWQ>mode))); if (BYTES_BIG_ENDIAN) emit_insn (gen_aarch64_sqshrun2_n<mode>_insn_be (operands[0], operands[1], operands[2], operands[3], - zeros, half_umax)); + zeros)); else emit_insn (gen_aarch64_sqshrun2_n<mode>_insn_le (operands[0], operands[1], operands[2], operands[3], - zeros, half_umax)); + zeros)); DONE; } ) @@ -7953,17 +7917,15 @@ [(set (match_operand:<VNARROWQ2> 0 "register_operand" "=w") (vec_concat:<VNARROWQ2> (match_operand:<VNARROWQ> 1 "register_operand" "0") - (truncate:<VNARROWQ> - (smin:<V2XWIDE> - (smax:<V2XWIDE> - (ashiftrt:<V2XWIDE> - (plus:<V2XWIDE> - (sign_extend:<V2XWIDE> - (match_operand:VQN 2 "register_operand" "w")) - (match_operand:<V2XWIDE> 4 "aarch64_int_rnd_operand")) - (match_operand:VQN 3 "aarch64_simd_shift_imm_vec_<vn_mode>")) - (match_operand:<V2XWIDE> 5 "aarch64_simd_imm_zero")) - (match_operand:<V2XWIDE> 6 "aarch64_simd_umax_quarter_mode")))))] + (us_truncate:<VNARROWQ> + (smax:<V2XWIDE> + (ashiftrt:<V2XWIDE> + (plus:<V2XWIDE> + (sign_extend:<V2XWIDE> + (match_operand:VQN 2 "register_operand" "w")) + (match_operand:<V2XWIDE> 4 "aarch64_int_rnd_operand")) + (match_operand:VQN 3 "aarch64_simd_shift_imm_vec_<vn_mode>")) + (match_operand:<V2XWIDE> 5 "aarch64_simd_imm_zero")))))] "TARGET_SIMD && !BYTES_BIG_ENDIAN && aarch64_const_vec_rnd_cst_p (operands[4], operands[3])" "sqrshrun2\t%<vn2>0.<V2ntype>, %<v>2.<Vtype>, %3" @@ -7973,17 +7935,15 @@ (define_insn "aarch64_sqrshrun2_n<mode>_insn_be" [(set (match_operand:<VNARROWQ2> 0 "register_operand" "=w") (vec_concat:<VNARROWQ2> - (truncate:<VNARROWQ> - (smin:<V2XWIDE> - (smax:<V2XWIDE> - (ashiftrt:<V2XWIDE> - (plus:<V2XWIDE> - (sign_extend:<V2XWIDE> - (match_operand:VQN 2 "register_operand" "w")) - (match_operand:<V2XWIDE> 4 "aarch64_int_rnd_operand")) - (match_operand:VQN 3 "aarch64_simd_shift_imm_vec_<vn_mode>")) - (match_operand:<V2XWIDE> 5 "aarch64_simd_imm_zero")) - (match_operand:<V2XWIDE> 6 "aarch64_simd_umax_quarter_mode"))) + (us_truncate:<VNARROWQ> + (smax:<V2XWIDE> + (ashiftrt:<V2XWIDE> + (plus:<V2XWIDE> + (sign_extend:<V2XWIDE> + (match_operand:VQN 2 "register_operand" "w")) + (match_operand:<V2XWIDE> 4 "aarch64_int_rnd_operand")) + (match_operand:VQN 3 "aarch64_simd_shift_imm_vec_<vn_mode>")) + (match_operand:<V2XWIDE> 5 "aarch64_simd_imm_zero"))) (match_operand:<VNARROWQ> 1 "register_operand" "0")))] "TARGET_SIMD && BYTES_BIG_ENDIAN && aarch64_const_vec_rnd_cst_p (operands[4], operands[3])" @@ -8003,18 +7963,15 @@ rtx rnd = immed_wide_int_const (rnd_wi, GET_MODE_INNER (<V2XWIDE>mode)); rnd = gen_const_vec_duplicate (<V2XWIDE>mode, rnd); rtx zero = CONST0_RTX (<V2XWIDE>mode); - rtx half_umax - = aarch64_simd_gen_const_vector_dup (<V2XWIDE>mode, - GET_MODE_MASK (GET_MODE_INNER (<VNARROWQ>mode))); operands[3] = gen_const_vec_duplicate (<MODE>mode, operands[3]); if (BYTES_BIG_ENDIAN) emit_insn (gen_aarch64_sqrshrun2_n<mode>_insn_be (operands[0], operands[1], operands[2], operands[3], rnd, - zero, half_umax)); + zero)); else emit_insn (gen_aarch64_sqrshrun2_n<mode>_insn_le (operands[0], operands[1], operands[2], operands[3], rnd, - zero, half_umax)); + zero)); DONE; } ) diff --git a/gcc/config/aarch64/predicates.md b/gcc/config/aarch64/predicates.md index 6f4d1dac6bf9..523974240f03 100644 --- a/gcc/config/aarch64/predicates.md +++ b/gcc/config/aarch64/predicates.md @@ -744,21 +744,6 @@ GET_MODE_UNIT_BITSIZE (GET_MODE (op)) / 2, GET_MODE_UNIT_BITSIZE (GET_MODE (op)) / 2)"))) -(define_predicate "aarch64_simd_umax_half_mode" - (and (match_code "const_vector") - (match_test "aarch64_const_vec_all_same_in_range_p (op, - (HOST_WIDE_INT_1U - << (GET_MODE_UNIT_BITSIZE (mode) / 2)) - 1, - (HOST_WIDE_INT_1U - << (GET_MODE_UNIT_BITSIZE (mode) / 2)) - 1)"))) - -(define_predicate "aarch64_simd_umax_quarter_mode" - (and (match_code "const_vector") - (match_test "aarch64_const_vec_all_same_in_range_p (op, - (HOST_WIDE_INT_1U - << (GET_MODE_UNIT_BITSIZE (mode) / 4)) - 1, - (HOST_WIDE_INT_1U - << (GET_MODE_UNIT_BITSIZE (mode) / 4)) - 1)"))) (define_predicate "aarch64_simd_shift_imm_vec_qi" (and (match_code "const_vector") (match_test "aarch64_const_vec_all_same_in_range_p (op, 1, 8)"))) diff --git a/gcc/testsuite/gcc.target/aarch64/vect-sat-trunc-3.c b/gcc/testsuite/gcc.target/aarch64/vect-sat-trunc-3.c new file mode 100644 index 000000000000..5efe52e80bbb --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/vect-sat-trunc-3.c @@ -0,0 +1,41 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -march=armv8-a" } */ +/* { dg-additional-options "-fno-schedule-insns -fno-schedule-insns2" } */ +/* { dg-final { check-function-bodies "**" "" } } */ + +typedef __UINT16_TYPE__ u16; + +static inline u16 +clip_u16 (int x) +{ + return (unsigned int) x > 65535u ? ((-x) >> 31) : x; +} + +/* +** clu: +** ... +** ldp q[0-9]+, q[0-9]+, \[x[0-9]+\] +** orr v[0-9]+\.4s, #1 +** sqxtun v[0-9]+\.4h, v[0-9]+\.4s +** orr v[0-9]+\.4s, #1 +** sqxtun v[0-9]+\.4h, v[0-9]+\.4s +** stp d[0-9]+, d[0-9]+, \[x[0-9]+\] +** ret +*/ +void +clu (u16 *__restrict r, const int *__restrict x) +{ + for (int i = 0; i < 8; i++) + r[i] = clip_u16 (x[i] | 1); +} + +/* The same in a variable-length loop. */ +void +clu_n (u16 *__restrict r, const int *__restrict x, int n) +{ + for (int i = 0; i < n; i++) + r[i] = clip_u16 (x[i] | 1); +} + +/* { dg-final { scan-assembler-times {\tsqxtun\tv[0-9]+\.4h, v[0-9]+\.4s} 3 } } */ +/* { dg-final { scan-assembler-not {\tuqxtn\tv[0-9]+\.4h, v[0-9]+\.4s} } } */
