Skip to content

Commit 04ccc72

Browse files
committed
E2 supports suspension
1 parent 46bb8d8 commit 04ccc72

File tree

3 files changed

+83
-28
lines changed

3 files changed

+83
-28
lines changed

crates/sdk/tests/integration_test.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use eyre::Result;
88
use openvm_build::GuestOptions;
99
use openvm_circuit::{
1010
self,
11-
arch::{instructions::exe::VmExe, ContinuationVmProof, ExecutionError, VirtualMachineError},
11+
arch::{
12+
instructions::exe::VmExe, ContinuationVmProof, ExecutionError, VirtualMachine,
13+
VirtualMachineError, VmExecState,
14+
},
1215
utils::test_system_config,
1316
};
1417
use openvm_continuations::verifier::{
@@ -19,15 +22,16 @@ use openvm_native_circuit::{execute_program_with_config, NativeConfig, NativeCpu
1922
use openvm_native_compiler::{conversion::CompilerOptions, prelude::*};
2023
use openvm_sdk::{
2124
codec::{Decode, Encode},
22-
config::{AggregationConfig, AppConfig, SdkSystemConfig, SdkVmConfig},
25+
config::{AggregationConfig, AppConfig, SdkSystemConfig, SdkVmBuilder, SdkVmConfig},
2326
prover::verify_app_proof,
24-
Sdk, StdIn,
27+
DefaultStarkEngine, Sdk, StdIn,
2528
};
2629
use openvm_stark_sdk::{
2730
config::{
2831
baby_bear_poseidon2::{BabyBearPoseidon2Config, BabyBearPoseidon2Engine},
2932
setup_tracing, FriParameters,
3033
},
34+
engine::StarkFriEngine,
3135
openvm_stark_backend::p3_field::FieldAlgebra,
3236
p3_baby_bear::BabyBear,
3337
};
@@ -265,6 +269,35 @@ fn test_public_values_and_leaf_verification() -> eyre::Result<()> {
265269
Ok(())
266270
}
267271

272+
#[test]
273+
fn test_metered_execution_suspension() -> eyre::Result<()> {
274+
setup_tracing();
275+
let app_log_blowup = 1;
276+
let app_config = small_test_app_config(app_log_blowup);
277+
let exe = app_exe_for_test();
278+
279+
let engine = DefaultStarkEngine::new(app_config.app_fri_params.fri_params);
280+
let (vm, _) = VirtualMachine::new_with_keygen(engine, SdkVmBuilder, app_config.app_vm_config)?;
281+
let executor_idx_to_air_idx = vm.executor_idx_to_air_idx();
282+
let interpreter = vm
283+
.executor()
284+
.metered_instance(&exe, &executor_idx_to_air_idx)?;
285+
let metered_ctx = vm.build_metered_ctx(&exe).with_segment_suspend(true);
286+
let vm_state = interpreter.create_initial_vm_state(vec![]);
287+
let mut vm_exec_state = VmExecState::new(vm_state, metered_ctx);
288+
vm_exec_state = interpreter.execute_metered_from_state_until_suspension(vm_exec_state)?;
289+
assert!(
290+
vm_exec_state.exit_code.is_ok(),
291+
"Execution exits with an error: {:?}",
292+
vm_exec_state.exit_code
293+
);
294+
assert!(
295+
vm_exec_state.exit_code?.is_none(),
296+
"Expect more than 1 segment but the execution terminates."
297+
);
298+
Ok(())
299+
}
300+
268301
#[cfg(feature = "evm-verify")]
269302
#[test]
270303
#[ignore = "slow"]

crates/vm/src/arch/execution_mode/metered/ctx.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub struct MeteredCtx<const PAGE_BITS: usize = DEFAULT_PAGE_BITS> {
2323
pub is_trace_height_constant: Vec<bool>,
2424
pub memory_ctx: MemoryCtx<PAGE_BITS>,
2525
pub segmentation_ctx: SegmentationCtx,
26+
pub segment_suspend: bool,
2627
}
2728

2829
impl<const PAGE_BITS: usize> MeteredCtx<PAGE_BITS> {
@@ -75,6 +76,7 @@ impl<const PAGE_BITS: usize> MeteredCtx<PAGE_BITS> {
7576
is_trace_height_constant,
7677
memory_ctx,
7778
segmentation_ctx,
79+
segment_suspend: false,
7880
};
7981
if !config.continuation_enabled {
8082
// force single segment
@@ -106,6 +108,11 @@ impl<const PAGE_BITS: usize> MeteredCtx<PAGE_BITS> {
106108
self
107109
}
108110

111+
pub fn with_segment_suspend(mut self, segment_suspend: bool) -> Self {
112+
self.segment_suspend = segment_suspend;
113+
self
114+
}
115+
109116
pub fn segments(&self) -> &[Segment] {
110117
&self.segmentation_ctx.segments
111118
}
@@ -121,7 +128,7 @@ impl<const PAGE_BITS: usize> MeteredCtx<PAGE_BITS> {
121128
}
122129

123130
#[inline(always)]
124-
pub fn check_and_segment(&mut self, instret: u64, segment_check_insns: u64) {
131+
pub fn check_and_segment(&mut self, instret: u64, segment_check_insns: u64) -> bool {
125132
let threshold = self
126133
.segmentation_ctx
127134
.instret_last_segment_check
@@ -131,7 +138,7 @@ impl<const PAGE_BITS: usize> MeteredCtx<PAGE_BITS> {
131138
"overflow in segment check threshold calculation"
132139
);
133140
if instret < threshold {
134-
return;
141+
return false;
135142
}
136143

137144
self.memory_ctx
@@ -145,6 +152,7 @@ impl<const PAGE_BITS: usize> MeteredCtx<PAGE_BITS> {
145152
if did_segment {
146153
self.reset_segment();
147154
}
155+
did_segment
148156
}
149157

150158
#[allow(dead_code)]
@@ -200,12 +208,11 @@ impl<const PAGE_BITS: usize> ExecutionCtxTrait for MeteredCtx<PAGE_BITS> {
200208
segment_check_insns: u64,
201209
exec_state: &mut VmExecState<F, GuestMemory, Self>,
202210
) -> bool {
203-
// E2 always runs until termination. Here we use the function as a hook called every
204-
// instruction.
211+
// If `segment_suspend` is set, suspend every segment. Otherwise, execute until termination.
205212
exec_state
206213
.ctx
207-
.check_and_segment(instret, segment_check_insns);
208-
false
214+
.check_and_segment(instret, segment_check_insns)
215+
&& exec_state.ctx.segment_suspend
209216
}
210217

211218
#[inline(always)]

crates/vm/src/arch/interpreter.rs

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,15 @@ where
201201
})
202202
}
203203

204+
pub fn create_initial_vm_state(&self, inputs: impl Into<Streams<F>>) -> VmState<F> {
205+
VmState::initial(
206+
&self.system_config,
207+
&self.init_memory,
208+
self.pc_start,
209+
inputs,
210+
)
211+
}
212+
204213
/// # Safety
205214
/// - This function assumes that the `pc` is within program bounds - this should be the case if
206215
/// the pc is checked to be in bounds before jumping to it.
@@ -381,12 +390,7 @@ where
381390
inputs: impl Into<Streams<F>>,
382391
ctx: MeteredCtx,
383392
) -> Result<(Vec<Segment>, VmState<F, GuestMemory>), ExecutionError> {
384-
let vm_state = VmState::initial(
385-
&self.system_config,
386-
&self.init_memory,
387-
self.pc_start,
388-
inputs,
389-
);
393+
let vm_state = self.create_initial_vm_state(inputs);
390394
self.execute_metered_from_state(vm_state, ctx)
391395
}
392396

@@ -405,22 +409,38 @@ where
405409
) -> Result<(Vec<Segment>, VmState<F, GuestMemory>), ExecutionError> {
406410
let mut exec_state = VmExecState::new(from_state, ctx);
407411

408-
let instret = exec_state.instret();
409-
let pc = exec_state.pc();
410-
let segmentation_check_insns = exec_state.ctx.segmentation_ctx.segment_check_insns;
412+
loop {
413+
exec_state = self.execute_metered_from_state_until_suspension(exec_state)?;
414+
// The execution has terminated.
415+
if exec_state.exit_code.is_ok() && exec_state.exit_code.as_ref().unwrap().is_some() {
416+
break;
417+
}
418+
if exec_state.exit_code.is_err() {
419+
return Err(exec_state.exit_code.unwrap_err());
420+
}
421+
}
422+
check_termination(exec_state.exit_code)?;
423+
let VmExecState { vm_state, ctx, .. } = exec_state;
424+
Ok((ctx.into_segments(), vm_state))
425+
}
426+
pub fn execute_metered_from_state_until_suspension(
427+
&self,
428+
mut from_state: VmExecState<F, GuestMemory, MeteredCtx>,
429+
) -> Result<VmExecState<F, GuestMemory, MeteredCtx>, ExecutionError> {
430+
let instret = from_state.instret();
431+
let pc = from_state.pc();
432+
let segmentation_check_insns = from_state.ctx.segmentation_ctx.segment_check_insns;
411433
// Start execution
412434
run!(
413435
"execute_metered",
414436
self,
415437
instret,
416438
pc,
417439
segmentation_check_insns,
418-
exec_state,
440+
from_state,
419441
MeteredCtx
420442
);
421-
check_termination(exec_state.exit_code)?;
422-
let VmExecState { vm_state, ctx, .. } = exec_state;
423-
Ok((ctx.into_segments(), vm_state))
443+
Ok(from_state)
424444
}
425445
}
426446

@@ -437,12 +457,7 @@ where
437457
inputs: impl Into<Streams<F>>,
438458
ctx: MeteredCostCtx,
439459
) -> Result<(u64, VmState<F, GuestMemory>), ExecutionError> {
440-
let vm_state = VmState::initial(
441-
&self.system_config,
442-
&self.init_memory,
443-
self.pc_start,
444-
inputs,
445-
);
460+
let vm_state = self.create_initial_vm_state(inputs);
446461
self.execute_metered_cost_from_state(vm_state, ctx)
447462
}
448463

0 commit comments

Comments
 (0)