https://gcc.gnu.org/g:f777e76b57d9ee1bab3866bf157e43b8a0672c2e
commit r17-1949-gf777e76b57d9ee1bab3866bf157e43b8a0672c2e Author: Xi Ruoyao <[email protected]> Date: Sun May 24 19:23:22 2026 +0800 LoongArch: add usdot_prod pattern [PR 125474] A hot loop in SPEC 2026 stockfish_r calculates the dot product of a sign-extended vector and a zero-extended vector. Add the corresponding usdot_prod pattern to use the instructions with such a semantic, for example xvmulwev.h.bu.b, to optimize the loop. PR target/125474 gcc/ * config/loongarch/simd.md (usdot_prod<wvec_half><mode>): New define_expand. (usdot_prod<wvec_quarter><mode>): New define_expand. gcc/testsuite/ * gcc.target/loongarch/vector/lasx/dot-prod-mixed-signedness.c: New test. Diff: --- gcc/config/loongarch/simd.md | 54 +++++++++++++++++++ .../vector/lasx/dot-prod-mixed-signedness.c | 63 ++++++++++++++++++++++ 2 files changed, 117 insertions(+) diff --git a/gcc/config/loongarch/simd.md b/gcc/config/loongarch/simd.md index 377e94fb356a..09e746c56e7d 100644 --- a/gcc/config/loongarch/simd.md +++ b/gcc/config/loongarch/simd.md @@ -934,6 +934,60 @@ DONE; }) +(define_expand "usdot_prod<wvec_half><mode>" + [(match_operand:<WVEC_HALF> 0 "register_operand") + (match_operand:IVEC 1 "register_operand") + (match_operand:IVEC 2 "register_operand") + (match_operand:<WVEC_HALF> 3 "reg_or_0_operand")] + "" +{ + rtx *op = operands; + + if (op[3] == CONST0_RTX (<WVEC_HALF>mode)) + emit_insn ( + gen_<simd_isa>_<x>vmulwev_<simdfmt_w>_<simdfmt>u_<simdfmt> ( + op[0], op[1], op[2])); + else + emit_insn ( + gen_<simd_isa>_<x>vmaddwev_<simdfmt_w>_<simdfmt>u_<simdfmt> ( + op[0], op[3], op[1], op[2])); + + emit_insn ( + gen_<simd_isa>_<x>vmaddwod_<simdfmt_w>_<simdfmt>u_<simdfmt> ( + op[0], op[0], op[1], op[2])); + DONE; +}) + +(define_expand "usdot_prod<wvec_quarter><mode>" + [(match_operand:<WVEC_QUARTER> 0 "register_operand") + (match_operand:IVEC_HB 1 "register_operand") + (match_operand:IVEC_HB 2 "register_operand") + (match_operand:<WVEC_QUARTER> 3 "reg_or_0_operand")] + "" +{ + rtx *op = operands; + rtx res_mulev = gen_reg_rtx (<WVEC_HALF>mode); + rtx res_mulod = gen_reg_rtx (<WVEC_HALF>mode); + rtx res_addev = gen_reg_rtx (<WVEC_QUARTER>mode); + rtx res_addod = gen_reg_rtx (<WVEC_QUARTER>mode); + emit_insn (gen_<simd_isa>_<x>vmulwev_<simdfmt_w>_<simdfmt>u_<simdfmt> + (res_mulev, op[1], op[2])); + emit_insn (gen_<simd_isa>_<x>vmulwod_<simdfmt_w>_<simdfmt>u_<simdfmt> + (res_mulod, op[1], op[2])); + emit_insn (gen_<simd_isa>_<x>vhaddw_<simdfmt_qw>_<simdfmt_w> + (res_addev, res_mulev, res_mulev)); + emit_insn (gen_<simd_isa>_<x>vhaddw_<simdfmt_qw>_<simdfmt_w> + (res_addod, res_mulod, res_mulod)); + + rtx sum = gen_rtx_PLUS (<WVEC_QUARTER>mode, res_addev, res_addod); + if (op[3] == CONST0_RTX (<WVEC_QUARTER>mode)) + emit_move_insn (op[0], sum); + else + emit_insn (gen_add<wvec_quarter>3 ( + op[0], force_reg (<WVEC_QUARTER>mode, sum), op[3])); + DONE; +}) + ; For "historical" reason we need a punned version of q_d variants. (define_mode_iterator DIVEC [(V2DI "ISA_HAS_LSX") (V4DI "ISA_HAS_LASX")]) diff --git a/gcc/testsuite/gcc.target/loongarch/vector/lasx/dot-prod-mixed-signedness.c b/gcc/testsuite/gcc.target/loongarch/vector/lasx/dot-prod-mixed-signedness.c new file mode 100644 index 000000000000..d5f42ebdc96d --- /dev/null +++ b/gcc/testsuite/gcc.target/loongarch/vector/lasx/dot-prod-mixed-signedness.c @@ -0,0 +1,63 @@ +/* { dg-options "-O3 -mlasx -fno-unroll-loops -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-times "DOT_PROD_EXPR" 1 "optimized" } } */ + +typedef unsigned int uint32_t; +typedef int int32_t; +typedef unsigned char uint8_t; +typedef char int8_t; +typedef uint32_t IndexType; + +[[gnu::noipa]] +void transform(int32_t *__restrict__ output, const int8_t *weights, + const int32_t *biases, const uint8_t *input) { + for (IndexType i = 0; i < 0x10; ++i) { + const IndexType offset = i * 0x1000; + + int32_t sum = biases[i]; + for (IndexType j = 0; j < 0x1000; ++j) { + sum += weights[offset + j] * input[j]; + } + output[i] = sum; + } +} + +[[gnu::noipa]] +void transform_novec(int32_t *__restrict__ output, + const int8_t *weights, + const int32_t *biases, + const uint8_t *input) { +#pragma GCC novector + for (IndexType i = 0; i < 0x10; ++i) { + const IndexType offset = i * 0x1000; + + int32_t sum = biases[i]; +#pragma GCC novector + for (IndexType j = 0; j < 0x1000; ++j) { + sum += weights[offset + j] * input[j]; + } + output[i] = sum; + } +} + +int8_t w[0x11000]; +uint8_t in[0x1000]; +int32_t out[0x10]; +int32_t out_novec[0x10]; +int32_t b[0x10]; + +int +main () +{ + for (unsigned i = 0; i < 0x11000; i++) + w[i] = (int8_t)(uint8_t)(i * i + 113 * i + 47); + for (unsigned i = 0x11000; i < 0x12000; i++) + in[i - 0x11000] = (uint8_t)(i * i + 113 * i + 47); + for (int i = 0; i < 0x10; i++) + b[i] = i; + + transform (out, w, b, in); + transform_novec (out_novec, w, b, in); + + if (__builtin_memcmp (out, out_novec, sizeof (out))) + __builtin_trap (); +}
