openvm_circuit/arch/execution_mode/metered/
memory_ctx.rs1use abi_stable::std_types::RVec;
2use openvm_instructions::riscv::{RV32_NUM_REGISTERS, RV32_REGISTER_AS, RV32_REGISTER_NUM_LIMBS};
3
4use crate::{
5 arch::{SystemConfig, BOUNDARY_AIR_ID, MERKLE_AIR_ID},
6 system::memory::{dimensions::MemoryDimensions, CHUNK},
7};
8
9const CHUNK_U32: u32 = CHUNK as u32;
12const CHUNK_BITS: u32 = CHUNK_U32.ilog2();
13
14pub const MAX_MEM_PAGE_OPS_PER_INSN: usize = 1 << 16;
16
17#[derive(Clone, Debug)]
18pub struct BitSet {
19 words: Box<[u64]>,
20}
21
22impl BitSet {
23 pub fn new(num_bits: usize) -> Self {
24 Self {
25 words: vec![0; num_bits.div_ceil(u64::BITS as usize)].into_boxed_slice(),
26 }
27 }
28
29 #[inline(always)]
30 pub fn insert(&mut self, index: usize) -> bool {
31 let word_index = index >> 6;
32 let bit_index = index & 63;
33 let mask = 1u64 << bit_index;
34
35 debug_assert!(word_index < self.words.len(), "BitSet index out of bounds");
36
37 let word = unsafe { self.words.get_unchecked_mut(word_index) };
41 let was_set = (*word & mask) != 0;
42 *word |= mask;
43 !was_set
44 }
45
46 #[inline(always)]
49 pub fn insert_range(&mut self, start: usize, end: usize) -> usize {
50 debug_assert!(start < end);
51 debug_assert!(end <= self.words.len() * 64, "BitSet range out of bounds");
52
53 let mut ret = 0;
54 let start_word_index = start >> 6;
55 let end_word_index = (end - 1) >> 6;
56 let start_bit = (start & 63) as u32;
57
58 if start_word_index == end_word_index {
59 let end_bit = ((end - 1) & 63) as u32 + 1;
60 let mask_bits = end_bit - start_bit;
61 let mask = (u64::MAX >> (64 - mask_bits)) << start_bit;
62 let word = unsafe { self.words.get_unchecked_mut(start_word_index) };
65 ret += mask_bits - (*word & mask).count_ones();
66 *word |= mask;
67 } else {
68 let end_bit = (end & 63) as u32;
69 let mask_bits = 64 - start_bit;
70 let mask = u64::MAX << start_bit;
71 let start_word = unsafe { self.words.get_unchecked_mut(start_word_index) };
74 ret += mask_bits - (*start_word & mask).count_ones();
75 *start_word |= mask;
76
77 let mask_bits = end_bit;
78 let mask = if end_bit == 0 {
79 0
80 } else {
81 u64::MAX >> (64 - end_bit)
82 };
83 let end_word = unsafe { self.words.get_unchecked_mut(end_word_index) };
86 ret += mask_bits - (*end_word & mask).count_ones();
87 *end_word |= mask;
88 }
89
90 if start_word_index + 1 < end_word_index {
91 for i in (start_word_index + 1)..end_word_index {
92 let word = unsafe { self.words.get_unchecked_mut(i) };
95 ret += word.count_zeros();
96 *word = u64::MAX;
97 }
98 }
99 ret as usize
100 }
101
102 #[inline(always)]
103 pub fn clear(&mut self) {
104 unsafe {
106 std::ptr::write_bytes(self.words.as_mut_ptr(), 0, self.words.len());
107 }
108 }
109}
110
111#[derive(Clone, Debug)]
112pub struct MemoryCtx<const PAGE_BITS: usize> {
113 memory_dimensions: MemoryDimensions,
114 pub page_indices: BitSet,
115 pub addr_space_access_count: RVec<u32>,
116 pub page_indices_since_checkpoint: Box<[u32]>,
117 pub page_indices_since_checkpoint_len: usize,
118}
119
120impl<const PAGE_BITS: usize> MemoryCtx<PAGE_BITS> {
121 pub fn new(config: &SystemConfig, segment_check_insns: u64) -> Self {
122 let memory_dimensions = config.memory_config.memory_dimensions();
123 let merkle_height = memory_dimensions.overall_height();
124
125 let bitset_size = 1 << (merkle_height.saturating_sub(PAGE_BITS));
126 let addr_space_size = (1 << memory_dimensions.addr_space_height) + 1;
127 let checkpoint_capacity = Self::calculate_checkpoint_capacity(segment_check_insns);
128
129 Self {
130 memory_dimensions,
131 page_indices: BitSet::new(bitset_size),
132 addr_space_access_count: vec![0; addr_space_size].into(),
133 page_indices_since_checkpoint: vec![0; checkpoint_capacity].into_boxed_slice(),
134 page_indices_since_checkpoint_len: 0,
135 }
136 }
137
138 #[inline(always)]
139 fn calculate_checkpoint_capacity(segment_check_insns: u64) -> usize {
140 segment_check_insns as usize * MAX_MEM_PAGE_OPS_PER_INSN
141 }
142
143 #[inline(always)]
144 pub(crate) fn add_register_merkle_heights(&mut self) {
145 self.update_boundary_merkle_heights(
146 RV32_REGISTER_AS,
147 0,
148 (RV32_NUM_REGISTERS * RV32_REGISTER_NUM_LIMBS) as u32,
149 );
150 }
151
152 #[inline(always)]
156 pub(crate) fn update_boundary_merkle_heights(
157 &mut self,
158 address_space: u32,
159 ptr: u32,
160 size: u32,
161 ) {
162 debug_assert!((address_space as usize) < self.addr_space_access_count.len());
163
164 let chunk_idx = ptr >> CHUNK_BITS;
165 let end_chunk_idx = (ptr + size - 1) >> CHUNK_BITS;
166 let num_blocks = end_chunk_idx - chunk_idx + 1;
167 let start_block_id = self
168 .memory_dimensions
169 .label_to_index((address_space, chunk_idx)) as u32;
170 let end_block_id = start_block_id + num_blocks;
171 let start_page_id = start_block_id >> PAGE_BITS;
172 let end_page_id = ((end_block_id - 1) >> PAGE_BITS) + 1;
173 assert!(
174 self.page_indices_since_checkpoint_len + (end_page_id - start_page_id) as usize
175 <= self.page_indices_since_checkpoint.len(),
176 "more than {MAX_MEM_PAGE_OPS_PER_INSN} memory pages accessed in a single instruction"
177 );
178
179 for page_id in start_page_id..end_page_id {
180 let len = self.page_indices_since_checkpoint_len;
182 debug_assert!(len < self.page_indices_since_checkpoint.len());
183 unsafe {
185 *self.page_indices_since_checkpoint.as_mut_ptr().add(len) = page_id;
186 }
187 self.page_indices_since_checkpoint_len = len + 1;
188
189 if self.page_indices.insert(page_id as usize) {
190 unsafe {
193 *self
194 .addr_space_access_count
195 .get_unchecked_mut(address_space as usize) += 1;
196 }
197 }
198 }
199 }
200
201 #[inline(always)]
203 pub(crate) fn initialize_segment(&mut self, trace_heights: &mut [u32]) {
204 self.page_indices.clear();
206
207 unsafe {
210 *trace_heights.get_unchecked_mut(BOUNDARY_AIR_ID) = 0;
211 *trace_heights.get_unchecked_mut(MERKLE_AIR_ID) = 0;
212 }
213 let poseidon2_idx = trace_heights.len() - 2;
214 unsafe {
216 *trace_heights.get_unchecked_mut(poseidon2_idx) = 0;
217 }
218
219 let mut addr_space_access_count = vec![0; self.addr_space_access_count.len()];
222 let pages_len = self.page_indices_since_checkpoint_len;
223 for i in 0..pages_len {
224 let page_id = unsafe { *self.page_indices_since_checkpoint.get_unchecked(i) } as usize;
226 if self.page_indices.insert(page_id) {
227 let (addr_space, _) = self
228 .memory_dimensions
229 .index_to_label((page_id as u64) << PAGE_BITS);
230 let addr_space_idx = addr_space as usize;
231 debug_assert!(addr_space_idx < addr_space_access_count.len());
232 unsafe {
235 *addr_space_access_count.get_unchecked_mut(addr_space_idx) += 1;
236 }
237 }
238 }
239 self.apply_height_updates(trace_heights, &addr_space_access_count);
240
241 self.add_register_merkle_heights();
243 self.lazy_update_boundary_heights(trace_heights);
244 }
245
246 #[inline(always)]
248 pub(crate) fn update_checkpoint(&mut self) {
249 self.page_indices_since_checkpoint_len = 0;
250 }
251
252 #[inline(always)]
275 fn apply_height_updates(&self, trace_heights: &mut [u32], addr_space_access_count: &[u32]) {
276 let page_access_count: u32 = addr_space_access_count.iter().sum();
277
278 let leaves = page_access_count << PAGE_BITS;
280 debug_assert!(trace_heights.len() >= 2);
281 let poseidon2_idx = trace_heights.len() - 2;
282
283 let merkle_height = self.memory_dimensions.overall_height();
284 let nodes_per_page = (((1 << PAGE_BITS) - 1) + (merkle_height - PAGE_BITS)) as u32;
285 unsafe {
287 *trace_heights.get_unchecked_mut(BOUNDARY_AIR_ID) += leaves * 2;
288 *trace_heights.get_unchecked_mut(poseidon2_idx) +=
290 leaves * 2 + nodes_per_page * page_access_count * 2;
291 *trace_heights.get_unchecked_mut(MERKLE_AIR_ID) +=
293 nodes_per_page * page_access_count * 2;
294 }
295 }
296
297 #[inline(always)]
299 pub(crate) fn lazy_update_boundary_heights(&mut self, trace_heights: &mut [u32]) {
300 self.apply_height_updates(trace_heights, &self.addr_space_access_count);
301 unsafe {
303 std::ptr::write_bytes(
304 self.addr_space_access_count.as_mut_ptr(),
305 0,
306 self.addr_space_access_count.len(),
307 );
308 }
309 }
310}
311
312#[cfg(test)]
313mod tests {
314 use super::*;
315
316 #[test]
317 fn test_bitset_insert_range() {
318 let mut bit_set = BitSet::new(8 * 64 + 1);
320 let num_flips = bit_set.insert_range(2, 29);
321 assert_eq!(num_flips, 27);
322 let num_flips = bit_set.insert_range(1, 31);
323 assert_eq!(num_flips, 3);
324
325 let num_flips = bit_set.insert_range(32, 65);
326 assert_eq!(num_flips, 33);
327 let num_flips = bit_set.insert_range(0, 66);
328 assert_eq!(num_flips, 3);
329 let num_flips = bit_set.insert_range(0, 66);
330 assert_eq!(num_flips, 0);
331
332 let num_flips = bit_set.insert_range(256, 320);
333 assert_eq!(num_flips, 64);
334 let num_flips = bit_set.insert_range(256, 377);
335 assert_eq!(num_flips, 57);
336 let num_flips = bit_set.insert_range(100, 513);
337 assert_eq!(num_flips, 413 - 121);
338 }
339}