Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
218 commits
Select commit Hold shift + click to select a range
6039a8d
tests: pin every operator's arg spec before the spec refactor
hunhoffe Sep 18, 2026
7d61112
tests: cover the shape relationships equal-size cases hide
hunhoffe Sep 18, 2026
d12210b
tests: pin RoPE's broadcast angles
hunhoffe Sep 18, 2026
e553f76
operators: separate the shape rule from the design
hunhoffe Sep 18, 2026
9e57e5e
operators: derive get_arg_spec from a declared shape function
hunhoffe Sep 18, 2026
e02bf16
operators: convert the remaining specs to shape functions
hunhoffe Sep 18, 2026
ee9d87c
tests: check catalog laziness in a fresh interpreter, against the cat…
hunhoffe Sep 18, 2026
4340332
operators: bind design parameters instead of restating them
hunhoffe Sep 18, 2026
f29bdaf
operators: bind the shared bases' design parameters too
hunhoffe Sep 19, 2026
d24f483
compilation: link arch-scoped kernel objects before flat ones
hunhoffe Sep 19, 2026
035e0a7
softmax: one operator, one file
hunhoffe Sep 19, 2026
88feb81
transpose, repeat, rope: one operator, one file
hunhoffe Sep 19, 2026
769450b
strided_copy, gemv, gemm, mha: one operator, one file
hunhoffe Sep 19, 2026
ff2abc6
mem_copy, dequant, axpy, leaky_relu: one operator, one file
hunhoffe Sep 19, 2026
a3b037a
rms_norm: one operator, one file
hunhoffe Sep 19, 2026
be07564
allocator: liveness-based static memory planning
hunhoffe Sep 19, 2026
518fbe0
tests: rename the allocator test module to avoid a basename collision
hunhoffe Sep 19, 2026
83d17c6
sequence: accept a planned buffer layout
hunhoffe Sep 19, 2026
2842b83
sequence: plan scratch offsets from liveness, opt-in
hunhoffe Sep 19, 2026
1c44ec4
capture: record a graph from ordinary Python dataflow
hunhoffe Sep 19, 2026
9ce8490
sequence: dispatch a captured graph, ahead of time or just in time
hunhoffe Sep 19, 2026
ef52102
tests: check the captured graph on the fused ELF path too
hunhoffe Sep 19, 2026
9966c93
sequence: never pool a sliced buffer
hunhoffe Sep 19, 2026
b5cd8e4
sequence: infer the buffer layout by default, and name it that way
hunhoffe Sep 19, 2026
e46ae42
tests: pin what CompilableDesign's cache key does and does not distin…
hunhoffe Sep 19, 2026
ae2ffb1
jit_compile: compile a fused sequence through CompilableDesign
hunhoffe Sep 19, 2026
35f5068
jit_compile: pass the aiecc flags a fused ELF needs
hunhoffe Sep 19, 2026
4f4a9c1
jit_compile: handle tracing, and key the cache on it
hunhoffe Sep 19, 2026
571041b
sequence: ask for the ELF path, not the artifact that produced it
hunhoffe Sep 19, 2026
d8a34a6
FusedDispatch: build the ELF through CompilableDesign
hunhoffe Sep 19, 2026
cb62952
compilation: delete FullElfArtifact and its rule
hunhoffe Sep 19, 2026
f12dade
jit_compile: stage inside the generator, and add the xclbin path
hunhoffe Sep 19, 2026
7335940
sequence: give fused MLIR its own filename
hunhoffe Sep 19, 2026
10503a6
compilation: key PythonGeneratedMLIRArtifact on a recipe hash
hunhoffe Sep 19, 2026
0d411ef
SeparateDispatch: build xclbin/insts through CompilableDesign
hunhoffe Sep 19, 2026
261aafc
compilation: delete dead xclbin_input chaining
hunhoffe Sep 19, 2026
02f78d8
compilation: turn fuse_mlir() into a plain generator function
hunhoffe Sep 19, 2026
870e65b
jit_compile: don't rebuild through aiecc when nothing changed
hunhoffe Sep 19, 2026
351747c
base: build standalone operator xclbin+insts through CompilableDesign
hunhoffe Sep 19, 2026
6ecdc36
requirements: bump mlir_aie to 1.4.4.dev26
hunhoffe Sep 19, 2026
68afbf6
compilation: use upstream symbol-prefix and binutil resolution
hunhoffe Sep 19, 2026
aa835da
models: declare llama 3.2's parameters as a module tree
hunhoffe Sep 19, 2026
4a98aee
llama: upload weights from the module tree, by name
hunhoffe Sep 19, 2026
55894e7
base: make compile() actually compile a standalone operator
hunhoffe Sep 19, 2026
d5cf6a5
gemv: pick a legal kernel vector size instead of asserting in C++
hunhoffe Sep 19, 2026
d71eb3f
jit_compile: compile an operator from its design, not from MLIR text
hunhoffe Sep 19, 2026
a9ac50f
jit_compile: key a device parameter by identity, not by its name
hunhoffe Sep 19, 2026
af5c32a
gemv: declare kernels as ExternalFunctions, and fuse children as chil…
hunhoffe Sep 19, 2026
0e9dd8e
operators: declare kernels once in the two shared bases
hunhoffe Sep 19, 2026
e98938b
operators: declare kernels once in axpy, dequant, mem_copy, transpose…
hunhoffe Sep 19, 2026
e52c035
operators: declare kernels once in rope, rms_norm and softmax
hunhoffe Sep 19, 2026
6eeb5aa
operators: declare kernels once in gemm and mha
hunhoffe Sep 19, 2026
cfd8530
flm/gemm: compile through CompilableDesign, and bind the device befor…
hunhoffe Sep 19, 2026
d5ce0e6
jit_compile: guard the device bind, and make its test actually catch …
hunhoffe Sep 19, 2026
08f707b
operators: compile lut_based_ops into the kernel, and delete the archive
hunhoffe Sep 19, 2026
38be4fc
flm/mm_prebuilt: compile insts through CompilableDesign, and delete w…
hunhoffe Sep 19, 2026
a48058c
stream: declare kernels as ExternalFunctions, and delete rename_symbols
hunhoffe Sep 19, 2026
a7d31a8
compilation: retire the kernel build path, and the tests that guarded it
hunhoffe Sep 19, 2026
2db48db
operator model: two draft plans for the interface/overlay rework
hunhoffe Sep 21, 2026
7819e54
operator model: consolidate the two drafts into one plan
hunhoffe Sep 21, 2026
370cbc0
operator model: replace the draft plan with the agreed design
hunhoffe Sep 21, 2026
dc38a3e
operator model: the declaration layer
hunhoffe Sep 21, 2026
ffd5cda
operator model: access patterns for the derived sequence
hunhoffe Sep 21, 2026
c9f188a
tiling: encode to the shim descriptor's real slot rules, and slice bu…
hunhoffe Sep 21, 2026
9e5512d
operator model: the library-owned build
hunhoffe Sep 21, 2026
5c0efba
gemv: declare the overlay and the operator; keep the kernel and seque…
hunhoffe Sep 21, 2026
810b954
operators: the two shared bases and their ten operators on the derive…
hunhoffe Sep 21, 2026
1b1dd4d
operator model: record what is built and what each piece still needs
hunhoffe Sep 21, 2026
664c167
operators: dequant, rms_norm, rope and softmax on the derived sequence
hunhoffe Sep 21, 2026
91b1dec
operator model: status after step 2
hunhoffe Sep 21, 2026
e1b771d
operators: repeat, strided_copy and transpose as declared overrides
hunhoffe Sep 21, 2026
14acae7
gemm: declare the overlay and the operator; the array and the sequenc…
hunhoffe Sep 21, 2026
579ad76
tests: pin the reshaped Softmax and Transpose specs, and cover Weight…
hunhoffe Sep 21, 2026
3140b4c
operator model: status through the first four overrides
hunhoffe Sep 21, 2026
0a88fd3
mha: declare the overlay and the operator; the sequence becomes slices
hunhoffe Sep 21, 2026
82c5f48
operator model: status through mha
hunhoffe Sep 21, 2026
bc809e4
flm/gemm: declare the configuration as the overlay and the shape as t…
hunhoffe Sep 21, 2026
62b934d
mem_copy: declare the overlay and the operator; idle fifos are placed…
hunhoffe Sep 21, 2026
42c3074
mm_prebuilt: declare the shipped overlay as a foreign overlay; the li…
hunhoffe Sep 21, 2026
6917ef3
operator model: status through mm_prebuilt and the foreign path
hunhoffe Sep 21, 2026
b7a3a4d
swiglu_prefill_stream: the group operator comes from Operator.from_spec
hunhoffe Sep 21, 2026
591a44d
step 4: delete bind(), the arg_spec fallback, the shape helpers and t…
hunhoffe Sep 21, 2026
292f0ff
repeat: the legacy dtype accessor the dtype tests read
hunhoffe Sep 21, 2026
69a07a1
graph functions: @iron.graph traces operator calls on handles
hunhoffe Sep 21, 2026
c5ba4f7
swiglu composites as graph functions; CompositeOperator goes
hunhoffe Sep 21, 2026
ef22cbf
llama decode as a graph function
hunhoffe Sep 21, 2026
7868512
packaging: compile(dev, boundaries=, image=) with the derivation rules
hunhoffe Sep 21, 2026
555f036
retire the recorder and the legacy value spellings; check every expor…
hunhoffe Sep 21, 2026
ff73cc5
docs: the declared operator model and graph functions in README and A…
hunhoffe Sep 21, 2026
c51b499
docs: two troubleshooting lines that still named design.py
hunhoffe Sep 21, 2026
afb9fed
tests: run every overlay's design() and every sequence under the stub
hunhoffe Sep 21, 2026
dd374da
a graph's value binding survives the operator's tuned copy
hunhoffe Sep 21, 2026
3756695
tests: flm/gemm's array and both sequence paths in the design probe
hunhoffe Sep 21, 2026
463759c
toolchain gate without a device: every operator lowers to an instruct…
hunhoffe Sep 21, 2026
7b0531f
operator model: the lowering gate and the per-operator diff against P…
hunhoffe Sep 21, 2026
1a4b342
toolchain gate: fused graphs build to full ELFs
hunhoffe Sep 21, 2026
d8b7ec1
toolchain gate: the xclbin image, one kernel per design
hunhoffe Sep 21, 2026
7072d59
graph functions: compile() links the image, the runtime waits for the…
hunhoffe Sep 21, 2026
7b225d3
graph reference against llama_cpu.py: parity, and the softmax length …
hunhoffe Sep 21, 2026
2f7d199
toolchain: name the hrx-xclbinutil partition bug from IRON
hunhoffe Sep 21, 2026
f5d1839
step 5, the halves a toolchain can settle: S1 and S4 build, S2 from s…
hunhoffe Sep 21, 2026
20fc20f
packaging: chunks(n) and the one-chunk xclbin, built on the assumptio…
hunhoffe Sep 21, 2026
f30fb90
per-call values on an xclbin image lower as dispatch-time scalars (§6)
hunhoffe Sep 21, 2026
d681d8e
values on a chunked image: forwarded through the fused sequence, stop…
hunhoffe Sep 21, 2026
ef83eb4
the auto dispatch policy goes; the rest are the plan's image builders
hunhoffe Sep 21, 2026
490d8cc
modules: several graphs over one buffer plan, one image with an entry…
hunhoffe Sep 21, 2026
4539f45
Revert "modules: several graphs over one buffer plan, one image with …
hunhoffe Sep 21, 2026
b301ccc
Revert "values on a chunked image: forwarded through the fused sequen…
hunhoffe Sep 21, 2026
12f6f75
shelve chunks, values on chunked images and the spike tests on a side…
hunhoffe Sep 21, 2026
e5e4e2b
one spelling per operator: retire _classic, __new__ and the alias tables
hunhoffe Sep 21, 2026
fad3057
tests: drop the stubbed design probe and the arg-spec pair; collect e…
hunhoffe Sep 21, 2026
a334bd8
flm GEMM: tune the overlay on demand for the names and the packing
hunhoffe Sep 21, 2026
f2a4630
tests: one golden() helper in place of 24 per-operator generators
hunhoffe Sep 21, 2026
0ea5514
toolchain tests: build the swiglu graph through compile(), once per i…
hunhoffe Sep 21, 2026
e4b0020
Operator carries its own compile surface; the legacy accessors and de…
hunhoffe Sep 21, 2026
12dab0e
sequence.py: a mode, its image and its callable, no dispatch hierarchy
hunhoffe Sep 21, 2026
2f16fc7
tests: the toolchain gates share their tools and fixtures; one scaled…
hunhoffe Sep 21, 2026
e3e9c45
operator tests: one operator_test() per operator; metrics recorded, n…
hunhoffe Sep 21, 2026
bbe2498
plan: §20, prefill as a graph function
hunhoffe Sep 21, 2026
54ac598
prefill, the operators: copies legalize, column-major GEMM weights at…
hunhoffe Sep 21, 2026
7984b74
Prefill step 4: PrefillGraph over the decode caches, traced at the sc…
hunhoffe Sep 21, 2026
bbaa6ce
Prefill step 5: the prefill reference matches the CPU prefill and han…
hunhoffe Sep 21, 2026
57ebf08
Prefill steps 6-7: toolchain gates for the prefill graph; the applica…
hunhoffe Sep 21, 2026
ec15681
Plan: prefill status through step 7
hunhoffe Sep 21, 2026
2502ba0
Toolchain: the ELF gate builds once, and the prefill graph at Llama s…
hunhoffe Sep 21, 2026
aacced5
Plan: prefill at Llama size, the sequence-lowering memory, the kernel…
hunhoffe Sep 21, 2026
c5165ef
Plan: where aiecc's memory goes on the fused prefill sequence
hunhoffe Sep 22, 2026
398dd9a
Note: aiecc clones the whole module per split item
hunhoffe Sep 22, 2026
36e3aaa
MHA issues one descriptor set per KV group: 768 descriptors a call be…
hunhoffe Sep 22, 2026
3f9333e
legalize merges nesting dimensions only as a fallback
hunhoffe Sep 22, 2026
1a70b01
Plan: the sixteen-layer prefill image after the MHA descriptor cut
hunhoffe Sep 22, 2026
9acf961
The model in one place: the tree with its forward, the graphs beside it
hunhoffe Sep 22, 2026
bcea46a
flm: the shipped binary is a second overlay of GEMM, not a second ope…
hunhoffe Sep 22, 2026
0082e9a
Note: the aiecc clone pruning has a draft on the mlir-aie branch
hunhoffe Sep 22, 2026
62965d3
The compile cache owns the paths; IRON keeps a record of what an imag…
hunhoffe Sep 22, 2026
6f3955a
The record's buffer map comes from the tuned operator
hunhoffe Sep 22, 2026
71df093
iron/common gives up what is not its own
hunhoffe Sep 22, 2026
1a475cb
One operator is one module, and it declares the shapes it is tested at
hunhoffe Sep 22, 2026
c8c32da
Foreign overlays are flm's business, behind two overlay hooks
hunhoffe Sep 22, 2026
a0135e3
One elementwise template, and the kernels come from aie.iron.kernels
hunhoffe Sep 22, 2026
c92b3e0
An overlay IRON did not build is external, and the module stays in co…
hunhoffe Sep 22, 2026
1cacc6e
AIEContext is gone: an operator takes the current device and nothing …
hunhoffe Sep 22, 2026
e449745
Peano throughout, and the last three kernels come from their factory
hunhoffe Sep 22, 2026
72016f9
An elementwise overlay with two outputs names its second fifo
hunhoffe Sep 22, 2026
3cd7982
Let the placer place what it can: only the load-bearing pins stay
hunhoffe Sep 22, 2026
800e0dd
One shim budget, one bank rule, and no prose about the past
hunhoffe Sep 22, 2026
c045be1
RMSNorm and Dequant are the elementwise template too
hunhoffe Sep 22, 2026
fe7e95d
One model, one package: iron/models moves into the application
hunhoffe Sep 22, 2026
87b129a
The library stops importing the operator collection it serves
hunhoffe Sep 22, 2026
b301253
Restyle step 1: no utils.py, no golden model, one name per thing
hunhoffe Sep 22, 2026
7f77f45
Restyle step 2: the declaration layer is a package, one file per role
hunhoffe Sep 22, 2026
072dce6
Restyle step 3: build.py is iron/common/design, and the sequence owns…
hunhoffe Sep 22, 2026
3f7f4ed
mlir-aie belongs at the top of a module; torch does not, and a test s…
hunhoffe Sep 22, 2026
2e1711e
An operator's torch is its reference's, so only a reference pays for it
hunhoffe Sep 22, 2026
c94894f
Drop the torch-boundary test: the rule should hold by construction, n…
hunhoffe Sep 22, 2026
45e567a
Restyle step 4a: what a design becomes is iron/common/image
hunhoffe Sep 22, 2026
f654f36
Restyle step 4b: graph.py is a package that reads in tracing order
hunhoffe Sep 22, 2026
e3fe018
The names sequence.py shed are imported from image/, not from image.s…
hunhoffe Sep 22, 2026
43fd0da
References are numpy, as mlir-aie's are, so IRON does not need torch
hunhoffe Sep 22, 2026
ff6e288
The infrastructure tests hand numpy too, and a typed array keeps its …
hunhoffe Sep 22, 2026
06808f0
GEMM reads its micro-kernel geometry from the kernel factory
hunhoffe Sep 22, 2026
36f94ff
GEMM asks the mm factory for its geometry
hunhoffe Sep 22, 2026
978c479
Audit: declare and image stop importing each other, and three dead do…
hunhoffe Sep 22, 2026
07fdd79
Target.kernel and Target.rtp are bound factories, not re-declared met…
hunhoffe Sep 22, 2026
c5f26d0
Callers name generator_for; Operator.generator is gone
hunhoffe Sep 22, 2026
10a46b9
Sequence.sync_parameters was its own name forwarded
hunhoffe Sep 22, 2026
10d92fc
One name, one meaning: transfers, place, plan; TracedStep and Step
hunhoffe Sep 22, 2026
b80458f
Revert "Callers name generator_for; Operator.generator is gone"
hunhoffe Sep 22, 2026
8a481fc
Shape inference and from_spec leave Operator
hunhoffe Sep 22, 2026
623ef96
black: three lines the moves pushed past the column limit
hunhoffe Sep 22, 2026
aecc68a
Build kernels from mlir-aie kernel factories; port transpose and gemv
hunhoffe Sep 25, 2026
29fd2a7
Port the Llama decode ops and elementwise bases to kernel factories
hunhoffe Sep 25, 2026
6268a74
Port gemm to the linalg.mm / zero / convert_copy kernel factories
hunhoffe Sep 25, 2026
17624f3
Port mha to the linalg.mha / zero / passthrough kernel factories
hunhoffe Sep 25, 2026
13979d3
Port dequant and mem_copy to the expand / passthrough kernel factories
hunhoffe Sep 25, 2026
774e248
Port flm/dequant to quant.q4nx_dequant; retire iron/common/kernels.py
hunhoffe Sep 25, 2026
07ebe07
Resolve the factory-kernel output dir before handing it to mlir-aie
hunhoffe Sep 25, 2026
d8cad99
Llama decode: mask softmax to the current context, not a running sum
hunhoffe Sep 25, 2026
480fa95
Llama test: check NPU logits against an fp32 CPU reference
hunhoffe Sep 25, 2026
e4fed44
Llama: flush the prefill KV hand-off to the device, not from it
hunhoffe Sep 25, 2026
263b83b
Llama test: guard run-to-run determinism of NPU logits
hunhoffe Sep 25, 2026
3731ae0
Flush scratch in the full-ELF sequence callable before each dispatch
hunhoffe Sep 25, 2026
4d3eeea
Tests: lean on mlir-aie's verify and benchmark utilities
hunhoffe Sep 25, 2026
afbc276
Tests: judge 1:1 operators by their kernel's tolerance contract
hunhoffe Sep 25, 2026
4f4c061
Tests: one reference per operator, judged by its kernel's contract
hunhoffe Sep 25, 2026
44a5667
Sequences: judge compare mode by each step's kernel contract
hunhoffe Sep 25, 2026
f2f0bc1
Tests and docs: small cleanups
hunhoffe Sep 25, 2026
ff3aaf5
Tracing: take the trace buffer from mlir-aie; real tests, no mocks
hunhoffe Sep 25, 2026
b8a909e
Tracing: write and decode dumps with mlir-aie's TraceConfig
hunhoffe Sep 25, 2026
44b849d
Merge remote-tracking branch 'origin/devel' into kernel-factories
Copilot Sep 25, 2026
a60d170
Make missing Llama weights fail in CI
Copilot Sep 25, 2026
2df9a14
Merge devel (ff0792d) into operator-model-rework-2, with the fork-sid…
hunhoffe Sep 25, 2026
0c7b829
A pyxrt without the ctrl scratchpad no longer reads as "no per-call v…
hunhoffe Sep 25, 2026
d1a1a91
Sigmoid and Tanh lower at a 1024-element tile
hunhoffe Sep 25, 2026
52460ab
Flush scratch in the full-ELF sequence callable before each dispatch
hunhoffe Sep 25, 2026
c209aeb
Clear the kernel registry around a fused build's key-only fusion
hunhoffe Sep 25, 2026
010c214
Port the flm gemm, gemv and swiglu tests to numpy
hunhoffe Sep 25, 2026
275cdd8
Sigmoid and Tanh default to, and are tested at, a 1024-element line
hunhoffe Sep 25, 2026
61dc0a6
Format with black
hunhoffe Sep 25, 2026
c56d720
Name kernels, devices and fused images by content, not position
hunhoffe Sep 25, 2026
52f841d
Merge kernel-factories (a60d170) into operator-model-rework-2, with t…
hunhoffe Sep 25, 2026
38a3d77
gemm: issue unrolled B fills row-block by row-block across columns
hunhoffe Sep 25, 2026
b4fd618
Upload graph weights without a temporary, and before Llama's timed run
hunhoffe Sep 25, 2026
594eb9a
Llama test: quote the graphs' measured KL
hunhoffe Sep 25, 2026
9c1a74e
Share one scratch arena across images: ArenaPlan and ScratchArena
hunhoffe Sep 26, 2026
dcd593c
Llama: torch-free safetensors weights, RoPE table and sampler
hunhoffe Sep 26, 2026
1cbcf5c
One graph function, one version per input signature, one arena
hunhoffe Sep 26, 2026
80b479c
Llama: the NPU path without torch
hunhoffe Sep 26, 2026
4fe284b
Toolchain lower(): clear the kernel registry after generating, too
hunhoffe Sep 26, 2026
83be132
Llama: one graph function over one set of weights and caches
hunhoffe Sep 26, 2026
be3173a
Llama: apply Llama 3's RoPE frequency scaling
hunhoffe Sep 26, 2026
aeb6c47
Llama: keep a bf16 RoPE table only as long as the images reach
hunhoffe Sep 26, 2026
85d04df
Llama: restate the accuracy test's KL figures under RoPE scaling
hunhoffe Sep 26, 2026
13a8d1c
Llama CLI: bound the prompt in tokens, not characters
hunhoffe Sep 26, 2026
07dee7e
Llama: drop each weight's checkpoint pages once it is on the device
hunhoffe Sep 26, 2026
f9629f9
Graph versions: load() loads the image even with nothing to upload
hunhoffe Sep 26, 2026
1fcf572
Llama accuracy test: bound the KL's mean, p90 and max over every step
hunhoffe Sep 26, 2026
26ce43f
Graph upload: copy each weight in pieces, releasing each as it lands
hunhoffe Sep 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/krackan-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,10 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: mlir-artifacts
# Builds land in mlir-aie's JIT cache, keyed on content; nothing is
# written beside the application any more.
path: |
iron/applications/llama_3.2_1b/build/*.mlir
iron/applications/llama_3.2_1b/build_elf/*.mlir
~/.npu/cache/*/*.mlir
retention-days: 14
if-no-files-found: warn

Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/phoenix-test-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,10 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: mlir-artifacts
# Builds land in mlir-aie's JIT cache, keyed on content; nothing is
# written beside the application any more.
path: |
iron/applications/llama_3.2_1b/build/*.mlir
iron/applications/llama_3.2_1b/build_elf/*.mlir
~/.npu/cache/*/*.mlir
retention-days: 14
if-no-files-found: warn

Expand Down
330 changes: 206 additions & 124 deletions AGENTS.md

Large diffs are not rendered by default.

129 changes: 129 additions & 0 deletions AIECC_MODULE_CLONES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# aiecc clones the whole module per split item

A note to act on later, in mlir-aie's `tools/aiecc`. Found while building
the Llama 3.2 1B prefill graph as one fused full-ELF image; the same
mechanism bounds every fused build.

## Symptom

The sixteen-layer prefill image (291 steps, 17 designs, 218 cores, one
runtime sequence per design plus the main one) does not build in a 16 GB
container: aiecc reaches 11 GB at the per-sequence split stage
(`(36/47) npu_seq_{0}.mlir`) and is killed. Four layers peak at 5.6 GB. A
one-layer build, sampled per stage in aiecc's own process:

| stage | resident memory |
|---|---|
| input through the control-packet stages (0–11) | 0.1 GB |
| per-core split, `perCore_{0}.mlir` (12) | 2.0 GB |
| per-core compile and link (16–26), peak | 5.2 GB |
| per-sequence split, `npu_seq_{0}.mlir` (36) | 3.3 GB |
| end (47) | 3.6 GB |

The fused module's text is 1.4 MB at sixteen layers. The main runtime
sequence, once materialized (every `aiex.run` inlined) and DMA-lowered,
holds 28,381 DMA tasks (MHA 768 per call, the down projection 320, the
other projections 128, against a whole decode layer's 430).

## Cause

`SplitIRAction` (`tools/aiecc/Actions.h`) walks a module for the key op
and **clones the entire module once per match**; each item is an
`OpInModule{module clone, op}` and the graph keeps every item:

```cpp
// SplitIRAction — walks a ModuleOp for KeyOp instances; clones the module
// once per match. Use `.filter` downstream to skip matches.
for (auto &match : matches) {
mlir::OwningOpRef<mlir::ModuleOp> clone = srcModule.clone();
...
}
```

The call sites, all in `tools/aiecc/aiecc.cpp`:

| split | key op | source module | clones | what the consumer reads |
|---|---|---|---|---|
| `allCores` (`perCore_{0}.mlir`) | `CoreOp` | `physical` | one per core (218) | that core, its device's tiles, buffers, locks and object fifos, the kernels it links |
| `physicalPerDevice` (`perDeviceCompile_{0}.mlir`) | `DeviceOp` | `physical` | one per device (17) | that device |
| `staticPerDevice` (`perDevice_{0}.mlir`) | `DeviceOp` | `npuLowered` under `--expand-load-pdis` | one per device (17) | that device: CDO, PDI |
| `npuLoweredPerDevice` (`perDeviceNPULowered_{0}.mlir`) | `DeviceOp` | `npuLowered` | one per device (17) | that device: the transaction sequence |
| `perSeq` (`npu_seq_{0}.mlir`) | `RuntimeSequenceOp` | `npuLowered` | one per sequence (17) | that sequence and the device it sits in; `buildNpuProgramSubgraph` translates it to the instruction binary |

So memory is (clones) × (module size) at each split, and the module size
after `npu_lowered.mlir` is dominated by the materialized main sequence.
The per-core split multiplies the pre-lowering module 218 times (the 2 GB
step), and the three splits over `npuLowered` multiply the lowered module
51 times. The comment at `perSeq` says why the full module is kept:
"SplitIRAction preserves the complete module for symbol resolution."

## Fix

A draft of this is on mlir-aie's `claude/mlir-aie-iron-upstream` branch
(`pruneSplitClone` in `tools/aiecc/Actions.h`), unbuilt here; the validation
below is what to run once it builds.

Prune each clone to what its consumer reads, keeping symbol resolution
working. Concretely, give `SplitIRAction` an optional prune callback run
on the clone after the matched op is found, and pass one at each call
site:

- **`perSeq`**: erase every other `RuntimeSequenceOp` in the clone. The
sequences are independent programs; after materialization the main
sequence no longer calls the designs' sequences (`aiex.run` has been
inlined), and a design's sequence references only its own device. Each
of the 16 design-sequence clones then drops the main sequence, and the
main sequence's clone drops nothing that matters: 17× becomes about 1×.
- **`npuLoweredPerDevice`**, **`staticPerDevice`**: erase the runtime
sequences of every other device (the transaction, CDO and PDI of a
device read that device's static configuration, not another device's
sequence). The main device's expanded sequence is the bulk, so the 16
design clones shrink to their own device.
- **`allCores`**: erase every runtime sequence, and every other device.
Core compilation reads the core, its device and the kernels it links,
never a runtime sequence. This turns the 2 GB base into kilobytes per
core, and removes the same multiplier from the per-core compile stages.

Erasing an op whose symbol is still referenced would break verification,
so the prune must leave the referenced symbols in place: `aie.device`
symbols referenced by `aiex.configure` / `load_pdi` ops in a kept
sequence, and `func.func` declarations a kept core calls. The safe rule
is to erase runtime sequences and whole devices only, and only when
nothing kept references their symbols (`SymbolTable::symbolKnownUseEmpty`
on the clone after the intended erasures, or erase and run the verifier
in a debug build).

A lighter alternative that also helps: do not retain the source module
of a split once its items exist, and let a consumer release its item once
mapped. The graph keeps every edge's items for the whole run today
(`this->out.items` in `Graph.h`), so the module and all its clones are
live together.

## Validating

1. Build mlir-aie with the change (this needs the LLVM/MLIR build; the
pip wheel cannot be rebuilt in place).
2. Regenerate the one-layer real-size prefill module from IRON:
`iron/tests/toolchain/full_elf.py::test_prefill_graph_builds_a_full_elf_at_llama_size_for_one_layer`
builds it and leaves `aie.mlir` and the kernel objects in the test's
`build/prefill_1b_shared.prj`. Then run aiecc on it directly with the
full-ELF flags (`--peano=<dir> --get-full-elf --full-elf-name=out.elf
--expand-load-pdis --get-scratchpad-parameters`) and sample its
resident memory per stage (the stage names are on stdout, separated by
carriage returns; `tr '\r' '\n'`). The table above is the baseline.
3. The sixteen-layer image is the target: `Llama1B` in
`iron/tests/common/llama_model.py` at its full depth, through
`LlamaGraph(cfg, cfg.context_length).trace(cfg, cfg.context_length)`
and `traced.sequence(...).compile()`. It should build in a few GB, and
its ELF should be byte-identical to one built without the prune (the
prune changes what is held, not what is emitted).
4. mlir-aie's own aiecc tests cover the split filters (`--device-name`,
`--sequence-name`); they must still pass, since filtering happens
after the split and reads the pruned clones.

## Expected

The per-core split falls from 2 GB to near zero for this module, the
per-core compile peak follows, and the per-sequence split stops scaling
with the number of designs. The sixteen-layer prefill image then costs
about what one lowered module costs, on the order of a gigabyte.
Loading
Loading