[gcc r17-2678] x86 SSE: Use insertps to zero V4SI/V4SF elements in a single instruction.
Roger Sayle via Gcc-cvs <[email protected]>
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:b13e5bf8ed954c5e15a4f5ba515fe58e63c5714d commit r17-2678-gb13e5bf8ed954c5e15a4f5ba515fe58e63c5714d Author: Roger Sayle <[email protected]> Date: Fri Jul 24 07:21:48 2026 +0100 x86 SSE: Use insertps to zero V4SI/V4SF elements in a single instruction. This patch teaches the x86 backend that the SSE4.1 insertps instruction can be used/abused to clear one or more elements of a V4SI or V4SF vector in a single instruction (i.e. without requiring pxor to clear a second register). Consider the test case typedef int v4si __attribute__ ((__vector_size__ (16))); v4si foo(v4si x) { x[2]=0; return x; } Currently with -O2 -mavx2, we generate: foo: xorl %eax, %eax vpinsrd $2, %eax, %xmm0, %xmm0 ret with this patch we now generate: foo: vinsertps $4, %xmm0, %xmm0, %xmm0 ret For the more complicated example: v4si bar(v4si x) { x[1]=0; x[3]=0; return x; } previously, we'd generate: bar: xorl %eax, %eax vpinsrd $1, %eax, %xmm0, %xmm0 vpinsrd $3, %eax, %xmm0, %xmm0 ret with this patch we now generate: bar: vinsertps $10, %xmm0, %xmm0, %xmm0 ret One improvement that I'll leave to an i386/SSE expert, is that setting elements 1, 2 and 3 [i.e. zero extending element 0] still falls back to the existing patterns (and tests for this are commented out in the new test cases). Tweaking sse_movss_v4si to consider using insertps requires expertise in register preferencing and instruction attributes that I'm happy to leave to someone else. 2026-07-24 Roger Sayle <[email protected]> Hongtao Liu <[email protected]> gcc/ChangeLog * config/i386/i386-expand.cc (ix86_expand_vec_set_builtin): Don't force op1 to a register when it is CONST0_RTX (mode1). (ix86_expand_vector_set_var): For now, force VAL to a register. (ix86_expand_vector_set): If val is CONST0_RTX, expand using the new sse4_1_insertps_v4s[if]_zero patterns on TARGET_SSE4_1. Otherwise, force val to a register (restoring previous behaviour). * config/i386/sse.md (sse4_1_insertps_<mode>_zero): New insn using vec_merge to select which elements to clear/preserve. (vec_set<mode>): Tweak operand 1 to allow both REGs and CONST0_RTX. gcc/testsuite/ChangeLog * gcc.target/i386/sse4_1-insertps-6.c: New test case. * gcc.target/i386/sse4_1-insertps-7.c: Likewise. Diff: --- gcc/config/i386/i386-expand.cc | 22 +++++++++++++++- gcc/config/i386/sse.md | 32 ++++++++++++++++++++++- gcc/testsuite/gcc.target/i386/sse4_1-insertps-6.c | 23 ++++++++++++++++ gcc/testsuite/gcc.target/i386/sse4_1-insertps-7.c | 26 ++++++++++++++++++ 4 files changed, 101 insertions(+), 2 deletions(-) diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc index fde8002eddbf..20914fcbad9d 100644 --- a/gcc/config/i386/i386-expand.cc +++ b/gcc/config/i386/i386-expand.cc @@ -14787,7 +14787,8 @@ ix86_expand_vec_set_builtin (tree exp) op1 = convert_modes (mode1, GET_MODE (op1), op1, true); op0 = force_reg (tmode, op0); - op1 = force_reg (mode1, op1); + if (op1 != CONST0_RTX (mode1)) + op1 = force_reg (mode1, op1); /* OP0 is the source of these builtin functions and shouldn't be modified. Create a copy, use it and return it as target. */ @@ -18987,6 +18988,8 @@ ix86_expand_vector_set_var (rtx target, rtx val, rtx idx) rtx valv,idxv,constv,idx_tmp; bool ok = false; + val = force_reg (GET_MODE_INNER (mode), val); + /* 512-bits vector byte/word broadcast and comparison only available under TARGET_AVX512BW, break 512-bits vector into two 256-bits vector when without TARGET_AVX512BW. */ @@ -19154,6 +19157,23 @@ ix86_expand_vector_set (bool mmx_ok, rtx target, rtx val, int elt) machine_mode mmode = VOIDmode; rtx (*gen_blendm) (rtx, rtx, rtx, rtx); + if (TARGET_SSE4_1 && mode == V4SImode && val == const0_rtx) + { + emit_insn (gen_sse4_1_insertps_v4si_zero (target, target, + CONST0_RTX (V4SImode), + GEN_INT ((1 << elt) ^ 15))); + return; + } + if (TARGET_SSE4_1 && mode == V4SFmode && val == CONST0_RTX (SFmode)) + { + emit_insn (gen_sse4_1_insertps_v4sf_zero (target, target, + CONST0_RTX (V4SFmode), + GEN_INT ((1 << elt) ^ 15))); + return; + } + + val = force_reg (GET_MODE_INNER (mode), val); + switch (mode) { case E_V2SImode: diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md index 7521034e128a..a3fa2ec5f8f3 100644 --- a/gcc/config/i386/sse.md +++ b/gcc/config/i386/sse.md @@ -12945,6 +12945,36 @@ (set_attr "prefix" "orig,orig,maybe_evex") (set_attr "mode" "V4SF")]) +;; Use sse4_1_insertps_v4s[if] to zero values in a vector. +;; operands[3] indicates which elements to preserve. +(define_insn "sse4_1_insertps_<mode>_zero" + [(set (match_operand:VI4F_128 0 "register_operand" "=x,v") + (vec_merge:VI4F_128 + (match_operand:VI4F_128 1 "register_operand" "0,v") + (match_operand:VI4F_128 2 "const0_operand") + (match_operand:SI 3 "const_0_to_15_operand")))] + "TARGET_SSE4_1 + && IN_RANGE (INTVAL (operands[3]), 1, 14)" +{ + operands[3] = GEN_INT (INTVAL (operands[3]) ^ 15); + switch (which_alternative) + { + case 0: + return "insertps\t{%3, %1, %0|%0, %1, %3}"; + case 1: + return "vinsertps\t{%3, %1, %1, %0|%0, %1, %1, %3}"; + default: + gcc_unreachable (); + } +} + [(set_attr "isa" "noavx,avx") + (set_attr "type" "sselog") + (set_attr "prefix_data16" "1,*") + (set_attr "prefix_extra" "1") + (set_attr "length_immediate" "1") + (set_attr "prefix" "orig,maybe_evex") + (set_attr "mode" "V4SF")]) + (define_split [(set (match_operand:VI4F_128 0 "memory_operand") (vec_merge:VI4F_128 @@ -12977,7 +13007,7 @@ (define_expand "vec_set<mode>" [(match_operand:V_128 0 "register_operand") - (match_operand:<ssescalarmode> 1 "register_operand") + (match_operand:<ssescalarmode> 1 "reg_or_0_operand") (match_operand 2 "vec_setm_sse41_operand")] "TARGET_SSE" { diff --git a/gcc/testsuite/gcc.target/i386/sse4_1-insertps-6.c b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-6.c new file mode 100644 index 000000000000..6ed110a0b4ed --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-6.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -msse4.1" } */ + +typedef int v4si __attribute__ ((__vector_size__ (16))); + +v4si sz_0(v4si x) { x[0]=0; return x; } +v4si sz_1(v4si x) { x[1]=0; return x; } +v4si sz_2(v4si x) { x[2]=0; return x; } +v4si sz_3(v4si x) { x[3]=0; return x; } + +v4si sz_01(v4si x) { x[0]=0; x[1]=0; return x; } +v4si sz_02(v4si x) { x[0]=0; x[2]=0; return x; } +v4si sz_03(v4si x) { x[0]=0; x[3]=0; return x; } +v4si sz_12(v4si x) { x[1]=0; x[2]=0; return x; } +v4si sz_13(v4si x) { x[1]=0; x[3]=0; return x; } +v4si sz_23(v4si x) { x[2]=0; x[3]=0; return x; } + +v4si sz_012(v4si x) { x[0]=0; x[1]=0; x[2]=0; return x; } +v4si sz_013(v4si x) { x[0]=0; x[1]=0; x[3]=0; return x; } +v4si sz_023(v4si x) { x[0]=0; x[2]=0; x[3]=0; return x; } +// v4si sz_123(v4si x) { x[1]=0; x[2]=0; x[3]=0; return x; } + +/* { dg-final { scan-assembler-times "\tv?insertps\t" 13 } } */ diff --git a/gcc/testsuite/gcc.target/i386/sse4_1-insertps-7.c b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-7.c new file mode 100644 index 000000000000..15d095a6279d --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/sse4_1-insertps-7.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -msse4.1" } */ + +typedef float v4sf __attribute__ ((__vector_size__ (16))); + +v4sf sz_0(v4sf x) { x[0]=0.0f; return x; } +v4sf sz_1(v4sf x) { x[1]=0.0f; return x; } +v4sf sz_2(v4sf x) { x[2]=0.0f; return x; } +v4sf sz_3(v4sf x) { x[3]=0.0f; return x; } + +v4sf sz_01(v4sf x) { x[0]=0.0f; x[1]=0.0f; return x; } +v4sf sz_02(v4sf x) { x[0]=0.0f; x[2]=0.0f; return x; } +v4sf sz_03(v4sf x) { x[0]=0.0f; x[3]=0.0f; return x; } +v4sf sz_12(v4sf x) { x[1]=0.0f; x[2]=0.0f; return x; } +v4sf sz_13(v4sf x) { x[1]=0.0f; x[3]=0.0f; return x; } +v4sf sz_23(v4sf x) { x[2]=0.0f; x[3]=0.0f; return x; } + +v4sf sz_012(v4sf x) { x[0]=0.0f; x[1]=0.0f; x[2]=0.0f; return x; } +v4sf sz_013(v4sf x) { x[0]=0.0f; x[1]=0.0f; x[3]=0.0f; return x; } +v4sf sz_023(v4sf x) { x[0]=0.0f; x[2]=0.0f; x[3]=0.0f; return x; } + +#if 0 +v4sf sz_123(v4sf x) { x[1]=0.0f; x[2]=0.0f; x[3]=0.0f; return x; } +#endif + +/* { dg-final { scan-assembler-times "\tv?insertps\t" 13 } } */