Re: [x86 SSE PATCH] Improved vector initialization/construction.

"H.J. Lu" <[email protected]> Sun, 2 Aug 2026 11:06:23 +0800
Newsgroups gmane.comp.gcc.patches
Message-ID <CAMe9rOq+_nCCMGX3c=6ODKPWxEsGWrWZB1__CfD+DDbhm-8yVQ@mail.gmail.com>
On Wed, Jul 22, 2026 at 4:05 PM Hongtao Liu <[email protected]> wrote:
>
> On Tue, Jul 21, 2026 at 5:11 PM Roger Sayle <[email protected]> wrote:
> >
> >
> > 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).
> >
>
>
> +          else if (TARGET_SSE2)
> +            {
> +              rtx tmp2 = gen_reg_rtx (V2DImode);
> +              emit_move_insn (tmp2, gen_lowpart (V2DImode, tmp));
> +              emit_insn (gen_vec_shl_v2di (target, tmp, GEN_INT (64)));
>
> Looks like a typo, should be tmp2 instead of tmp?
>
> +    case E_V2DFmode:
> +      if (!REG_P (var) || !MEM_P (var))
> +        var = force_reg (DFmode, var);
> +      x = gen_rtx_VEC_CONCAT (V2DFmode, var, CONST0_RTX (DFmode));
> +      if (!one_var)
> +        emit_insn (gen_rtx_SET (target, x));
> +      else if (TARGET_SSE2)
> +        {
> +          tmp = gen_reg_rtx (V2DFmode);
> +          emit_insn (gen_rtx_SET (tmp, x));
> +          emit_insn (gen_vec_shl_v2df (target, tmp, GEN_INT (64)));
> +        }
> +      else
> +        {
> +          rtx tmp1 = gen_reg_rtx (V2DFmode);
> +          emit_insn (gen_rtx_SET (tmp1, x));
> +          rtx tmp2 = gen_reg_rtx (V4SImode);
> +          emit_move_insn (tmp2, gen_lowpart (V4SImode, tmp1));
> +          emit_insn (gen_sse_shufps_v4si (tmp2, tmp2, tmp2,
> +                                          GEN_INT (2), GEN_INT (3),
> +                                          GEN_INT (4), GEN_INT (5)));
> +          emit_move_insn (target, gen_lowpart (V2DFmode, tmp2));
> +        }
>
> vec_initv2df is guarded under TARGET_SSE2, so I think the else part is
> redundant.
>
> +static int
> +nonzero_int_const_count (rtx *ops, int n)
> +{
> +  int result = 0;
> +  int i;
> +  for (i = 0; i < n; i++)
> +    if (CONST_INT_P (ops[i]) && ops[i] != const0_rtx)  ---- here
> check CONST_INT_P (ops[i])
> +      result++;
> +  return result;
> +}
>
> ...
> +  else if (nonzero_int_const_count (ops, 4) >= 2)
> +    {
> +      rtx csts[4];
> +      int i;
> +      for (i = 0; i < 4; i++)
> +        if (CONST_DOUBLE_P (ops[i]))    ---- here check
> CONST_DOUBLE_P (ops[i]))
> +          {
> +            csts[i] = ops[i];
> +            vars[i] = CONST0_RTX (SFmode);
> +          }
> +        else
> +          {
> +            csts[i] = CONST0_RTX (SFmode);
> +            vars[i] = ops[i];
> +          }
>
> nonzero_int_const_count  check CONST_INT_P, but in the float cases, it
> should be CONST_DOUBLE_P? it's always 0 the float helpers(v4sf/v8f)?
>
>
> +    case E_V4DImode:
> +      if (TARGET_AVX2 && (TARGET_64BIT || MEM_P (var)))
> +        {
> +          if (!REG_P (var) || !MEM_P (var))
> +            var = force_reg (DImode, var);
>
> !REG_P (var) && !MEM_P (var)?
>
> +    case E_V4DFmode:
> +      if (TARGET_AVX2)
> +        {
> +          if (!REG_P (var) || !MEM_P (var))
> +            var = force_reg (DFmode, var);
> Ditto
>
> +      return true;
> +    case E_V2DFmode:
> +      if (!REG_P (var) || !MEM_P (var))
> +        var = force_reg (DFmode, var);
> +      x = gen_rtx_VEC_CONCAT (V2DFmode, var, CONST0_RTX (DFmode));
> Ditto.
>
> +      if (TARGET_64BIT || MEM_P (var))
> +        {
> +          if (!REG_P (var) || !MEM_P (var))
> +            var = force_reg (DImode, var);
> +          x = gen_rtx_VEC_CONCAT (V2DImode, var, CONST0_RTX (DImode));
> Ditto.
>
> +/* Helper function.  Determine if the given OPS array of size N
> +   contains only zeros and one other value (possible repeated).
> +   If TRUE, *VAR returns the value, PERM[i] contains 0 for for
>
> Double *for* in the comments.
>
> +/* Helper function.  Determine if the given OPS array of size N
> +   contains only zeros and two other values (possible repeated).
> +   If TRUE, VARS returns the values, PERM[i] contains 0 for for
>
> Ditto.
>
> Could you also add some testcases to cover your changes, it can be in
> a separate commit.
>

With

commit f0bd50e831792692ffcedf288b312167a9339260
Author: Roger Sayle <[email protected]>
Date:   Sat Aug 1 17:51:02 2026 +0100

    x86 SSE: Improved vector initialization/construction.

I got

FAIL: gcc.target/i386/sse-init-v2df-1.c scan-assembler-times movq 10

I also got

FAIL: gcc.target/i386/sse2-init-v2df-1.c scan-assembler-times movsd 2
FAIL: gcc.target/i386/sse2-init-v2df-1.c scan-assembler-times unpcklpd 3
FAIL: gcc.target/i386/sse2-init-v2df-2.c scan-assembler-times movsd 3
FAIL: gcc.target/i386/sse2-init-v2df-2.c scan-assembler-times unpcklpd 2
FAIL: gcc.target/i386/sse2-init-v2di-3.c scan-assembler-times movq 9
FAIL: gcc.target/i386/sse2-init-v2di-3.c scan-assembler-times punpcklqdq 3
FAIL: gcc.target/i386/sse2-init-v2di-4.c scan-assembler-times movd 4
FAIL: gcc.target/i386/sse2-init-v2di-4.c scan-assembler-times movq 6
FAIL: gcc.target/i386/sse2-init-v2di-4.c scan-assembler-times punpcklqdq 2

with

$ make check-gcc RUNTESTFLAGS="--target_board='unix{-m32\
-march=x86-64-v2,-march=x86-64-v2}'  i386.exp=sse2-init-*.c"


-- 
H.J.