Skip to content

Commit d529fc0

Browse files
authored
Support for continuations in execution (#111)
* Managing the stack from the JAAF family instructions. * Code reuse. * Frame allocator that can track sparse allocated ranges. * Persisting WomController across segments. * Carrying across segments only the values in active frames. * Fixing memory reset. * Sharing FP. * Also saving newly allocated frames. * Adapting the tests to use AllocateFrame. * Commented out stack print. * Small things.
1 parent 02d597b commit d529fc0

File tree

5 files changed

+275
-122
lines changed

5 files changed

+275
-122
lines changed

extensions/circuit/src/adapters/allocate_frame.rs

Lines changed: 110 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::borrow::Borrow;
1+
use std::{
2+
borrow::Borrow,
3+
sync::{Arc, Mutex},
4+
};
25

36
use openvm_circuit::{
47
arch::{
@@ -23,15 +26,16 @@ use struct_reflection::{StructReflection, StructReflectionHelper};
2326

2427
use crate::{
2528
FrameBridge, FrameBus, FrameState, VmAdapterChipWom, WomBridge, WomController, WomRecord,
26-
adapters::{compose, decompose},
29+
adapters::{compose, decompose, frame_allocator::FrameAllocator},
2730
};
2831

2932
use super::RV32_REGISTER_NUM_LIMBS;
3033

3134
#[derive(Debug)]
3235
pub struct AllocateFrameAdapterChipWom {
3336
pub air: AllocateFrameAdapterAirWom,
34-
next_fp: u32,
37+
frame_stack: Arc<Mutex<Vec<u32>>>,
38+
frame_allocator: Arc<Mutex<FrameAllocator>>,
3539
}
3640

3741
impl AllocateFrameAdapterChipWom {
@@ -41,6 +45,8 @@ impl AllocateFrameAdapterChipWom {
4145
frame_bus: FrameBus,
4246
memory_bridge: MemoryBridge,
4347
wom_bridge: WomBridge,
48+
frame_allocator: Arc<Mutex<FrameAllocator>>,
49+
frame_stack: Arc<Mutex<Vec<u32>>>,
4450
) -> Self {
4551
Self {
4652
air: AllocateFrameAdapterAirWom {
@@ -49,8 +55,8 @@ impl AllocateFrameAdapterChipWom {
4955
frame_bridge: FrameBridge::new(frame_bus),
5056
_memory_bridge: memory_bridge,
5157
},
52-
// Start from 8 because 0 and 4 are used by the startup code.
53-
next_fp: 8,
58+
frame_allocator,
59+
frame_stack,
5460
}
5561
}
5662
}
@@ -214,9 +220,18 @@ impl<F: PrimeField32> VmAdapterChipWom<F> for AllocateFrameAdapterChipWom {
214220
};
215221
let amount_bytes = RV32_REGISTER_NUM_LIMBS as u32 * amount;
216222

217-
let allocated_ptr = self.next_fp;
218-
219-
self.next_fp += amount_bytes;
223+
let allocated_ptr = self
224+
.frame_allocator
225+
.lock()
226+
.unwrap()
227+
.allocate(amount_bytes)
228+
.expect("WOM frame allocation failed: not enough free contiguous space");
229+
230+
{
231+
let mut frame_stack = self.frame_stack.lock().unwrap();
232+
frame_stack.push(allocated_ptr);
233+
//println!("A STACK: {frame_stack:?}");
234+
}
220235

221236
let amount_bytes = decompose(amount_bytes);
222237

@@ -280,3 +295,90 @@ impl<F: PrimeField32> VmAdapterChipWom<F> for AllocateFrameAdapterChipWom {
280295
&self.air
281296
}
282297
}
298+
299+
pub mod frame_allocator {
300+
use std::collections::{BTreeMap, btree_map::Entry};
301+
302+
use serde::{Deserialize, Serialize};
303+
304+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
305+
pub struct Range {
306+
pub start: u32,
307+
pub end: u32,
308+
}
309+
310+
#[derive(Debug, Serialize, Deserialize)]
311+
pub struct FrameAllocator {
312+
/// The set of free frame ranges, indexed by its length.
313+
free_ranges: BTreeMap<u32, Vec<Range>>,
314+
/// The set of allocated frame ranges. Maps start address to end address.
315+
allocated_ranges: BTreeMap<u32, u32>,
316+
}
317+
318+
impl FrameAllocator {
319+
pub fn new(largest_address: u32, existing_allocations: BTreeMap<u32, u32>) -> Self {
320+
// Initialize free_ranges based on existing_allocations
321+
let mut free_ranges: BTreeMap<u32, Vec<Range>> = BTreeMap::new();
322+
let mut current_start = 0;
323+
324+
for (start, end) in &existing_allocations {
325+
if current_start < *start {
326+
let range = Range {
327+
start: current_start,
328+
end: *start,
329+
};
330+
let length = range.end - range.start;
331+
free_ranges.entry(length).or_default().push(range);
332+
}
333+
current_start = *end;
334+
}
335+
336+
if current_start < largest_address {
337+
let range = Range {
338+
start: current_start,
339+
end: largest_address,
340+
};
341+
let length = range.end - range.start;
342+
free_ranges.entry(length).or_default().push(range);
343+
}
344+
345+
Self {
346+
free_ranges,
347+
allocated_ranges: existing_allocations,
348+
}
349+
}
350+
351+
pub fn allocate(&mut self, size: u32) -> Option<u32> {
352+
// Find a free range that can accommodate the requested size
353+
let fittest_len = *self.free_ranges.range_mut(size..).next()?.0;
354+
355+
// Apparently there is no way find this entry without searching again...
356+
// TODO: change this when https://github.com/rust-lang/rust/issues/107540
357+
// is stabilized.
358+
let Entry::Occupied(mut entry) = self.free_ranges.entry(fittest_len) else {
359+
unreachable!();
360+
};
361+
let mut range = entry.get_mut().pop().unwrap();
362+
if entry.get().is_empty() {
363+
entry.remove();
364+
}
365+
366+
let allocated_start = range.start;
367+
range.start += size;
368+
369+
if range.start < range.end {
370+
let length = range.end - range.start;
371+
self.free_ranges.entry(length).or_default().push(range);
372+
}
373+
374+
self.allocated_ranges
375+
.insert(allocated_start, allocated_start + size);
376+
377+
Some(allocated_start)
378+
}
379+
380+
pub fn get_allocated_ranges(&self) -> &BTreeMap<u32, u32> {
381+
&self.allocated_ranges
382+
}
383+
}
384+
}

extensions/circuit/src/adapters/jaaf.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
use std::{array, borrow::Borrow, marker::PhantomData};
1+
use std::{
2+
array,
3+
borrow::Borrow,
4+
marker::PhantomData,
5+
sync::{Arc, Mutex},
6+
};
27

38
use openvm_circuit::{
49
arch::{
@@ -51,6 +56,7 @@ pub struct JaafInstruction<T> {
5156
#[derive(Debug)]
5257
pub struct JaafAdapterChipWom<F: Field> {
5358
pub air: JaafAdapterAirWom,
59+
frame_stack: Arc<Mutex<Vec<u32>>>,
5460
_marker: PhantomData<F>,
5561
}
5662

@@ -59,6 +65,7 @@ impl<F: PrimeField32> JaafAdapterChipWom<F> {
5965
execution_bus: ExecutionBus,
6066
program_bus: ProgramBus,
6167
frame_bus: FrameBus,
68+
frame_stack: Arc<Mutex<Vec<u32>>>,
6269
memory_bridge: MemoryBridge,
6370
wom_bridge: WomBridge,
6471
) -> Self {
@@ -69,6 +76,7 @@ impl<F: PrimeField32> JaafAdapterChipWom<F> {
6976
_memory_bridge: memory_bridge,
7077
wom_bridge,
7178
},
79+
frame_stack,
7280
_marker: PhantomData,
7381
}
7482
}
@@ -356,6 +364,41 @@ impl<F: PrimeField32> VmAdapterChipWom<F> for JaafAdapterChipWom<F> {
356364
(None, None)
357365
};
358366

367+
// Update the frame stack according to the opcode
368+
{
369+
let mut frame_stack = self.frame_stack.lock().unwrap();
370+
match local_opcode {
371+
RET | JAAF => {
372+
// The newly activated frame must be in the frame stack.
373+
let idx = frame_stack.iter().rposition(|x| x == &to_fp).unwrap();
374+
375+
// Test if the activated frame has just been allocated with AllocateFrame
376+
// (i.e., is at the top of the stack)
377+
if idx == frame_stack.len() - 1 {
378+
// Yes, so this has to be a JAAF instruction jumping to a new frame.
379+
assert_eq!(local_opcode, JAAF);
380+
381+
// The current frame can be safely dropped, since this opcode doesn't save it,
382+
// rendering it unreachable.
383+
let curr_frame_idx = idx - 1;
384+
let curr_frame = frame_stack.swap_remove(curr_frame_idx);
385+
386+
// Since we are jumping to the last frame in the stack, which is the new
387+
// frame, the current frame must be the one before it, that we just removed.
388+
assert_eq!(curr_frame, from_frame.fp);
389+
} else {
390+
// Found an old frame, truncate the stack to that frame
391+
frame_stack.truncate(idx + 1);
392+
}
393+
//println!("J STACK: {frame_stack:?}");
394+
}
395+
CALL | CALL_INDIRECT | JAAF_SAVE => {
396+
// These calls always target a new frame, which is already on top of the stack.
397+
assert_eq!(frame_stack.last(), Some(&to_fp));
398+
}
399+
}
400+
}
401+
359402
Ok((
360403
ExecutionState {
361404
pc: to_pc,

0 commit comments

Comments
 (0)