On Wed, Jul 15, 2026 at 5:29 PM Roger Sayle <[email protected]> wrote:
>
>
> While investigating improvements to x86's stv2 pass (to correctly cost
> moves between SI<->V4SI and DI<->V2DI), I noticed that we're currently
> relatively inefficient for DI mode transfers on 32-bit targets with
> SSE2, where reload ultimately decides to perform these moves via the
> stack. It's possible to do better by making the highpart and lowpart
> registers explicit before reload.
>
> Consider the test case below:
>
> typedef long long v2di __attribute__ ((__vector_size__ (16)));
>
> long long foo(v2di x)
> {
> return x[0];
> }
>
> long long ext();
> v2di mem;
>
> void bar()
> {
> long long x = ext();
> mem = (v2di){x,0};
> }
>
> where foo tests V2DI->DI mode, and bar tests DI->V2DI mode.
> Currently -m32 -O2 -msse2 generates:
>
> foo: subl $28, %esp
> movq %xmm0, 8(%esp)
> movl 8(%esp), %eax
> movl 12(%esp), %edx
> addl $28, %esp
> ret
>
> bar: subl $28, %esp
> call ext
> movl %eax, 8(%esp)
> movl %edx, 12(%esp)
> movq 8(%esp), %xmm0
> movaps %xmm0, mem
> addl $28, %esp
> ret
>
> With this patch, we now avoid going via the stack:
>
> foo: movd %xmm0, %eax
> pshufd $225, %xmm0, %xmm0
> movd %xmm0, %edx
> ret
>
> bar: subl $12, %esp
> call ext
> movd %eax, %xmm0
> movd %edx, %xmm1
> punpckldq %xmm1, %xmm0
> movaps %xmm0, mem
> addl $12, %esp
> ret
>
>
> 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-15 Roger Sayle <[email protected]>
>
> gcc/ChangeLog
> * config/i386/sse.md (define_split): Split *vec_extractv2di_0_sse
> before reload with TARGET_SSE2 on !TARGET_64BIT.
> (define_split): Likewise split *vec_concatv2di_0 before reload
> with TARGET_SSE2 on !TARGET_64BIT.
>
> gcc/testsuite/ChangeLog
> * gcc.target/i386/sse2-stv-7.c: New test case.
OK, with a couple of non-functional changes/additions:
+;; Split *vec_extractv2di_0_sse before reload with -m32 -msse2 -mno-sse4.1
+;; to avoid going via memory.
+(define_split
+ [(set (match_operand:DI 0 "register_operand")
+ (vec_select:DI
+ (match_operand:V2DI 1 "register_operand")
+ (parallel [(const_int 0)])))]
+ "TARGET_SSE2 && !TARGET_SSE4_1 && !TARGET_64BIT
+ && TARGET_INTER_UNIT_MOVES_FROM_VEC
+ && ix86_pre_reload_split ()"
Please always put !TARGET_64BIT at the first place in the insn
condition, so it is immediately clear that this is a 32-bit only
pattern.
+;; Split *vec_concatv2di_0 before reload with -m32 -msse2 -mno-sse4.1
+;; to avoid going via memory. Also helps reload with -m32 -msse4.1.
+(define_split
+ [(set (match_operand:V2DI 0 "register_operand")
+ (vec_concat:V2DI
+ (match_operand:DI 1 "register_operand")
+ (const_int 0)))]
+ "TARGET_SSE2 && !TARGET_64BIT
+ && TARGET_INTER_UNIT_MOVES_TO_VEC
+ && ix86_pre_reload_split ()"
+ [(const_int 0)]
+{
+ rtx lo, hi;
+ rtx op0 = operands[0];
+ split_double_mode (DImode, &operands[1], 1, &lo, &hi);
+ rtx tmp1 = gen_reg_rtx (V4SImode);
+ emit_insn (gen_vec_setv4si_0 (tmp1, CONST0_RTX (V4SImode), lo));
+ rtx result = gen_reg_rtx (V4SImode);
+ if (!TARGET_SSE4_1)
+ {
+ rtx tmp2 = gen_reg_rtx (V4SImode);
+ emit_insn (gen_vec_setv4si_0 (tmp2, CONST0_RTX (V4SImode), hi));
+ emit_insn (gen_vec_interleave_lowv4si (result, tmp1, tmp2));
+ }
+ else
+ emit_insn (gen_sse4_1_pinsrd (result, tmp1, hi, GEN_INT (2)));
+ emit_move_insn (op0, gen_lowpart (V2DImode, result));
+ DONE;
+})
I'd rewrite this pattern slightly to avoid "[(const_int)]", op0
variable, emit_move-insn and "DONE":
...
[(set (match_dup 0) (match_dup 3))]
...
{
...
rtx operands[3] = gen_lowpart (V2DImode, result));
}
Bonus points for swapping the arms of "if" to avoid negative test.
+++ b/gcc/testsuite/gcc.target/i386/sse2-stv-7.c
@@ -0,0 +1,23 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -msse2 -mno-sse4.1" } */
Please also add a companion test for -msse4.1 (scan for pinsrd,
scan-assembler-not movq) to also check alternative code path in the
second splitter.
Thanks,
Uros.