diff --git a/crates/vm/src/bytecode/hint.rs b/crates/vm/src/bytecode/hint.rs index b9f3e60a..4a44dcdf 100644 --- a/crates/vm/src/bytecode/hint.rs +++ b/crates/vm/src/bytecode/hint.rs @@ -3,7 +3,11 @@ use std::{ fmt::{Display, Formatter}, }; +use p3_field::{Field, PrimeCharacteristicRing}; +use utils::ToUsize; + use super::MemOrConstant; +use crate::{DIMENSION, F, Memory, RunnerError}; /// Hints are special instructions for the prover to resolve non-determinism. /// @@ -50,6 +54,91 @@ pub enum Hint { }, } +impl Hint { + pub fn execute( + &self, + memory: &mut Memory, + fp: usize, + ap: &mut usize, + ap_vec: &mut usize, + std_out: &mut String, + cpu_cycles: usize, + last_checkpoint_cpu_cycles: &mut usize, + checkpoint_ap: &mut usize, + checkpoint_ap_vec: &mut usize, + ) -> Result<(), RunnerError> { + match self { + Self::RequestMemory { + offset, + size, + vectorized, + } => { + let sz = size.read_value(memory, fp)?.to_usize(); + if *vectorized { + // find the next multiple of 8 + memory.set(fp + *offset, F::from_usize(*ap_vec))?; + *ap_vec += sz; + } else { + memory.set(fp + *offset, F::from_usize(*ap))?; + *ap += sz; + } + // does not increase PC + } + Self::DecomposeBits { + res_offset, + to_decompose, + } => { + let to_decompose_value = to_decompose.read_value(memory, fp)?.to_usize(); + for i in 0..F::bits() { + let bit = if to_decompose_value & (1 << i) != 0 { + F::ONE + } else { + F::ZERO + }; + memory.set(fp + *res_offset + i, bit)?; + } + } + Self::Inverse { arg, res_offset } => { + let value = arg.read_value(memory, fp)?; + let result = value.try_inverse().unwrap_or(F::ZERO); + memory.set(fp + *res_offset, result)?; + } + Self::Print { line_info, content } => { + let values = content + .iter() + .map(|m| Ok(m.read_value(memory, fp)?.to_string())) + .collect::, RunnerError>>()?; + + // Logs for performance analysis: + if values.first().is_some_and(|s| s == "123456789") { + use utils::pretty_integer; + if values.len() == 1 { + *std_out += "[CHECKPOINT]\n"; + } else { + let new_no_vec = *ap - *checkpoint_ap; + let new_vec = (*ap_vec - *checkpoint_ap_vec) * DIMENSION; + *std_out += &format!( + "[CHECKPOINT {}] new CPU cycles: {}, new runtime memory: {} ({:.1}% vec)\n", + values[1], + pretty_integer(cpu_cycles - *last_checkpoint_cpu_cycles), + pretty_integer(new_no_vec + new_vec), + new_vec as f64 / (new_no_vec + new_vec) as f64 * 100.0 + ); + } + *last_checkpoint_cpu_cycles = cpu_cycles; + *checkpoint_ap = *ap; + *checkpoint_ap_vec = *ap_vec; + } else { + let line_info = line_info.replace(';', ""); + *std_out += &format!("\"{}\" -> {}\n", line_info, values.join(", ")); + // does not increase PC + } + } + } + Ok(()) + } +} + impl Display for Hint { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { diff --git a/crates/vm/src/runner.rs b/crates/vm/src/runner.rs index 6080a839..d78f9160 100644 --- a/crates/vm/src/runner.rs +++ b/crates/vm/src/runner.rs @@ -1,4 +1,4 @@ -use p3_field::{BasedVectorSpace, Field, PrimeCharacteristicRing, dot_product}; +use p3_field::{BasedVectorSpace, PrimeCharacteristicRing, dot_product}; use p3_symmetric::Permutation; use utils::{ToUsize, build_poseidon16, build_poseidon24, pretty_integer}; use whir_p3::poly::{evals::EvaluationsList, multilinear::MultilinearPoint}; @@ -6,7 +6,7 @@ use whir_p3::poly::{evals::EvaluationsList, multilinear::MultilinearPoint}; use crate::{ DIMENSION, EF, F, MAX_MEMORY_SIZE, Memory, POSEIDON_16_NULL_HASH_PTR, POSEIDON_24_NULL_HASH_PTR, PUBLIC_INPUT_START, RunnerError, ZERO_VEC_PTR, - bytecode::{Bytecode, Hint, Instruction}, + bytecode::{Bytecode, Instruction}, }; #[must_use] @@ -123,77 +123,17 @@ fn execute_bytecode_helper( cpu_cycles += 1; for hint in bytecode.hints.get(&pc).unwrap_or(&vec![]) { - match hint { - Hint::RequestMemory { - offset, - size, - vectorized, - } => { - let size = size.read_value(&memory, fp)?.to_usize(); - - if *vectorized { - // find the next multiple of 8 - memory.set(fp + *offset, F::from_usize(ap_vec))?; - ap_vec += size; - } else { - memory.set(fp + *offset, F::from_usize(ap))?; - ap += size; - } - // does not increase PC - } - Hint::DecomposeBits { - res_offset, - to_decompose, - } => { - let to_decompose_value = to_decompose.read_value(&memory, fp)?.to_usize(); - for i in 0..F::bits() { - let bit = if to_decompose_value & (1 << i) != 0 { - F::ONE - } else { - F::ZERO - }; - memory.set(fp + *res_offset + i, bit)?; - } - } - Hint::Inverse { arg, res_offset } => { - let value = arg.read_value(&memory, fp)?; - let result = value.try_inverse().unwrap_or(F::ZERO); - memory.set(fp + *res_offset, result)?; - } - Hint::Print { line_info, content } => { - let values = content - .iter() - .map(|value| Ok(value.read_value(&memory, fp)?.to_string())) - .collect::, _>>()?; - // Logs for performance analysis: - if values[0] == "123456789" { - if values.len() == 1 { - *std_out += "[CHECKPOINT]\n"; - } else { - assert_eq!(values.len(), 2); - let new_no_vec_memory = ap - checkpoint_ap; - let new_vec_memory = (ap_vec - checkpoint_ap_vec) * DIMENSION; - *std_out += &format!( - "[CHECKPOINT {}] new CPU cycles: {}, new runtime memory: {} ({:.1}% vec)\n", - values[1], - pretty_integer(cpu_cycles - last_checkpoint_cpu_cycles), - pretty_integer(new_no_vec_memory + new_vec_memory), - new_vec_memory as f64 / (new_no_vec_memory + new_vec_memory) as f64 - * 100.0 - ); - } - - last_checkpoint_cpu_cycles = cpu_cycles; - checkpoint_ap = ap; - checkpoint_ap_vec = ap_vec; - continue; - } - - let line_info = line_info.replace(';', ""); - *std_out += &format!("\"{}\" -> {}\n", line_info, values.join(", ")); - // does not increase PC - } - } + hint.execute( + &mut memory, + fp, + &mut ap, + &mut ap_vec, + std_out, + cpu_cycles, + &mut last_checkpoint_cpu_cycles, + &mut checkpoint_ap, + &mut checkpoint_ap_vec, + )?; } let instruction = &bytecode.instructions[pc];