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 } } */