Skip to content

Commit 3bbf1da

Browse files
authored
[HAL] Renumber export ordinals after pruning (#24162)
Backend serializers index the flatbuffer `exports` vector by ordinal and assume a dense [0, N) range. When PruneExecutablesPass removed unused exports it left ordinals sparse (e.g. [0, 2, 3, 4] with 1 pruned), causing the serializer to write past the end of its vector and produce invalid flatbuffers. This showed up as flatcc verification failures at runtime for models with unused `hal.executable.source` exports. Renumbering to a dense [0, N) range here restores the invariant for all backends without touching any serializer or runtime code. Signed-off-by: Jorn Tuyls <jorn.tuyls@gmail.com>
1 parent 63b9cce commit 3bbf1da

2 files changed

Lines changed: 49 additions & 0 deletions

File tree

compiler/src/iree/compiler/Dialect/HAL/Transforms/PruneExecutables.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,23 @@ struct PruneExecutablesPass
174174
eraseOps(exportRefAttrs.getArrayRef(), referenceMap);
175175
eraseOps(variantRefAttrs, referenceMap);
176176
eraseOps(executableRefAttrs, referenceMap);
177+
178+
// Renumber export ordinals to be contiguous from 0. Backend serializers
179+
// index their flatbuffer `exports` vector by ordinal and assume a dense
180+
// [0, N) range; leaving gaps from pruned exports produces invalid
181+
// flatbuffers. Export symbol references are resolved by name via
182+
// ResolveExportOrdinalsPass, so renumbering is a safe rewrite.
183+
Builder builder(moduleOp.getContext());
184+
for (auto executableOp : moduleOp.getOps<IREE::HAL::ExecutableOp>()) {
185+
for (auto variantOp :
186+
executableOp.getOps<IREE::HAL::ExecutableVariantOp>()) {
187+
int64_t nextOrdinal = 0;
188+
for (auto exportOp :
189+
variantOp.getOps<IREE::HAL::ExecutableExportOp>()) {
190+
exportOp.setOrdinalAttr(builder.getIndexAttr(nextOrdinal++));
191+
}
192+
}
193+
}
177194
}
178195
};
179196

compiler/src/iree/compiler/Dialect/HAL/Transforms/test/prune_executables.mlir

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,35 @@ hal.executable private @exe {
9999
util.func private @user() attributes {
100100
some.ref = @exe::@variant::@used_export
101101
}
102+
103+
// -----
104+
105+
// Tests that after pruning unused exports the remaining exports are
106+
// renumbered to have contiguous ordinals starting from 0. Back-end
107+
// serializers index their flatbuffer exports vector by ordinal; if ordinals
108+
// are left sparse the downstream vector has gaps that compound into invalid
109+
// flatbuffers (ExportDef_ref_t at index N overwrites at a smaller index when
110+
// the vector is sized by pre-prune export count).
111+
112+
#pipeline_layout_renumber = #hal.pipeline.layout<bindings = [
113+
#hal.pipeline.binding<storage_buffer>
114+
]>
115+
hal.executable private @exe_renumber {
116+
hal.executable.variant public @variant target(<"backend", "format">) {
117+
// CHECK-NOT: @unused_a
118+
hal.executable.export public @unused_a ordinal(1) layout(#pipeline_layout_renumber)
119+
// CHECK: hal.executable.export public @used_0 ordinal(0)
120+
hal.executable.export public @used_0 ordinal(0) layout(#pipeline_layout_renumber)
121+
// CHECK: hal.executable.export public @used_1 ordinal(1)
122+
hal.executable.export public @used_1 ordinal(2) layout(#pipeline_layout_renumber)
123+
// CHECK-NOT: @unused_b
124+
hal.executable.export public @unused_b ordinal(3) layout(#pipeline_layout_renumber)
125+
// CHECK: hal.executable.export public @used_2 ordinal(2)
126+
hal.executable.export public @used_2 ordinal(4) layout(#pipeline_layout_renumber)
127+
}
128+
}
129+
util.func private @multi_user() attributes {
130+
ref_0 = @exe_renumber::@variant::@used_0,
131+
ref_1 = @exe_renumber::@variant::@used_1,
132+
ref_2 = @exe_renumber::@variant::@used_2
133+
}

0 commit comments

Comments
 (0)