Hi Richard and Richard,
Many thanks for the reviews. Thanks for explaining that VEC_CONCAT
is in memory order, so that the first field needs to be SUBREG_BYTE (op) == 0
rather than subreg_lowpart_p (op) [I'm fortunate enough that for x86_64
both these conditions are equivalent].
I'm currently tweaking my patch to avoid subreg_lowpart_p (except
where the inner mode and the outer mode have the same size, in which
case I believe subreg_lowpart_p is equivalent to SUBREG_BYTE == 0).
However, to confirm that I now have this straight, do you agree that the
existing VEC_CONCAT transformation in simplify-rtx.cc (immediately before
my change) is broken/non-portable:
simplify_rtx.cc line 5589
/* (vec_concat:
(subreg_lowpart:N OP)
(vec_select:N OP P)) --> OP when P selects the high half
of the OP. */
if (GET_CODE (trueop0) == SUBREG
&& subreg_lowpart_p (trueop0)
&& GET_CODE (trueop1) == VEC_SELECT
&& SUBREG_REG (trueop0) == XEXP (trueop1, 0)
&& !side_effects_p (XEXP (trueop1, 0))
&& vec_series_highpart_p (op1_mode, mode, XEXP (trueop1, 1)))
return XEXP (trueop1, 0);
Notice the use of "subreg_lowpart_p (trueop0)" is the same idiom that
you're asking me to avoid/correct.
Presumably, if all else fails, adding a check for !WORDS_BIG_ENDIAN &&
!BYTES_BIG_ENDIAN to these transformations would be safe? In fact,
my test that vec_concat's second operand has the form "x >> N" only
makes sense for little-endian targets.
Thanks in advance,
Roger
--
> -----Original Message-----
> From: Richard Sandiford <[email protected]>
> Sent: 29 June 2026 09:24
> To: Richard Biener <[email protected]>
> Cc: Roger Sayle <[email protected]>; GCC Patches <gcc-
> [email protected]>; Jeffrey Law <[email protected]>
> Subject: Re: [PATCH] PR target/48609: RTL simplifications for x86 complex arg
> passing.
>
> Richard Biener <[email protected]> writes:
> > On Sat, Jun 27, 2026 at 8:00 PM Roger Sayle
> > <[email protected]>
> > wrote:
> >>
> >>
> >>
> >> 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?
> >
> > Thanks for catching - I wonder whether vector lane endianess can play
> > a role here and be different from word endianess?
>
> Yeah, I think endianness is a concern. vec_concat operates in memory order,
> so
> element 0 is always the first in memory. Similarly, subreg offsets operate in
> memory order, so SUBREG_BYTE 0 is always first in memory.
>
> So I think it's not:
>
> (vec_concat (subreg lowpart (subreg highpart))
>
> but rather:
>
> (vec_concat (subreg at offset 0) (subreg at offset N/2))
>
> Richard
>
> > I suppose there must be similar simplifications around, so you're
> > conviced this is fine?
> >
> > ISTR both arm and powerpc have/had their "difficulties" with vectors
> > and big-endian operation.
> >
> > Richard?
> >
> >>
> >>
> >>
> >>
> >>
> >> 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
> >>
> >> --
> >>
> >>