Skip to content

Commit 6a4d8bc

Browse files
jtuylsclaude
andcommitted
[hal] Add ObjectLike trait to hal.executable.source
Every other executable op in the pipeline (flow.executable, stream.executable, hal.executable) has ObjectLike. `hal.executable.source` satisfies the same requirements — symbol table, isolated from above, only referenced by symbol — but was missing the trait. This matters for iree-link: when linking input-level MLIR modules that use extern dispatch, hal.executable.source ops are transitive symbol dependencies of the linked functions (referenced via `flow.dispatch @name::@export`). Without ObjectLike, the linker's symbol indexing skips them (LinkModules.cpp filters IsolatedFromAbove ops that aren't ObjectLike or FunctionOpInterface), causing silent "unresolved external symbol" errors. All 10 ObjectLike consumers in the compiler handle hal.executable.source correctly: analysis passes (affinity, device, async copy) skip it, DeduplicateExecutables can deduplicate it, and MaterializeInterfaces already processes it explicitly by type. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Jorn <jorn.tuyls@gmail.com>
1 parent ffcbc3b commit 6a4d8bc

File tree

5 files changed

+79
-0
lines changed

5 files changed

+79
-0
lines changed

compiler/src/iree/compiler/Dialect/HAL/IR/HALOps.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2616,6 +2616,7 @@ def HAL_ExecutableSourceOp : HAL_Op<"executable.source", [
26162616
SingleBlockImplicitTerminator<"IREE::HAL::ExecutableSourceEndOp">,
26172617
Symbol,
26182618
SymbolTable,
2619+
Util_ObjectLike,
26192620
]> {
26202621
let summary = [{Generic source contents of an executable op.}];
26212622
let description = [{

tools/test/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ iree_lit_test_suite(
4141
"iree-dump-module.mlir",
4242
"iree-dump-parameters.txt",
4343
"iree-link-bundle.mlir",
44+
"iree-link-executable-source.mlir",
4445
"iree-link.mlir",
4546
"iree-run-mlir.mlir",
4647
"iree-run-module-expected.mlir",
@@ -71,6 +72,7 @@ iree_lit_test_suite(
7172
"iree-link-globals-a.mlir",
7273
"iree-link-globals-b.mlir",
7374
"iree-link-globals-c.mlir",
75+
"iree-link-executable-source-module-d.mlir",
7476
"iree-link-module-a.mlir",
7577
"iree-link-module-b.mlir",
7678
"iree-link-module-c.mlir",
@@ -82,6 +84,7 @@ iree_lit_test_suite(
8284
"iree-link-anonymous.mlir",
8385
"iree-tokenize.json",
8486
"iree-tokenize.tiktoken",
87+
"iree-link-executable-source-module-d.mlir",
8588
"iree-link-globals-a.mlir",
8689
"iree-link-globals-b.mlir",
8790
"iree-link-globals-c.mlir",

tools/test/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ iree_lit_test_suite(
2929
"iree-dump-module.mlir"
3030
"iree-dump-parameters.txt"
3131
"iree-link-bundle.mlir"
32+
"iree-link-executable-source.mlir"
3233
"iree-link.mlir"
3334
"iree-run-mlir.mlir"
3435
"iree-run-module-expected.mlir"
@@ -68,6 +69,7 @@ iree_lit_test_suite(
6869
MLIROpBaseTdFiles
6970
echo_npy.py
7071
iree-link-anonymous.mlir
72+
iree-link-executable-source-module-d.mlir
7173
iree-link-globals-a.mlir
7274
iree-link-globals-b.mlir
7375
iree-link-globals-c.mlir
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Source module with hal.executable.source (extern kernel dispatch).
2+
// Used by iree-link-executable-source.mlir.
3+
4+
module @module_d {
5+
// Extern kernel executable (e.g., a pre-compiled GPU kernel object).
6+
hal.executable.source private @extern_kernel attributes {
7+
objects = #hal.executable.objects<{}>
8+
} {
9+
hal.executable.export public @entry ordinal(0)
10+
layout(#hal.pipeline.layout<constants = 2, bindings = [
11+
#hal.pipeline.binding<storage_buffer, ReadOnly>,
12+
#hal.pipeline.binding<storage_buffer>
13+
]>) count(%device: !hal.device) -> (index, index, index) {
14+
%c1 = arith.constant 1 : index
15+
hal.return %c1, %c1, %c1 : index, index, index
16+
} attributes {workgroup_size = [64 : index, 1 : index, 1 : index]}
17+
}
18+
19+
// Function that dispatches to the extern kernel.
20+
util.func public @transform(%arg0: tensor<4xf32>) -> tensor<4xf32> {
21+
%c4 = arith.constant 4 : i32
22+
%c1 = arith.constant 1 : index
23+
%0 = flow.dispatch @extern_kernel::@entry(%c4, %arg0)
24+
: (i32, tensor<4xf32>) -> tensor<4xf32>
25+
util.return %0 : tensor<4xf32>
26+
}
27+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Tests linking modules containing hal.executable.source ops.
2+
// hal.executable.source is used for hand-authored extern dispatch kernels
3+
// (e.g., pre-compiled GPU kernel objects). The linker must copy these
4+
// alongside the functions that reference them via flow.dispatch.
5+
6+
// RUN: iree-link \
7+
// RUN: --link-module=%p/iree-link-executable-source-module-d.mlir \
8+
// RUN: %s | FileCheck %s
9+
10+
// External reference to module D's transform function.
11+
util.func private @module_d.transform(%arg0: tensor<4xf32>) -> tensor<4xf32>
12+
13+
// Main entry point that calls the linked function.
14+
util.func public @main(%arg0: tensor<4xf32>) -> tensor<4xf32> {
15+
%0 = util.call @module_d.transform(%arg0) : (tensor<4xf32>) -> tensor<4xf32>
16+
util.return %0 : tensor<4xf32>
17+
}
18+
19+
// After linking:
20+
// 1. The function body is filled in (not just a declaration).
21+
// CHECK-DAG: util.func public @main
22+
// CHECK-DAG: util.func private @module_d.transform
23+
24+
// 2. The flow.dispatch reference in the linked function body is intact.
25+
// CHECK-DAG: flow.dispatch @extern_kernel::@entry
26+
27+
// 3. The hal.executable.source is copied with its body intact.
28+
// CHECK-DAG: hal.executable.source private @extern_kernel
29+
// CHECK-DAG: hal.executable.export public @entry ordinal(0)
30+
// CHECK-DAG: workgroup_size = [64 : index, 1 : index, 1 : index]
31+
32+
// -----
33+
34+
// Test that hal.executable.source internal symbols are NOT treated as
35+
// external dependencies. The nested hal.executable.export should not
36+
// appear as a linkable symbol.
37+
38+
// RUN: iree-link --list-symbols \
39+
// RUN: %p/iree-link-executable-source-module-d.mlir \
40+
// RUN: | FileCheck %s --check-prefix=SYMBOLS
41+
42+
// Only the public function should be listed, not the executable or its exports.
43+
// SYMBOLS: Public symbols in
44+
// SYMBOLS: @transform
45+
// SYMBOLS-NOT: @extern_kernel
46+
// SYMBOLS-NOT: @entry

0 commit comments

Comments
 (0)