Hi Hongtao,
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.
Alas this patch causes a single regression: avx10_2-vmovd-1.c.
AVX 10.2's vmovd (like pslldq+psrldq) can also be used to perform
{x[0],0,0,0}. I was hoping Hongtao, Uros or HJ, can suggest the
right way to allow reload to select the most appropriate form for
the target ISA.
This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
and make -k check, both with and without --target_board=unix{-m32}
with a single new failure. Ok for mainline?
2026-08-25 Roger Sayle <[email protected]>
gcc/ChangeLog
* config/i386/sse.md (sse4_1_insertps_<mode>_perm_0): New
define_insn to implement V4SI or V4SF permute using insertps.
(sse4_1_insertps_<mode>_perm_1): Likewise.
(sse4_1_insertps_<mode>_perm_2): Likewise.
(sse4_1_insertps_<mode>_perm_3): Likewise.
gcc/testsuite/ChangeLog
* gcc.target/i386/sse4_1-insertps-8.c: New test case.
* gcc.target/i386/sse4_1-insertps-9.c: Likewise.
Thanks again,
Roger
--
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 326d22a6d89..7f9d6a4c73f 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -13000,6 +13000,131 @@
(set_attr "prefix" "orig,maybe_evex")
(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_0"
+ [(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"))
+ (parallel [(match_operand:SI 3 "const_0_to_3_operand")
+ (const_int 5)
+ (const_int 6)
+ (const_int 7)])))]
+ "TARGET_SSE4_1"
+{
+ operands[3] = GEN_INT ((INTVAL (operands[3]) << 6) + 14);
+ 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")])
+
+(define_insn "*sse4_1_insertps_<mode>_perm_1"
+ [(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"))
+ (parallel [(const_int 4)
+ (match_operand:SI 3 "const_0_to_3_operand")
+ (const_int 6)
+ (const_int 7)])))]
+ "TARGET_SSE4_1"
+{
+ operands[3] = GEN_INT ((INTVAL (operands[3]) << 6) + (32 + 13));
+ 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")])
+
+(define_insn "*sse4_1_insertps_<mode>_perm_2"
+ [(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"))
+ (parallel [(const_int 4)
+ (const_int 5)
+ (match_operand:SI 3 "const_0_to_3_operand")
+ (const_int 7)])))]
+ "TARGET_SSE4_1"
+{
+ operands[3] = GEN_INT ((INTVAL (operands[3]) << 6) + (64 + 11));
+ 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")])
+
+(define_insn "*sse4_1_insertps_<mode>_perm_3"
+ [(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"))
+ (parallel [(const_int 4)
+ (const_int 5)
+ (const_int 6)
+ (match_operand:SI 3 "const_0_to_3_operand")])))]
+ "TARGET_SSE4_1"
+{
+ operands[3] = GEN_INT ((INTVAL (operands[3]) << 6) + (96 + 7));
+ 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")])
+
(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 00000000000..5ba57600260
--- /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 00000000000..76d25d724bd
--- /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" } } */