[RVV] Enable SpaceMiT vendor IME (#24706)
## Summary
This PR adds an s8s8s32 mmt4d ukernel for SpaceMiT IME (`+xsmtvdot` /
`smt.vmadot`)
and wires the compiler to use it. The tile is fixed at 12x16x8 (3x4 grid
of 4x4x8 atoms). Under `+xsmtvdot` we
select that shape and auto-enable mmt4d; plain `+v` targets are
unchanged.
Also, runtime detection can't use the usual ISA hwprobe bits for this
vendor extension, so we set the feature bit from SpaceMiT X60
`mvendorid`/`marchid`. And adds lit coverage, mmt4d unit/benchmark
entries, and an
`riscv_64:ime:+v,+zvl256b,+xsmtvdot` e2e matmul variant. Validated on
Banana Pi (BPI-F3 / X60): cpuinfo, mmt4d_test, and the ukernel e2e path.
Fixes #24576
Co-authored-by: Coden <github.com/coden-s>
Co-authored-by: Andrew <github.com/akuhlken>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Terence <terrencemahlatini@gmail.com>
---------
Signed-off-by: Terence <terrencemahlatini@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
diff --git a/compiler/src/iree/compiler/Codegen/Common/test/materialize_encoding_riscv.mlir b/compiler/src/iree/compiler/Codegen/Common/test/materialize_encoding_riscv.mlir
index 327a069..651353c 100644
--- a/compiler/src/iree/compiler/Codegen/Common/test/materialize_encoding_riscv.mlir
+++ b/compiler/src/iree/compiler/Codegen/Common/test/materialize_encoding_riscv.mlir
@@ -242,6 +242,150 @@
// -----
+// RISC-V 64 + xsmtvdot + zvl256b: IME 12x16x8 tile (3x4 of 4x4x8).
+#map = affine_map<(d0, d1, d2) -> (d0, d2)>
+#map1 = affine_map<(d0, d1, d2) -> (d2, d1)>
+#map2 = affine_map<(d0, d1, d2) -> (d0, d1)>
+#encoding_lhs = #iree_encoding.encoding<operand_index = 0, op_type = matmul, element_types = [i8, i8, i32], user_indexing_maps = [#map, #map1, #map2], iteration_sizes = [?, ?, ?]>
+#encoding_rhs = #iree_encoding.encoding<operand_index = 1, op_type = matmul, element_types = [i8, i8, i32], user_indexing_maps = [#map, #map1, #map2], iteration_sizes = [?, ?, ?]>
+#encoding_result = #iree_encoding.encoding<operand_index = 2, op_type = matmul, element_types = [i8, i8, i32], user_indexing_maps = [#map, #map1, #map2], iteration_sizes = [?, ?, ?]>
+func.func @matmul_lowering_i8i8i32_riscv64_zvl256b(
+ %lhs: tensor<?x?xi8, #encoding_lhs>,
+ %rhs: tensor<?x?xi8, #encoding_rhs>,
+ %result: tensor<?x?xi32, #encoding_result>
+) -> tensor<?x?xi32, #encoding_result> attributes {
+ hal.executable.target = #hal.executable.target<"llvm-cpu", "xyz", {target_triple="riscv64-xyz-xyz", cpu_features="+v,+zvl256b,+xsmtvdot", iree.encoding.resolver = #iree_cpu.cpu_encoding_resolver<>}>
+} {
+ %out = linalg.matmul
+ ins(%lhs, %rhs : tensor<?x?xi8, #encoding_lhs>,
+ tensor<?x?xi8, #encoding_rhs>)
+ outs(%result : tensor<?x?xi32, #encoding_result>)
+ -> tensor<?x?xi32, #encoding_result>
+ return %out : tensor<?x?xi32, #encoding_result>
+}
+// CHECK-LABEL: func @matmul_lowering_i8i8i32_riscv64_zvl256b(
+// STATIC-SAME: %[[LHS:[a-zA-Z0-9]+]]: tensor<?x?x12x8xi8>
+// STATIC-SAME: %[[RHS:[a-zA-Z0-9]+]]: tensor<?x?x16x8xi8>
+// STATIC-SAME: %[[ACC:[a-zA-Z0-9]+]]: tensor<?x?x12x16xi32>
+// SCALABLE-SAME: %[[LHS:[a-zA-Z0-9]+]]: tensor<?x?x7x1xi8>
+// SCALABLE-SAME: %[[RHS:[a-zA-Z0-9]+]]: tensor<?x?x?x1xi8>
+// SCALABLE-SAME: %[[ACC:[a-zA-Z0-9]+]]: tensor<?x?x7x?xi32>
+// CHECK: %[[MMT4D:.+]] = linalg.mmt4d
+// CHECK-SAME: ins(%[[LHS]], %[[RHS]]
+// CHECK-SAME: outs(%[[ACC]]
+// CHECK: return %[[MMT4D]]
+
+// -----
+
+// RISC-V 64 + xsmtvdot + zvl1024b: IME 24x32x16 tile (3x4 of 8x8x16).
+#map = affine_map<(d0, d1, d2) -> (d0, d2)>
+#map1 = affine_map<(d0, d1, d2) -> (d2, d1)>
+#map2 = affine_map<(d0, d1, d2) -> (d0, d1)>
+#encoding_lhs = #iree_encoding.encoding<operand_index = 0, op_type = matmul, element_types = [i8, i8, i32], user_indexing_maps = [#map, #map1, #map2], iteration_sizes = [?, ?, ?]>
+#encoding_rhs = #iree_encoding.encoding<operand_index = 1, op_type = matmul, element_types = [i8, i8, i32], user_indexing_maps = [#map, #map1, #map2], iteration_sizes = [?, ?, ?]>
+#encoding_result = #iree_encoding.encoding<operand_index = 2, op_type = matmul, element_types = [i8, i8, i32], user_indexing_maps = [#map, #map1, #map2], iteration_sizes = [?, ?, ?]>
+func.func @matmul_lowering_i8i8i32_riscv64_zvl1024b(
+ %lhs: tensor<?x?xi8, #encoding_lhs>,
+ %rhs: tensor<?x?xi8, #encoding_rhs>,
+ %result: tensor<?x?xi32, #encoding_result>
+) -> tensor<?x?xi32, #encoding_result> attributes {
+ hal.executable.target = #hal.executable.target<"llvm-cpu", "xyz", {target_triple="riscv64-xyz-xyz", cpu_features="+v,+zvl1024b,+xsmtvdot", iree.encoding.resolver = #iree_cpu.cpu_encoding_resolver<>}>
+} {
+ %out = linalg.matmul
+ ins(%lhs, %rhs : tensor<?x?xi8, #encoding_lhs>,
+ tensor<?x?xi8, #encoding_rhs>)
+ outs(%result : tensor<?x?xi32, #encoding_result>)
+ -> tensor<?x?xi32, #encoding_result>
+ return %out : tensor<?x?xi32, #encoding_result>
+}
+// CHECK-LABEL: func @matmul_lowering_i8i8i32_riscv64_zvl1024b(
+// STATIC-SAME: %[[LHS:[a-zA-Z0-9]+]]: tensor<?x?x24x16xi8>
+// STATIC-SAME: %[[RHS:[a-zA-Z0-9]+]]: tensor<?x?x32x16xi8>
+// STATIC-SAME: %[[ACC:[a-zA-Z0-9]+]]: tensor<?x?x24x32xi32>
+// SCALABLE-SAME: %[[LHS:[a-zA-Z0-9]+]]: tensor<?x?x7x1xi8>
+// SCALABLE-SAME: %[[RHS:[a-zA-Z0-9]+]]: tensor<?x?x?x1xi8>
+// SCALABLE-SAME: %[[ACC:[a-zA-Z0-9]+]]: tensor<?x?x7x?xi32>
+// CHECK: %[[MMT4D:.+]] = linalg.mmt4d
+// CHECK-SAME: ins(%[[LHS]], %[[RHS]]
+// CHECK-SAME: outs(%[[ACC]]
+// CHECK: return %[[MMT4D]]
+
+// -----
+
+// RISC-V 64 + xsmtvdot + zvl4096b: IME 48x64x32 tile (3x4 of 16x16x32).
+#map = affine_map<(d0, d1, d2) -> (d0, d2)>
+#map1 = affine_map<(d0, d1, d2) -> (d2, d1)>
+#map2 = affine_map<(d0, d1, d2) -> (d0, d1)>
+#encoding_lhs = #iree_encoding.encoding<operand_index = 0, op_type = matmul, element_types = [i8, i8, i32], user_indexing_maps = [#map, #map1, #map2], iteration_sizes = [?, ?, ?]>
+#encoding_rhs = #iree_encoding.encoding<operand_index = 1, op_type = matmul, element_types = [i8, i8, i32], user_indexing_maps = [#map, #map1, #map2], iteration_sizes = [?, ?, ?]>
+#encoding_result = #iree_encoding.encoding<operand_index = 2, op_type = matmul, element_types = [i8, i8, i32], user_indexing_maps = [#map, #map1, #map2], iteration_sizes = [?, ?, ?]>
+func.func @matmul_lowering_i8i8i32_riscv64_zvl4096b(
+ %lhs: tensor<?x?xi8, #encoding_lhs>,
+ %rhs: tensor<?x?xi8, #encoding_rhs>,
+ %result: tensor<?x?xi32, #encoding_result>
+) -> tensor<?x?xi32, #encoding_result> attributes {
+ hal.executable.target = #hal.executable.target<"llvm-cpu", "xyz", {target_triple="riscv64-xyz-xyz", cpu_features="+v,+zvl4096b,+xsmtvdot", iree.encoding.resolver = #iree_cpu.cpu_encoding_resolver<>}>
+} {
+ %out = linalg.matmul
+ ins(%lhs, %rhs : tensor<?x?xi8, #encoding_lhs>,
+ tensor<?x?xi8, #encoding_rhs>)
+ outs(%result : tensor<?x?xi32, #encoding_result>)
+ -> tensor<?x?xi32, #encoding_result>
+ return %out : tensor<?x?xi32, #encoding_result>
+}
+// CHECK-LABEL: func @matmul_lowering_i8i8i32_riscv64_zvl4096b(
+// STATIC-SAME: %[[LHS:[a-zA-Z0-9]+]]: tensor<?x?x48x32xi8>
+// STATIC-SAME: %[[RHS:[a-zA-Z0-9]+]]: tensor<?x?x64x32xi8>
+// STATIC-SAME: %[[ACC:[a-zA-Z0-9]+]]: tensor<?x?x48x64xi32>
+// SCALABLE-SAME: %[[LHS:[a-zA-Z0-9]+]]: tensor<?x?x7x1xi8>
+// SCALABLE-SAME: %[[RHS:[a-zA-Z0-9]+]]: tensor<?x?x?x1xi8>
+// SCALABLE-SAME: %[[ACC:[a-zA-Z0-9]+]]: tensor<?x?x7x?xi32>
+// CHECK: %[[MMT4D:.+]] = linalg.mmt4d
+// CHECK-SAME: ins(%[[LHS]], %[[RHS]]
+// CHECK-SAME: outs(%[[ACC]]
+// CHECK: return %[[MMT4D]]
+
+// -----
+
+// RISC-V 64 + xsmtvdot + zvl512b: VLEN not in {256, 1024, 4096}, so fall
+// through to standard RVV i8 tiles. IME vmadot is not selected.
+// Static: N0=vlen/8=64. Scalable: N is vscale-based (base VLEN=64).
+#map = affine_map<(d0, d1, d2) -> (d0, d2)>
+#map1 = affine_map<(d0, d1, d2) -> (d2, d1)>
+#map2 = affine_map<(d0, d1, d2) -> (d0, d1)>
+#encoding_lhs = #iree_encoding.encoding<operand_index = 0, op_type = matmul, element_types = [i8, i8, i32], user_indexing_maps = [#map, #map1, #map2], iteration_sizes = [?, ?, ?]>
+#encoding_rhs = #iree_encoding.encoding<operand_index = 1, op_type = matmul, element_types = [i8, i8, i32], user_indexing_maps = [#map, #map1, #map2], iteration_sizes = [?, ?, ?]>
+#encoding_result = #iree_encoding.encoding<operand_index = 2, op_type = matmul, element_types = [i8, i8, i32], user_indexing_maps = [#map, #map1, #map2], iteration_sizes = [?, ?, ?]>
+func.func @matmul_lowering_i8i8i32_riscv64_zvl512b_fallback(
+ %lhs: tensor<?x?xi8, #encoding_lhs>,
+ %rhs: tensor<?x?xi8, #encoding_rhs>,
+ %result: tensor<?x?xi32, #encoding_result>
+) -> tensor<?x?xi32, #encoding_result> attributes {
+ hal.executable.target = #hal.executable.target<"llvm-cpu", "xyz", {target_triple="riscv64-xyz-xyz", cpu_features="+v,+zvl512b,+xsmtvdot", iree.encoding.resolver = #iree_cpu.cpu_encoding_resolver<>}>
+} {
+ %out = linalg.matmul
+ ins(%lhs, %rhs : tensor<?x?xi8, #encoding_lhs>,
+ tensor<?x?xi8, #encoding_rhs>)
+ outs(%result : tensor<?x?xi32, #encoding_result>)
+ -> tensor<?x?xi32, #encoding_result>
+ return %out : tensor<?x?xi32, #encoding_result>
+}
+// CHECK-LABEL: func @matmul_lowering_i8i8i32_riscv64_zvl512b_fallback(
+// STATIC-SAME: %[[LHS:[a-zA-Z0-9]+]]: tensor<?x?x7x1xi8>
+// STATIC-SAME: %[[RHS:[a-zA-Z0-9]+]]: tensor<?x?x64x1xi8>
+// STATIC-SAME: %[[ACC:[a-zA-Z0-9]+]]: tensor<?x?x7x64xi32>
+
+// SCALABLE-SAME: %[[LHS:[a-zA-Z0-9]+]]: tensor<?x?x7x1xi8>
+// SCALABLE-SAME: %[[RHS:[a-zA-Z0-9]+]]: tensor<?x?x?x1xi8>
+// SCALABLE-SAME: %[[ACC:[a-zA-Z0-9]+]]: tensor<?x?x7x?xi32>
+
+// CHECK: %[[MMT4D:.+]] = linalg.mmt4d
+// CHECK-SAME: ins(%[[LHS]], %[[RHS]]
+// CHECK-SAME: outs(%[[ACC]]
+// CHECK: return %[[MMT4D]]
+
+// -----
+
// Checks that we don't transpose M/N for the narrow-N case when scalable inner tiles are present.
// For scalable vectors, we want to keep the N dimension scalable.
diff --git a/compiler/src/iree/compiler/Codegen/ExternalInterfaces/CPUEncodingExternalModels.cpp b/compiler/src/iree/compiler/Codegen/ExternalInterfaces/CPUEncodingExternalModels.cpp
index 7260db7..4ea7b07 100644
--- a/compiler/src/iree/compiler/Codegen/ExternalInterfaces/CPUEncodingExternalModels.cpp
+++ b/compiler/src/iree/compiler/Codegen/ExternalInterfaces/CPUEncodingExternalModels.cpp
@@ -1240,14 +1240,30 @@
};
}
}
- // 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.
+ // Integer 8 path.
if (lhs.isSignlessInteger(8) && rhs.isSignlessInteger(8) &&
out.isSignlessInteger(32)) {
int N0 = vlen / 8;
+ // SpaceMiT IME (vmadot): fixed hardware tiles for VLEN ∈ {256, 1024,
+ // 4096}. Tile is a 3×4 grid of the SEW=8 MAC atom (4×4×8 / 8×8×16 /
+ // 16×16×32). Other VLENs / scalable mode fall through to standard RVV
+ // tiles.
+ if (hasFeature(config, "+xsmtvdot") && !isScalableVectorizationEnabled()) {
+ int atom = 0;
+ if (vlen == 256) {
+ atom = 4;
+ } else if (vlen == 1024) {
+ atom = 8;
+ } else if (vlen == 4096) {
+ atom = 16;
+ }
+ if (atom != 0) {
+ return {TileMxNxK{3 * atom, 4 * atom, 2 * atom}};
+ }
+ }
+ // Standard RVV widening multiply-accumulate.
+ // Widening chain: i8(m1) -> i16(m2) -> i32(m4).
+ // M0=7 with LMUL=4 accumulators: 7*4 + 2(prod) + 1(rhs) = 31 regs.
return {
TileMxNxK{7, N0, 1}, // Primary shape for RVV widening int8 kernels.
TileMxNxK{4, N0, 1}, // Truncation of the above.
diff --git a/compiler/src/iree/compiler/Codegen/Utils/Utils.cpp b/compiler/src/iree/compiler/Codegen/Utils/Utils.cpp
index 1e30292..34d30e4 100644
--- a/compiler/src/iree/compiler/Codegen/Utils/Utils.cpp
+++ b/compiler/src/iree/compiler/Codegen/Utils/Utils.cpp
@@ -209,6 +209,12 @@
if (isAArch64(targetConfig)) {
return "mmt4d";
}
+ // Auto-enable mmt4d for IME-capable RISC-V targets so callers do not need
+ // --iree-llvmcpu-enable-ukernels=mmt4d explicitly. Plain +v targets are
+ // unaffected; they still require the explicit flag.
+ if (isRISCV64(targetConfig) && hasFeature(targetConfig, "+xsmtvdot")) {
+ return "mmt4d";
+ }
return kNone;
}
diff --git a/runtime/src/iree/base/internal/cpu.c b/runtime/src/iree/base/internal/cpu.c
index 1862851..3ba85de 100644
--- a/runtime/src/iree/base/internal/cpu.c
+++ b/runtime/src/iree/base/internal/cpu.c
@@ -402,14 +402,32 @@
// NOTE: not all kernel versions have all of the constants we need defined so as
// a practice we always define the feature bits we need locally.
+#define IREE_RISCV_HWPROBE_KEY_MVENDORID 0
+#define IREE_RISCV_HWPROBE_KEY_MARCHID 1
#define IREE_RISCV_HWPROBE_KEY_IMA_EXT_0 4
#define IREE_RISCV_HWPROBE_IMA_V (1 << 2)
#define IREE_RISCV_HWPROBE_EXT_ZVFH (1 << 30)
#define IREE_RISCV_HWPROBE_EXT_ZVFHMIN (1ULL << 31)
#define IREE_RISCV_HWPROBE_EXT_ZVFBFWMA (1ULL << 54)
+// SpaceMiT K1 machine identity, used to enable the vendor IME (XSMTVDot /
+// `vmadot`) matrix extension.
+#define IREE_RISCV_MVENDORID_SPACEMIT 0x710ULL
+#define IREE_RISCV_MARCHID_SPACEMIT_X60 0x8000000058000001ULL
+
+// Indices into `kv_pairs` below. hwprobe fills each pair's `value` in place, so
+// these must match the array element order.
+enum {
+ IREE_RISCV_HWPROBE_PAIR_IMA_EXT_0 = 0,
+ IREE_RISCV_HWPROBE_PAIR_MVENDORID,
+ IREE_RISCV_HWPROBE_PAIR_MARCHID,
+};
+
static struct riscv_hwprobe kv_pairs[] = {
- {IREE_RISCV_HWPROBE_KEY_IMA_EXT_0, 0}};
+ {IREE_RISCV_HWPROBE_KEY_IMA_EXT_0, 0},
+ {IREE_RISCV_HWPROBE_KEY_MVENDORID, 0},
+ {IREE_RISCV_HWPROBE_KEY_MARCHID, 0},
+};
long riscv_hwprobe(struct riscv_hwprobe* pairs, size_t pair_count,
size_t cpusetsize, cpu_set_t* cpus, unsigned int flags) {
@@ -418,12 +436,13 @@
}
static void iree_cpu_initialize_from_platform_riscv_64(uint64_t* out_fields) {
- long rc = riscv_hwprobe(&kv_pairs[0], 1, 0, NULL, 0);
+ long rc = riscv_hwprobe(&kv_pairs[0], IREE_ARRAYSIZE(kv_pairs), 0, NULL, 0);
if (rc != 0) {
fprintf(stderr, "riscv_hwprobe syscall failed");
exit(1);
}
- unsigned long long hwprobe = kv_pairs[0].value;
+ unsigned long long hwprobe =
+ kv_pairs[IREE_RISCV_HWPROBE_PAIR_IMA_EXT_0].value;
IREE_COPY_BITS(out_fields[0], IREE_CPU_DATA0_RISCV_64_V, hwprobe,
IREE_RISCV_HWPROBE_IMA_V);
IREE_COPY_BITS(out_fields[0], IREE_CPU_DATA0_RISCV_64_ZVFHMIN, hwprobe,
@@ -432,6 +451,28 @@
IREE_RISCV_HWPROBE_EXT_ZVFH);
IREE_COPY_BITS(out_fields[0], IREE_CPU_DATA0_RISCV_64_ZVFBFWMA, hwprobe,
IREE_RISCV_HWPROBE_EXT_ZVFBFWMA);
+
+ // XSMTVDot (SpaceMiT IME): absent from hwprobe / cpuinfo. Approximating via
+ // X60 mvendorid/marchid until a vendor hwprobe key exists. this host bit
+ // mainly unblocks ukernel tests/benchmarks.
+ unsigned long long mvendorid =
+ kv_pairs[IREE_RISCV_HWPROBE_PAIR_MVENDORID].value;
+ unsigned long long marchid = kv_pairs[IREE_RISCV_HWPROBE_PAIR_MARCHID].value;
+ if (mvendorid == IREE_RISCV_MVENDORID_SPACEMIT &&
+ marchid == IREE_RISCV_MARCHID_SPACEMIT_X60) {
+ out_fields[0] |= IREE_CPU_DATA0_RISCV_64_XSMTVDOT;
+ }
+
+ // No stable hwprobe VLEN key yet; reading vlenb (CSR 0xc22) when V is
+ // present. Sets only the three IME-relevant zvl*b bits (inclusive).
+ if (out_fields[0] & IREE_CPU_DATA0_RISCV_64_V) {
+ uint64_t vlenb = 0;
+ __asm__ volatile("csrr %0, 0xc22" : "=r"(vlenb));
+ uint64_t vlen_bits = vlenb * 8;
+ if (vlen_bits >= 256) out_fields[0] |= IREE_CPU_DATA0_RISCV_64_ZVL256B;
+ if (vlen_bits >= 1024) out_fields[0] |= IREE_CPU_DATA0_RISCV_64_ZVL1024B;
+ if (vlen_bits >= 4096) out_fields[0] |= IREE_CPU_DATA0_RISCV_64_ZVL4096B;
+ }
}
#else
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 02dae9e..79fee62 100644
--- a/runtime/src/iree/builtins/ukernel/arch/riscv_64/BUILD.bazel
+++ b/runtime/src/iree/builtins/ukernel/arch/riscv_64/BUILD.bazel
@@ -80,6 +80,16 @@
)
iree_bitcode_library(
+ name = "ukernel_bitcode_arch_riscv_64_xsmtvdot",
+ srcs = [
+ "mmt4d_riscv_64_xsmtvdot.c",
+ ],
+ arch = "riscv_64",
+ copts = ["-march=rv64gc_xsmtvdot"],
+ internal_hdrs = UKERNEL_RISCV_64_INTERNAL_HEADERS,
+)
+
+iree_bitcode_library(
name = "ukernel_bitcode_arch_riscv_64_zvfbfwma",
srcs = [
"mmt4d_riscv_64_zvfbfwma.c",
@@ -96,6 +106,7 @@
"ukernel_bitcode_arch_riscv_64_v.bc",
"ukernel_bitcode_arch_riscv_64_zvfhmin.bc",
"ukernel_bitcode_arch_riscv_64_zvfh.bc",
+ "ukernel_bitcode_arch_riscv_64_xsmtvdot.bc",
"ukernel_bitcode_arch_riscv_64_zvfbfwma.bc",
],
)
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 f155c06..3ba7d6f 100644
--- a/runtime/src/iree/builtins/ukernel/arch/riscv_64/CMakeLists.txt
+++ b/runtime/src/iree/builtins/ukernel/arch/riscv_64/CMakeLists.txt
@@ -93,6 +93,25 @@
iree_bitcode_library(
NAME
+ ukernel_bitcode_arch_riscv_64_xsmtvdot
+ ARCH
+ riscv_64
+ INTERNAL_HDRS
+ "${PROJECT_BINARY_DIR}/runtime/src/iree/builtins/ukernel/internal_headers_filegroup.stamp"
+ "${PROJECT_BINARY_DIR}/runtime/src/iree/schemas/cpu_data_headers_filegroup.stamp"
+ "common_riscv_64.h"
+ "mmt4d_riscv_64_internal.h"
+ "mmt4d_riscv_64_tiles.inl"
+ "pack_riscv_64_internal.h"
+ "unpack_riscv_64_internal.h"
+ SRCS
+ "mmt4d_riscv_64_xsmtvdot.c"
+ COPTS
+ "-march=rv64gc_xsmtvdot"
+)
+
+iree_bitcode_library(
+ NAME
ukernel_bitcode_arch_riscv_64_zvfbfwma
ARCH
riscv_64
@@ -116,6 +135,7 @@
SRCS
"ukernel_bitcode_arch_riscv_64_entry_points.bc"
"ukernel_bitcode_arch_riscv_64_v.bc"
+ "ukernel_bitcode_arch_riscv_64_xsmtvdot.bc"
"ukernel_bitcode_arch_riscv_64_zvfbfwma.bc"
"ukernel_bitcode_arch_riscv_64_zvfh.bc"
"ukernel_bitcode_arch_riscv_64_zvfhmin.bc"
@@ -147,6 +167,11 @@
"-march=rv64gc_zvfh"
)
+iree_select_compiler_opts(IREE_UK_COPTS_RISCV_64_XSMTVDOT
+ CLANG_OR_GCC
+ "-march=rv64gc_xsmtvdot"
+)
+
iree_select_compiler_opts(IREE_UK_COPTS_RISCV_64_ZVFBFWMA
CLANG_OR_GCC
"-march=rv64gcv_zvfbfwma"
@@ -154,6 +179,7 @@
check_cxx_compiler_flag("${IREE_UK_COPTS_RISCV_64_V}" IREE_UK_BUILD_RISCV_64_V)
check_cxx_compiler_flag("${IREE_UK_COPTS_RISCV_64_ZVFH}" IREE_UK_BUILD_RISCV_64_ZVFH)
+check_cxx_compiler_flag("${IREE_UK_COPTS_RISCV_64_XSMTVDOT}" IREE_UK_BUILD_RISCV_64_XSMTVDOT)
check_cxx_compiler_flag("${IREE_UK_COPTS_RISCV_64_ZVFBFWMA}" IREE_UK_BUILD_RISCV_64_ZVFBFWMA)
# CPU features that we will try checking compiler support for, unless
@@ -242,6 +268,20 @@
list(APPEND IREE_UK_RISCV_64_DEPS "::riscv_64_zvfh")
endif() # IREE_UK_BUILD_RISCV_64_ZVFH
+if(IREE_UK_BUILD_RISCV_64_XSMTVDOT)
+iree_cc_library(
+ NAME
+ riscv_64_xsmtvdot
+ SRCS
+ "mmt4d_riscv_64_xsmtvdot.c"
+ COPTS
+ "${IREE_UK_COPTS_RISCV_64_XSMTVDOT}"
+ DEPS
+ iree::builtins::ukernel::internal_headers
+)
+list(APPEND IREE_UK_RISCV_64_DEPS "::riscv_64_xsmtvdot")
+endif() # IREE_UK_BUILD_RISCV_64_XSMTVDOT
+
if(IREE_UK_BUILD_RISCV_64_ZVFBFWMA)
iree_cc_library(
NAME
diff --git a/runtime/src/iree/builtins/ukernel/arch/riscv_64/common_riscv_64.h b/runtime/src/iree/builtins/ukernel/arch/riscv_64/common_riscv_64.h
index b68d961..e864e59 100644
--- a/runtime/src/iree/builtins/ukernel/arch/riscv_64/common_riscv_64.h
+++ b/runtime/src/iree/builtins/ukernel/arch/riscv_64/common_riscv_64.h
@@ -15,6 +15,7 @@
#define IREE_UK_BUILD_RISCV_64_V
#define IREE_UK_BUILD_RISCV_64_ZVFHMIN
#define IREE_UK_BUILD_RISCV_64_ZVFH
+#define IREE_UK_BUILD_RISCV_64_XSMTVDOT
#define IREE_UK_BUILD_RISCV_64_ZVFBFWMA
#else
// Compiling with the system toolchain. Include the configured header.
@@ -39,6 +40,32 @@
return iree_uk_all_bits_set(cpu_data[0], IREE_CPU_DATA0_RISCV_64_ZVFH);
}
+static inline bool iree_uk_cpu_riscv_64_xsmtvdot(
+ const iree_uk_uint64_t* cpu_data) {
+ return iree_uk_all_bits_set(cpu_data[0], IREE_CPU_DATA0_RISCV_64_XSMTVDOT);
+}
+
+// IME tile shapes require a full vmadot operand panel in one vector register:
+// 12x16x8 needs VLEN>=256, 24x32x16 needs VLEN>=1024, 48x64x32 needs
+// VLEN>=4096.
+static inline bool iree_uk_cpu_riscv_64_xsmtvdot_zvl256b(
+ const iree_uk_uint64_t* cpu_data) {
+ return iree_uk_cpu_riscv_64_xsmtvdot(cpu_data) &&
+ iree_uk_all_bits_set(cpu_data[0], IREE_CPU_DATA0_RISCV_64_ZVL256B);
+}
+
+static inline bool iree_uk_cpu_riscv_64_xsmtvdot_zvl1024b(
+ const iree_uk_uint64_t* cpu_data) {
+ return iree_uk_cpu_riscv_64_xsmtvdot(cpu_data) &&
+ iree_uk_all_bits_set(cpu_data[0], IREE_CPU_DATA0_RISCV_64_ZVL1024B);
+}
+
+static inline bool iree_uk_cpu_riscv_64_xsmtvdot_zvl4096b(
+ const iree_uk_uint64_t* cpu_data) {
+ return iree_uk_cpu_riscv_64_xsmtvdot(cpu_data) &&
+ iree_uk_all_bits_set(cpu_data[0], IREE_CPU_DATA0_RISCV_64_ZVL4096B);
+}
+
static inline bool iree_uk_cpu_riscv_64_zvfbfwma(
const iree_uk_uint64_t* cpu_data) {
return iree_uk_all_bits_set(cpu_data[0], IREE_CPU_DATA0_RISCV_64_ZVFBFWMA);
diff --git a/runtime/src/iree/builtins/ukernel/arch/riscv_64/config_riscv_64.h.in b/runtime/src/iree/builtins/ukernel/arch/riscv_64/config_riscv_64.h.in
index bee185d..a1dbaab 100644
--- a/runtime/src/iree/builtins/ukernel/arch/riscv_64/config_riscv_64.h.in
+++ b/runtime/src/iree/builtins/ukernel/arch/riscv_64/config_riscv_64.h.in
@@ -15,5 +15,6 @@
#cmakedefine IREE_UK_BUILD_RISCV_64_ZVFHMIN
#cmakedefine IREE_UK_BUILD_RISCV_64_ZVFH
#cmakedefine IREE_UK_BUILD_RISCV_64_ZVFBFWMA
+#cmakedefine IREE_UK_BUILD_RISCV_64_XSMTVDOT
#endif // IREE_BUILTINS_UKERNEL_ARCH_RISCV_64_CONFIG_RISCV_64_H_
diff --git a/runtime/src/iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_entry_point.c b/runtime/src/iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_entry_point.c
index bdde1c5..4f9026d 100644
--- a/runtime/src/iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_entry_point.c
+++ b/runtime/src/iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_entry_point.c
@@ -44,6 +44,19 @@
#define IREE_UK_MMT4D_TILE_riscv_64_zvfh(lhs, rhs, out, m0, k0)
#endif
+#ifdef IREE_UK_BUILD_RISCV_64_XSMTVDOT
+#define IREE_UK_MMT4D_TILE_riscv_64_xsmtvdot_zvl256b(lhs, rhs, out, m0, k0) \
+ IREE_UK_MMT4D_TILE_IMPL_riscv_64(lhs, rhs, out, m0, k0, _xsmtvdot_zvl256b)
+#define IREE_UK_MMT4D_TILE_riscv_64_xsmtvdot_zvl1024b(lhs, rhs, out, m0, k0) \
+ IREE_UK_MMT4D_TILE_IMPL_riscv_64(lhs, rhs, out, m0, k0, _xsmtvdot_zvl1024b)
+#define IREE_UK_MMT4D_TILE_riscv_64_xsmtvdot_zvl4096b(lhs, rhs, out, m0, k0) \
+ IREE_UK_MMT4D_TILE_IMPL_riscv_64(lhs, rhs, out, m0, k0, _xsmtvdot_zvl4096b)
+#else
+#define IREE_UK_MMT4D_TILE_riscv_64_xsmtvdot_zvl256b(lhs, rhs, out, m0, k0)
+#define IREE_UK_MMT4D_TILE_riscv_64_xsmtvdot_zvl1024b(lhs, rhs, out, m0, k0)
+#define IREE_UK_MMT4D_TILE_riscv_64_xsmtvdot_zvl4096b(lhs, rhs, out, m0, k0)
+#endif
+
#ifdef IREE_UK_BUILD_RISCV_64_ZVFBFWMA
#define IREE_UK_MMT4D_TILE_riscv_64_zvfbfwma(lhs, rhs, out, m0, k0) \
IREE_UK_MMT4D_TILE_IMPL_riscv_64(lhs, rhs, out, m0, k0, _zvfbfwma)
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 c67baa5..af48484 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
@@ -38,6 +38,10 @@
IREE_UK_MMT4D_TILE(riscv_64, f16, f16, f16, 4, 1, _zvfh)
IREE_UK_MMT4D_TILE(riscv_64, f16, f16, f16, 7, 1, _zvfh)
+IREE_UK_MMT4D_TILE(riscv_64, s8, s8, s32, 12, 8, _xsmtvdot_zvl256b)
+IREE_UK_MMT4D_TILE(riscv_64, s8, s8, s32, 24, 16, _xsmtvdot_zvl1024b)
+IREE_UK_MMT4D_TILE(riscv_64, s8, s8, s32, 48, 32, _xsmtvdot_zvl4096b)
+
IREE_UK_MMT4D_TILE(riscv_64, bf16, bf16, f32, 1, 1, _zvfbfwma)
IREE_UK_MMT4D_TILE(riscv_64, bf16, bf16, f32, 2, 1, _zvfbfwma)
IREE_UK_MMT4D_TILE(riscv_64, bf16, bf16, f32, 4, 1, _zvfbfwma)
diff --git a/runtime/src/iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_xsmtvdot.c b/runtime/src/iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_xsmtvdot.c
new file mode 100644
index 0000000..08c84b9
--- /dev/null
+++ b/runtime/src/iree/builtins/ukernel/arch/riscv_64/mmt4d_riscv_64_xsmtvdot.c
@@ -0,0 +1,154 @@
+// 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
+
+// SpaceMiT IME (XSMTVDot) s8s8s32 mmt4d.
+//
+// One `smt.vmadot` at SEW=8 is a square MAC atom selected by vl (not VLMAX):
+// atom=4 (vl*SEW=256) -> 4x4x8 -> tile 12x16x8
+// atom=8 (vl*SEW=1024) -> 8x8x16 -> tile 24x32x16
+// atom=16 (vl*SEW=4096) -> 16x16x32 -> tile 48x64x32
+// The macro-tile is a fixed 3x4 atom grid (12 vmadot per K panel). Each
+// {vd,vd+1} int32 accumulator group holds one atom x atom output block,
+// row-major and contiguous within the group, and stays resident across K.
+//
+// Panels:
+// lhs : int8 [K1][M0][K0] K0-contiguous
+// rhs : int8 [K1][N0][K0] transposed (N0,K0), K0-contiguous
+// out : int32 [M0][N0]
+
+#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"
+
+enum { IME_MT = 3, IME_NT = 4 };
+
+// Byte-offset index vector mapping accumulator-group element i (row-major
+// atom x atom, i = r*atom + c) to its position in the strided (M0,N0) output
+// block: out offset (in elements) = r*N0 + c = i + (N0 - atom) * (i / atom).
+static inline vuint32m2_t ime_out_index(int N0, int atom, int log2_atom,
+ size_t vl32) {
+ vuint32m2_t vi = __riscv_vid_v_u32m2(vl32);
+ vuint32m2_t row = __riscv_vsrl_vx_u32m2(vi, (size_t)log2_atom, vl32);
+ vuint32m2_t off =
+ __riscv_vmacc_vx_u32m2(vi, (uint32_t)(N0 - atom), row, vl32);
+ return __riscv_vsll_vx_u32m2(off, 2, vl32);
+}
+
+// Shared 3x4-atom xsmtvdot body. The macro-tile is a fixed 3x4 grid of the
+// SEW=8 MAC atom, so M0 alone determines the atom edge (4 / 8 / 16) and hence
+// N0 = 4*atom and K0 = 2*atom.
+IREE_UK_ATTRIBUTE_ALWAYS_INLINE static inline void
+iree_uk_mmt4d_tile_s8s8s32_12xXXx8_to_48xXXx32_riscv_64_xsmtvdot(
+ 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) {
+ const int atom = M0 / IME_MT;
+ const int N0 = IME_NT * atom;
+ const int K0 = 2 * atom;
+ IREE_UK_ASSERT(atom == 4 || atom == 8 || atom == 16);
+ IREE_UK_ASSERT(params->M0 == M0);
+ IREE_UK_ASSERT(params->N0 == N0);
+ IREE_UK_ASSERT(params->K0 == K0);
+
+ const iree_uk_int8_t* IREE_UK_RESTRICT lhs = lhs_panel;
+ const iree_uk_int8_t* IREE_UK_RESTRICT rhs = rhs_panel;
+ iree_uk_int32_t* IREE_UK_RESTRICT out = out_tile;
+
+ const int K1 = (int)(params->K);
+ const int accumulate = (params->flags & IREE_UK_FLAG_MMT4D_ACCUMULATE) != 0;
+ const int atom_elems = atom * atom;
+ const int panel_stride = atom * K0;
+ const int log2_atom = __builtin_ctz((unsigned int)atom);
+ const size_t vl8 = (size_t)panel_stride;
+ const size_t vl32 = (size_t)atom_elems;
+
+ // out_tile element address of accumulator group (mt,nt), where
+ // acc<mt*IME_NT+nt> holds A[mt] . B[nt].
+#define IME_OUT(mt, nt) (out + ((mt) * atom) * N0 + (nt) * atom)
+
+ vint32m2_t acc0, acc1, acc2, acc3, acc4, acc5, acc6, acc7, acc8, acc9, acc10,
+ acc11;
+ if (accumulate) {
+ vuint32m2_t idx = ime_out_index(N0, atom, log2_atom, vl32);
+ acc0 = __riscv_vluxei32_v_i32m2(IME_OUT(0, 0), idx, vl32);
+ acc1 = __riscv_vluxei32_v_i32m2(IME_OUT(0, 1), idx, vl32);
+ acc2 = __riscv_vluxei32_v_i32m2(IME_OUT(0, 2), idx, vl32);
+ acc3 = __riscv_vluxei32_v_i32m2(IME_OUT(0, 3), idx, vl32);
+ acc4 = __riscv_vluxei32_v_i32m2(IME_OUT(1, 0), idx, vl32);
+ acc5 = __riscv_vluxei32_v_i32m2(IME_OUT(1, 1), idx, vl32);
+ acc6 = __riscv_vluxei32_v_i32m2(IME_OUT(1, 2), idx, vl32);
+ acc7 = __riscv_vluxei32_v_i32m2(IME_OUT(1, 3), idx, vl32);
+ acc8 = __riscv_vluxei32_v_i32m2(IME_OUT(2, 0), idx, vl32);
+ acc9 = __riscv_vluxei32_v_i32m2(IME_OUT(2, 1), idx, vl32);
+ acc10 = __riscv_vluxei32_v_i32m2(IME_OUT(2, 2), idx, vl32);
+ acc11 = __riscv_vluxei32_v_i32m2(IME_OUT(2, 3), idx, vl32);
+ } else {
+ acc0 = __riscv_vmv_v_x_i32m2(0, vl32);
+ acc1 = acc2 = acc3 = acc4 = acc5 = acc6 = acc7 = acc8 = acc9 = acc10 =
+ acc11 = acc0;
+ }
+
+ for (int k = 0; k < K1; ++k) {
+ vint8m1_t a0 = __riscv_vle8_v_i8m1(lhs + 0 * panel_stride, vl8);
+ vint8m1_t a1 = __riscv_vle8_v_i8m1(lhs + 1 * panel_stride, vl8);
+ vint8m1_t a2 = __riscv_vle8_v_i8m1(lhs + 2 * panel_stride, vl8);
+ vint8m1_t b0 = __riscv_vle8_v_i8m1(rhs + 0 * panel_stride, vl8);
+ vint8m1_t b1 = __riscv_vle8_v_i8m1(rhs + 1 * panel_stride, vl8);
+ vint8m1_t b2 = __riscv_vle8_v_i8m1(rhs + 2 * panel_stride, vl8);
+ vint8m1_t b3 = __riscv_vle8_v_i8m1(rhs + 3 * panel_stride, vl8);
+ lhs += IME_MT * panel_stride;
+ rhs += IME_NT * panel_stride;
+
+ __asm__ volatile(
+ " vsetvli x0, %[vl], e8, m1, ta, ma \n\t"
+ " smt.vmadot %[c0], %[a0], %[b0] \n\t"
+ " smt.vmadot %[c1], %[a0], %[b1] \n\t"
+ " smt.vmadot %[c2], %[a0], %[b2] \n\t"
+ " smt.vmadot %[c3], %[a0], %[b3] \n\t"
+ " smt.vmadot %[c4], %[a1], %[b0] \n\t"
+ " smt.vmadot %[c5], %[a1], %[b1] \n\t"
+ " smt.vmadot %[c6], %[a1], %[b2] \n\t"
+ " smt.vmadot %[c7], %[a1], %[b3] \n\t"
+ " smt.vmadot %[c8], %[a2], %[b0] \n\t"
+ " smt.vmadot %[c9], %[a2], %[b1] \n\t"
+ " smt.vmadot %[c10], %[a2], %[b2] \n\t"
+ " smt.vmadot %[c11], %[a2], %[b3] \n\t"
+ : [c0] "+vr"(acc0), [c1] "+vr"(acc1), [c2] "+vr"(acc2),
+ [c3] "+vr"(acc3), [c4] "+vr"(acc4), [c5] "+vr"(acc5),
+ [c6] "+vr"(acc6), [c7] "+vr"(acc7), [c8] "+vr"(acc8),
+ [c9] "+vr"(acc9), [c10] "+vr"(acc10), [c11] "+vr"(acc11)
+ : [a0] "vr"(a0), [a1] "vr"(a1), [a2] "vr"(a2), [b0] "vr"(b0),
+ [b1] "vr"(b1), [b2] "vr"(b2), [b3] "vr"(b3), [vl] "r"(vl8)
+ :);
+ }
+
+ vuint32m2_t idx = ime_out_index(N0, atom, log2_atom, vl32);
+ __riscv_vsuxei32_v_i32m2(IME_OUT(0, 0), idx, acc0, vl32);
+ __riscv_vsuxei32_v_i32m2(IME_OUT(0, 1), idx, acc1, vl32);
+ __riscv_vsuxei32_v_i32m2(IME_OUT(0, 2), idx, acc2, vl32);
+ __riscv_vsuxei32_v_i32m2(IME_OUT(0, 3), idx, acc3, vl32);
+ __riscv_vsuxei32_v_i32m2(IME_OUT(1, 0), idx, acc4, vl32);
+ __riscv_vsuxei32_v_i32m2(IME_OUT(1, 1), idx, acc5, vl32);
+ __riscv_vsuxei32_v_i32m2(IME_OUT(1, 2), idx, acc6, vl32);
+ __riscv_vsuxei32_v_i32m2(IME_OUT(1, 3), idx, acc7, vl32);
+ __riscv_vsuxei32_v_i32m2(IME_OUT(2, 0), idx, acc8, vl32);
+ __riscv_vsuxei32_v_i32m2(IME_OUT(2, 1), idx, acc9, vl32);
+ __riscv_vsuxei32_v_i32m2(IME_OUT(2, 2), idx, acc10, vl32);
+ __riscv_vsuxei32_v_i32m2(IME_OUT(2, 3), idx, acc11, vl32);
+
+#undef IME_OUT
+}
+
+IREE_UK_MMT4D_TILE_FUNC_IMPL_FOR_M0(
+ iree_uk_mmt4d_tile_s8s8s32_12xXXx8_to_48xXXx32_riscv_64_xsmtvdot,
+ iree_uk_mmt4d_tile_s8s8s32_12xXXx8_riscv_64_xsmtvdot_zvl256b, 12)
+IREE_UK_MMT4D_TILE_FUNC_IMPL_FOR_M0(
+ iree_uk_mmt4d_tile_s8s8s32_12xXXx8_to_48xXXx32_riscv_64_xsmtvdot,
+ iree_uk_mmt4d_tile_s8s8s32_24xXXx16_riscv_64_xsmtvdot_zvl1024b, 24)
+IREE_UK_MMT4D_TILE_FUNC_IMPL_FOR_M0(
+ iree_uk_mmt4d_tile_s8s8s32_12xXXx8_to_48xXXx32_riscv_64_xsmtvdot,
+ iree_uk_mmt4d_tile_s8s8s32_48xXXx32_riscv_64_xsmtvdot_zvl4096b, 48)
diff --git a/runtime/src/iree/builtins/ukernel/tools/mmt4d_benchmark.c b/runtime/src/iree/builtins/ukernel/tools/mmt4d_benchmark.c
index 7474bb5..c0f631c 100644
--- a/runtime/src/iree/builtins/ukernel/tools/mmt4d_benchmark.c
+++ b/runtime/src/iree/builtins/ukernel/tools/mmt4d_benchmark.c
@@ -5,6 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <stdio.h>
+#include <string.h>
#include "iree/base/api.h"
#include "iree/base/tooling/flags.h"
@@ -110,10 +111,12 @@
int N0, int K0,
const char* cpu_features) {
// Test narrowed, power-of-two values of M0, as mmt4d kernels tend to have
- // narrow variants for handling these cases.
- for (int narrowM0 = 1; narrowM0 < M0; narrowM0 *= 2) {
- iree_uk_benchmark_register_mmt4d_impl(flags, narrowM0, N0, K0, cpu_features,
- "");
+ // narrow variants for handling these cases. xsmtvdot has no narrow variants.
+ if (!strstr(cpu_features, "xsmtvdot")) {
+ for (int narrowM0 = 1; narrowM0 < M0; narrowM0 *= 2) {
+ iree_uk_benchmark_register_mmt4d_impl(flags, narrowM0, N0, K0,
+ cpu_features, "");
+ }
}
iree_uk_benchmark_register_mmt4d_impl(flags, M0, N0, K0, cpu_features, "");
}
@@ -199,6 +202,12 @@
IREE_UK_FLAG_MMT4D_SKIP_INTERMEDIATE_ROUNDINGS |
IREE_UK_FLAG_MMT4D_TYPE_F16F16F16,
7, 16, 1, "zvfh");
+ iree_uk_benchmark_register_mmt4d(IREE_UK_FLAG_MMT4D_TYPE_S8S8S32, 12, 16, 8,
+ "xsmtvdot,zvl256b");
+ iree_uk_benchmark_register_mmt4d(IREE_UK_FLAG_MMT4D_TYPE_S8S8S32, 24, 32, 16,
+ "xsmtvdot,zvl1024b");
+ iree_uk_benchmark_register_mmt4d(IREE_UK_FLAG_MMT4D_TYPE_S8S8S32, 48, 64, 32,
+ "xsmtvdot,zvl4096b");
#else // defined(IREE_ARCH_ARM_64)
// Architectures on which we do not have any optimized ukernel code.
// Benchmark some arbitrary tile shape.
diff --git a/runtime/src/iree/builtins/ukernel/tools/mmt4d_test.c b/runtime/src/iree/builtins/ukernel/tools/mmt4d_test.c
index 8b6e17a..68bcc1b 100644
--- a/runtime/src/iree/builtins/ukernel/tools/mmt4d_test.c
+++ b/runtime/src/iree/builtins/ukernel/tools/mmt4d_test.c
@@ -4,6 +4,8 @@
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#include <string.h>
+
#include "iree/base/api.h"
#include "iree/base/internal/math.h"
#include "iree/builtins/ukernel/api.h"
@@ -532,9 +534,11 @@
// currently an implementation detail within each arch/ subdirectory.
flags |= IREE_UK_FLAG_MMT4D_ALLOW_GENERIC_FALLBACK_TILE_FUNCTION;
// Test narrowed, power-of-two values of M0, as mmt4d kernels tend to have
- // narrow variants for handling these cases.
- for (int narrowM0 = 1; narrowM0 < M0; narrowM0 *= 2) {
- iree_uk_test_mmt4d_impl(flags, narrowM0, N0, K0, cpu_features);
+ // narrow variants for handling these cases. xsmtvdot has no narrow variants.
+ if (!strstr(cpu_features, "xsmtvdot")) {
+ for (int narrowM0 = 1; narrowM0 < M0; narrowM0 *= 2) {
+ iree_uk_test_mmt4d_impl(flags, narrowM0, N0, K0, cpu_features);
+ }
}
iree_uk_test_mmt4d_impl(flags, M0, N0, K0, cpu_features);
}
@@ -615,6 +619,12 @@
iree_uk_test_mmt4d(IREE_UK_FLAG_MMT4D_SKIP_INTERMEDIATE_ROUNDINGS |
IREE_UK_FLAG_MMT4D_TYPE_F16F16F16,
7, 16, 1, "zvfh");
+ iree_uk_test_mmt4d(IREE_UK_FLAG_MMT4D_TYPE_S8S8S32, 12, 16, 8,
+ "xsmtvdot,zvl256b");
+ iree_uk_test_mmt4d(IREE_UK_FLAG_MMT4D_TYPE_S8S8S32, 24, 32, 16,
+ "xsmtvdot,zvl1024b");
+ iree_uk_test_mmt4d(IREE_UK_FLAG_MMT4D_TYPE_S8S8S32, 48, 64, 32,
+ "xsmtvdot,zvl4096b");
iree_uk_test_mmt4d(IREE_UK_FLAG_MMT4D_TYPE_BF16BF16F32, 7, 16, 1, "zvfbfwma");
#endif // defined(IREE_ARCH_ARM_64)
diff --git a/runtime/src/iree/schemas/cpu_feature_bits.inl b/runtime/src/iree/schemas/cpu_feature_bits.inl
index b634143..b0aa262 100644
--- a/runtime/src/iree/schemas/cpu_feature_bits.inl
+++ b/runtime/src/iree/schemas/cpu_feature_bits.inl
@@ -126,3 +126,9 @@
IREE_CPU_FEATURE_BIT(RISCV_64, 0, 1, ZVFHMIN, "zvfhmin")
IREE_CPU_FEATURE_BIT(RISCV_64, 0, 2, ZVFH, "zvfh")
IREE_CPU_FEATURE_BIT(RISCV_64, 0, 3, ZVFBFWMA, "zvfbfwma")
+IREE_CPU_FEATURE_BIT(RISCV_64, 0, 4, XSMTVDOT, "xsmtvdot")
+// Minimum VLEN guarantees (LLVM zvl*b) used to gate SpaceMiT IME
+// tile shapes that require a full vmadot operand panel in one vector register.
+IREE_CPU_FEATURE_BIT(RISCV_64, 0, 5, ZVL256B, "zvl256b")
+IREE_CPU_FEATURE_BIT(RISCV_64, 0, 6, ZVL1024B, "zvl1024b")
+IREE_CPU_FEATURE_BIT(RISCV_64, 0, 7, ZVL4096B, "zvl4096b")
diff --git a/tests/e2e/matmul/BUILD.bazel b/tests/e2e/matmul/BUILD.bazel
index cf42715..ef83b21 100644
--- a/tests/e2e/matmul/BUILD.bazel
+++ b/tests/e2e/matmul/BUILD.bazel
@@ -176,6 +176,7 @@
"arm_64:dotprod:+dotprod",
"arm_64:i8mm:+i8mm",
"x86_64:avx512vnni:" + ",".join(X86_64_AVX512_VNNI),
+ "riscv_64:ime:+v,+zvl256b,+xsmtvdot",
] if lhs_rhs_type == "i8" and acc_type == "i32" else [
"x86_64:avx2:" + ",".join(X86_64_AVX2),
"x86_64:avx512:" + ",".join(X86_64_AVX512),
diff --git a/tests/e2e/matmul/CMakeLists.txt b/tests/e2e/matmul/CMakeLists.txt
index 40f516d..5e93079 100644
--- a/tests/e2e/matmul/CMakeLists.txt
+++ b/tests/e2e/matmul/CMakeLists.txt
@@ -144,6 +144,7 @@
"arm_64:dotprod:+dotprod"
"arm_64:i8mm:+i8mm"
"x86_64:avx512vnni:+avx,+avx2,+fma,+f16c,+avx512f,+avx512vl,+avx512cd,+avx512bw,+avx512dq,+avx512vnni"
+ "riscv_64:ime:+v,+zvl256b,+xsmtvdot"
)
iree_generated_e2e_runner_test(
@@ -344,6 +345,7 @@
"arm_64:dotprod:+dotprod"
"arm_64:i8mm:+i8mm"
"x86_64:avx512vnni:+avx,+avx2,+fma,+f16c,+avx512f,+avx512vl,+avx512cd,+avx512bw,+avx512dq,+avx512vnni"
+ "riscv_64:ime:+v,+zvl256b,+xsmtvdot"
)
iree_generated_e2e_runner_test(