miden_ace_codegen/layout/
policy.rs1use super::{
2 InputCounts, InputLayout, InputRegion, LayoutRegions, StarkVarIndices, plan::MultiAirVarIndices,
3};
4use crate::{EXT_DEGREE, randomness};
5
6#[derive(Clone, Copy)]
7enum Alignment {
8 Unaligned = 1,
9 Word = 2,
10 DoubleWord = 4,
11 QuadWord = 8,
12}
13
14#[derive(Clone, Copy)]
15struct LayoutPolicy {
16 public_values: Alignment,
17 randomness: Alignment,
18 main: Alignment,
19 aux: Alignment,
20 quotient: Alignment,
21 aux_bus_boundary: Alignment,
22 stark_vars: Alignment,
23 end_align: Option<Alignment>,
24}
25
26impl LayoutPolicy {
27 fn native() -> Self {
28 Self {
29 public_values: Alignment::Unaligned,
30 randomness: Alignment::Unaligned,
31 main: Alignment::Unaligned,
32 aux: Alignment::Unaligned,
33 quotient: Alignment::Unaligned,
34 aux_bus_boundary: Alignment::Unaligned,
35 stark_vars: Alignment::Unaligned,
36 end_align: None,
37 }
38 }
39
40 fn masm() -> Self {
41 Self {
42 public_values: Alignment::QuadWord,
43 randomness: Alignment::Word,
44 main: Alignment::DoubleWord,
45 aux: Alignment::DoubleWord,
46 quotient: Alignment::DoubleWord,
47 aux_bus_boundary: Alignment::Word,
48 stark_vars: Alignment::Word,
49 end_align: Some(Alignment::Word),
50 }
51 }
52}
53
54struct LayoutBuilder {
55 offset: usize,
56}
57
58impl LayoutBuilder {
59 fn new() -> Self {
60 Self { offset: 0 }
61 }
62
63 fn align(&mut self, alignment: Alignment) {
64 self.offset = self.offset.next_multiple_of(alignment as usize);
65 }
66
67 fn alloc(&mut self, width: usize, alignment: Alignment) -> InputRegion {
68 self.align(alignment);
69 let region = InputRegion { offset: self.offset, width };
70 self.offset += width;
71 region
72 }
73}
74
75impl InputLayout {
76 pub fn new(counts: InputCounts) -> Self {
78 Self::build_with_policy(counts, LayoutPolicy::native(), 1)
79 }
80
81 pub fn new_masm(counts: InputCounts) -> Self {
83 Self::build_with_policy(counts, LayoutPolicy::masm(), 1)
84 }
85
86 pub fn new_multi_air(counts: InputCounts, num_airs: usize) -> Self {
88 Self::build_with_policy(counts, LayoutPolicy::native(), num_airs)
89 }
90
91 pub fn new_masm_multi_air(counts: InputCounts, num_airs: usize) -> Self {
94 Self::build_with_policy(counts, LayoutPolicy::masm(), num_airs)
95 }
96
97 fn build_with_policy(counts: InputCounts, policy: LayoutPolicy, num_airs: usize) -> Self {
98 assert!(num_airs >= 1, "layout requires at least one AIR");
99
100 const NUM_STARK_VARS_BASE: usize = 10;
106 let is_multi_air = num_airs >= 2;
107 let num_stark_vars = NUM_STARK_VARS_BASE + if is_multi_air { 4 * num_airs } else { 0 };
108
109 let mut builder = LayoutBuilder::new();
110
111 let public_values = builder.alloc(counts.num_public, policy.public_values);
112 const NUM_RANDOMNESS_INPUTS: usize = 2;
114 let randomness = builder.alloc(NUM_RANDOMNESS_INPUTS, policy.randomness);
115 let (aux_rand_alpha, aux_rand_beta) = randomness::aux_rand_indices(randomness);
116 let main_curr = builder.alloc(counts.width, policy.main);
117 let aux_coord_width = counts.aux_width * EXT_DEGREE;
118 let aux_curr = builder.alloc(aux_coord_width, policy.aux);
119 let quotient_curr = builder.alloc(counts.num_quotient_chunks * EXT_DEGREE, policy.quotient);
120 let main_next = builder.alloc(counts.width, policy.main);
121 let aux_next = builder.alloc(aux_coord_width, policy.aux);
122 let quotient_next = builder.alloc(counts.num_quotient_chunks * EXT_DEGREE, policy.quotient);
123 let aux_bus_boundary = builder.alloc(counts.num_aux_boundary, policy.aux_bus_boundary);
124
125 let stark_vars = builder.alloc(num_stark_vars, policy.stark_vars);
126
127 let b = stark_vars.offset;
145 let alpha = b;
146 let z_pow_n = b + 1;
147 let z_k = b + 2;
148 let is_first = b + 3;
149 let is_last = b + 4;
150 let is_transition = b + 5;
151 let reserved = b + 6;
152 let weight0 = b + 7;
153 let f = b + 8;
154 let s0 = b + 9;
155 let multi_air = is_multi_air.then_some(MultiAirVarIndices { base: b + 10, num_airs });
159
160 if let Some(end_align) = policy.end_align {
161 builder.align(end_align);
162 }
163
164 Self {
165 regions: LayoutRegions {
166 public_values,
167 randomness,
168 main_curr,
169 aux_curr,
170 quotient_curr,
171 main_next,
172 aux_next,
173 quotient_next,
174 aux_bus_boundary,
175 stark_vars,
176 },
177 aux_rand_alpha,
178 aux_rand_beta,
179 stark: StarkVarIndices {
180 alpha,
181 z_pow_n,
182 z_k,
183 is_first,
184 is_last,
185 is_transition,
186 reserved,
187 weight0,
188 f,
189 s0,
190 multi_air,
191 },
192 total_inputs: builder.offset,
193 counts,
194 }
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::super::{InputCounts, InputKey, InputLayout};
201
202 #[test]
203 fn multi_air_layout_generalizes_over_num_airs() {
204 let counts = InputCounts {
205 width: 1,
206 aux_width: 1,
207 num_aux_boundary: 3,
208 num_public: 8,
209 num_randomness: 2,
210 num_periodic: 0,
211 num_quotient_chunks: 1,
212 };
213
214 let layout = InputLayout::new_multi_air(counts, 3);
216 let base = layout.index(InputKey::MultiAirBeta(0)).unwrap();
217 assert_eq!(layout.index(InputKey::MultiAirBeta(2)), Some(base + 2));
218 assert_eq!(layout.index(InputKey::IsFirstAir(0)), Some(base + 3));
219 assert_eq!(layout.index(InputKey::IsTransitionAir(2)), Some(base + 3 + 3 * 2 + 2));
220 assert_eq!(layout.index(InputKey::MultiAirBeta(3)), None, "AIR index out of range");
221 assert_eq!(layout.index(InputKey::IsFirstAir(3)), None, "AIR index out of range");
222
223 let layout2 = InputLayout::new_multi_air(counts, 2);
226 let base2 = layout2.index(InputKey::MultiAirBeta(0)).unwrap();
227 assert_eq!(layout2.index(InputKey::MultiAirBeta(1)), Some(base2 + 1));
228 assert_eq!(layout2.index(InputKey::IsFirstAir(0)), Some(base2 + 2));
229 assert_eq!(layout2.index(InputKey::IsTransitionAir(1)), Some(base2 + 7));
230 }
231}