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