This patch teaches the x86 backend that the SSE4.1 insertps instruction
can be used/abused to clear one or more elements of a V4SI or V4SF vector
in a single instruction (i.e. without requiring xor to clear a second
register).

Consider the test case

typedef int v4si __attribute__ ((__vector_size__ (16)));
v4si foo(v4si x) { x[2]=0; return x; }

Currently with -O2 -mavx2, we generate:

foo:    xorl    %eax, %eax
        vpinsrd $2, %eax, %xmm0, %xmm0
        ret

with this patch we now generate:

foo:    vinsertps       $4, %xmm0, %xmm0, %xmm0
        ret

For the more complicated example:

v4si bar(v4si x) { x[1]=0; x[3]=0; return x; }

previously, we'd generate:

bar:    xorl    %eax, %eax
        vpinsrd $1, %eax, %xmm0, %xmm0
        vpinsrd $3, %eax, %xmm0, %xmm0
        ret

with this patch we now generate:

bar:    vinsertps       $10, %xmm0, %xmm0, %xmm0
        ret


One improvement that I'll leave to an i386/SSE expert, is that setting
elements 1, 2 and 3 [i.e. zero extending element 0] still falls back
to the existing patterns (and tests for this are commented out in the
new test cases).  Tweaking sse_movss_v4si to consider using insertps
requires expertise in register preferencing and instruction attributes
that I'm happy to leave to someone else.

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-07-19  Roger Sayle  <[email protected]>

gcc/ChangeLog
        * config/i386/i386-expand.cc (ix86_expand_vec_set_builtin): Don't
        force op1 to a register when it is CONST0_RTX (mode1).
        (ix86_expand_vector_set_var): For now, force VAL to a register.
        (ix86_expand_vector_set): If val is CONST0_RTX, expand using
        the new sse4_1_insertps_v4s[if]_zero patterns on TARGET_SSE4_1.
        Otherwise, force val to a register (restoring previous behaviour).
        * config/i386/sse.md (sse4_1_insertps_<mode>_zero): New insn
        with using vec_merge to select which elements to clear.
        (*sse4_1_insertps_<mode>_zero): Likewise, a variant with the
        const0_operand second, and operand3 selecting elements to preserve.
        (vec_set<mode>): Tweak operand 1 to allow both REGs and CONST0_RTX.

gcc/testsuite/ChangeLog
        * gcc.target/i386/sse4_1-insertps-6.c: New test case.
        * gcc.target/i386/sse4_1-insertps-7.c: Likewise.


Thanks in advance,
Roger
--

diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc
index a448e470f73..9f75b2f35c8 100644
--- a/gcc/config/i386/i386-expand.cc
+++ b/gcc/config/i386/i386-expand.cc
@@ -14783,7 +14783,8 @@ ix86_expand_vec_set_builtin (tree exp)
     op1 = convert_modes (mode1, GET_MODE (op1), op1, true);
 
   op0 = force_reg (tmode, op0);
-  op1 = force_reg (mode1, op1);
+  if (op1 != CONST0_RTX (mode1))
+    op1 = force_reg (mode1, op1);
 
   /* OP0 is the source of these builtin functions and shouldn't be
      modified.  Create a copy, use it and return it as target.  */
@@ -18983,6 +18984,8 @@ ix86_expand_vector_set_var (rtx target, rtx val, rtx 
idx)
   rtx valv,idxv,constv,idx_tmp;
   bool ok = false;
 
+  val = force_reg (GET_MODE_INNER (mode), val);
+
   /* 512-bits vector byte/word broadcast and comparison only available
      under TARGET_AVX512BW, break 512-bits vector into two 256-bits vector
      when without TARGET_AVX512BW.  */
@@ -19150,6 +19153,23 @@ ix86_expand_vector_set (bool mmx_ok, rtx target, rtx 
val, int elt)
   machine_mode mmode = VOIDmode;
   rtx (*gen_blendm) (rtx, rtx, rtx, rtx);
 
+  if (TARGET_SSE4_1 && mode == V4SImode && val == const0_rtx)
+    {
+      emit_insn (gen_sse4_1_insertps_v4si_zero (target, target,
+                                               CONST0_RTX (V4SImode),
+                                               GEN_INT (1 << elt)));
+      return;
+    }
+  if (TARGET_SSE4_1 && mode == V4SFmode && val == CONST0_RTX (SFmode))
+    {
+      emit_insn (gen_sse4_1_insertps_v4sf_zero (target, target,
+                                               CONST0_RTX (V4SFmode),
+                                               GEN_INT (1 << elt)));
+      return;
+    }
+
+  val = force_reg (GET_MODE_INNER (mode), val);
+
   switch (mode)
     {
     case E_V2SImode:
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 0f35b20006d..1b4863d844d 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -12656,6 +12656,56 @@
    (set_attr "prefix" "orig,orig,maybe_evex")
    (set_attr "mode" "V4SF")])
 
+;; Use sse4_1_insertps_v4s[if] to zero values in a vector.
+;; variant 1: operands[3] indicates which bit to clear.
+(define_insn "sse4_1_insertps_<mode>_zero"
+  [(set (match_operand:VI4F_128 0 "register_operand" "=x,v")
+       (vec_merge:VI4F_128
+         (match_operand:VI4F_128 2 "const0_operand")
+         (match_operand:VI4F_128 1 "register_operand" "0,v")
+         (match_operand:SI 3 "const_0_to_15_operand")))]
+  "TARGET_SSE4_1
+   && IN_RANGE (INTVAL (operands[3]), 1, 14)"
+  "@
+  insertps\t{%3, %1, %0|%0, %1, %3}
+  vinsertps\t{%3, %1, %1, %0|%0, %1, %1, %3}"
+  [(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")])
+
+;; variant 2: operands[3] indicates which bit to preserve.
+(define_insn "*sse4_1_insertps_<mode>_zero"
+  [(set (match_operand:VI4F_128 0 "register_operand" "=x,v")
+       (vec_merge:VI4F_128
+         (match_operand:VI4F_128 1 "register_operand" "0,v")
+         (match_operand:VI4F_128 2 "const0_operand")
+         (match_operand:SI 3 "const_0_to_15_operand")))]
+  "TARGET_SSE4_1
+   && IN_RANGE (INTVAL (operands[3]), 1, 14)"
+{
+  operands[3] = GEN_INT (INTVAL (operands[3]) ^ 15);
+  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
@@ -12688,7 +12738,7 @@
 
 (define_expand "vec_set<mode>"
   [(match_operand:V_128 0 "register_operand")
-   (match_operand:<ssescalarmode> 1 "register_operand")
+   (match_operand:<ssescalarmode> 1 "reg_or_0_operand")
    (match_operand 2 "vec_setm_sse41_operand")]
   "TARGET_SSE"
 {
diff --git a/gcc/testsuite/gcc.target/i386/sse4_1-insertps-6.c 
b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-6.c
new file mode 100644
index 00000000000..6ed110a0b4e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-6.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -msse4.1" } */
+
+typedef int v4si __attribute__ ((__vector_size__ (16)));
+
+v4si sz_0(v4si x) { x[0]=0; return x; }
+v4si sz_1(v4si x) { x[1]=0; return x; }
+v4si sz_2(v4si x) { x[2]=0; return x; }
+v4si sz_3(v4si x) { x[3]=0; return x; }
+
+v4si sz_01(v4si x) { x[0]=0; x[1]=0; return x; }
+v4si sz_02(v4si x) { x[0]=0; x[2]=0; return x; }
+v4si sz_03(v4si x) { x[0]=0; x[3]=0; return x; }
+v4si sz_12(v4si x) { x[1]=0; x[2]=0; return x; }
+v4si sz_13(v4si x) { x[1]=0; x[3]=0; return x; }
+v4si sz_23(v4si x) { x[2]=0; x[3]=0; return x; }
+
+v4si sz_012(v4si x) { x[0]=0; x[1]=0; x[2]=0; return x; }
+v4si sz_013(v4si x) { x[0]=0; x[1]=0; x[3]=0; return x; }
+v4si sz_023(v4si x) { x[0]=0; x[2]=0; x[3]=0; return x; }
+// v4si sz_123(v4si x) { x[1]=0; x[2]=0; x[3]=0; return x; }
+
+/* { dg-final { scan-assembler-times "\tv?insertps\t" 13 } } */
diff --git a/gcc/testsuite/gcc.target/i386/sse4_1-insertps-7.c 
b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-7.c
new file mode 100644
index 00000000000..15d095a6279
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-7.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -msse4.1" } */
+
+typedef float v4sf __attribute__ ((__vector_size__ (16)));
+
+v4sf sz_0(v4sf x) { x[0]=0.0f; return x; }
+v4sf sz_1(v4sf x) { x[1]=0.0f; return x; }
+v4sf sz_2(v4sf x) { x[2]=0.0f; return x; }
+v4sf sz_3(v4sf x) { x[3]=0.0f; return x; }
+
+v4sf sz_01(v4sf x) { x[0]=0.0f; x[1]=0.0f; return x; }
+v4sf sz_02(v4sf x) { x[0]=0.0f; x[2]=0.0f; return x; }
+v4sf sz_03(v4sf x) { x[0]=0.0f; x[3]=0.0f; return x; }
+v4sf sz_12(v4sf x) { x[1]=0.0f; x[2]=0.0f; return x; }
+v4sf sz_13(v4sf x) { x[1]=0.0f; x[3]=0.0f; return x; }
+v4sf sz_23(v4sf x) { x[2]=0.0f; x[3]=0.0f; return x; }
+
+v4sf sz_012(v4sf x) { x[0]=0.0f; x[1]=0.0f; x[2]=0.0f; return x; }
+v4sf sz_013(v4sf x) { x[0]=0.0f; x[1]=0.0f; x[3]=0.0f; return x; }
+v4sf sz_023(v4sf x) { x[0]=0.0f; x[2]=0.0f; x[3]=0.0f; return x; }
+
+#if 0
+v4sf sz_123(v4sf x) { x[1]=0.0f; x[2]=0.0f; x[3]=0.0f; return x; }
+#endif
+
+/* { dg-final { scan-assembler-times "\tv?insertps\t" 13 } } */

Reply via email to