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.
---
Change from v1: rewrite the test in C and put it into
gcc.target/loongarch/vector/lasx.
Bootstrapped and regtested on loongarch64-linux-gnu. Ok for trunk?
gcc/config/loongarch/simd.md | 54 ++++++++++++++++
.../vector/lasx/dot-prod-mixed-signedness.c | 63 +++++++++++++++++++
2 files changed, 117 insertions(+)
create mode 100644
gcc/testsuite/gcc.target/loongarch/vector/lasx/dot-prod-mixed-signedness.c
diff --git a/gcc/config/loongarch/simd.md b/gcc/config/loongarch/simd.md
index 377e94fb356..09e746c56e7 100644
--- a/gcc/config/loongarch/simd.md
+++ b/gcc/config/loongarch/simd.md
@@ -934,6 +934,60 @@ (define_expand
"<simd_isa>_<x>vmaddw<ev_od>_<simdfmt_w>_<simdfmt>u_<simdfmt>"
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 00000000000..d5f42ebdc96
--- /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 ();
+}
--
2.54.0