[Codegen][CPU] Register embedded ukernel bitcode + provider lookup. (#24568)
Wires the LLVMCPU plugin and the `#iree_cpu.ukernel_provider` attribute
to actually resolve ukernel bitcode:
* On plugin init, the embedded `iree_uk_cpu_bitcode` TOC is iterated and
each `.bc` is added to the global `EmbeddedDataDirectory` (mirrors
`addAMDGPUUkernelBitcodeToGlobalEmbeddedDataDirectory` in
`ROCMTarget.cpp`).
* `UKernelProviderAttr::createAndReplaceWithUkernelOp` now resolves the
bitcode by name: user-supplied `hal.executable.objects` ancestors first
(BYO and BYO-override paths), then the embedded directory. If found and
not already on the op, the attribute is attached to the source op; the
default `LowerBitcodeUKernelsPass` rewrite then preserves it on the
resulting `ukernel.generic`. The provider still returns `std::nullopt`
for the rewrite itself — specialized `inner_tiled` handling (scalar
`intrinsics_{m,n,k}` + outer K count + ACC inner stride) lands with
SelectUKernels in a follow-up.
* Filename match is `<ukernelName>.<features>.bc` (or bare
`<ukernelName>.bc`), matching the build rules under
`compiler/plugins/target/LLVMCPU/builtins/ukernel/`. Multi-variant
disambiguation by target feature set also lands with SelectUKernels.
BUILD.bazel: LLVMCPU plugin gains a dep on
`//compiler/plugins/target/LLVMCPU/builtins/ukernel:iree_uk_cpu_bitcode`,
and the `iree_cpu` dialect picks up HAL IR + `EmbeddedDataDirectory` so
the provider implementation can live with the attribute (consistent with
how `ROCMUkernelBitcodeSupport.cpp` sits inside the ROCM dialect).
Tests: a three-way lit test (`lookup_builtin_and_byo.mlir`) covers each
branch of the lookup:
1. Built-in only — no user objects; expect a `dense_resource<…>` with
the embedded bf16 bitcode (2732 bytes) attached.
2. BYO override — user supplies bitcode under the built-in's filename;
expect the user's bytes to flow through unchanged (and CHECK-NOT against
`dense_resource` to catch a regression where the provider silently swaps
in the embedded copy).
3. Pure BYO — user-only name with no matching built-in; expect the
user's bitcode to surface on the rewritten op.
Progress towards https://github.com/iree-org/iree/issues/24574.
Signed-off-by: Benoit Jacob <jacob.benoit.1@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>diff --git a/compiler/plugins/target/LLVMCPU/BUILD.bazel b/compiler/plugins/target/LLVMCPU/BUILD.bazel
index 8181ef5..c2bb725 100644
--- a/compiler/plugins/target/LLVMCPU/BUILD.bazel
+++ b/compiler/plugins/target/LLVMCPU/BUILD.bazel
@@ -32,6 +32,7 @@
":LinkerTool",
":StaticLibraryGenerator",
"//compiler/plugins/target/LLVMCPU/builtins:Builtins",
+ "//compiler/plugins/target/LLVMCPU/builtins/ukernel:iree_uk_cpu_bitcode",
"//compiler/src/iree/compiler/Codegen/Common",
"//compiler/src/iree/compiler/Codegen/Dialect/CPU/IR:IREECPUDialect",
"//compiler/src/iree/compiler/Codegen/Dialect/Codegen/IR:IREECodegenDialect",
diff --git a/compiler/plugins/target/LLVMCPU/CMakeLists.txt b/compiler/plugins/target/LLVMCPU/CMakeLists.txt
index dc9e1d6..345f389 100644
--- a/compiler/plugins/target/LLVMCPU/CMakeLists.txt
+++ b/compiler/plugins/target/LLVMCPU/CMakeLists.txt
@@ -69,6 +69,7 @@
iree::compiler::PluginAPI
iree::compiler::Utils
iree::compiler::plugins::target::LLVMCPU::builtins::Builtins
+ iree::compiler::plugins::target::LLVMCPU::builtins::ukernel::iree_uk_cpu_bitcode
PUBLIC
)
diff --git a/compiler/plugins/target/LLVMCPU/LLVMCPUTarget.cpp b/compiler/plugins/target/LLVMCPU/LLVMCPUTarget.cpp
index e5334da..ad04206 100644
--- a/compiler/plugins/target/LLVMCPU/LLVMCPUTarget.cpp
+++ b/compiler/plugins/target/LLVMCPU/LLVMCPUTarget.cpp
@@ -14,6 +14,7 @@
#include "compiler/plugins/target/LLVMCPU/builtins/Device.h"
#include "compiler/plugins/target/LLVMCPU/builtins/Musl.h"
#include "compiler/plugins/target/LLVMCPU/builtins/UKernel.h"
+#include "compiler/plugins/target/LLVMCPU/builtins/ukernel/iree_uk_cpu_bitcode.h"
#include "iree/compiler/Codegen/Common/Passes.h"
#include "iree/compiler/Codegen/Dialect/CPU/IR/IREECPUDialect.h"
#include "iree/compiler/Codegen/Dialect/CPU/IR/IREECPUTypes.h"
@@ -28,6 +29,7 @@
#include "iree/compiler/Dialect/HAL/Utils/LLVMLinkerUtils.h"
#include "iree/compiler/Dialect/LinalgExt/IR/LinalgExtDialect.h"
#include "iree/compiler/PluginAPI/Client.h"
+#include "iree/compiler/Utils/EmbeddedDataDirectory.h"
#include "iree/compiler/Utils/ModuleUtils.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.h"
@@ -910,6 +912,23 @@
CPUCodegenOptions codegenOptions;
};
+// Iterates over the embedded LLVMCPU C-ukernel bitcode TOC and inserts each
+// entry into the global `EmbeddedDataDirectory`. The CPU `UKernelProvider`
+// looks files up there by filename (after first checking any user-supplied
+// `hal.executable.objects` above the op), so this is what makes built-in
+// ukernels available to `iree-compile` for ops carrying a
+// `iree_codegen.ukernel` descriptor that does not come with its own bitcode.
+// Mirrors `addAMDGPUUkernelBitcodeToGlobalEmbeddedDataDirectory` in
+// `compiler/plugins/target/ROCM/ROCMTarget.cpp`.
+static void addLLVMCPUUkernelBitcodeToGlobalEmbeddedDataDirectory() {
+ EmbeddedDataDirectory::withGlobal([](EmbeddedDataDirectory &dir) {
+ const iree_file_toc_t *toc = iree_uk_cpu_bitcode_create();
+ for (size_t i = 0; i < iree_uk_cpu_bitcode_size(); ++i) {
+ dir.addFile(toc[i].name, llvm::StringRef{toc[i].data, toc[i].size});
+ }
+ });
+}
+
} // namespace
} // namespace mlir::iree_compiler::IREE::HAL
@@ -920,5 +939,7 @@
mlir::iree_compiler::PluginRegistrar *registrar) {
registrar->registerPlugin<mlir::iree_compiler::IREE::HAL::LLVMCPUSession>(
"hal_target_llvm_cpu");
+ mlir::iree_compiler::IREE::HAL::
+ addLLVMCPUUkernelBitcodeToGlobalEmbeddedDataDirectory();
return true;
}
diff --git a/compiler/plugins/target/LLVMCPU/builtins/ukernel/test/BUILD.bazel b/compiler/plugins/target/LLVMCPU/builtins/ukernel/test/BUILD.bazel
index 5ff5b44..ff9a4b3 100644
--- a/compiler/plugins/target/LLVMCPU/builtins/ukernel/test/BUILD.bazel
+++ b/compiler/plugins/target/LLVMCPU/builtins/ukernel/test/BUILD.bazel
@@ -17,6 +17,7 @@
srcs = enforce_glob(
# keep sorted
[
+ "lookup_builtin_and_byo.mlir",
"lower_inner_tiled_to_bitcode_ukernel.mlir",
"lower_inner_tiled_to_bitcode_ukernel_i8_vnni.mlir",
],
diff --git a/compiler/plugins/target/LLVMCPU/builtins/ukernel/test/CMakeLists.txt b/compiler/plugins/target/LLVMCPU/builtins/ukernel/test/CMakeLists.txt
index 1671a1e..9da2113 100644
--- a/compiler/plugins/target/LLVMCPU/builtins/ukernel/test/CMakeLists.txt
+++ b/compiler/plugins/target/LLVMCPU/builtins/ukernel/test/CMakeLists.txt
@@ -14,6 +14,7 @@
NAME
lit
SRCS
+ "lookup_builtin_and_byo.mlir"
"lower_inner_tiled_to_bitcode_ukernel.mlir"
"lower_inner_tiled_to_bitcode_ukernel_i8_vnni.mlir"
TOOLS
diff --git a/compiler/plugins/target/LLVMCPU/builtins/ukernel/test/lookup_builtin_and_byo.mlir b/compiler/plugins/target/LLVMCPU/builtins/ukernel/test/lookup_builtin_and_byo.mlir
new file mode 100644
index 0000000..586c697
--- /dev/null
+++ b/compiler/plugins/target/LLVMCPU/builtins/ukernel/test/lookup_builtin_and_byo.mlir
@@ -0,0 +1,161 @@
+// RUN: iree-opt --iree-codegen-lower-bitcode-ukernels --split-input-file %s | FileCheck %s
+
+// Three-way test of the `#iree_cpu.ukernel_provider` bitcode lookup:
+//
+// (1) Built-in only. Op carries a ukernel descriptor naming a built-in but
+// no `hal.executable.objects`; the provider must find the matching
+// bitcode in the global `EmbeddedDataDirectory` (populated at LLVMCPU
+// plugin init from the embedded TOC) and attach it.
+//
+// (2) Bring-your-own override. Op carries a user-supplied
+// `hal.executable.objects` with a matching filename. The provider must
+// prefer the user's bytes over the built-in — never silently swap in
+// the embedded copy. The user bytes here (`dense<[0xAA, ...]>`) are
+// intentionally distinguishable from the real built-in bitcode.
+//
+// (3) Pure BYO with a name that does not match any built-in. The provider
+// must still attach the user's bitcode and the rewrite must succeed.
+//
+// Together these guard the three branches of `getUKernelBitcode` against
+// silent regressions.
+
+#executable_target = #hal.executable.target<"llvm-cpu", "embedded-elf-x86_64", {
+ iree_codegen.ukernel_provider = #iree_cpu.ukernel_provider
+}>
+#map = affine_map<(d0, d1, d2) -> (d0, d2)>
+#map1 = affine_map<(d0, d1, d2) -> (d1, d2)>
+#map2 = affine_map<(d0, d1, d2) -> (d0, d1)>
+
+// (1) Built-in only: no user-supplied hal.executable.objects → expect the
+// embedded bf16 ukernel bitcode to be attached as a `dense_resource`.
+// CHECK-LABEL: @builtin_lookup
+// CHECK: %[[UK:.+]] = iree_codegen.ukernel.generic
+// CHECK-SAME: {hal.executable.objects = [
+// CHECK-SAME: #hal.executable.object<{
+// CHECK-SAME: path = "iree_uk_mma_x86_avx512bf16_1x16x2_f32_bf16.x86_64_avx512bf16.bc"
+// CHECK-SAME: data = dense_resource<iree_uk_mma_x86_avx512bf16_1x16x2_f32_bf16.x86_64_avx512bf16.bc>
+// CHECK-SAME: iree_codegen.ukernel = #iree_codegen.ukernel_descriptor<"iree_uk_mma_x86_avx512bf16_1x16x2_f32_bf16", bitcode>}
+// CHECK-SAME: "iree_uk_mma_x86_avx512bf16_1x16x2_f32_bf16"
+module attributes {hal.executable.target = #executable_target} {
+ func.func @builtin_lookup(
+ %arg0: tensor<16x2xbf16>, %arg1: tensor<16x2xbf16>) -> tensor<16x16xf32> {
+ %cst = arith.constant 0.000000e+00 : f32
+ %0 = tensor.empty() : tensor<16x16xf32>
+ %1 = linalg.fill ins(%cst : f32) outs(%0 : tensor<16x16xf32>) -> tensor<16x16xf32>
+ %2 = linalg.generic {
+ indexing_maps = [#map, #map1, #map2],
+ iterator_types = ["parallel", "parallel", "reduction"]
+ } ins(%arg0, %arg1 : tensor<16x2xbf16>, tensor<16x2xbf16>)
+ outs(%1 : tensor<16x16xf32>) attrs = {
+ iree_codegen.ukernel = #iree_codegen.ukernel_descriptor<
+ "iree_uk_mma_x86_avx512bf16_1x16x2_f32_bf16", bitcode>
+ } {
+ ^bb0(%in: bf16, %in_0: bf16, %out: f32):
+ %3 = arith.extf %in : bf16 to f32
+ %4 = arith.extf %in_0 : bf16 to f32
+ %5 = arith.mulf %3, %4 : f32
+ %6 = arith.addf %out, %5 : f32
+ linalg.yield %6 : f32
+ } -> tensor<16x16xf32>
+ return %2 : tensor<16x16xf32>
+ }
+}
+
+// -----
+
+#executable_target = #hal.executable.target<"llvm-cpu", "embedded-elf-x86_64", {
+ iree_codegen.ukernel_provider = #iree_cpu.ukernel_provider
+}>
+#map = affine_map<(d0, d1, d2) -> (d0, d2)>
+#map1 = affine_map<(d0, d1, d2) -> (d1, d2)>
+#map2 = affine_map<(d0, d1, d2) -> (d0, d1)>
+
+// (2) Override: user bytes (0xAA-pattern, all four lanes equal — prints as
+// the signless-i8 collapsed splat `dense<-86>`) must reach the
+// rewritten op unchanged. If the provider silently replaced them with
+// the embedded bitcode, the splat below would be swapped for a
+// `dense_resource<…>` and the CHECK-NOT would fail.
+// CHECK-LABEL: @byo_overrides_builtin
+// CHECK: %[[UK:.+]] = iree_codegen.ukernel.generic
+// CHECK-SAME: {hal.executable.objects = [
+// CHECK-SAME: #hal.executable.object<{
+// CHECK-SAME: path = "iree_uk_mma_x86_avx512bf16_1x16x2_f32_bf16.x86_64_avx512bf16.bc"
+// CHECK-SAME: data = dense<-86>
+// CHECK-NOT: dense_resource
+// CHECK-SAME: "iree_uk_mma_x86_avx512bf16_1x16x2_f32_bf16"
+module attributes {hal.executable.target = #executable_target} {
+ func.func @byo_overrides_builtin(
+ %arg0: tensor<16x2xbf16>, %arg1: tensor<16x2xbf16>) -> tensor<16x16xf32> {
+ %cst = arith.constant 0.000000e+00 : f32
+ %0 = tensor.empty() : tensor<16x16xf32>
+ %1 = linalg.fill ins(%cst : f32) outs(%0 : tensor<16x16xf32>) -> tensor<16x16xf32>
+ %2 = linalg.generic {
+ indexing_maps = [#map, #map1, #map2],
+ iterator_types = ["parallel", "parallel", "reduction"]
+ } ins(%arg0, %arg1 : tensor<16x2xbf16>, tensor<16x2xbf16>)
+ outs(%1 : tensor<16x16xf32>) attrs = {
+ iree_codegen.ukernel = #iree_codegen.ukernel_descriptor<
+ "iree_uk_mma_x86_avx512bf16_1x16x2_f32_bf16", bitcode>,
+ hal.executable.objects = [
+ #hal.executable.object<{
+ path = "iree_uk_mma_x86_avx512bf16_1x16x2_f32_bf16.x86_64_avx512bf16.bc",
+ data = dense<[170, 170, 170, 170]> : vector<4xi8>}>
+ ]
+ } {
+ ^bb0(%in: bf16, %in_0: bf16, %out: f32):
+ %3 = arith.extf %in : bf16 to f32
+ %4 = arith.extf %in_0 : bf16 to f32
+ %5 = arith.mulf %3, %4 : f32
+ %6 = arith.addf %out, %5 : f32
+ linalg.yield %6 : f32
+ } -> tensor<16x16xf32>
+ return %2 : tensor<16x16xf32>
+ }
+}
+
+// -----
+
+#executable_target = #hal.executable.target<"llvm-cpu", "embedded-elf-x86_64", {
+ iree_codegen.ukernel_provider = #iree_cpu.ukernel_provider
+}>
+#map = affine_map<(d0, d1, d2) -> (d0, d2)>
+#map1 = affine_map<(d0, d1, d2) -> (d1, d2)>
+#map2 = affine_map<(d0, d1, d2) -> (d0, d1)>
+
+// (3) Pure BYO: user-only name with no matching built-in. The provider must
+// still surface the user's bitcode on the rewritten op.
+// CHECK-LABEL: @byo_pure_with_custom_name
+// CHECK: %[[UK:.+]] = iree_codegen.ukernel.generic
+// CHECK-SAME: {hal.executable.objects = [
+// CHECK-SAME: path = "my_custom_external_ukernel.bc"
+// CHECK-SAME: data = dense<[10, 20, 30, 40]>
+// CHECK-SAME: "my_custom_external_ukernel"
+module attributes {hal.executable.target = #executable_target} {
+ func.func @byo_pure_with_custom_name(
+ %arg0: tensor<16x2xbf16>, %arg1: tensor<16x2xbf16>) -> tensor<16x16xf32> {
+ %cst = arith.constant 0.000000e+00 : f32
+ %0 = tensor.empty() : tensor<16x16xf32>
+ %1 = linalg.fill ins(%cst : f32) outs(%0 : tensor<16x16xf32>) -> tensor<16x16xf32>
+ %2 = linalg.generic {
+ indexing_maps = [#map, #map1, #map2],
+ iterator_types = ["parallel", "parallel", "reduction"]
+ } ins(%arg0, %arg1 : tensor<16x2xbf16>, tensor<16x2xbf16>)
+ outs(%1 : tensor<16x16xf32>) attrs = {
+ iree_codegen.ukernel = #iree_codegen.ukernel_descriptor<
+ "my_custom_external_ukernel", bitcode>,
+ hal.executable.objects = [
+ #hal.executable.object<{
+ path = "my_custom_external_ukernel.bc",
+ data = dense<[10, 20, 30, 40]> : vector<4xi8>}>
+ ]
+ } {
+ ^bb0(%in: bf16, %in_0: bf16, %out: f32):
+ %3 = arith.extf %in : bf16 to f32
+ %4 = arith.extf %in_0 : bf16 to f32
+ %5 = arith.mulf %3, %4 : f32
+ %6 = arith.addf %out, %5 : f32
+ linalg.yield %6 : f32
+ } -> tensor<16x16xf32>
+ return %2 : tensor<16x16xf32>
+ }
+}
diff --git a/compiler/src/iree/compiler/Codegen/Dialect/CPU/IR/BUILD.bazel b/compiler/src/iree/compiler/Codegen/Dialect/CPU/IR/BUILD.bazel
index efa42af..8ef2115 100644
--- a/compiler/src/iree/compiler/Codegen/Dialect/CPU/IR/BUILD.bazel
+++ b/compiler/src/iree/compiler/Codegen/Dialect/CPU/IR/BUILD.bazel
@@ -63,6 +63,9 @@
"//compiler/src/iree/compiler/Codegen/Dialect/Codegen/Utils",
"//compiler/src/iree/compiler/Codegen/Dialect/Codegen/Utils:MMAUtils",
"//compiler/src/iree/compiler/Dialect/Encoding/IR",
+ "//compiler/src/iree/compiler/Dialect/HAL/IR",
+ "//compiler/src/iree/compiler/Dialect/Util/IR",
+ "//compiler/src/iree/compiler/Utils",
"@llvm-project//llvm:Support",
"@llvm-project//mlir:AffineDialect",
"@llvm-project//mlir:ArithDialect",
diff --git a/compiler/src/iree/compiler/Codegen/Dialect/CPU/IR/CMakeLists.txt b/compiler/src/iree/compiler/Codegen/Dialect/CPU/IR/CMakeLists.txt
index e3fe554..6ff9835 100644
--- a/compiler/src/iree/compiler/Codegen/Dialect/CPU/IR/CMakeLists.txt
+++ b/compiler/src/iree/compiler/Codegen/Dialect/CPU/IR/CMakeLists.txt
@@ -45,6 +45,9 @@
iree::compiler::Codegen::Dialect::Codegen::Utils
iree::compiler::Codegen::Dialect::Codegen::Utils::MMAUtils
iree::compiler::Dialect::Encoding::IR
+ iree::compiler::Dialect::HAL::IR
+ iree::compiler::Dialect::Util::IR
+ iree::compiler::Utils
PUBLIC
)
diff --git a/compiler/src/iree/compiler/Codegen/Dialect/CPU/IR/IREECPUAttrs.cpp b/compiler/src/iree/compiler/Codegen/Dialect/CPU/IR/IREECPUAttrs.cpp
index 60ed072..e1c867b 100644
--- a/compiler/src/iree/compiler/Codegen/Dialect/CPU/IR/IREECPUAttrs.cpp
+++ b/compiler/src/iree/compiler/Codegen/Dialect/CPU/IR/IREECPUAttrs.cpp
@@ -10,12 +10,16 @@
#include "iree/compiler/Codegen/Dialect/Codegen/IR/UKernelOps.h"
#include "iree/compiler/Codegen/Dialect/Codegen/Utils/MMAUtils.h"
#include "iree/compiler/Dialect/Encoding/IR/EncodingTypes.h"
+#include "iree/compiler/Dialect/HAL/IR/HALOps.h"
+#include "iree/compiler/Dialect/HAL/IR/HALTypes.h"
+#include "iree/compiler/Utils/EmbeddedDataDirectory.h"
#include "llvm/ADT/STLExtras.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/Linalg/IR/Linalg.h"
#include "mlir/Dialect/Linalg/IR/LinalgInterfaces.h"
#include "mlir/Dialect/Vector/IR/VectorOps.h"
+#include "mlir/IR/AsmState.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinAttributes.h"
@@ -1099,15 +1103,139 @@
// UKernelProviderAttr
//===----------------------------------------------------------------------===//
+constexpr StringLiteral kHalExecutableObjectsAttrName =
+ "hal.executable.objects";
+
+// Walks parents from `op` to return the nearest `hal.executable.objects`
+// array attribute. If a parent `hal.executable.variant` is reached, its
+// `objects` attribute is returned. Adapted from
+// `ExecutableTargetAttr::lookup`. This is how user-supplied bitcode reaches
+// the ukernel provider: a source MLIR program can attach
+// `hal.executable.objects` at any ancestor of the op (commonly the
+// module, the executable variant, or the op itself).
+static ArrayAttr lookUpExecutableObjects(Operation *op) {
+ MLIRContext *context = op->getContext();
+ auto attrId = StringAttr::get(context, kHalExecutableObjectsAttrName);
+ while (op) {
+ if (auto variantOp = dyn_cast<IREE::HAL::ExecutableVariantOp>(op)) {
+ if (std::optional<ArrayAttr> objects = variantOp.getObjects()) {
+ return *objects;
+ }
+ }
+ if (auto attr = op->getAttrOfType<ArrayAttr>(attrId)) {
+ return attr;
+ }
+ op = op->getParentOp();
+ }
+ return {};
+}
+
+// Returns true if `path` matches `<ukernelName>.<features>.bc`, the
+// filename convention used by the bitcode build rules under
+// `compiler/plugins/target/LLVMCPU/builtins/ukernel/`. The `<features>`
+// part is opaque to the lookup; this CPU framework currently picks
+// whichever built-in (or user-supplied) variant exists for the given
+// algorithm name. Multi-feature-variant disambiguation will arrive with
+// the SelectUKernels pass.
+static bool isMatchingUKernelFile(StringRef path, StringRef ukernelName) {
+ if (!path.ends_with(".bc")) {
+ return false;
+ }
+ if (!path.starts_with(ukernelName)) {
+ return false;
+ }
+ // Either exact name (`<name>.bc`) or features-suffixed
+ // (`<name>.<features>.bc`).
+ size_t after = ukernelName.size();
+ return after == path.size() - 3 ||
+ (after < path.size() - 3 && path[after] == '.');
+}
+
+// Returns an `ExecutableObjectAttr` carrying the bitcode for `ukernelName`.
+// First searches `sourceExecutableObjects` (the user-supplied
+// `hal.executable.objects` ancestor — the bring-your-own-ukernel path),
+// then falls back to the global `EmbeddedDataDirectory` (populated at
+// LLVMCPU plugin init from the embedded TOC). Returns null if no match.
+static IREE::HAL::ExecutableObjectAttr
+getUKernelBitcode(MLIRContext *context, ArrayAttr sourceExecutableObjects,
+ StringRef ukernelName) {
+ if (sourceExecutableObjects) {
+ for (Attribute a : sourceExecutableObjects) {
+ auto object = dyn_cast<IREE::HAL::ExecutableObjectAttr>(a);
+ if (object && isMatchingUKernelFile(object.getPath(), ukernelName)) {
+ return object;
+ }
+ }
+ }
+ std::optional<StringRef> matchedFilename;
+ std::optional<StringRef> matchedBytes;
+ EmbeddedDataDirectory::withGlobal([&](EmbeddedDataDirectory &dir) {
+ for (auto &entry : dir.getMap()) {
+ if (isMatchingUKernelFile(entry.getKey(), ukernelName)) {
+ matchedFilename = entry.getKey();
+ matchedBytes = entry.getValue();
+ return;
+ }
+ }
+ });
+ if (!matchedFilename) {
+ return {};
+ }
+ AsmResourceBlob blob = HeapAsmResourceBlob::allocateAndCopyInferAlign(
+ ArrayRef<char>(matchedBytes->data(), matchedBytes->size()));
+ auto bitcodeDenseAttr = DenseI8ResourceElementsAttr::get(
+ VectorType::get({static_cast<int64_t>(matchedBytes->size())},
+ IntegerType::get(context, 8)),
+ *matchedFilename, std::move(blob));
+ return IREE::HAL::ExecutableObjectAttr::get(
+ context, StringAttr::get(context, *matchedFilename),
+ cast<IREE::Util::SerializableAttrInterface>(bitcodeDenseAttr));
+}
+
std::optional<LogicalResult> UKernelProviderAttr::createAndReplaceWithUkernelOp(
RewriterBase &rewriter, StringRef name, DictionaryAttr targetConfiguration,
Operation *contextualOp, ArrayRef<Value> inputs, ArrayRef<Value> outputs,
SmallVectorImpl<Value> &otherOperands) const {
- // Fall through to the default UKernelGenericOp construction in
- // LowerBitcodeUKernelsPass. Specialized handling of `inner_tiled` ops
- // (threading `intrinsics_{m,n,k}` and the outer K count as scalar operands)
- // will be added in a follow-up commit alongside the SelectUKernels pass that
- // sets the `iree_codegen.ukernel` descriptor and attaches the bitcode.
+ // Resolve the bitcode for this ukernel: user-supplied
+ // `hal.executable.objects` first (BYO and BYO-override paths), falling
+ // back to the built-in bitcode embedded into `iree-compile` at LLVMCPU
+ // plugin init. If found, attach it as `hal.executable.objects` on the
+ // source op so the default `LowerBitcodeUKernelsPass` rewrite preserves
+ // it on the resulting `ukernel.generic`. If the source op already
+ // carries a matching object, this is a no-op (the BYO case).
+ //
+ // We return `std::nullopt` to let the pass run its default
+ // `UKernelGenericOp` construction. Specialized `inner_tiled` handling
+ // (threading `intrinsics_{m,n,k}` and the outer K count as scalar
+ // operands, and computing the ACC inner stride) lands in a follow-up
+ // alongside SelectUKernels.
+ MLIRContext *context = rewriter.getContext();
+ ArrayAttr sourceExecutableObjects = lookUpExecutableObjects(contextualOp);
+ IREE::HAL::ExecutableObjectAttr bitcodeObject =
+ getUKernelBitcode(context, sourceExecutableObjects, name);
+ if (bitcodeObject) {
+ auto attrId = StringAttr::get(context, kHalExecutableObjectsAttrName);
+ ArrayAttr existing = contextualOp->getAttrOfType<ArrayAttr>(attrId);
+ bool alreadyOnOp = false;
+ if (existing) {
+ for (Attribute a : existing) {
+ if (a == bitcodeObject) {
+ alreadyOnOp = true;
+ break;
+ }
+ }
+ }
+ if (!alreadyOnOp) {
+ SmallVector<Attribute> objects;
+ if (existing) {
+ objects.append(existing.begin(), existing.end());
+ }
+ objects.push_back(bitcodeObject);
+ rewriter.modifyOpInPlace(contextualOp, [&] {
+ contextualOp->setAttr(attrId, ArrayAttr::get(context, objects));
+ });
+ }
+ }
return std::nullopt;
}