On Mon, Jul 20, 2026 at 6:37 AM Roger Sayle <[email protected]> wrote:
>
>
> 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.
BTW, AVX10.2 support vmovd xmm0, xmm0 to zero extend element 0.
>
> 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_MERGE canonicalization prefers the register first and zero second,
complementing the mask (gcc/simplify-rtx.cc:7808).
The final RTL always matches the second *sse4_1_insertps_*_zero pattern.
So just keep one named pattern in canonical form:
(vec_merge reg const0 preserve_mask)
Then make ix86_expand_vector_set emit (~(1 << elt) & 15) as the preserve mask.
+ 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);
+
...
+;; Use sse4_1_insertps_v4s[if] to zero values in a vector.
+;; Operands[3] indicates which bits 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);
+ return "%vinsertps\t{%3, %d1, %0|%0, %d1, %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")])
--
BR,
Hongtao