PeaceMaker: certified kernels
A compiler hands you a code object. It does not hand you a proof that every memory wait is in place, that no two waves race through shared memory, or that the hardware's instruction hazards are covered. On RDNA those are the programmer's problem, and the bugs they cause are intermittent.
PeaceMaker (PM) is hipfire's own kernel authoring and assembly path. It writes RDNA code objects itself, reads them back byte for byte, and checks them before they ship. In 0.4.1 several of Flash-Next's hottest prefill kernels run from PM-built modules by default, each one a byte-identical replacement for the hipcc kernel it displaces.
01 What can go wrong below the compiler
the idea
RDNA issues memory operations asynchronously. A load or an LDS store
returns immediately and increments a counter; the program must wait on
that counter (s_waitcnt on gfx11, s_wait_dscnt
and friends on gfx12) before it reads the result, or before a barrier
tells other waves the data is ready. Some instruction pairs also need a
gap between them (s_delay_alu, s_wait_alu
guards). Get one wrong and the kernel still runs, usually gives the right
answer, and occasionally does not.
wave A wave B
ds_store [slot 0] ← x ·
· (store still in flight) ·
s_barrier ─────────────────── s_barrier
ds_load [slot 0] ← may read the old value
fix: s_waitcnt lgkmcnt(0) (gfx11) / s_wait_dscnt 0 (gfx12) before s_barrier hipfire has hit exactly this class in production kernels: a Strix Halo verify-attention co-residency race and a gfx11 FA2 mailbox race. 0.4.1 also adds explicit store waits before every LDS-only barrier in the Gated DeltaNet kernels.
02 The PM pipeline
how hipfire does itAuthor: races as type errors
peacemaker-author puts types in front of the builder. Targets
are types (Gfx1100, Gfx1151, Gfx1201);
an LDS region carries its ownership state (Free,
Writing, Published); a store returns a
Pending token that must become Drained before the
barrier that publishes it; a loop must carry the same state on entry and
on its back edge. The two production races above are now
rustc compile errors, pinned by compile-fail tests. The
types emit nothing of their own, so kernels ported onto this core
re-emit byte-identical assembly.
Build and emit: no ROCm in the path
The builder lowers each instruction through hipfire's own gfx11/gfx12
encoding tables and writes the whole code object: .text,
the 64-byte kernel descriptors, the MessagePack AMDGPU metadata note,
symbol and dynamic tables, and the offload-bundle wrapper. The ROCm
assembler/linker path survives only as a test oracle. Across every
PM-emitted symbol on gfx1100, gfx1151 and gfx1201 (55 symbols, as 55
single-symbol objects and 12 modules), every section other than the
version-stamp comment is byte-identical to what llvm-mc +
ld.lld + clang-offload-bundler produce.
Lift: the bytes are the proof
peacemaker-lift turns a code object back into a typed
program: every instruction word is decoded through pinned opcode tables
(unknown encodings are rejected), re-encoded from its typed fields and
compared word for word, and the module is re-emitted and must equal the
input byte for byte. Analyses then run on exactly the bytes that ship,
not on a source that might compile differently.
Certify: pm_check
- wait replay over the gfx11 VM / LGKM / VS counters (and the gfx12 equivalents), across the control-flow graph and loop back-edges;
- hazard replay: gfx1100 transcendental use, VCMPX → PERMLANE, WMMA chaining, joined over back-edges;
- barrier pairing and the rule that every LDS store is drained before a barrier;
s_clause/s_delay_aluwindows and register resources;lds_bounds: every LDS address is evaluated for every lane of every wave of the workgroup, and every access must end inside the launch allocation.
Any open obligation fails certification. The shipped QSA and MQ6 modules certify with zero obligations.
03 Twins: same math, better schedule
how hipfire does itA PM kernel that replaces a hipcc kernel is written as an exact twin: the same unpack, the same dequantization instructions, the same WMMA lane map and the same ascending accumulation chain per output. Only tiling and scheduling change. The gfx1201 MQ6 trunk GEMM is a good example:
| hipcc kernel | PM twin | |
|---|---|---|
| work per wave | one dequant feeds 8 WMMAs | a wave owns 16 rows × 256 tokens; one dequant feeds 16 WMMAs |
| activation staging | — | double-buffered 32-K LDS chunks, one barrier per chunk |
| B fragments | wait on LDS before each WMMA | 8-deep register ring, no per-WMMA s_wait_dscnt 0 |
| arithmetic | identical: 6-bit unpack, v_cvt_f32_ubyte0, v_cvt_f16_f32, one v_fma_f16, ascending 16-K chain, 0 + acc F32 epilogue | |
Twins are checked by memcmp against the hipcc object, not
by tolerance. For that MQ6 kernel: zero differing bytes over the six
pp8192 trace shapes and 216 further runs over 12 shapes, including
ragged tails and inputs that produce infinities and NaNs. For the
Strix Halo QSA selector: every score bit, the whole ordered selection
and its fill byte-identical across 83 cases and 707 records, including
real 32K/64K captures of all 12 QSA layers.
04 Checking the kernels PM didn't write
Because the lifter reads any gfx11/gfx12 code object, the same analyses
run over hipcc output. peacemaker audit --lds-barrier flags
any path, loop back-edges included, that reaches a workgroup barrier
with an LDS store still pending: the bug class from § 01. Over
the gfx1201 JIT caches (412 modules, 62 distinct objects) it lifted
every module byte-exactly and reported 0 findings. A module the tables
cannot fully decode is reported as unchecked, never as clean.
05 What ships PM-built in 0.4.1
| kernel | arch | route | opt-out |
|---|---|---|---|
| QSA gathered attention (convert + attention) | gfx1151, gfx1201 | default, byte-exact replacement | HIPFIRE_QWEN4_QSA_PM=0 |
| QSA selector: rows16 score + select-from-scores | gfx1151 | default for prefill calls of ≥ 512 rows; byte-exact | HIPFIRE_QWEN4_QSA_SCORE_PM=0, …_SELECT_PM=0 |
| QSA selector score for the opt-in q8 state | gfx1151 | default when q8 QSA is chosen; byte-exact | HIPFIRE_QWEN4_QSA_SCORE_PM=0 |
| MQ6 trunk GEMM | gfx1201; large shapes on gfx1151 | default, byte-exact replacement | HIPFIRE_QWEN4_MQ6_X4_PM=0 |
| Symmetric IU4 MoE grouped expert GEMMs | gfx1151, gfx1201 | default for symmetric artifacts (the default Flash-Next pull); a new numeric route, KLD-gated | HIPFIRE_QWEN4_MOE_SYM_IU4=0 |
| 27B dense IU4 prefill, fused gate/up → A4 epilogue | gfx1151 | default on admitted shapes, byte-exact | HIPFIRE_V2B_A4_EPI=0 |
Graph capture and recorded replays, decode and verify, and unsupported
shapes keep the hipcc kernels where the table says “prefill”.
The certified modules are the *.hxaco files in
kernels/.
06 What it buys you
lgkmcnt race; gfx11 FA2 mailbox race07 Limits
- PM targets gfx1100, gfx1151 and gfx1201. Other architectures run hipcc kernels.
- Byte-identity is checked per kernel against its hipcc twin; the symmetric IU4 MoE route is a different numeric route from the F16 one it replaces and is KLD-gated, not bit-exact.
- The audit covers what the decode tables cover; anything else is reported unchecked.
- The XDNA2 NPU work that shares the PM name space is experimental and outside inference.
08 Sources
crates/hipfire-isa: the checked builder, native writer andpm_check(src/pm_check.rs).crates/peacemaker-author: typed kernel language M0.crates/peacemaker-liftandcrates/peacemaker-ir: lifter, codecs and analyses.- CHANGELOG, v0.4.1 (“PM / kernel toolchain” and the Flash-Next PM entries) and the v0.4.1 release notes (exact-replacement defaults).