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.

Reply via email to