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" } } */

Reply via email to