Skip to content

Commit a579d10

Browse files
authored
bytecode execution: reorganize (#22)
1 parent 38c8d9c commit a579d10

File tree

6 files changed

+240
-220
lines changed

6 files changed

+240
-220
lines changed

crates/compiler/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use vm::{Bytecode, F, Label, PUBLIC_INPUT_START, ZERO_VEC_PTR, execute_bytecode};
1+
use vm::{Bytecode, F, Label, PUBLIC_INPUT_START, ZERO_VEC_PTR};
22

33
use crate::{
44
a_simplify_lang::simplify_program, b_compile_intermediate::compile_to_intermediate_bytecode,
@@ -37,5 +37,5 @@ pub fn compile_program(program: &str) -> Bytecode {
3737

3838
pub fn compile_and_run(program: &str, public_input: &[F], private_input: &[F]) {
3939
let bytecode = compile_program(program);
40-
let _ = execute_bytecode(&bytecode, public_input, private_input);
40+
let _ = bytecode.execute(public_input, private_input);
4141
}

crates/vm/src/bytecode/mod.rs

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ use std::{
44
fmt::{Display, Formatter},
55
};
66

7+
use utils::{build_poseidon16, build_poseidon24, pretty_integer};
8+
9+
use crate::{
10+
DIMENSION, ExecutionResult, F, MAX_MEMORY_SIZE, Memory, PUBLIC_INPUT_START, RunnerError,
11+
build_public_memory,
12+
};
13+
714
pub mod operand;
815
pub use operand::*;
916
pub mod hint;
@@ -23,6 +30,151 @@ pub struct Bytecode {
2330
pub ending_pc: usize,
2431
}
2532

33+
impl Bytecode {
34+
#[must_use]
35+
pub fn execute(&self, public_input: &[F], private_input: &[F]) -> ExecutionResult {
36+
let mut std_out = String::new();
37+
let first = self
38+
.execute_internal(
39+
public_input,
40+
private_input,
41+
MAX_MEMORY_SIZE / 2,
42+
false,
43+
&mut std_out,
44+
)
45+
.unwrap_or_else(|err| {
46+
if !std_out.is_empty() {
47+
print!("{std_out}");
48+
}
49+
panic!("Error during bytecode execution: {err}");
50+
});
51+
52+
self.execute_internal(
53+
public_input,
54+
private_input,
55+
first.no_vec_runtime_memory,
56+
true,
57+
&mut String::new(),
58+
)
59+
.unwrap()
60+
}
61+
62+
pub(crate) fn execute_internal(
63+
&self,
64+
public_input: &[F],
65+
private_input: &[F],
66+
no_vec_runtime_memory: usize,
67+
final_execution: bool,
68+
std_out: &mut String,
69+
) -> Result<ExecutionResult, RunnerError> {
70+
// TODO avoid rebuilding each time
71+
let (p16, p24) = (build_poseidon16(), build_poseidon24());
72+
73+
// set public memory
74+
let mut memory = Memory::new(build_public_memory(public_input));
75+
76+
let public_memory_size = (PUBLIC_INPUT_START + public_input.len()).next_power_of_two();
77+
let mut fp = public_memory_size;
78+
79+
private_input
80+
.iter()
81+
.copied()
82+
.enumerate()
83+
.try_for_each(|(i, v)| memory.set(fp + i, v))?;
84+
85+
fp = (fp + private_input.len()).next_multiple_of(DIMENSION);
86+
87+
let initial_ap = fp + self.starting_frame_memory;
88+
let initial_ap_vec =
89+
(initial_ap + no_vec_runtime_memory).next_multiple_of(DIMENSION) / DIMENSION;
90+
91+
let (mut pc, mut ap, mut ap_vec) = (0, initial_ap, initial_ap_vec);
92+
93+
let mut poseidon16_calls = 0;
94+
let mut poseidon24_calls = 0;
95+
let mut dot_product_ext_ext_calls = 0;
96+
let mut dot_product_base_ext_calls = 0;
97+
let mut cpu_cycles = 0;
98+
99+
let mut last_checkpoint_cpu_cycles = 0;
100+
let mut checkpoint_ap = initial_ap;
101+
let mut checkpoint_ap_vec = ap_vec;
102+
103+
let mut pcs = Vec::new();
104+
let mut fps = Vec::new();
105+
106+
while pc != self.ending_pc {
107+
if pc >= self.instructions.len() {
108+
return Err(RunnerError::PCOutOfBounds);
109+
}
110+
111+
pcs.push(pc);
112+
fps.push(fp);
113+
114+
cpu_cycles += 1;
115+
116+
// hints (no PC change)
117+
for hint in self.hints.get(&pc).unwrap_or(&vec![]) {
118+
hint.execute(
119+
&mut memory,
120+
fp,
121+
&mut ap,
122+
&mut ap_vec,
123+
std_out,
124+
cpu_cycles,
125+
&mut last_checkpoint_cpu_cycles,
126+
&mut checkpoint_ap,
127+
&mut checkpoint_ap_vec,
128+
)?;
129+
}
130+
131+
// instruction
132+
self.instructions[pc].execute(
133+
&mut memory,
134+
&mut fp,
135+
&mut pc,
136+
&p16,
137+
&p24,
138+
&mut poseidon16_calls,
139+
&mut poseidon24_calls,
140+
&mut dot_product_ext_ext_calls,
141+
&mut dot_product_base_ext_calls,
142+
)?;
143+
}
144+
145+
debug_assert_eq!(pc, self.ending_pc);
146+
pcs.push(pc);
147+
fps.push(fp);
148+
149+
if final_execution {
150+
if !std_out.is_empty() {
151+
print!("{std_out}");
152+
}
153+
print_report(
154+
self,
155+
public_input.len(),
156+
private_input.len(),
157+
cpu_cycles,
158+
&memory,
159+
initial_ap_vec,
160+
ap_vec,
161+
poseidon16_calls,
162+
poseidon24_calls,
163+
dot_product_ext_ext_calls,
164+
dot_product_base_ext_calls,
165+
);
166+
}
167+
168+
Ok(ExecutionResult {
169+
no_vec_runtime_memory: ap - initial_ap,
170+
public_memory_size,
171+
memory,
172+
pcs,
173+
fps,
174+
})
175+
}
176+
}
177+
26178
impl Display for Bytecode {
27179
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
28180
for (pc, instruction) in self.instructions.iter().enumerate() {
@@ -36,3 +188,62 @@ impl Display for Bytecode {
36188
Ok(())
37189
}
38190
}
191+
192+
fn print_report(
193+
bc: &Bytecode,
194+
public_in_len: usize,
195+
private_in_len: usize,
196+
cycles: usize,
197+
memory: &Memory,
198+
initial_ap_vec: usize,
199+
ap_vec: usize,
200+
p16_calls: usize,
201+
p24_calls: usize,
202+
dp_ext_ext_calls: usize,
203+
dp_base_ext_calls: usize,
204+
) {
205+
println!("\nBytecode size: {}", pretty_integer(bc.instructions.len()));
206+
println!("Public input size: {}", pretty_integer(public_in_len));
207+
println!("Private input size: {}", pretty_integer(private_in_len));
208+
println!("Executed {} instructions", pretty_integer(cycles));
209+
210+
let runtime_mem = memory.0.len() - (PUBLIC_INPUT_START + public_in_len);
211+
println!(
212+
"Runtime memory: {} ({:.2}% vec)",
213+
pretty_integer(runtime_mem),
214+
(DIMENSION * (ap_vec - initial_ap_vec)) as f64 / runtime_mem as f64 * 100.0
215+
);
216+
217+
let total_poseidon = p16_calls + p24_calls;
218+
if total_poseidon > 0 {
219+
println!(
220+
"Poseidon2_16 calls: {}, Poseidon2_24 calls: {} (1 poseidon per {} instructions)",
221+
pretty_integer(p16_calls),
222+
pretty_integer(p24_calls),
223+
cycles / total_poseidon
224+
);
225+
}
226+
if dp_ext_ext_calls > 0 {
227+
println!(
228+
"DotProductExtExt calls: {}",
229+
pretty_integer(dp_ext_ext_calls)
230+
);
231+
}
232+
if dp_base_ext_calls > 0 {
233+
println!(
234+
"DotProductBaseExt calls: {}",
235+
pretty_integer(dp_base_ext_calls)
236+
);
237+
}
238+
239+
let used_cells = memory
240+
.0
241+
.iter()
242+
.skip(PUBLIC_INPUT_START + public_in_len)
243+
.filter(|&&x| x.is_some())
244+
.count();
245+
println!(
246+
"Memory usage: {:.1}%",
247+
used_cells as f64 / runtime_mem as f64 * 100.0
248+
);
249+
}

crates/vm/src/memory.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
use p3_field::{BasedVectorSpace, ExtensionField};
1+
use p3_field::{BasedVectorSpace, ExtensionField, PrimeCharacteristicRing};
2+
use p3_symmetric::Permutation;
23
use rayon::prelude::*;
4+
use utils::{build_poseidon16, build_poseidon24};
35

4-
use crate::{DIMENSION, EF, F, RunnerError};
6+
use crate::{
7+
DIMENSION, EF, F, POSEIDON_16_NULL_HASH_PTR, POSEIDON_24_NULL_HASH_PTR, PUBLIC_INPUT_START,
8+
RunnerError, ZERO_VEC_PTR,
9+
};
510

611
pub(crate) const MAX_MEMORY_SIZE: usize = 1 << 23;
712

@@ -86,3 +91,19 @@ impl Memory {
8691
.try_for_each(|(i, &v)| self.set(index * DIMENSION + i, v))
8792
}
8893
}
94+
95+
#[must_use]
96+
pub fn build_public_memory(public_input: &[F]) -> Vec<F> {
97+
// padded to a power of two
98+
let public_memory_len = (PUBLIC_INPUT_START + public_input.len()).next_power_of_two();
99+
let mut public_memory = F::zero_vec(public_memory_len);
100+
public_memory[PUBLIC_INPUT_START..][..public_input.len()].copy_from_slice(public_input);
101+
for pm in public_memory.iter_mut().take((ZERO_VEC_PTR + 2) * 8) {
102+
*pm = F::ZERO; // zero vector
103+
}
104+
public_memory[POSEIDON_16_NULL_HASH_PTR * 8..(POSEIDON_16_NULL_HASH_PTR + 2) * 8]
105+
.copy_from_slice(&build_poseidon16().permute([F::ZERO; 16]));
106+
public_memory[POSEIDON_24_NULL_HASH_PTR * 8..(POSEIDON_24_NULL_HASH_PTR + 1) * 8]
107+
.copy_from_slice(&build_poseidon24().permute([F::ZERO; 24])[16..]);
108+
public_memory
109+
}

0 commit comments

Comments
 (0)