1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
//! Declarative GGUF quant-type formatter ("decomplect quant support", Cut 2).
//!
//! A GGUF *decode-only* quant type is, mechanically, always the same five things:
//!
//! 1. a `#[repr(C)] #[derive(Clone, Copy)]` block struct whose field layout is a
//! bit-for-bit replica of llama.cpp's `block_<t>` in `ggml-common.h`,
//! 2. a compile-time `assert!(size_of::<Block>() == N)` pinning that layout to the
//! on-disk GGUF byte size,
//! 3. `to_float` — the *only* part that actually differs per type: the dequant
//! (`dequantize_row_<t>` ported from `ggml-quants.c`),
//! 4. `from_float` — a panic stub (these types are decode-only; we never quantize
//! *to* them), and
//! 5. `vec_dot` / `vec_dot_unopt` — the *identical* "dequantize the super-block to
//! f32, then dot it against the Q8_0-quantized lhs" pattern for every type
//! (only the block size changes; see [`vec_dot_dequant_q8_0`]).
//!
//! Today each type spells all five out by hand (≈55-75 lines), *plus* ~6 match arms
//! in `mod.rs` (`from_u32`, `to_u32`, `cpu_zeros`, `from_data`, `type_size`,
//! `block_size`) *plus* one in `ggml_file.rs` — seven independent places that must
//! agree or the type silently miswires. [`quant_format!`] collapses (1)-(5) into one
//! declaration whose only freeform part is the decode body, and [`for_each_quant!`] /
//! the [`quant_table!`]-generated arms collapse the mod.rs wiring into one table.
//!
//! Adding a quant type becomes: one `quant_format!` block + one row in the table.
//! Not seven hand-kept-in-sync sites.
//!
//! This module is **purely additive** — it introduces the machinery and a `#[cfg(test)]`
//! equivalence proof against the existing hand-written impls. The real migration of the
//! 11 IQ types in `iq_quants.rs` happens separately (see `quant-macro-migration.md`); it
//! deletes the boilerplate and replaces it with `quant_format!` calls.
use ;
use f16;
/// The one shared matmul building block behind every decode-only type's `vec_dot`.
///
/// Dequantizes each block of `xs` to f32 (via `T::to_float`) and dots it against the
/// Q8_0-quantized lhs. Generic over the block size: a `BLOCK` of `B` elements spans
/// `B / QK8_0` Q8_0 blocks (every decode-only quant type has `B` a multiple of 32).
///
/// This is the generalization of `iq_quants::vec_dot_qk_q8_0` (which hard-codes QK_K)
/// to *any* block size, so the macro can use one helper for QK_K, Q1_0 (128) and
/// NVFP4 (64) alike. The body is byte-for-byte the loop the hand-written impls inline.
/// Declare ONE decode-only GGUF quant type: its block struct, byte-size assertion, and
/// full `impl GgmlType` (decode-from-body, `from_float` panic stub, and the standard
/// dequant-then-dot-against-Q8_0 `vec_dot`/`vec_dot_unopt`).
///
/// # Example
///
/// ```ignore
/// quant_format! {
/// /// IQ4_XS block: f16 super-block scale, 6-bit sub-block scales split across
/// /// scales_h/scales_l, then 128 bytes of packed 4-bit codebook indices.
/// name: BlockIQ4xs,
/// dtype: IQ4_XS, // a GgmlDType variant
/// block_elems: QK_K, // elements per block (BLCK_SIZE)
/// byte_size: 136, // on-disk size_of::<Block>(), asserted at compile time
/// vec_dot: BlockQ8_0, // VecDotType
/// fields: {
/// d: f16,
/// scales_h: u16,
/// scales_l: [u8; QK_K / 64],
/// qs: [u8; QK_K / 2],
/// },
/// // The ONLY freeform part: dequantize_row_iq4_xs, verbatim from ggml-quants.c.
/// // `xs: &[Self]` in, `ys: &mut [f32]` out — write `ys.len()` elements.
/// decode: |xs, ys| {
/// let nb = ys.len() / QK_K;
/// for i in 0..nb { /* ... fill ys ... */ }
/// },
/// }
/// ```
///
/// `name`, `dtype`, `block_elems`, `byte_size`, `vec_dot` and the `fields` are pure
/// declaration. `decode` is the existing `to_float` body, dropped in unchanged. Field
/// visibility is `pub(crate)` to match the existing hand-written blocks.
,
decode:
) =>
// Pin the in-memory layout to the GGUF on-disk byte size. If the fields don't
// add up to `byte_size`, this fails to compile — the miswire is impossible.
const _: = assert!;
}
};
}
pub use quant_format;
/// A single declarative table of every quant type, driving the `mod.rs` wiring so that
/// `from_u32` / `to_u32` / `cpu_zeros` / `from_data` / `type_size` / `block_size` are
/// generated from ONE list instead of six hand-maintained `match` blocks.
///
/// Each row is `Variant => Block @ ggml_id`. Call it with a callback macro that takes the
/// whole list; see [`impl_dtype_wiring!`] for the concrete expansion used by `mod.rs`, and
/// the `#[cfg(test)]` `for_each_quant_smoke` test below for how a consumer iterates it.
///
/// (Left as the canonical *source of truth* the migration points `mod.rs` at; wiring the
/// real `GgmlDType` impl to it is the final, mechanical migration step — it must land in
/// the same commit that removes the six hand-written match blocks, and is intentionally
/// NOT done here to avoid colliding with the concurrent edits to `mod.rs`.)
// `quant_format.rs` is a leaf file module, so its `proof` child would normally live at
// `quant_format/proof.rs`; point at the sibling `proof.rs` instead to keep both files flat.