https://gcc.gnu.org/g:15cabcf0bd5d964181d43fb6b8c7daeebf874e24
commit r17-4079-g15cabcf0bd5d964181d43fb6b8c7daeebf874e24 Author: Roger Sayle <[email protected]> Date: Thu Sep 10 13:30:10 2026 +0100 i386: More uses of insertps to implement VEC_PERM_EXPR on x86 SSE. This patch makes more use of SSE 4.1's insertps instruction to implement some vector permutations for V4SI and V4SF vectors. While expanding VEC_PERM_EXPR, the i386 backend currently attempts (and fails) to recognize (via recog) RTL of the form: (set (reg:V4SI 100) (vec_select:V4SI (vec_concat:V8SI (reg/v:V4SI 99) (const_vector:V4SI [ (const_int 0 [0]) repeated x4 ])) (parallel [ (const_int 0 [0]) (const_int 5 [0x5]) (const_int 6 [0x6]) (const_int 7 [0x7]) ]))) This is extracting an element from one vector and inserting it (somewhere) in a vector of zeros. It turns out all 16 cases (4 source positions x 4 destination positions) can be supported by adding four additional define_insns to the machine description [which unfortunately have identical output templates/attributes]. As a motivating example: typedef int v4si __attribute__ ((__vector_size__ (16))); v4si i00(v4si x) { return (v4si){x[0],0,0,0}; } which currently with -O2 -msse4.1 generates three instructions: i00: movdqa %xmm0, %xmm1 pxor %xmm0, %xmm0 pblendw $3, %xmm1, %xmm0 ret with this patch, we now generate a single instruction: i00: insertps $14, %xmm0, %xmm0 ret The deeper motivation is that (as shown by the recent sse2-stv-1.c issue) the STV2 pass doesn't yet consider (zero_extend:DI (reg:SI)) to be a candidate. Clearly the insertps (above) can be used to perform zero extension in/on SSE registers, instead of transferring values to a GPR, performing the extension, and transferring it back. 2026-09-10 Roger Sayle <[email protected]> Hongtao Liu <[email protected]> gcc/ChangeLog * config/i386/predicates.md (insertps_parallel): New predicate. * config/i386/sse.md (sse4_1_insertps_<mode>_perm): New define_insn to implement V4SI or V4SF permute using insertps. (*sse2_insertps_<mode>_perm): Emulate the above insn on SSE2 by splitting to one or two of pslldq, psrldq and pshufd. gcc/testsuite/ChangeLog * gcc.target/i386/sse4_1-insertps-8.c: New test case. * gcc.target/i386/sse4_1-insertps-9.c: Likewise. * gcc.target/i386/vperm-v4sf-3-sse4.c: Likewise. * gcc.target/i386/vperm-v4sf-3.c: Likewise. * gcc.target/i386/vperm-v4si-3-sse4.c: Likewise. * gcc.target/i386/vperm-v4si-3.c: Likewise. Diff: --- gcc/config/i386/predicates.md | 24 +++++ gcc/config/i386/sse.md | 105 ++++++++++++++++++++++ gcc/testsuite/gcc.target/i386/sse4_1-insertps-8.c | 31 +++++++ gcc/testsuite/gcc.target/i386/sse4_1-insertps-9.c | 31 +++++++ gcc/testsuite/gcc.target/i386/vperm-v4sf-3-sse4.c | 6 ++ gcc/testsuite/gcc.target/i386/vperm-v4sf-3.c | 74 +++++++++++++++ gcc/testsuite/gcc.target/i386/vperm-v4si-3-sse4.c | 6 ++ gcc/testsuite/gcc.target/i386/vperm-v4si-3.c | 73 +++++++++++++++ 8 files changed, 350 insertions(+) diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md index 289bf807e110..d295edd023de 100644 --- a/gcc/config/i386/predicates.md +++ b/gcc/config/i386/predicates.md @@ -2047,6 +2047,30 @@ return true; }) +;; Return true if OP is a parallel for an insertps vec_select, +;; where one of the two operands of the vec_concat is const0_operand. +(define_predicate "insertps_parallel" + (and (match_code "parallel") + (match_code "const_int" "a")) +{ + int i; + + if (XVECLEN (op, 0) != 4) + return false; + + /* One element in [0..3], and the other 3 in [4..7]. */ + bool found = false; + for (i = 0; i < 4; ++i) + if (INTVAL (XVECEXP (op, 0, i)) < 4) + { + if (found) + return false; + found = true; + } + + return found; +}) + ;; Return true if OP is a const vector with duplicate value. (define_predicate "const_vector_duplicate_operand" (match_code "const_vector") diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md index eb3b8eb2fe05..a19472d63aec 100644 --- a/gcc/config/i386/sse.md +++ b/gcc/config/i386/sse.md @@ -13088,6 +13088,111 @@ (set_attr "prefix" "orig") (set_attr "mode" "V4SF")]) +;; Use sse4_1_insertps_v4s[if] to permute to a one non-zero vector. +(define_insn "*sse4_1_insertps_<mode>_perm" + [(set (match_operand:VI4F_128 0 "register_operand" "=x,v") + (vec_select:VI4F_128 + (vec_concat:<ssedoublevecmode> + (match_operand:VI4F_128 1 "register_operand" "0,v") + (match_operand:VI4F_128 2 "const0_operand")) + (match_parallel 3 "insertps_parallel" + [(match_operand 4 "const_int_operand")])))] + "TARGET_SSE4_1" +{ + rtx op3 = operands[3]; + int src, dst; + + for (dst = 0; dst < 4; dst++) + { + src = INTVAL (XVECEXP (op3, 0, dst)); + if (src < 4) + break; + } + + if (TARGET_AVX10_2 && src == 0 && dst == 0) + return "vmovd\t{%1, %0|%0, %1}"; + operands[3] = GEN_INT ((src << 6) + (dst << 4) + (15 - (1 << dst))); + switch (which_alternative) + { + case 0: + return "insertps\t{%3, %1, %0|%0, %1, %3}"; + case 1: + return "vinsertps\t{%3, %1, %1, %0|%0, %1, %1, %3}"; + default: + gcc_unreachable (); + } +} + [(set_attr "isa" "noavx,avx") + (set_attr "type" "sselog") + (set_attr "prefix_data16" "1,*") + (set_attr "prefix_extra" "1") + (set_attr "length_immediate" "1") + (set_attr "prefix" "orig,maybe_evex") + (set_attr "mode" "V4SF")]) + +;; Emulate the above insn on SSE2 using two of pslldq, psrldq and pshufd. +(define_insn_and_split "*sse2_insertps_<mode>_perm" + [(set (match_operand:VI4F_128 0 "register_operand") + (vec_select:VI4F_128 + (vec_concat:<ssedoublevecmode> + (match_operand:VI4F_128 1 "register_operand") + (match_operand:VI4F_128 2 "const0_operand")) + (match_parallel 3 "insertps_parallel" + [(match_operand 4 "const_int_operand")])))] + "TARGET_SSE2 && !TARGET_SSE4_1 && ix86_pre_reload_split ()" + "#" + "&& 1" + [(const_int 0)] +{ + rtx op3 = operands[3]; + int src, dst; + + for (dst = 0; dst < 4; dst++) + { + src = INTVAL (XVECEXP (op3, 0, dst)); + if (src < 4) + break; + } + + if (src == 0 && dst == 3) + emit_insn (gen_vec_shl_<mode> (operands[0], operands[1], GEN_INT (96))); + else if (src == 3 && dst == 0) + emit_insn (gen_vec_shr_<mode> (operands[0], operands[1], GEN_INT (96))); + else + { + rtx tmp1 = gen_reg_rtx (<MODE>mode); + if (src == 0 || dst == 0) + { + emit_insn (gen_vec_shl_<mode> (tmp1, operands[1], + GEN_INT ((3 - src) * 32))); + emit_insn (gen_vec_shr_<mode> (operands[0], tmp1, + GEN_INT ((3 - dst) * 32))); + } + else + { + emit_insn (gen_vec_shr_<mode> (tmp1, operands[1], + GEN_INT (src * 32))); + if (src == 3 || dst == 3) + emit_insn (gen_vec_shl_<mode> (operands[0], tmp1, + GEN_INT (dst * 32))); + else /* (src == 1 || src == 2) && (dst == 1 || dst == 2). */ + { + rtx tmp2 = gen_lowpart (V4SImode, tmp1); + rtx tmp3 = <MODE>mode == V4SImode ? operands[0] + : gen_reg_rtx (V4SImode); + emit_insn (gen_sse2_pshufd_1 (tmp3, tmp2, + GEN_INT (3), + GEN_INT (dst == 1 ? 0 : 3), + GEN_INT (dst == 2 ? 0 : 3), + GEN_INT (3))); + if (<MODE>mode != V4SImode) + emit_move_insn (operands[0], gen_lowpart (<MODE>mode, tmp3)); + } + } + } + DONE; +}) + (define_split [(set (match_operand:VI4F_128 0 "memory_operand") (vec_merge:VI4F_128 diff --git a/gcc/testsuite/gcc.target/i386/sse4_1-insertps-8.c b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-8.c new file mode 100644 index 000000000000..5ba57600260a --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-8.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -msse4.1 -mno-avx" } */ + +typedef int v4si __attribute__ ((__vector_size__ (16))); + +v4si i00(v4si x) { return (v4si){x[0],0,0,0}; } +v4si i10(v4si x) { return (v4si){x[1],0,0,0}; } +v4si i20(v4si x) { return (v4si){x[2],0,0,0}; } +v4si i30(v4si x) { return (v4si){x[3],0,0,0}; } + +v4si i01(v4si x) { return (v4si){0,x[0],0,0}; } +v4si i11(v4si x) { return (v4si){0,x[1],0,0}; } +v4si i21(v4si x) { return (v4si){0,x[2],0,0}; } +v4si i31(v4si x) { return (v4si){0,x[3],0,0}; } + +v4si i02(v4si x) { return (v4si){0,0,x[0],0}; } +v4si i12(v4si x) { return (v4si){0,0,x[1],0}; } +v4si i22(v4si x) { return (v4si){0,0,x[2],0}; } +v4si i32(v4si x) { return (v4si){0,0,x[3],0}; } + +v4si i03(v4si x) { return (v4si){0,0,0,x[0]}; } +v4si i13(v4si x) { return (v4si){0,0,0,x[1]}; } +v4si i23(v4si x) { return (v4si){0,0,0,x[2]}; } +v4si i33(v4si x) { return (v4si){0,0,0,x[3]}; } + +/* { dg-final { scan-assembler-times "\tv?insertps\t" 14 } } */ +/* { dg-final { scan-assembler-times "\tv?pslldq\t" 1 } } */ +/* { dg-final { scan-assembler-times "\tv?psrldq\t" 1 } } */ +/* { dg-final { scan-assembler-not "\tv?movdqa\t" } } */ +/* { dg-final { scan-assembler-not "\tv?movss\t" } } */ +/* { dg-final { scan-assembler-not "\tv?pxor\t" } } */ diff --git a/gcc/testsuite/gcc.target/i386/sse4_1-insertps-9.c b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-9.c new file mode 100644 index 000000000000..76d25d724bdf --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-9.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -msse4.1 -mno-avx" } */ + +typedef float v4sf __attribute__ ((__vector_size__ (16))); + +v4sf f00(v4sf x) { return (v4sf){x[0],0.0f,0.0f,0.0f}; } +v4sf f10(v4sf x) { return (v4sf){x[1],0.0f,0.0f,0.0f}; } +v4sf f20(v4sf x) { return (v4sf){x[2],0.0f,0.0f,0.0f}; } +v4sf f30(v4sf x) { return (v4sf){x[3],0.0f,0.0f,0.0f}; } + +v4sf f01(v4sf x) { return (v4sf){0.0f,x[0],0.0f,0.0f}; } +v4sf f11(v4sf x) { return (v4sf){0.0f,x[1],0.0f,0.0f}; } +v4sf f21(v4sf x) { return (v4sf){0.0f,x[2],0.0f,0.0f}; } +v4sf f31(v4sf x) { return (v4sf){0.0f,x[3],0.0f,0.0f}; } + +v4sf f02(v4sf x) { return (v4sf){0.0f,0.0f,x[0],0.0f}; } +v4sf f12(v4sf x) { return (v4sf){0.0f,0.0f,x[1],0.0f}; } +v4sf f22(v4sf x) { return (v4sf){0.0f,0.0f,x[2],0.0f}; } +v4sf f32(v4sf x) { return (v4sf){0.0f,0.0f,x[3],0.0f}; } + +v4sf f03(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[0]}; } +v4sf f13(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[1]}; } +v4sf f23(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[2]}; } +v4sf f33(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[3]}; } + +/* { dg-final { scan-assembler-times "\tv?insertps\t" 14 } } */ +/* { dg-final { scan-assembler-times "\tv?pslldq\t" 1 } } */ +/* { dg-final { scan-assembler-times "\tv?psrldq\t" 1 } } */ +/* { dg-final { scan-assembler-not "\tv?movdqa\t" } } */ +/* { dg-final { scan-assembler-not "\tv?movss\t" } } */ +/* { dg-final { scan-assembler-not "\tv?pxor\t" } } */ diff --git a/gcc/testsuite/gcc.target/i386/vperm-v4sf-3-sse4.c b/gcc/testsuite/gcc.target/i386/vperm-v4sf-3-sse4.c new file mode 100644 index 000000000000..d79e54513903 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/vperm-v4sf-3-sse4.c @@ -0,0 +1,6 @@ +/* { dg-do run } */ +/* { dg-require-effective-target sse4 } */ +/* { dg-options "-O -msse4.1" } */ +#include "isa-check.h" +#define CHECK_ISA +#include "vperm-v4sf-3.c" diff --git a/gcc/testsuite/gcc.target/i386/vperm-v4sf-3.c b/gcc/testsuite/gcc.target/i386/vperm-v4sf-3.c new file mode 100644 index 000000000000..6ed939bef32f --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/vperm-v4sf-3.c @@ -0,0 +1,74 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +typedef float v4sf __attribute__ ((__vector_size__ (16))); + +v4sf f00(v4sf x) { return (v4sf){x[0],0.0f,0.0f,0.0f}; } +v4sf f10(v4sf x) { return (v4sf){x[1],0.0f,0.0f,0.0f}; } +v4sf f20(v4sf x) { return (v4sf){x[2],0.0f,0.0f,0.0f}; } +v4sf f30(v4sf x) { return (v4sf){x[3],0.0f,0.0f,0.0f}; } + +v4sf f01(v4sf x) { return (v4sf){0.0f,x[0],0.0f,0.0f}; } +v4sf f11(v4sf x) { return (v4sf){0.0f,x[1],0.0f,0.0f}; } +v4sf f21(v4sf x) { return (v4sf){0.0f,x[2],0.0f,0.0f}; } +v4sf f31(v4sf x) { return (v4sf){0.0f,x[3],0.0f,0.0f}; } + +v4sf f02(v4sf x) { return (v4sf){0.0f,0.0f,x[0],0.0f}; } +v4sf f12(v4sf x) { return (v4sf){0.0f,0.0f,x[1],0.0f}; } +v4sf f22(v4sf x) { return (v4sf){0.0f,0.0f,x[2],0.0f}; } +v4sf f32(v4sf x) { return (v4sf){0.0f,0.0f,x[3],0.0f}; } + +v4sf f03(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[0]}; } +v4sf f13(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[1]}; } +v4sf f23(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[2]}; } +v4sf f33(v4sf x) { return (v4sf){0.0f,0.0f,0.0f,x[3]}; } + + +typedef v4sf (*fun_t)(v4sf); +typedef struct { + fun_t fun; + float a0, a1, a2, a3; + float b0, b1, b2, b3; +} test_t; + +const test_t tests[16] = { + { f00, 1.0f, 0.0f, 0.0f, 0.0f, 4.0f, 0.0f, 0.0f, 0.0f }, + { f10, 2.0f, 0.0f, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f }, + { f20, 3.0f, 0.0f, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f }, + { f30, 4.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f }, + { f01, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 4.0f, 0.0f, 0.0f }, + { f11, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f }, + { f21, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f }, + { f31, 0.0f, 4.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }, + { f02, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 4.0f, 0.0f }, + { f12, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 3.0f, 0.0f }, + { f22, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 2.0f, 0.0f }, + { f32, 0.0f, 0.0f, 4.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f }, + { f03, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 4.0f }, + { f13, 0.0f, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 3.0f }, + { f23, 0.0f, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 2.0f }, + { f33, 0.0f, 0.0f, 0.0f, 4.0f, 0.0f, 0.0f, 0.0f, 1.0f } +}; + +int main() +{ +#ifdef CHECK_ISA + check_isa (); +#endif + + int i; + v4sf a = (v4sf){ 1.0f, 2.0f, 3.0f, 4.0f }; + v4sf b = (v4sf){ 4.0f, 3.0f, 2.0f, 1.0f }; + for (i = 0; i < 16; i++) { + const test_t *p = &tests[i]; + v4sf o1 = (*p->fun)(a); + if (o1[0] != p->a0 || o1[1] != p->a1 || o1[2] != p->a2 || o1[3] != p->a3) + __builtin_abort (); + + v4sf o2 = (*p->fun)(b); + if (o2[0] != p->b0 || o2[1] != p->b1 || o2[2] != p->b2 || o2[3] != p->b3) + __builtin_abort (); + } + return 0; +} + diff --git a/gcc/testsuite/gcc.target/i386/vperm-v4si-3-sse4.c b/gcc/testsuite/gcc.target/i386/vperm-v4si-3-sse4.c new file mode 100644 index 000000000000..75cca2fc232e --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/vperm-v4si-3-sse4.c @@ -0,0 +1,6 @@ +/* { dg-do run } */ +/* { dg-require-effective-target sse4 } */ +/* { dg-options "-O -msse4.1" } */ +#include "isa-check.h" +#define CHECK_ISA +#include "vperm-v4si-3.c" diff --git a/gcc/testsuite/gcc.target/i386/vperm-v4si-3.c b/gcc/testsuite/gcc.target/i386/vperm-v4si-3.c new file mode 100644 index 000000000000..2ad9bf1d7f09 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/vperm-v4si-3.c @@ -0,0 +1,73 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +typedef int v4si __attribute__ ((__vector_size__ (16))); + +v4si i00(v4si x) { return (v4si){x[0],0,0,0}; } +v4si i10(v4si x) { return (v4si){x[1],0,0,0}; } +v4si i20(v4si x) { return (v4si){x[2],0,0,0}; } +v4si i30(v4si x) { return (v4si){x[3],0,0,0}; } + +v4si i01(v4si x) { return (v4si){0,x[0],0,0}; } +v4si i11(v4si x) { return (v4si){0,x[1],0,0}; } +v4si i21(v4si x) { return (v4si){0,x[2],0,0}; } +v4si i31(v4si x) { return (v4si){0,x[3],0,0}; } + +v4si i02(v4si x) { return (v4si){0,0,x[0],0}; } +v4si i12(v4si x) { return (v4si){0,0,x[1],0}; } +v4si i22(v4si x) { return (v4si){0,0,x[2],0}; } +v4si i32(v4si x) { return (v4si){0,0,x[3],0}; } + +v4si i03(v4si x) { return (v4si){0,0,0,x[0]}; } +v4si i13(v4si x) { return (v4si){0,0,0,x[1]}; } +v4si i23(v4si x) { return (v4si){0,0,0,x[2]}; } +v4si i33(v4si x) { return (v4si){0,0,0,x[3]}; } + +typedef v4si (*fun_t)(v4si); +typedef struct { + fun_t fun; + int a0, a1, a2, a3; + int b0, b1, b2, b3; +} test_t; + +const test_t tests[16] = { + { i00, 1, 0, 0, 0, 4, 0, 0, 0 }, + { i10, 2, 0, 0, 0, 3, 0, 0, 0 }, + { i20, 3, 0, 0, 0, 2, 0, 0, 0 }, + { i30, 4, 0, 0, 0, 1, 0, 0, 0 }, + { i01, 0, 1, 0, 0, 0, 4, 0, 0 }, + { i11, 0, 2, 0, 0, 0, 3, 0, 0 }, + { i21, 0, 3, 0, 0, 0, 2, 0, 0 }, + { i31, 0, 4, 0, 0, 0, 1, 0, 0 }, + { i02, 0, 0, 1, 0, 0, 0, 4, 0 }, + { i12, 0, 0, 2, 0, 0, 0, 3, 0 }, + { i22, 0, 0, 3, 0, 0, 0, 2, 0 }, + { i32, 0, 0, 4, 0, 0, 0, 1, 0 }, + { i03, 0, 0, 0, 1, 0, 0, 0, 4 }, + { i13, 0, 0, 0, 2, 0, 0, 0, 3 }, + { i23, 0, 0, 0, 3, 0, 0, 0, 2 }, + { i33, 0, 0, 0, 4, 0, 0, 0, 1 } +}; + +int main() +{ +#ifdef CHECK_ISA + check_isa (); +#endif + + int i; + v4si a = (v4si){ 1, 2, 3, 4 }; + v4si b = (v4si){ 4, 3, 2, 1 }; + for (i = 0; i < 16; i++) { + const test_t *p = &tests[i]; + v4si o1 = (*p->fun)(a); + if (o1[0] != p->a0 || o1[1] != p->a1 || o1[2] != p->a2 || o1[3] != p->a3) + __builtin_abort (); + + v4si o2 = (*p->fun)(b); + if (o2[0] != p->b0 || o2[1] != p->b1 || o2[2] != p->b2 || o2[3] != p->b3) + __builtin_abort (); + } + return 0; +} +
