Skip to content

HLO: skainet-io-iree-params with IrpaWriter + emit #flow.parameter.named (PR C of #523)#525

Merged
michalharakal merged 1 commit intodevelopfrom
feature/hlo-iree-params-writer
Apr 19, 2026
Merged

HLO: skainet-io-iree-params with IrpaWriter + emit #flow.parameter.named (PR C of #523)#525
michalharakal merged 1 commit intodevelopfrom
feature/hlo-iree-params-writer

Conversation

@michalharakal
Copy link
Copy Markdown
Contributor

Summary

  • New module skainet-io-iree-params (peer of skainet-io-gguf / skainet-io-safetensors) with IrpaWriter: streams IREE parameter archives (.irpa) from List<ExternalParameterRef>.
  • Completes PR B's MLIR emission: util.global private @key = #flow.parameter.named<\"scope\"::\"key\"> : type instead of an uninitialized global — this is what binds the symbol to the archive entry at iree-compile --iree-opt-import-parameters=<path> time.
  • MlirValidator updated to accept module-scope util.global bindings (they use @-prefixed symbols, not %-prefixed SSA values).

Format notes

Byte layout per IREE's parameter_archive.h: 40-byte v0 header + 48-byte segment refs, then entry / metadata / storage segments with 16- and 64-byte alignments respectively. The C entry-header struct has an implicit 4-byte pad after u32 type to align u64 flags — the writer emits that explicitly and a byte-level test pins the 80-byte entry size so the wire format cannot drift silently.

Scope is not encoded in the archive. A .irpa binds to one scope at runtime via --parameters=<scope>=<file>.irpa. IrpaWriter.groupByScope(refs) helps callers split multi-scope inputs.

What's NOT here

  • No real iree-compile round-trip yet — needs an IREE toolchain in CI. Byte-level layout tests stand in: any drift from the reference C format breaks them.
  • No mmap-backed BufferHandle path — that's PR E (Mapped/FileBacked). Writer accepts Owned + Borrowed today; other flavors raise with a pointer to PR E.
  • No caller flipped to external mode — that's PR D (skainet-whisper wiring).

Test plan

  • IrpaWriterTest.testHeaderMagicAndVersion\"IRPA\" magic, v0.0, header_size = 40.
  • testEntryCountAndSegmentOffsets — segment offsets match the spec layout for a 2-entry archive.
  • testDataEntryLayout — 80-byte entry record with the post-type pad in the right place.
  • testKeysAndDataRoundTripExactly — concatenated keys + per-entry data at 64-aligned storage offsets.
  • testEmptyInputIsRejectedLoudly, testGroupByScopePreservesOrder, testOwnedBufferHandleWithOffsetAndBorrowedBoth.
  • Full :skainet-compile:skainet-compile-hlo:jvmTest and :skainet-io:skainet-io-iree-params:jvmTest green.
  • Manual verification against iree-compile --iree-opt-import-parameters=<written.irpa> once a CI toolchain exists.

Part of #523. After this lands, PR D (skainet-whisper wiring) and PR E (mmap in gguf + safetensors) can proceed in parallel on disjoint modules.

🤖 Generated with Claude Code

Writes IREE parameter archives (.irpa) consumable by
`iree-compile --iree-opt-import-parameters=<path>` and by
`iree-run-module --parameters=<scope>=<path>`.

The archive format per IREE's `parameter_archive.h`:

  +--- 40 B ----+ fixed header (magic "IRPA", version, counts)
  +--- 48 B ----+ three segment references
  +--- pad 16 --+
  +-------------+ entry segment: 80-byte DATA records
  +-------------+ metadata segment: concatenated key bytes
  +--- pad 64 --+
  +-------------+ storage segment: raw tensor bytes per entry

All u16/u32/u64 values little-endian. The C entry-header struct has
an implicit 4-byte pad after `u32 type` to align the following `u64
flags` — the writer emits that pad explicitly and a byte-level test
pins the 80-byte layout so future changes can't silently re-break it.

No scope column in the archive itself: scope is a runtime binding
(`--parameters=<scope>=<file>`). Callers with multiple scopes group
via `IrpaWriter.groupByScope(refs)` and emit one .irpa per scope.

The writer delegates byte sourcing to `BufferHandle`; Owned and
Borrowed variants are wired today, with Mapped / FileBacked landing
in PR E (#523) to give the gguf and safetensors loaders a zero-copy
path into the archive.

### Companion fix in skainet-compile-hlo

PR B (#524) emitted `util.global private @key : type` without an
initializer, which iree-compile treats as uninitialized — it would
not import anything from a .irpa. PR C completes the emission:

    util.global private @key = #flow.parameter.named<"scope"::"key"> : type
    %r = util.global.load @key : type

`MlirValidator` was also taught to accept module-scope `util.global`
assignments: the `@`-prefixed symbol is a global, not an SSA value,
and must not trip the existing `%`-only SSA-format check.

### Tests

- IrpaWriterTest pins the byte layout against the IREE spec —
  header magic/version, segment offsets, entry-record fields, key
  concatenation, data placement with 64-byte per-entry alignment,
  Owned / Borrowed handle support, groupByScope ordering, empty-
  input rejection.
- Existing ConstantMaterializationPolicyTest updated to assert the
  new `#flow.parameter.named<...>` initializer on every externalized
  global.
- Full `:skainet-compile:skainet-compile-hlo:jvmTest` and
  `:skainet-io:skainet-io-iree-params:jvmTest` pass.

### Deferred verification

A real `iree-compile --iree-opt-import-parameters=<written.irpa>`
round-trip test will land once CI has an IREE toolchain available.
Byte-level layout tests are a close proxy — any deviation from the
reference C format breaks both.

Part of #523.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@michalharakal michalharakal merged commit 041d556 into develop Apr 19, 2026
2 of 4 checks passed
@michalharakal michalharakal deleted the feature/hlo-iree-params-writer branch April 19, 2026 11:12
michalharakal added a commit that referenced this pull request Apr 19, 2026
Fixes `./gradlew allTests` configuration failure on develop: the
module declared a `js()` target but depends on :skainet-compile-hlo,
which does not publish one. Gradle resolution fails with a
platform-type variant mismatch (consumer wants `js`, producer only
has `wasm`).

Wasm targets stay — they use a distinct `wasm` platform attribute
that :skainet-compile-hlo does publish.

This fix was originally part of PR #525 (commit fdd378e) but got
dropped during the merge; only the first commit landed. Re-applying
as a standalone, narrow fix.

Closes #527

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant