@@ -13059,6 +13059,133 @@
(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_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"
+{
+ if (TARGET_AVX10_2 && operands[3] == const0_rtx)
+ return "vmovd\t{%1, %0|%0, %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) + (16 + 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) + (32 + 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) + (48 + 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
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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"
new file mode 100644
@@ -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;
+}
+
new file mode 100644
@@ -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"
new file mode 100644
@@ -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;
+}
+
Hi Hongtao, Very many thanks for the review/feedback, and great catch on my mistake with the insertps immediate encoding. This revised patch implements all of your suggested changes (fixing the regression of avx10_2-vmovd-1.c) and I've also added execution tests to confirm the correct encoding on sse4.1, and also with the default/prevailing options. 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 no new failures. Ok for mainline? 2026-09-02 Roger Sayle <roger@nextmovesoftware.com> Hongtao Liu <hongtao.liu@intel.com> 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. * 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. Thanks again. Roger -- > -----Original Message----- > From: Hongtao Liu <crazylht@gmail.com> > Sent: 26 August 2026 07:42 > To: Roger Sayle <roger@nextmovesoftware.com> > Cc: Patches GCC <gcc-patches@gcc.gnu.org>; Liu, Hongtao > <hongtao.liu@intel.com>; Uros Bizjak <ubizjak@gmail.com> > Subject: Re: [x86 SSE PATCH] More uses of insertps to implement > VEC_PERM_EXPR. > > On Wed, Aug 26, 2026 at 4:25 AM Roger Sayle <roger@nextmovesoftware.com> > wrote: > > > 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. > I think you can just emit vmovd\t{%1, %0|%0, %1} from perm_0 when INTVAL > (operands[3]) == 0 && TARGET_AVX10_2, > > +(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)); > > Should it be (16 + 13), not (32 + 13), similar for perm_2 (32 + 11), > perm_3 (48 + 7)? > > According to SMD: > IF (SRC = REG) THEN COUNT_S := imm8[7:6] > ELSE COUNT_S := 0 > COUNT_D := imm8[5:4] > ZMASK := imm8[3:0] > > so INTVAL (operands[3]) << 6) is correct, 13 is correct, but count_d is imm[5:4], > should be << 4 which is 16, not 32. > > ┌─────────┬─────────┬─────────┬─────────────────────┬──────── > ────────────────┐ > │ pattern │ dst elt │ count_d │ zmask = 15 ^ (1<<d) │ term > │ > ├─────────┼─────────┼─────────┼─────────────────────┼─────── > ─────────────────┤ > │ perm_0 │ 0 │ 0 │ 14 │ + 14 (already > correct) │ > ├─────────┼─────────┼─────────┼─────────────────────┼─────── > ─────────────────┤ > │ perm_1 │ 1 │ 16 │ 13 │ + (16 + 13) = 29 > │ > ├─────────┼─────────┼─────────┼─────────────────────┼─────── > ─────────────────┤ > │ perm_2 │ 2 │ 32 │ 11 │ + (32 + 11) = 43 > │ > ├─────────┼─────────┼─────────┼─────────────────────┼─────── > ─────────────────┤ > │ perm_3 │ 3 │ 48 │ 7 │ + (48 + 7) = 55 > │ > └─────────┴─────────┴─────────┴─────────────────────┴──────── > ────────────────┘ > > -- > BR, > Hongtao