https://gcc.gnu.org/g:ccd676ca79aa783e9e52de213a5619265ec9a8d2

commit r17-2580-gccd676ca79aa783e9e52de213a5619265ec9a8d2
Author: Roger Sayle <[email protected]>
Date:   Tue Jul 21 09:45:15 2026 +0100

    i386: Split DI<->V2DI patterns before reload with -m32.
    
    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
    
    2026-07-21  Roger Sayle  <[email protected]>
                Uros Bizjak  <[email protected]>
    
    gcc/ChangeLog
            * config/i386/sse.md (define_split): Split *vec_extractv2di_0_sse
            before reload on !TARGET_64BIT with TARGET_SSE2.
            (define_split): Likewise split *vec_concatv2di_0 before reload
            on !TARGET_64BIT with TARGET_SSE2.
    
    gcc/testsuite/ChangeLog
            * gcc.target/i386/sse2-stv-7.c: New test case.
            * gcc.target/i386/sse4_1-stv-13.c: Likewise.

Diff:
---
 gcc/config/i386/sse.md                        | 53 +++++++++++++++++++++++++++
 gcc/testsuite/gcc.target/i386/sse2-stv-7.c    | 22 +++++++++++
 gcc/testsuite/gcc.target/i386/sse4_1-stv-13.c | 16 ++++++++
 3 files changed, 91 insertions(+)

diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 10850189043c..122dfb367646 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -21931,6 +21931,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_64BIT && TARGET_SSE2 && !TARGET_SSE4_1
+   && 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
@@ -22497,6 +22522,34 @@
           ]
           (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_64BIT && TARGET_SSE2
+   && TARGET_INTER_UNIT_MOVES_TO_VEC
+   && ix86_pre_reload_split ()"
+  [(set (match_dup 0) (match_dup 2))]
+{
+  rtx lo, hi;
+  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)
+    emit_insn (gen_sse4_1_pinsrd (result, tmp1, hi, GEN_INT (2)));
+  else
+    {
+      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));
+    }
+  operands[2] = gen_lowpart (V2DImode, result);
+})
+
 ;; 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 000000000000..a173980e53aa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/sse2-stv-7.c
@@ -0,0 +1,22 @@
+/* { 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" } } */
diff --git a/gcc/testsuite/gcc.target/i386/sse4_1-stv-13.c 
b/gcc/testsuite/gcc.target/i386/sse4_1-stv-13.c
new file mode 100644
index 000000000000..d14dfb3371db
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/sse4_1-stv-13.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2 -msse4.1" } */
+
+typedef long long v2di __attribute__ ((__vector_size__ (16)));
+
+long long ext();
+v2di mem;
+
+void bar()
+{
+  long long x = ext();
+  mem = (v2di){x,0};
+}
+
+/* { dg-final { scan-assembler "pinsrd" } } */
+/* { dg-final { scan-assembler-not "movq" } } */

Reply via email to