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.
Thanks in advance,
Roger
--
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 0f35b20006d..b9ec2bc9a03 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -21681,6 +21681,31 @@
split_double_mode (DImode, &operands[0], 1, &operands[2], &operands[3]);
})
+;; 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 ()"
+ [(set (match_dup 2) (match_dup 4))
+ (set (match_dup 5)
+ (vec_select:V4SI
+ (match_dup 6)
+ (parallel [(const_int 1) (const_int 0)
+ (const_int 2) (const_int 3)])))
+ (set (match_dup 3) (match_dup 7))]
+{
+ operands[4] = gen_lowpart (SImode, operands[1]);
+ operands[5] = gen_reg_rtx (V4SImode);
+ operands[6] = gen_lowpart (V4SImode, operands[1]);
+ operands[7] = gen_lowpart (SImode, operands[5]);
+ split_double_mode (DImode, &operands[0], 1, &operands[2], &operands[3]);
+})
+
(define_split
[(set (match_operand:SWI48x 0 "nonimmediate_operand")
(vec_select:SWI48x
@@ -22247,6 +22272,36 @@
]
(symbol_ref "true")))])
+;; 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;
+})
+
;; vmovq clears also the higher bits.
(define_insn "vec_set<mode>_0"
[(set (match_operand:VI8_AVX_AVX512F 0 "register_operand" "=v,v")
diff --git a/gcc/testsuite/gcc.target/i386/sse2-stv-7.c
b/gcc/testsuite/gcc.target/i386/sse2-stv-7.c
new file mode 100644
index 00000000000..95853b17594
--- /dev/null
+++ 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" } */
+
+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};
+}
+
+/* { dg-final { scan-assembler "pshufd" } } */
+/* { dg-final { scan-assembler "punpckldq" } } */
+/* { dg-final { scan-assembler-not "movq" } } */
+