[LLVMCPU][RISCV] Add RVV int8 vector-contract custom kernels for widening i8*i8->i32 matmul (#23734)
Fixes iree-org/iree#23730
This PR adds RVV custom vector-contract kernel support for widening
i8*i8->i32 matmul on RISC-V.
It updates:
- CPU encoding tile enumeration for RVV int8->i32 matmul
- LLVMCPU kernel dispatch RVV vector sizes
- VectorContractCustomKernels with an RVV intrinsic-based int8 path
- lit tests for vector-contract custom kernels and mmt4d vector lowering
Something I would like some feedback on is what would be your preferred
way to test this e2e. So far I compile it and run it on the Banana-pi
SpacemiT X60.
Assisted-by: ChatGPT
---------
Signed-off-by: Agustin N. Coppari Hollmann <copparihollmann@gmail.com>
diff --git a/compiler/src/iree/compiler/Codegen/ExternalInterfaces/CPUEncodingExternalModels.cpp b/compiler/src/iree/compiler/Codegen/ExternalInterfaces/CPUEncodingExternalModels.cpp
index d168cc4..a9e0670 100644
--- a/compiler/src/iree/compiler/Codegen/ExternalInterfaces/CPUEncodingExternalModels.cpp
+++ b/compiler/src/iree/compiler/Codegen/ExternalInterfaces/CPUEncodingExternalModels.cpp
@@ -1038,6 +1038,21 @@
};
}
}
+ // Integer 8 path: standard RVV widening multiply-accumulate.
+ // Same formula as the f32 path: N0 = VLEN/8, targeting LMUL=4 for i32
+ // accumulators. The widening chain is i8(m1) -> i16(m2) -> i32(m4),
+ // fully utilizing the register capacity at any VLEN.
+ // M0=7 with LMUL=4 accumulators: 7*4 + 2(prod) + 1(rhs) = 31 regs.
+ if (lhs.isSignlessInteger(8) && rhs.isSignlessInteger(8) &&
+ out.isSignlessInteger(32)) {
+ int N0 = vlen / 8;
+ return {
+ TileMxNxK{7, N0, 1}, // Primary shape for RVV widening int8 kernels.
+ TileMxNxK{4, N0, 1}, // Truncation of the above.
+ TileMxNxK{2, N0, 1}, // Truncation of the above.
+ TileMxNxK{1, N0, 1}, // Truncation of the above.
+ };
+ }
// Fallback - no architecture-optimized tile size for this case.
return {};
}
diff --git a/runtime/src/iree/builtins/ukernel/arch/riscv_64/BUILD.bazel b/runtime/src/iree/builtins/ukernel/arch/riscv_64/BUILD.bazel
index 3706e9a..108ca5b 100644
--- a/runtime/src/iree/builtins/ukernel/arch/riscv_64/BUILD.bazel
+++ b/runtime/src/iree/builtins/ukernel/arch/riscv_64/BUILD.bazel
@@ -51,6 +51,7 @@
name = "ukernel_bitcode_arch_riscv_64_v",
srcs = [
"mmt4d_riscv_64_v.c",
+ "mmt4d_riscv_64_v_i8.c",
],
arch = "riscv_64",
copts = ["-march=rv64gcv"],
diff --git a/runtime/src/iree/builtins/ukernel/arch/riscv_64/CMakeLists.txt b/runtime/src/iree/builtins/ukernel/arch/riscv_64/CMakeLists.txt
index 146ec63..b02f6c7 100644
--- a/runtime/src/iree/builtins/ukernel/arch/riscv_64/CMakeLists.txt
+++ b/runtime/src/iree/builtins/ukernel/arch/riscv_64/CMakeLists.txt
@@ -47,6 +47,7 @@
"unpack_riscv_64_internal.h"
SRCS
"mmt4d_riscv_64_v.c"
+ "mmt4d_riscv_64_v_i8.c"
COPTS
"-march=rv64gcv"
)
@@ -177,6 +178,7 @@
riscv_64_v
SRCS
"mmt4d_riscv_64_v.c"
+ "mmt4d_riscv_64_v_i8.c"
COPTS
"${IREE_UK_COPTS_RISCV_64_V}"
DEPS
diff --git a/runtime/src/iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_tiles.inl b/runtime/src/iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_tiles.inl
index 4f4d041..7e8713f 100644
--- a/runtime/src/iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_tiles.inl
+++ b/runtime/src/iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_tiles.inl
@@ -13,6 +13,11 @@
IREE_UK_MMT4D_TILE(riscv_64, f32, f32, f32, 4, 1, _v)
IREE_UK_MMT4D_TILE(riscv_64, f32, f32, f32, 7, 1, _v)
+IREE_UK_MMT4D_TILE(riscv_64, s8, s8, s32, 1, 1, _v)
+IREE_UK_MMT4D_TILE(riscv_64, s8, s8, s32, 2, 1, _v)
+IREE_UK_MMT4D_TILE(riscv_64, s8, s8, s32, 4, 1, _v)
+IREE_UK_MMT4D_TILE(riscv_64, s8, s8, s32, 7, 1, _v)
+
IREE_UK_MMT4D_TILE(riscv_64, f16, f16, f32, 1, 1, _zvfhmin)
IREE_UK_MMT4D_TILE(riscv_64, f16, f16, f32, 2, 1, _zvfhmin)
IREE_UK_MMT4D_TILE(riscv_64, f16, f16, f32, 4, 1, _zvfhmin)
diff --git a/runtime/src/iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_v_i8.c b/runtime/src/iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_v_i8.c
new file mode 100644
index 0000000..73a8d58
--- /dev/null
+++ b/runtime/src/iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_v_i8.c
@@ -0,0 +1,146 @@
+// Copyright 2026 The IREE Authors
+//
+// Licensed under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include <riscv_vector.h>
+
+#include "iree/builtins/ukernel/arch/riscv_64/common_riscv_64.h"
+#include "iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_internal.h"
+
+IREE_UK_ATTRIBUTE_ALWAYS_INLINE static inline void
+iree_uk_mmt4d_tile_s8s8s32_1xXXx1_to_7xXXx1_riscv_64_v(
+ void* IREE_UK_RESTRICT out_tile, const void* IREE_UK_RESTRICT lhs_panel,
+ const void* IREE_UK_RESTRICT rhs_panel,
+ const iree_uk_mmt4d_params_t* params, int M0) {
+ IREE_UK_ASSERT(M0 >= 1 && M0 <= 7);
+ const iree_uk_int8_t* IREE_UK_RESTRICT lhs_ptr = lhs_panel;
+ const iree_uk_int8_t* IREE_UK_RESTRICT rhs_ptr = rhs_panel;
+ iree_uk_int32_t* IREE_UK_RESTRICT out_ptr = out_tile;
+
+ vint32m4_t acc0, acc1, acc2, acc3, acc4, acc5, acc6;
+ int N0 = params->N0;
+ size_t vl = N0;
+
+ if (M0 == 1) {
+ if (params->flags & IREE_UK_FLAG_MMT4D_ACCUMULATE) {
+ acc0 = __riscv_vle32_v_i32m4(out_ptr, vl);
+ } else {
+ acc0 = __riscv_vmv_v_x_i32m4(0, vl);
+ }
+ for (int k = 0; k < params->K; ++k) {
+ vint8m1_t rhs = __riscv_vle8_v_i8m1(rhs_ptr, vl);
+ vint16m2_t rhs_wide = __riscv_vsext_vf2_i16m2(rhs, vl);
+ rhs_ptr += N0;
+ iree_uk_int16_t lhs = (iree_uk_int16_t)*lhs_ptr++;
+ acc0 = __riscv_vwmacc_vx_i32m4(acc0, lhs, rhs_wide, vl);
+ }
+ __riscv_vse32_v_i32m4(out_ptr, acc0, vl);
+ } else if (M0 == 2) {
+ if (params->flags & IREE_UK_FLAG_MMT4D_ACCUMULATE) {
+ acc0 = __riscv_vle32_v_i32m4(out_ptr, vl);
+ acc1 = __riscv_vle32_v_i32m4(out_ptr + N0, vl);
+ } else {
+ acc0 = __riscv_vmv_v_x_i32m4(0, vl);
+ acc1 = __riscv_vmv_v_x_i32m4(0, vl);
+ }
+ for (int k = 0; k < params->K; ++k) {
+ vint8m1_t rhs = __riscv_vle8_v_i8m1(rhs_ptr, vl);
+ vint16m2_t rhs_wide = __riscv_vsext_vf2_i16m2(rhs, vl);
+ rhs_ptr += N0;
+ iree_uk_int16_t lhs0 = (iree_uk_int16_t)*lhs_ptr++;
+ iree_uk_int16_t lhs1 = (iree_uk_int16_t)*lhs_ptr++;
+ acc0 = __riscv_vwmacc_vx_i32m4(acc0, lhs0, rhs_wide, vl);
+ acc1 = __riscv_vwmacc_vx_i32m4(acc1, lhs1, rhs_wide, vl);
+ }
+ __riscv_vse32_v_i32m4(out_ptr, acc0, vl);
+ __riscv_vse32_v_i32m4(out_ptr + N0, acc1, vl);
+ } else if (M0 == 4) {
+ if (params->flags & IREE_UK_FLAG_MMT4D_ACCUMULATE) {
+ acc0 = __riscv_vle32_v_i32m4(out_ptr, vl);
+ acc1 = __riscv_vle32_v_i32m4(out_ptr + N0, vl);
+ acc2 = __riscv_vle32_v_i32m4(out_ptr + N0 * 2, vl);
+ acc3 = __riscv_vle32_v_i32m4(out_ptr + N0 * 3, vl);
+ } else {
+ acc0 = __riscv_vmv_v_x_i32m4(0, vl);
+ acc1 = __riscv_vmv_v_x_i32m4(0, vl);
+ acc2 = __riscv_vmv_v_x_i32m4(0, vl);
+ acc3 = __riscv_vmv_v_x_i32m4(0, vl);
+ }
+ for (int k = 0; k < params->K; ++k) {
+ vint8m1_t rhs = __riscv_vle8_v_i8m1(rhs_ptr, vl);
+ vint16m2_t rhs_wide = __riscv_vsext_vf2_i16m2(rhs, vl);
+ rhs_ptr += N0;
+ iree_uk_int16_t lhs0 = (iree_uk_int16_t)*lhs_ptr++;
+ iree_uk_int16_t lhs1 = (iree_uk_int16_t)*lhs_ptr++;
+ iree_uk_int16_t lhs2 = (iree_uk_int16_t)*lhs_ptr++;
+ iree_uk_int16_t lhs3 = (iree_uk_int16_t)*lhs_ptr++;
+ acc0 = __riscv_vwmacc_vx_i32m4(acc0, lhs0, rhs_wide, vl);
+ acc1 = __riscv_vwmacc_vx_i32m4(acc1, lhs1, rhs_wide, vl);
+ acc2 = __riscv_vwmacc_vx_i32m4(acc2, lhs2, rhs_wide, vl);
+ acc3 = __riscv_vwmacc_vx_i32m4(acc3, lhs3, rhs_wide, vl);
+ }
+ __riscv_vse32_v_i32m4(out_ptr, acc0, vl);
+ __riscv_vse32_v_i32m4(out_ptr + N0, acc1, vl);
+ __riscv_vse32_v_i32m4(out_ptr + N0 * 2, acc2, vl);
+ __riscv_vse32_v_i32m4(out_ptr + N0 * 3, acc3, vl);
+ } else if (M0 == 7) {
+ if (params->flags & IREE_UK_FLAG_MMT4D_ACCUMULATE) {
+ acc0 = __riscv_vle32_v_i32m4(out_ptr, vl);
+ acc1 = __riscv_vle32_v_i32m4(out_ptr + N0, vl);
+ acc2 = __riscv_vle32_v_i32m4(out_ptr + N0 * 2, vl);
+ acc3 = __riscv_vle32_v_i32m4(out_ptr + N0 * 3, vl);
+ acc4 = __riscv_vle32_v_i32m4(out_ptr + N0 * 4, vl);
+ acc5 = __riscv_vle32_v_i32m4(out_ptr + N0 * 5, vl);
+ acc6 = __riscv_vle32_v_i32m4(out_ptr + N0 * 6, vl);
+ } else {
+ acc0 = __riscv_vmv_v_x_i32m4(0, vl);
+ acc1 = __riscv_vmv_v_x_i32m4(0, vl);
+ acc2 = __riscv_vmv_v_x_i32m4(0, vl);
+ acc3 = __riscv_vmv_v_x_i32m4(0, vl);
+ acc4 = __riscv_vmv_v_x_i32m4(0, vl);
+ acc5 = __riscv_vmv_v_x_i32m4(0, vl);
+ acc6 = __riscv_vmv_v_x_i32m4(0, vl);
+ }
+ for (int k = 0; k < params->K; ++k) {
+ vint8m1_t rhs = __riscv_vle8_v_i8m1(rhs_ptr, vl);
+ vint16m2_t rhs_wide = __riscv_vsext_vf2_i16m2(rhs, vl);
+ rhs_ptr += N0;
+ iree_uk_int16_t lhs0 = (iree_uk_int16_t)*lhs_ptr++;
+ iree_uk_int16_t lhs1 = (iree_uk_int16_t)*lhs_ptr++;
+ iree_uk_int16_t lhs2 = (iree_uk_int16_t)*lhs_ptr++;
+ iree_uk_int16_t lhs3 = (iree_uk_int16_t)*lhs_ptr++;
+ iree_uk_int16_t lhs4 = (iree_uk_int16_t)*lhs_ptr++;
+ iree_uk_int16_t lhs5 = (iree_uk_int16_t)*lhs_ptr++;
+ iree_uk_int16_t lhs6 = (iree_uk_int16_t)*lhs_ptr++;
+ acc0 = __riscv_vwmacc_vx_i32m4(acc0, lhs0, rhs_wide, vl);
+ acc1 = __riscv_vwmacc_vx_i32m4(acc1, lhs1, rhs_wide, vl);
+ acc2 = __riscv_vwmacc_vx_i32m4(acc2, lhs2, rhs_wide, vl);
+ acc3 = __riscv_vwmacc_vx_i32m4(acc3, lhs3, rhs_wide, vl);
+ acc4 = __riscv_vwmacc_vx_i32m4(acc4, lhs4, rhs_wide, vl);
+ acc5 = __riscv_vwmacc_vx_i32m4(acc5, lhs5, rhs_wide, vl);
+ acc6 = __riscv_vwmacc_vx_i32m4(acc6, lhs6, rhs_wide, vl);
+ }
+ __riscv_vse32_v_i32m4(out_ptr, acc0, vl);
+ __riscv_vse32_v_i32m4(out_ptr + N0, acc1, vl);
+ __riscv_vse32_v_i32m4(out_ptr + N0 * 2, acc2, vl);
+ __riscv_vse32_v_i32m4(out_ptr + N0 * 3, acc3, vl);
+ __riscv_vse32_v_i32m4(out_ptr + N0 * 4, acc4, vl);
+ __riscv_vse32_v_i32m4(out_ptr + N0 * 5, acc5, vl);
+ __riscv_vse32_v_i32m4(out_ptr + N0 * 6, acc6, vl);
+ }
+}
+
+IREE_UK_MMT4D_TILE_FUNC_IMPL_FOR_M0(
+ iree_uk_mmt4d_tile_s8s8s32_1xXXx1_to_7xXXx1_riscv_64_v,
+ iree_uk_mmt4d_tile_s8s8s32_1xXXx1_riscv_64_v, 1)
+IREE_UK_MMT4D_TILE_FUNC_IMPL_FOR_M0(
+ iree_uk_mmt4d_tile_s8s8s32_1xXXx1_to_7xXXx1_riscv_64_v,
+ iree_uk_mmt4d_tile_s8s8s32_2xXXx1_riscv_64_v, 2)
+IREE_UK_MMT4D_TILE_FUNC_IMPL_FOR_M0(
+ iree_uk_mmt4d_tile_s8s8s32_1xXXx1_to_7xXXx1_riscv_64_v,
+ iree_uk_mmt4d_tile_s8s8s32_4xXXx1_riscv_64_v, 4)
+IREE_UK_MMT4D_TILE_FUNC_IMPL_FOR_M0(
+ iree_uk_mmt4d_tile_s8s8s32_1xXXx1_to_7xXXx1_riscv_64_v,
+ iree_uk_mmt4d_tile_s8s8s32_7xXXx1_riscv_64_v, 7)
diff --git a/runtime/src/iree/builtins/ukernel/arch/riscv_64/query_tile_sizes_riscv_64_entry_point.c b/runtime/src/iree/builtins/ukernel/arch/riscv_64/query_tile_sizes_riscv_64_entry_point.c
index 352a072..f8e8133 100644
--- a/runtime/src/iree/builtins/ukernel/arch/riscv_64/query_tile_sizes_riscv_64_entry_point.c
+++ b/runtime/src/iree/builtins/ukernel/arch/riscv_64/query_tile_sizes_riscv_64_entry_point.c
@@ -19,6 +19,18 @@
return (iree_uk_matmul_tile_sizes_t){.M = 8, .K = 1, .N = 8};
}
+static iree_uk_matmul_tile_sizes_t
+iree_uk_query_matmul_tile_sizes_riscv_64_i8i8i32(
+ const iree_uk_query_tile_sizes_2d_params_t* params) {
+#if defined(IREE_UK_BUILD_RISCV_64_V)
+ if (iree_uk_cpu_riscv_64_v(params->cpu_data)) {
+ return (iree_uk_matmul_tile_sizes_t){.M = 7, .K = 1, .N = 32};
+ }
+#endif
+ // generic fallback
+ return (iree_uk_matmul_tile_sizes_t){.M = 8, .K = 4, .N = 8};
+}
+
bool iree_uk_query_matmul_tile_sizes_arch(
const iree_uk_query_tile_sizes_2d_params_t* params,
iree_uk_matmul_tile_sizes_t* out_matmul_tile_sizes) {
@@ -27,6 +39,10 @@
*out_matmul_tile_sizes =
iree_uk_query_matmul_tile_sizes_riscv_64_f32f32f32(params);
return true;
+ } else if (op == IREE_UK_FLAG_QUERY_TILE_SIZES_OPERATION_MATMUL_I8I8I32) {
+ *out_matmul_tile_sizes =
+ iree_uk_query_matmul_tile_sizes_riscv_64_i8i8i32(params);
+ return true;
} else {
// Shouldn't happen, validated earlier.
return false;
diff --git a/runtime/src/iree/builtins/ukernel/tools/mmt4d_benchmark.c b/runtime/src/iree/builtins/ukernel/tools/mmt4d_benchmark.c
index 6ffc3c6..7474bb5 100644
--- a/runtime/src/iree/builtins/ukernel/tools/mmt4d_benchmark.c
+++ b/runtime/src/iree/builtins/ukernel/tools/mmt4d_benchmark.c
@@ -185,6 +185,8 @@
#elif defined(IREE_ARCH_RISCV_64)
iree_uk_benchmark_register_mmt4d(IREE_UK_FLAG_MMT4D_TYPE_F32F32F32, 7, 16, 1,
"v");
+ iree_uk_benchmark_register_mmt4d(IREE_UK_FLAG_MMT4D_TYPE_S8S8S32, 7, 32, 1,
+ "v");
iree_uk_benchmark_register_mmt4d(IREE_UK_FLAG_MMT4D_TYPE_F16F16F32, 6, 16, 1,
"zvfhmin");
iree_uk_benchmark_register_mmt4d(IREE_UK_FLAG_MMT4D_TYPE_F16F16F16, 6, 16, 1,
diff --git a/runtime/src/iree/builtins/ukernel/tools/mmt4d_test.c b/runtime/src/iree/builtins/ukernel/tools/mmt4d_test.c
index 474962e..d931467 100644
--- a/runtime/src/iree/builtins/ukernel/tools/mmt4d_test.c
+++ b/runtime/src/iree/builtins/ukernel/tools/mmt4d_test.c
@@ -605,6 +605,7 @@
#elif defined(IREE_ARCH_RISCV_64)
iree_uk_test_mmt4d(IREE_UK_FLAG_MMT4D_TYPE_F32F32F32, 7, 16, 1, "v");
+ iree_uk_test_mmt4d(IREE_UK_FLAG_MMT4D_TYPE_S8S8S32, 7, 32, 1, "v");
iree_uk_test_mmt4d(IREE_UK_FLAG_MMT4D_TYPE_F16F16F32, 6, 16, 1, "zvfhmin");
iree_uk_test_mmt4d(IREE_UK_FLAG_MMT4D_SKIP_INTERMEDIATE_ROUNDINGS |
IREE_UK_FLAG_MMT4D_TYPE_F16F16F16,