]> git.ipfire.org Git - thirdparty/gcc.git/commit
x86 SSE: Improved vector initialization/construction.
authorRoger Sayle <roger@nextmovesoftware.com>
Sat, 1 Aug 2026 16:51:02 +0000 (17:51 +0100)
committerRoger Sayle <roger@nextmovesoftware.com>
Sat, 1 Aug 2026 16:53:52 +0000 (17:53 +0100)
commitf0bd50e831792692ffcedf288b312167a9339260
tree2ab9207729a69850c530fd2910a55973d578e19d
parent3f57fe8cc686317b22b32e0c77b7c2eddae022cb
x86 SSE: Improved vector initialization/construction.

This patch is a reorganization of x86's vector initialization (vec_init)
functionality to generate more efficient implementations in most/many
cases.  Previously, for most (128-bit and 256-bit) vectors types,
i386-expand.cc made use of "concat" recursion to divide-and-conquor;
splitting each vector into upper and lower halves, initializing them,
then concatenating the results together.  Simple and orthogonal, but
alas inefficient.  This idiom is unable to take advantage of SSE's
zero extension semantics, shuffle/permutation instructions, byte-level
shifts, element insertion instructions nor vector-mode logic operations.
Unfortunately the reality is that these ISAs are irregular, as are the
patterns provided by the backend expose their instructions (which are
often available in one mode but not another).

The patch below recognizes/accepts these asymmetries, and provides
"custom" vector initialization functions for most 128-bit and 256-bit
vector modes.  There are too many optimization/improvements to list
them all, but some examples are given below:

v4si f1(int x, int y) { return (v4si){x,y,0,0}; }

Before with -O2:
f1_old: movd    %edi, %xmm0
        movd    %esi, %xmm1
        punpckldq       %xmm1, %xmm0
        movq    %xmm0, %xmm0
        ret

After with -O2:
f1_new: movd    %edi, %xmm0
        movd    %esi, %xmm1
        punpckldq       %xmm1, %xmm0
        ret

v4si f2(int x) { return (v4si){0,x,x,0}; }

Before with -O2:
f2_old: movd    %edi, %xmm2
        pxor    %xmm0, %xmm0
        movd    %edi, %xmm1
        punpckldq       %xmm2, %xmm0
        punpcklqdq      %xmm1, %xmm0
        ret

f2_new: movd    %edi, %xmm0
        shufps  $65, %xmm0, %xmm0
        ret

v4si f3(int x) { return (v4si){x,1,x,2}; }

Before with -O2:
f3_old: movl    $2, %eax
        movd    %edi, %xmm0
        movd    %eax, %xmm2
        movl    $1, %eax
        movdqa  %xmm0, %xmm1
        movd    %eax, %xmm3
        punpckldq       %xmm2, %xmm1
        punpckldq       %xmm3, %xmm0
        punpcklqdq      %xmm1, %xmm0
        ret

After with -O2:
f3_new: movd    %edi, %xmm0
        shufps  $68, %xmm0, %xmm0
        por     .LC0(%rip), %xmm0
        ret

v16qi f4(char x) { return (v16qi){x,0,0,0,0,0,0,0,0,x,0,0,0,0,0,0}; }

Before with -O2 -mavx2:
f4_old: vmovd   %edi, %xmm0
        xorl    %eax, %eax
        vpxor   %xmm1, %xmm1, %xmm1
        vpinsrb $1, %eax, %xmm0, %xmm0
        vpinsrb $1, %edi, %xmm1, %xmm1
        vpmovzxwd       %xmm0, %xmm0
        vpmovzxwd       %xmm1, %xmm1
        vpmovzxdq       %xmm1, %xmm1
        vpmovzxdq       %xmm0, %xmm0
        vpunpcklqdq     %xmm1, %xmm0, %xmm0
        ret

After with -O2 -mavx2:
f4_new: movzbl  %dil, %eax
        vmovd   %eax, %xmm0
        vpinsrb $9, %edi, %xmm0, %xmm0
        ret

Unfortunately, despite all of the goodness there remains one testsuite
regression: avx512vl-concatv4si-1.c whose f2 function currently expects
3 instructions before the return:

orig: vmovd   (%rdi), %xmm2
        vpinsrd $1, 4(%rdi), %xmm2, %xmm1
        vpunpcklqdq     %xmm1, %xmm0, %xmm16
        ret

where actually an optimal implementation should require only two:

ideal: vpinsrd $2, (%rdi), %xmm0, %xmm0
        vpinsrd $3, 4(%rdi), %xmm0, %xmm0
ret

but unfortunately with this patch we currently (for now) generate:

curr:   vmovd   %xmm0, %eax
        vpextrd $1, %xmm0, %edx
        vmovd   %eax, %xmm0
        vpinsrd $1, %edx, %xmm0, %xmm0
        vpinsrd $2, (%rdi), %xmm0, %xmm0
        vpinsrd $3, 4(%rdi), %xmm0, %xmm0
        vmovdqa32       %xmm0, %xmm16
        ret

which actually contains our two optimal instructions, but between
combine, simplify-rtx and sse.md's define_insn_and_splits, we fail
to notice that the remaining operations (converting V2SI to V4SI)
are a no-op.  I beg the reviewers'/maintainers' indulgence to allow
this to fail for the time being, to be solved in a follow-up patch.
This current patch is large enough already, and this remaining quirk
needs to be resolved outside the RTL expansion pass, in the later
RTL optimizers (where it is currently a missed optimization).

2026-08-01  Roger Sayle  <roger@nextmovesoftware.com>
    Hongtao Liu  <hongtao.liu@intel.com>

gcc/ChangeLog
* config/i386/i386-expand.cc (ix86_expand_vector_init_one_nonzero):
Improved implementations for V2DI, V2DF, V4SI, V4SF, V4DI and V4DF
modes.  Return false for V2SI and V2SF modes if the one non-zero
element isn't the first/lowest.  Improved implementations for V8HI,
V16QI, V2HI and V8QI modes.
(nonzero_int_const_count): New helper function to count the
number of non-zero integer constants in a given array.
(nonzero_float_const_count): Likewise for SFmode floats.
(nonzero_double_const_count): Likewise for DFmode doubles.
(ix86_expand_vector_init_insert): New function to initialize a
V4SI, V8HI or V16QI vector using a sequence of pinsr[bwd] insns.
(onevar_perm_p): New local helper function.
(twovar_perm_p): Likewise.
(ix86_expand_vector_init_v2di): New mode-specific function.
(ix86_expand_vector_init_v2df): Likewise.
(ix86_expand_vector_init_v4si): Likewise.
(ix86_expand_vector_init_v4sf): Likewise.
(ix86_expand_vector_init_v8hi): Likewise.
(ix86_expand_vector_init_v16qi): Likewise.
(ix86_expand_vector_init_v4di): Likewise.
(ix86_expand_vector_init_v4df): Likewise.
(ix86_expand_vector_init_v8si): Likewise.
(ix86_expand_vector_init_v8sf): Likewise.
(ix86_expand_vector_init_general): Call the above custom helper
functions for the relevant modes.
* config/i386/sse.md (*vec_interleave_lowv4si_sse): New pattern
for (V4SImode) unpcklps on TARGET_SSE but not TARGET_SSE2.

gcc/testsuite/ChangeLog
* gcc.target/i386/avx512vl-concatv4si-1.c: Tweak exisiting test.
* gcc.target/i386/avx-init-v16qi-1.c: New test case.
* gcc.target/i386/avx-init-v2df-1.c: Likewise.
* gcc.target/i386/avx-init-v2df-2.c: Likewise.
* gcc.target/i386/avx-init-v2di-1.c: Likewise.
* gcc.target/i386/avx-init-v2di-2.c: Likewise.
* gcc.target/i386/avx-init-v4sf-1.c: Likewise.
* gcc.target/i386/avx-init-v4sf-2.c: Likewise.
* gcc.target/i386/avx-init-v4si-1.c: Likewise.
* gcc.target/i386/avx-init-v8hi-1.c: Likewise.
* gcc.target/i386/avx2-init-v4sf-1.c: Likewise.
* gcc.target/i386/avx2-init-v4si-1.c: Likewise.
* gcc.target/i386/avx2-init-v4si-2.c: Likewise.
* gcc.target/i386/avx512vl-init-v2di-1.c: Likewise.
* gcc.target/i386/avx512vl-init-v2di-2.c: Likewise.
* gcc.target/i386/avx512vl-init-v4si-1.c: Likewise.
* gcc.target/i386/sse-init-v16qi-1.c: Likewise.
* gcc.target/i386/sse-init-v2df-1.c: Likewise.
* gcc.target/i386/sse-init-v2df-2.c: Likewise.
* gcc.target/i386/sse-init-v2di-1.c: Likewise.
* gcc.target/i386/sse-init-v2di-2.c: Likewise.
* gcc.target/i386/sse-init-v4sf-2.c: Likewise.
* gcc.target/i386/sse-init-v4sf-3.c: Likewise.
* gcc.target/i386/sse-init-v4si-1.c: Likewise.
* gcc.target/i386/sse-init-v8hi-1.c: Likewise.
* gcc.target/i386/sse2-init-v16qi-2.c: Likewise.
* gcc.target/i386/sse2-init-v16qi-3.c: Likewise.
* gcc.target/i386/sse2-init-v2df-1.c: Likewise.
* gcc.target/i386/sse2-init-v2df-2.c: Likewise.
* gcc.target/i386/sse2-init-v2di-3.c: Likewise.
* gcc.target/i386/sse2-init-v2di-4.c: Likewise.
* gcc.target/i386/sse2-init-v4sf-1.c: Likewise.
* gcc.target/i386/sse2-init-v4sf-2.c: Likewise.
* gcc.target/i386/sse2-init-v4si-2.c: Likewise.
* gcc.target/i386/sse2-init-v8hi-2.c: Likewise.
* gcc.target/i386/sse4_1-init-v16qi-2.c: Likewise.
* gcc.target/i386/sse4_1-init-v2df-1.c: Likewise.
* gcc.target/i386/sse4_1-init-v2df-2.c: Likewise.
* gcc.target/i386/sse4_1-init-v2di-2.c: Likewise.
* gcc.target/i386/sse4_1-init-v2di-3.c: Likewise.
* gcc.target/i386/sse4_1-init-v4sf-2.c: Likewise.
* gcc.target/i386/sse4_1-init-v4sf-3.c: Likewise.
* gcc.target/i386/sse4_1-init-v4si-2.c: Likewise.
45 files changed:
gcc/config/i386/i386-expand.cc
gcc/config/i386/sse.md
gcc/testsuite/gcc.target/i386/avx-init-v16qi-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/avx-init-v2df-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/avx-init-v2df-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/avx-init-v2di-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/avx-init-v2di-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/avx-init-v4sf-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/avx-init-v4sf-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/avx-init-v4si-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/avx-init-v8hi-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/avx2-init-v4sf-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/avx2-init-v4si-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/avx2-init-v4si-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/avx512vl-concatv4si-1.c
gcc/testsuite/gcc.target/i386/avx512vl-init-v2di-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/avx512vl-init-v2di-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/avx512vl-init-v4si-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse-init-v16qi-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse-init-v2df-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse-init-v2df-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse-init-v2di-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse-init-v2di-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse-init-v4sf-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse-init-v4sf-3.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse-init-v4si-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse-init-v8hi-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse2-init-v16qi-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse2-init-v16qi-3.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse2-init-v2df-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse2-init-v2df-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse2-init-v2di-3.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse2-init-v2di-4.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse2-init-v4sf-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse2-init-v4sf-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse2-init-v4si-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse2-init-v8hi-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse4_1-init-v16qi-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse4_1-init-v2df-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse4_1-init-v2df-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse4_1-init-v2di-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse4_1-init-v2di-3.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse4_1-init-v4sf-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse4_1-init-v4sf-3.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/sse4_1-init-v4si-2.c [new file with mode: 0644]