[CPU] Vectorize parallel dims for non-AVX512 x86 matmul defaults (#24701)
See GitHub issue: https://github.com/iree-org/iree/issues/24700
`getDefaultMatmulVectorSizes` used `{1, 1, vectorSize}` for non-AVX512
x86, which vectorizes only the reduction (`K`) dim and leaves the
parallel/output dims scalar. This forces a horizontal reduction per
output element and is dramatically slower for f32 matmul_like /
batch_matmul kernels compared to v3.5 (~2.4x end-to-end regression on an
affected model; ~92x on an isolated `512x512x512` matmul at `x86-64-v3`,
`-O1`). Mirror the AArch64/RISC-V defaults by register-blocking the
matmul with `{8, vectorSize, vectorSize}`, vectorizing both parallel
dims (`M` unroll, `N` vector) in addition to `K`.
Numerics are unchanged. As a side effect, dynamic-shape accumulating
GEMMs may now hoist a couple of tiny fixed-size remainder scratch
buffers (`memref.alloca`) from register-blocked peeling; the accumulator
stays in registers and is written directly to the destination (no heap
alloc, no `linalg.generic` copy). The `pipeline_tests.mlir`
accumulating-GEMM guard is relaxed to forbid only heap allocation, and
the `select_x86_64` expected configs are updated `([1, 1, 0] -> [8, 4,
0])`.
---------
Signed-off-by: Paul Stark <paul.stark@cdprojektred.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>diff --git a/compiler/src/iree/compiler/Codegen/LLVMCPU/KernelDispatch.cpp b/compiler/src/iree/compiler/Codegen/LLVMCPU/KernelDispatch.cpp
index c075923..600ade9 100644
--- a/compiler/src/iree/compiler/Codegen/LLVMCPU/KernelDispatch.cpp
+++ b/compiler/src/iree/compiler/Codegen/LLVMCPU/KernelDispatch.cpp
@@ -1263,7 +1263,10 @@
if (hasAVX512fFeature(targetAttr.getConfiguration())) {
sizes.append({8, 32, 16});
} else {
- sizes.append({1, 1, vectorSize});
+ // For non-AVX512 x86, register-block the matmul by vectorizing
+ // both parallel dims (M unroll, N vector) in addition to the reduction
+ // dim.
+ sizes.append({8, vectorSize, vectorSize});
}
return;
}
diff --git a/compiler/src/iree/compiler/Codegen/LLVMCPU/test/pipeline_tests.mlir b/compiler/src/iree/compiler/Codegen/LLVMCPU/test/pipeline_tests.mlir
index edf6260..d0acc85 100644
--- a/compiler/src/iree/compiler/Codegen/LLVMCPU/test/pipeline_tests.mlir
+++ b/compiler/src/iree/compiler/Codegen/LLVMCPU/test/pipeline_tests.mlir
@@ -616,8 +616,8 @@
// -----
-// Verify that stack buffer is not created in accumulating GEMMs dispatches;
-// it direct writes the result into the destination buffer.
+// Verify that accumulating GEMM dispatches write the result directly into the
+// destination buffer.
#executable_target_embedded_elf_x86_64_ = #hal.executable.target<"llvm-cpu", "embedded-elf-x86_64", {cpu_features = "", native_vector_size = 16 : index, target_triple = "x86_64-none-elf"}>
#pipeline_layout = #hal.pipeline.layout<constants = 3, bindings = [
@@ -648,7 +648,13 @@
return
}
// CHECK-LABEL: func.func @matmul_accumulate_from_readonly(
-// CHECK-NOT: memref.alloc
+// Register-blocked peeling emits small fixed-size stack buffers.
+// The memref.alloc and linalg.generic guards are repeated on both
+// sides of the alloca check so they cover the whole function.
+// CHECK-NOT: memref.alloc(
+// CHECK-NOT: linalg.generic
+// CHECK: memref.alloca(
+// CHECK-NOT: memref.alloc(
// CHECK-NOT: linalg.generic
// -----
diff --git a/compiler/src/iree/compiler/Codegen/LLVMCPU/test/select_x86_64_lowering_strategy.mlir b/compiler/src/iree/compiler/Codegen/LLVMCPU/test/select_x86_64_lowering_strategy.mlir
index d73394e..a551c3c 100644
--- a/compiler/src/iree/compiler/Codegen/LLVMCPU/test/select_x86_64_lowering_strategy.mlir
+++ b/compiler/src/iree/compiler/Codegen/LLVMCPU/test/select_x86_64_lowering_strategy.mlir
@@ -556,7 +556,7 @@
%7 = linalg.matmul ins(%3, %4 : tensor<128x384xi8>, tensor<384x1536xi8>) outs(%6 : tensor<128x1536xi32>) -> tensor<128x1536xi32>
return %7 : tensor<128x1536xi32>
}
-// CHECK-DAG: #[[CONFIG:.+]] = #iree_cpu.lowering_config<cache_parallel = [64, 64, 0], distribution = [64, 64, 0], vector_common_parallel = [1, 1, 0], vector_reduction = [0, 0, 4]>
+// CHECK-DAG: #[[CONFIG:.+]] = #iree_cpu.lowering_config<cache_parallel = [64, 64, 0], distribution = [64, 64, 0], vector_common_parallel = [8, 4, 0], vector_reduction = [0, 0, 4]>
// CHECK-DAG: #[[TRANSLATION:.+]] = #iree_codegen.translation_info<pipeline = #iree_cpu.pipeline<DoubleTilingExpert>, {{\{}}enable_loop_peeling}>
// CHECK: func.func @matmul_i8_i8_i32_static(
// CHECK-SAME: translation_info = #[[TRANSLATION]]
@@ -1036,7 +1036,7 @@
} -> tensor<2304x144xi8>
return %11 : tensor<2304x144xi8>
}
-// CHECK-DAG: #[[CONFIG:.+]] = #iree_cpu.lowering_config<cache_parallel = [64, 48, 0], distribution = [64, 48, 0], vector_common_parallel = [1, 1, 0], vector_reduction = [0, 0, 4]>
+// CHECK-DAG: #[[CONFIG:.+]] = #iree_cpu.lowering_config<cache_parallel = [64, 48, 0], distribution = [64, 48, 0], vector_common_parallel = [8, 4, 0], vector_reduction = [0, 0, 4]>
// CHECK-DAG: #[[TRANSLATION:.+]] = #iree_codegen.translation_info<pipeline = #iree_cpu.pipeline<DoubleTilingExpert>, {{\{}}enable_loop_peeling}>
// CHECK: func.func @quant_model(
// CHECK-SAME: translation_info = #[[TRANSLATION]]