metrale_model_layers/circuit_exec/fixed.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-30: The device addresses a program is compiled against: the model's buffers and
4//! metadata uploads ([`Fixed`]) and the MTP draft head's ([`DraftFixed`]). Split from
5//! `compile.rs`.
6//!
7//! Owner: model-layers circuit executor.
8//! Invariants:
9//! - Every address here is fixed after boot, so a captured program stays valid.
10
11use metrale_gpu_runtime::gpu::DevicePtr;
12
13use super::bindings::DraftRows;
14use crate::layer::AttnMetadataDev;
15
16/// 2026-09-28: Device addresses that never move after boot.
17#[derive(Clone)]
18pub struct Fixed {
19 /// 2026-09-28: The residual stream (`BufferArena::hidden_states`).
20 pub hidden: DevicePtr,
21 /// 2026-09-28: The copy of the stream the norms write (`BufferArena::residual`).
22 pub residual: DevicePtr,
23 /// 2026-09-28: The logits buffer the step returns.
24 pub logits: DevicePtr,
25 /// 2026-09-29: Where a verify's argmax writes each row's token (`i32` per row): the
26 /// scratch buffer's start, which the verify reads back.
27 pub tokens: DevicePtr,
28 /// 2026-09-28: The single-sequence step's attention metadata, at its fixed upload
29 /// address. `max_blocks_per_seq` is ignored: a step supplies it.
30 pub meta: AttnMetadataDev,
31 /// 2026-09-28: The multi-sequence step's attention metadata (one row per sequence), at its
32 /// fixed upload address; `max_blocks_per_seq` is ignored.
33 pub batch_meta: AttnMetadataDev,
34 /// 2026-09-29: The MTP verify step's attention metadata (one row per verified token), at
35 /// its fixed upload address; `max_blocks_per_seq` is ignored.
36 pub verify_meta: AttnMetadataDev,
37 /// 2026-09-29: The MTP draft head's buffers; `None` without a bound draft head.
38 pub draft: Option<DraftFixed>,
39 /// 2026-09-30: The batched MTP verify's attention metadata (one row per verified row,
40 /// `stage_verify_metadata` at scratch + 32768); `max_blocks_per_seq` is ignored.
41 pub verify_batch_meta: AttnMetadataDev,
42 /// 2026-09-30: The batched verify's per-GDN-layer WY pointer tables
43 /// (`upload_verify_wy_tables`, `VERIFY_WY_LAYER_STRIDE_BYTES` apart); NULL without MTP.
44 pub verify_wy_tables: DevicePtr,
45 /// 2026-09-30: Where the batched verify's argmax writes each row's token: the mapped host
46 /// blob's device alias, or scratch when there is none (`verify_rows_argmax`).
47 pub verify_batch_tokens: DevicePtr,
48 /// 2026-09-28: The quantized-activation scratch the NVFP4 MMQ GEMMs read
49 /// (`BufferArena::ffn_act_q8`), sized for the widest batch.
50 pub ffn_act_q8: DevicePtr,
51 /// 2026-09-28: K pool per attention layer (`PagedKvCache::k_pool_ptr`).
52 pub k_pools: Vec<DevicePtr>,
53 /// 2026-09-28: V pool per attention layer.
54 pub v_pools: Vec<DevicePtr>,
55 /// 2026-09-28: Tokens per KV block.
56 pub block_size: u32,
57 /// 2026-09-28: `PagedKvCache::cache_stride`.
58 pub cache_stride: u64,
59}
60
61/// 2026-09-29: The MTP draft head's fixed buffers.
62#[derive(Clone)]
63pub struct DraftFixed {
64 /// 2026-09-29: Where the host puts the token's embedding row (`MtpHead::forward_one`'s
65 /// `ssm_qkvz`).
66 pub embed: DevicePtr,
67 /// 2026-09-29: The draft step's attention metadata (`mtp_meta::mtp_attn_meta_dev`);
68 /// `max_blocks_per_seq` is ignored.
69 pub meta: AttnMetadataDev,
70 /// 2026-09-29: The draft cache's pools and geometry.
71 pub k_pool: DevicePtr,
72 pub v_pool: DevicePtr,
73 pub block_size: u32,
74 pub cache_stride: u64,
75 /// 2026-09-29: The vocabulary rows the draft lm_head scores (`DraftBinding::vocab`).
76 pub vocab: u32,
77 /// 2026-09-30: The head's n-row draft (the batched propose) facts (`DraftBinding::rows`);
78 /// `None` when the head drafts one row at a time.
79 pub rows: Option<DraftRows>,
80}
81
82impl DraftFixed {
83 /// 2026-09-30: The attention metadata an `n`-row draft step uploads: the batched
84 /// propose's, at its allocation (`mtp_meta::mtp_attn_meta_batch_dev`); one row reads the
85 /// single-row step's.
86 pub fn meta_rows(&self, n: u64) -> anyhow::Result<AttnMetadataDev> {
87 if n == 1 {
88 return Ok(self.meta);
89 }
90 let rows = self.rows.as_ref().ok_or_else(|| {
91 anyhow::anyhow!("a {n}-row draft plan for a head outside the batched propose")
92 })?;
93 Ok(crate::layers::mtp_meta::mtp_attn_meta_batch_dev(
94 rows.meta,
95 usize::try_from(n)?,
96 ))
97 }
98}