triton_vm/
memory_layout.rs1pub use constraint_builder::codegen::MEM_PAGE_SIZE;
2
3use air::challenge_id::ChallengeId;
4use arbitrary::Arbitrary;
5use itertools::Itertools;
6use strum::EnumCount;
7use twenty_first::prelude::*;
8
9use crate::error::USIZE_TO_U64_ERR;
10use crate::table::master_table::MasterAuxTable;
11use crate::table::master_table::MasterMainTable;
12
13#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Arbitrary)]
18pub struct DynamicTasmConstraintEvaluationMemoryLayout {
19 pub free_mem_page_ptr: BFieldElement,
24
25 pub challenges_ptr: BFieldElement,
30}
31
32#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Arbitrary)]
37pub struct StaticTasmConstraintEvaluationMemoryLayout {
38 pub free_mem_page_ptr: BFieldElement,
42
43 pub curr_main_row_ptr: BFieldElement,
46
47 pub curr_aux_row_ptr: BFieldElement,
50
51 pub next_main_row_ptr: BFieldElement,
54
55 pub next_aux_row_ptr: BFieldElement,
58
59 pub challenges_ptr: BFieldElement,
64}
65
66pub trait IntegralMemoryLayout {
67 fn is_integral(&self) -> bool {
70 let memory_regions = self.memory_regions();
71 if memory_regions.iter().unique().count() != memory_regions.len() {
72 return false;
73 }
74
75 let disjoint_from_all_others = |region| {
76 let mut all_other_regions = memory_regions.iter().filter(|&r| r != region);
77 all_other_regions.all(|r| r.disjoint_from(region))
78 };
79 memory_regions.iter().all(disjoint_from_all_others)
80 }
81
82 fn memory_regions(&self) -> Box<[MemoryRegion]>;
83}
84
85impl IntegralMemoryLayout for StaticTasmConstraintEvaluationMemoryLayout {
86 fn memory_regions(&self) -> Box<[MemoryRegion]> {
87 let as_u64 = |x: usize| u64::try_from(x).expect(USIZE_TO_U64_ERR);
88
89 let num_main_cols = as_u64(MasterMainTable::NUM_COLUMNS);
90 let num_aux_cols = as_u64(MasterAuxTable::NUM_COLUMNS);
91 let num_challenge_ids = as_u64(ChallengeId::COUNT);
92
93 debug_assert!(num_main_cols <= MEM_PAGE_SIZE);
94 debug_assert!(num_aux_cols <= MEM_PAGE_SIZE);
95 debug_assert!(num_challenge_ids <= MEM_PAGE_SIZE);
96
97 let all_regions = [
98 MemoryRegion::new(self.free_mem_page_ptr, MEM_PAGE_SIZE),
99 MemoryRegion::new(self.curr_main_row_ptr, num_main_cols),
100 MemoryRegion::new(self.curr_aux_row_ptr, num_aux_cols),
101 MemoryRegion::new(self.next_main_row_ptr, num_main_cols),
102 MemoryRegion::new(self.next_aux_row_ptr, num_aux_cols),
103 MemoryRegion::new(self.challenges_ptr, num_challenge_ids),
104 ];
105
106 Box::new(all_regions)
107 }
108}
109
110impl IntegralMemoryLayout for DynamicTasmConstraintEvaluationMemoryLayout {
111 fn memory_regions(&self) -> Box<[MemoryRegion]> {
112 let all_regions = [
113 MemoryRegion::new(self.free_mem_page_ptr, MEM_PAGE_SIZE),
114 MemoryRegion::new(self.challenges_ptr, ChallengeId::COUNT as u64),
115 ];
116 Box::new(all_regions)
117 }
118}
119
120#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
121pub struct MemoryRegion {
122 start: BFieldElement,
123 size: u64,
124}
125
126impl MemoryRegion {
127 pub fn new<A: Into<u64>>(address: A, size: u64) -> Self {
128 let start = bfe!(address.into());
129
130 Self { start, size }
131 }
132
133 pub fn disjoint_from(self, other: &Self) -> bool {
134 !self.overlaps(other)
135 }
136
137 pub fn overlaps(self, other: &Self) -> bool {
138 self.contains_address(other.start) || other.contains_address(self.start)
139 }
140
141 pub fn contains_address<A: Into<u64>>(self, addr: A) -> bool {
142 let addr = u128::from(addr.into());
144 let start = u128::from(self.start.value());
145 let end = start + u128::from(self.size);
146 (start..end).contains(&addr)
147 }
148}
149
150#[cfg(test)]
151#[cfg_attr(coverage_nightly, coverage(off))]
152mod tests {
153 use proptest::prelude::*;
154 use proptest_arbitrary_adapter::arb;
155 use twenty_first::bfe;
156
157 use super::*;
158 use crate::tests::proptest;
159 use crate::tests::test;
160
161 impl Default for StaticTasmConstraintEvaluationMemoryLayout {
162 fn default() -> Self {
164 let mem_page = |i| bfe!(i * MEM_PAGE_SIZE);
165 StaticTasmConstraintEvaluationMemoryLayout {
166 free_mem_page_ptr: mem_page(0),
167 curr_main_row_ptr: mem_page(1),
168 curr_aux_row_ptr: mem_page(2),
169 next_main_row_ptr: mem_page(3),
170 next_aux_row_ptr: mem_page(4),
171 challenges_ptr: mem_page(5),
172 }
173 }
174 }
175
176 impl Default for DynamicTasmConstraintEvaluationMemoryLayout {
177 fn default() -> Self {
179 let mem_page = |i| bfe!(i * MEM_PAGE_SIZE);
180 DynamicTasmConstraintEvaluationMemoryLayout {
181 free_mem_page_ptr: mem_page(0),
182 challenges_ptr: mem_page(1),
183 }
184 }
185 }
186
187 #[macro_rules_attr::apply(proptest)]
188 fn size_0_memory_region_contains_no_addresses(
189 #[strategy(arb())] region_start: BFieldElement,
190 #[strategy(arb())] address: BFieldElement,
191 ) {
192 let region = MemoryRegion::new(region_start, 0);
193 prop_assert!(!region.contains_address(region_start));
194 prop_assert!(!region.contains_address(address));
195 }
196
197 #[macro_rules_attr::apply(proptest)]
198 fn size_1_memory_region_contains_only_start_address(
199 #[strategy(arb())] region_start: BFieldElement,
200 #[strategy(arb())]
201 #[filter(#region_start != #address)]
202 address: BFieldElement,
203 ) {
204 let region = MemoryRegion::new(region_start, 1);
205 prop_assert!(region.contains_address(region_start));
206 prop_assert!(!region.contains_address(address));
207 }
208
209 #[macro_rules_attr::apply(test)]
210 fn definitely_integral_memory_layout_is_detected_as_integral() {
211 assert!(StaticTasmConstraintEvaluationMemoryLayout::default().is_integral());
212 assert!(DynamicTasmConstraintEvaluationMemoryLayout::default().is_integral());
213 }
214
215 #[macro_rules_attr::apply(test)]
216 fn definitely_non_integral_memory_layout_is_detected_as_non_integral() {
217 let layout = StaticTasmConstraintEvaluationMemoryLayout {
218 free_mem_page_ptr: bfe!(0),
219 curr_main_row_ptr: bfe!(1),
220 curr_aux_row_ptr: bfe!(2),
221 next_main_row_ptr: bfe!(3),
222 next_aux_row_ptr: bfe!(4),
223 challenges_ptr: bfe!(5),
224 };
225 assert!(!layout.is_integral());
226
227 let layout = DynamicTasmConstraintEvaluationMemoryLayout {
228 free_mem_page_ptr: bfe!(0),
229 challenges_ptr: bfe!(5),
230 };
231 assert!(!layout.is_integral());
232 }
233
234 #[macro_rules_attr::apply(test)]
235 fn memory_layout_integrity_check_does_not_panic_due_to_arithmetic_overflow() {
236 let mem_layout = DynamicTasmConstraintEvaluationMemoryLayout {
237 free_mem_page_ptr: bfe!(BFieldElement::MAX),
238 challenges_ptr: bfe!(1_u64 << 63),
239 };
240 assert!(mem_layout.is_integral());
241 }
242}