This patch adds an RTL simplification to simplify-rtx.cc to resolve
PR target/48609, inefficient passing of complex values on x86.
The motivating example, for the bugzilla PR, is:
typedef _Complex float SCtype;
extern SCtype bar;
void foo (SCtype x)
{
bar = x;
}
which currently with -O2 generates some spurious instructions:
foo: movdqa %xmm0, %xmm1
shufps $85, %xmm0, %xmm0
unpcklps %xmm0, %xmm1
movlps %xmm1, bar(%rip)
ret
with this patch we now generate (the optimal):
foo: movlps %xmm0, bar(%rip)
ret
The insight is that combine reports these attempts:
Trying 7, 4 -> 14:
7: {r111:SI#0=r105:DI 0>>0x20;clobber flags:CC;}
REG_UNUSED flags:CC
REG_DEAD r105:DI
4: r108:SI=r105:DI#0
14: r112:V2SF=vec_concat(r108:SI#0,r111:SI#0)
REG_DEAD r111:SI
REG_DEAD r108:SI
Failed to match this instruction:
(set (reg:V2SF 112 [ _7 ])
(vec_concat:V2SF (subreg:SF (subreg:SI (reg:DI 105 [ xD.2967 ]) 0) 0)
(subreg:SF (subreg:SI (zero_extract:DI (reg:DI 105 [ xD.2967 ])
(const_int 32 [0x20])
(const_int 32 [0x20])) 0) 0)))
Failed to match this instruction:
(set (reg:V2SF 112 [ _7 ])
(vec_concat:V2SF (subreg:SF (subreg:SI (reg:DI 105 [ xD.2967 ]) 0) 0)
(subreg:SF (subreg:SI (lshiftrt:DI (reg:DI 105 [ xD.2967 ])
(const_int 32 [0x20])) 0) 0)))
Conceptually, this pattern is the equivalent of the RTL expression
(vec_concat (subreg_lowpart (reg X)) (subreg_highpart (reg X)) which
of course is equal to the original (reg X). The ABI splits the
complex argument into __real__ and __imag__ parts, which it then
tries to recombine with vec_concat, resulting in this strange no-op.
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-06-27 Roger Sayle <[email protected]>
gcc/ChangeLog
PR target/48609
* simplify-rtx.cc (simplify_binary_operation_1) <case VEC_CONCAT>:
Optimize VEC_CONCAT of a low-part with a high-part as a no-op.
gcc/testsuite/ChangeLog
PR target/48609
* gcc.target/i386/pr48609.c: New test case.
Thanks in advance,
Roger
--
diff --git a/gcc/simplify-rtx.cc b/gcc/simplify-rtx.cc
index 872ae032869..fa135c8f7e0 100644
--- a/gcc/simplify-rtx.cc
+++ b/gcc/simplify-rtx.cc
@@ -5573,6 +5573,39 @@ simplify_ashift:
&& !side_effects_p (XEXP (trueop1, 0))
&& vec_series_highpart_p (op1_mode, mode, XEXP (trueop1, 1)))
return XEXP (trueop1, 0);
+ /* (vec_concat
+ (subreg_lowpart:N OP)
+ (subreg_highpart:N OP)) --> OP. */
+ if (GET_CODE (trueop0) == SUBREG
+ && GET_CODE (trueop1) == SUBREG
+ && op0_mode == op1_mode
+ && subreg_lowpart_p (trueop0))
+ {
+ rtx res = SUBREG_REG (trueop0);
+ if (GET_CODE (res) == SUBREG
+ && subreg_lowpart_p (res)
+ && known_eq (GET_MODE_SIZE (GET_MODE (res)),
+ GET_MODE_SIZE (op0_mode)))
+ res = SUBREG_REG (res);
+ if (known_eq (GET_MODE_SIZE (GET_MODE (res)),
+ GET_MODE_SIZE (mode)))
+ {
+ rtx tmp = SUBREG_REG (trueop1);
+ if (GET_CODE (tmp) == SUBREG
+ && subreg_lowpart_p (tmp)
+ && known_eq (GET_MODE_SIZE (GET_MODE (tmp)),
+ GET_MODE_SIZE (op1_mode)))
+ tmp = SUBREG_REG (tmp);
+ if (subreg_lowpart_p (trueop1)
+ && GET_CODE (tmp) == LSHIFTRT
+ && rtx_equal_p (XEXP (tmp, 0), res)
+ && CONST_INT_P (XEXP (tmp, 1))
+ && SCALAR_INT_MODE_P (GET_MODE (tmp))
+ && known_eq (GET_MODE_BITSIZE (op1_mode),
+ INTVAL (XEXP (tmp, 1))))
+ return gen_lowpart (mode, res);
+ }
+ }
}
return 0;
diff --git a/gcc/testsuite/gcc.target/i386/pr48609.c
b/gcc/testsuite/gcc.target/i386/pr48609.c
new file mode 100644
index 00000000000..8af9d883761
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr48609.c
@@ -0,0 +1,13 @@
+/* PR target/48609 */
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -msse2" } */
+typedef _Complex float SCtype;
+extern SCtype bar;
+void foo (SCtype x)
+{
+ bar = x;
+}
+
+/* { dg-final { scan-assembler-not "movdqa" } } */
+/* { dg-final { scan-assembler-not "shufps" } } */
+/* { dg-final { scan-assembler-not "unpcklps" } } */