[gcc r17-2678] x86 SSE: Use insertps to zero V4SI/V4SF elements in a single instruction.

Roger Sayle via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:b13e5bf8ed954c5e15a4f5ba515fe58e63c5714d

commit r17-2678-gb13e5bf8ed954c5e15a4f5ba515fe58e63c5714d
Author: Roger Sayle <[email protected]>
Date:   Fri Jul 24 07:21:48 2026 +0100

    x86 SSE: Use insertps to zero V4SI/V4SF elements in a single instruction.
    
    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 pxor 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.
    
    2026-07-24  Roger Sayle  <[email protected]>
                Hongtao Liu  <[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
            using vec_merge to select which elements to clear/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.

Diff:
---
 gcc/config/i386/i386-expand.cc                    | 22 +++++++++++++++-
 gcc/config/i386/sse.md                            | 32 ++++++++++++++++++++++-
 gcc/testsuite/gcc.target/i386/sse4_1-insertps-6.c | 23 ++++++++++++++++
 gcc/testsuite/gcc.target/i386/sse4_1-insertps-7.c | 26 ++++++++++++++++++
 4 files changed, 101 insertions(+), 2 deletions(-)

diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc
index fde8002eddbf..20914fcbad9d 100644
--- a/gcc/config/i386/i386-expand.cc
+++ b/gcc/config/i386/i386-expand.cc
@@ -14787,7 +14787,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.  */
@@ -18987,6 +18988,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.  */
@@ -19154,6 +19157,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) ^ 15)));
+      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) ^ 15)));
+      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 7521034e128a..a3fa2ec5f8f3 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -12945,6 +12945,36 @@
    (set_attr "prefix" "orig,orig,maybe_evex")
    (set_attr "mode" "V4SF")])
 
+;; Use sse4_1_insertps_v4s[if] to zero values in a vector.
+;; operands[3] indicates which elements 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
@@ -12977,7 +13007,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 000000000000..6ed110a0b4ed
--- /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 000000000000..15d095a6279d
--- /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 } } */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.