https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126619

--- Comment #7 from GCC Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Roger Sayle <[email protected]>:

https://gcc.gnu.org/g:45c9868f1b20481f4eebdcce8fffc7d61724095b

commit r17-3636-g45c9868f1b20481f4eebdcce8fffc7d61724095b
Author: Roger Sayle <[email protected]>
Date:   Tue Aug 25 08:50:48 2026 -0500

    PR target/126619: Improve V4SF vector initialization on x86 SSE.

    This patch addresses PR target/126619, a performance regression caused by
    my recent update of SSE vector initialization on x86_64.  The new idiom
    attempts to benefit from the implicit zero extension provided by movss
    and movd, but this causes problems for (V4SF) cases were the (SFmode)
    value is already in a register.  Without newer extensions, GCC's approach
    to zero extension (i.e. vec_init of (V4SF){ x, 0, 0, 0 }) is to perform
    an interunit transfer to a general purpose integer register, and then
    transfer the value back again.  Inter-unit moves are expensive, especially
    on older microarchitectures.

    The problem is fixed in several ways.  The first is to tweak register
    preferencing in vec_set<mode>_0's define_insn, so that general registers
    are only used if the source/destination is already an integer GPR.
    This changes reload from generating (two instructions):

            movd %xmm0, %eax
            movd %eax, %xmm0

    to instead using:

            pxor %xmm1, %xmm1
            movss %xmm0, %xmm1
            movaps %xmm1, %xmm0

    which requires 3 instructions, and 1 extra register, but requires
    no inter-unit moves.  This matches what clang/llvm does.

    However, it's possible to do better, borrowing an idiom from
    the middle-end's expansion of integer zero-extensions.

            pslldq  $12, %xmm0
            psrldq  $12, %xmm0

    uses two instructions, and doesn't require an extra register.
    Indeed, initializing the vector (V4SF){ 0, 0, 0, x } can be
    done in a single instruction, as it doesn't require a "right"
    shift.

    Additionally, for cases such as (V4SF){ a, b, c, d }, where
    there is no benefit from zero extension, we should continue
    using GCC's original CONCAT of V2SF idiom, avoiding any overhead
    of zero extension (Hongtao's suggestion in the Bugzilla PR).

    Additionally, there are some additional V4SF initialization
    tweaks.  When loading from memory, where zero extension is
    free "onevar_perm"s should construct { x, 0, 0, 0 } then
    perform a shuffle using shufps, but when the source is a
    register, it should construct { 0, 0, 0, x } (using the
    single shift instruction described above), and perform a
    modified shuffle using shufps from there.

    With TARGET_SSE4_1, the first insertps can be used to clear
    (initialize) all the other elements to zero, and the remaining
    non-zero elements can be inserted with regular insertps.

    As explained above, optimal code generation depends upon
    knowing whether the source elements are in memory or in
    registers.  Currently this decision is made during RTL
    expansion even though the final allocations/sources aren't
    known until reload [CSE can convert a MEM to a REG, and
    reload can spill a REG to a MEM].  Things work fine when the
    tree-ssa optimizers correctly predict things well, but there
    are one two cases than could still be improved (in either
    combine or peephole2) where late changes are made to the
    RTL.

    2026-08-25  Roger Sayle  <[email protected]>
                Hongtao Liu  <[email protected]>

    gcc/ChangeLog
            PR target/126619
            * config/i386/i386-expand.cc
            (ix86_expand_vector_init_one_nonzero) <case E_V4SFmode>:
            Improved initialization of one non-zero element V4SF vectors.
            (ix86_expand_vector_init_v4sf): Reuse the above function
            ix86_expand_vector_init_one_nonzero where possible.  Various
            improvements.  Fall back to using (the original)
            ix86_expand_vector_init_concat in the general case, when
            SSE 4.1 instructions aren't available.

            * config/i386/sse.md (vec_set<mode>_0): Prefer to avoid
            inter-unit moves to general purpose registers in reload.
            (vec_setv4sf_sse4_1): Remove asterisk to expose to i386-expand.
            (sse4_1_insertps_v4sf_init): Variant of insertps that clears
            all the other elements of the destination to zero.
            (sse2_insertps_v4sf_3): Implementation of the above instruction
            available on SSE2 by using the pslldq instruction.

    gcc/testsuite/ChangeLog
            PR target/126619
            * gcc.target/i386/avx-init-v4sf-1.c: Update test case.
            * gcc.target/i386/avx-init-v4sf-2.c: Likewise.
            * gcc.target/i386/avx2-init-v4sf-1.c: Likewise.
            * gcc.target/i386/sse-init-v4sf-2.c: Likewise.
            * gcc.target/i386/sse-init-v4sf-3.c: Likewise.
            * gcc.target/i386/sse2-init-v4sf-1.c: Likewise.
            * gcc.target/i386/sse2-init-v4sf-2.c: Likewise.
            * gcc.target/i386/sse4_1-init-v4sf-2.c: Likewise.
            * gcc.target/i386/sse4_1-init-v4sf-3.c: Likewise.

Reply via email to