From: Roger Sayle Date: Tue, 21 Jul 2026 08:45:15 +0000 (+0100) Subject: i386: Split DI<->V2DI patterns before reload with -m32. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ccd676ca79aa783e9e52de213a5619265ec9a8d2;p=thirdparty%2Fgcc.git 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 Uros Bizjak 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 --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md index 10850189043..122dfb36764 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_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..a173980e53a --- /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 00000000000..d14dfb3371d --- /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" } } */