mirror of
https://github.com/powdr-labs/powdr.git
synced 2026-04-20 03:03:25 -04:00
lean executor without trace
This commit is contained in:
@@ -438,7 +438,12 @@ fn run_command(command: Commands) {
|
||||
vec![(GoldilocksField::from(0), inputs)]
|
||||
.into_iter()
|
||||
.collect();
|
||||
riscv_executor::execute::<GoldilocksField>(&contents, &inputs, &default_input());
|
||||
riscv_executor::execute::<GoldilocksField>(
|
||||
&contents,
|
||||
&inputs,
|
||||
&default_input(),
|
||||
riscv_executor::ExecMode::Fast,
|
||||
);
|
||||
}
|
||||
(false, true) => {
|
||||
unimplemented!("Running witgen with continuations is not supported yet.")
|
||||
@@ -588,7 +593,12 @@ fn handle_riscv_asm<F: FieldElement>(
|
||||
(true, false) => {
|
||||
let mut inputs_hash: HashMap<F, Vec<F>> = HashMap::default();
|
||||
inputs_hash.insert(0u32.into(), inputs);
|
||||
riscv_executor::execute::<F>(contents, &inputs_hash, &default_input());
|
||||
riscv_executor::execute::<F>(
|
||||
contents,
|
||||
&inputs_hash,
|
||||
&default_input(),
|
||||
riscv_executor::ExecMode::Fast,
|
||||
);
|
||||
}
|
||||
(false, true) => {
|
||||
unimplemented!("Running witgen with continuations is not supported yet.")
|
||||
@@ -637,8 +647,14 @@ fn rust_continuations<F: FieldElement>(file_name: &str, contents: &str, inputs:
|
||||
|
||||
log::info!("Executing powdr-asm...");
|
||||
let (full_trace, memory_accesses) = {
|
||||
let trace =
|
||||
riscv_executor::execute_ast::<F>(&program, &inputs, &bootloader_inputs, usize::MAX).0;
|
||||
let trace = riscv_executor::execute_ast::<F>(
|
||||
&program,
|
||||
&inputs,
|
||||
&bootloader_inputs,
|
||||
usize::MAX,
|
||||
riscv_executor::ExecMode::Trace,
|
||||
)
|
||||
.0;
|
||||
(transposed_trace::<F>(&trace), trace.mem)
|
||||
};
|
||||
|
||||
@@ -662,8 +678,13 @@ fn rust_continuations<F: FieldElement>(file_name: &str, contents: &str, inputs:
|
||||
let degree = F::from(degree).to_degree();
|
||||
let num_rows = degree as usize - 2;
|
||||
let (chunk_trace, memory_snapshot_update) = {
|
||||
let (trace, memory_snapshot_update) =
|
||||
riscv_executor::execute_ast::<F>(&program, &inputs, &bootloader_inputs, num_rows);
|
||||
let (trace, memory_snapshot_update) = riscv_executor::execute_ast::<F>(
|
||||
&program,
|
||||
&inputs,
|
||||
&bootloader_inputs,
|
||||
num_rows,
|
||||
riscv_executor::ExecMode::Trace,
|
||||
);
|
||||
(transposed_trace(&trace), memory_snapshot_update)
|
||||
};
|
||||
log::info!("{} memory slots updated.", memory_snapshot_update.len());
|
||||
|
||||
@@ -23,6 +23,7 @@ pub fn verify_riscv_asm_string(file_name: &str, contents: &str, inputs: Vec<Gold
|
||||
&inputs_hash.clone(),
|
||||
&default_input(),
|
||||
usize::MAX,
|
||||
riscv_executor::ExecMode::Fast,
|
||||
);
|
||||
}),
|
||||
&temp_dir,
|
||||
|
||||
@@ -103,8 +103,8 @@ pub struct MemOperation {
|
||||
pub address: u32,
|
||||
}
|
||||
|
||||
pub struct ExecutionTrace<'a> {
|
||||
pub reg_map: HashMap<&'a str, usize>,
|
||||
pub struct ExecutionTrace {
|
||||
pub reg_map: HashMap<String, usize>,
|
||||
|
||||
/// Values of the registers in the execution trace.
|
||||
///
|
||||
@@ -114,9 +114,12 @@ pub struct ExecutionTrace<'a> {
|
||||
|
||||
/// Writes and reads to memory.
|
||||
pub mem: Vec<MemOperation>,
|
||||
|
||||
/// The length of the trace.
|
||||
pub len: u64,
|
||||
}
|
||||
|
||||
impl<'a> ExecutionTrace<'a> {
|
||||
impl ExecutionTrace {
|
||||
/// Split the values of the registers' trace into rows.
|
||||
pub fn regs_rows(&self) -> impl Iterator<Item = &[Elem]> {
|
||||
self.regs.chunks_exact(self.reg_map.len())
|
||||
@@ -134,7 +137,7 @@ mod builder {
|
||||
use number::FieldElement;
|
||||
|
||||
use crate::{
|
||||
Elem, ExecutionTrace, MemOperation, MemOperationKind, MemoryState, PC_INITIAL_VAL,
|
||||
Elem, ExecMode, ExecutionTrace, MemOperation, MemOperationKind, MemoryState, PC_INITIAL_VAL,
|
||||
};
|
||||
|
||||
fn register_names<T: FieldElement>(main: &Machine<T>) -> Vec<&str> {
|
||||
@@ -150,8 +153,8 @@ mod builder {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub struct TraceBuilder<'a, 'b> {
|
||||
trace: ExecutionTrace<'a>,
|
||||
pub struct TraceBuilder<'b> {
|
||||
trace: ExecutionTrace,
|
||||
|
||||
/// First register of current row.
|
||||
/// Next row is reg_map.len() elems ahead.
|
||||
@@ -174,9 +177,14 @@ mod builder {
|
||||
|
||||
/// Current memory.
|
||||
mem: HashMap<u32, u32>,
|
||||
|
||||
/// The execution mode we running.
|
||||
/// Fast: do not save the register's trace and memory accesses.
|
||||
/// Trace: save everything - needed for continuations.
|
||||
mode: ExecMode,
|
||||
}
|
||||
|
||||
impl<'a, 'b> TraceBuilder<'a, 'b> {
|
||||
impl<'a, 'b> TraceBuilder<'b> {
|
||||
/// Creates a new builder.
|
||||
///
|
||||
/// May fail if max_rows_len is too small or if the main machine is
|
||||
@@ -186,12 +194,13 @@ mod builder {
|
||||
main: &'a Machine<T>,
|
||||
batch_to_line_map: &'b [u32],
|
||||
max_rows_len: usize,
|
||||
) -> Result<Self, Box<(ExecutionTrace<'a>, MemoryState)>> {
|
||||
mode: ExecMode,
|
||||
) -> Result<Self, Box<(ExecutionTrace, MemoryState)>> {
|
||||
let reg_map = register_names(main)
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, name)| (name, i))
|
||||
.collect::<HashMap<&str, usize>>();
|
||||
.map(|(i, name)| (name.to_string(), i))
|
||||
.collect::<HashMap<String, usize>>();
|
||||
|
||||
let reg_len = reg_map.len();
|
||||
|
||||
@@ -215,11 +224,13 @@ mod builder {
|
||||
reg_map,
|
||||
regs: values,
|
||||
mem: Vec::new(),
|
||||
len: 0,
|
||||
},
|
||||
next_statement_line: 1,
|
||||
batch_to_line_map,
|
||||
max_curr_idx: max_rows_len.saturating_sub(1).saturating_mul(reg_len),
|
||||
mem: HashMap::new(),
|
||||
mode,
|
||||
};
|
||||
|
||||
if ret.has_enough_rows() || ret.set_next_pc().is_none() {
|
||||
@@ -274,10 +285,19 @@ mod builder {
|
||||
/// advance to next row, returns the index to the statement that must be
|
||||
/// executed now, or None if the execution is finished
|
||||
pub fn advance(&mut self, was_nop: bool) -> Option<u32> {
|
||||
if !was_nop {
|
||||
self.trace.len += 1;
|
||||
}
|
||||
|
||||
if self.get_reg_idx(self.pc_idx) != self.get_reg_idx_next(self.pc_idx) {
|
||||
// PC changed, create a new line
|
||||
self.curr_idx += self.reg_len();
|
||||
self.trace.regs.extend_from_within(self.curr_idx..);
|
||||
if let ExecMode::Trace = self.mode {
|
||||
// PC changed, create a new line
|
||||
self.curr_idx += self.reg_len();
|
||||
self.trace.regs.extend_from_within(self.curr_idx..);
|
||||
} else if !was_nop {
|
||||
let next_idx = self.curr_idx + self.reg_len();
|
||||
self.trace.regs.copy_within(next_idx.., self.curr_idx);
|
||||
}
|
||||
|
||||
// If we are at the limit of rows, stop the execution
|
||||
if self.has_enough_rows() {
|
||||
@@ -301,26 +321,30 @@ mod builder {
|
||||
}
|
||||
|
||||
pub(crate) fn set_mem(&mut self, addr: u32, val: u32) {
|
||||
self.trace.mem.push(MemOperation {
|
||||
idx: self.curr_idx / self.reg_len() + 1,
|
||||
kind: MemOperationKind::Write,
|
||||
address: addr,
|
||||
});
|
||||
if let ExecMode::Trace = self.mode {
|
||||
self.trace.mem.push(MemOperation {
|
||||
idx: self.curr_idx / self.reg_len() + 1,
|
||||
kind: MemOperationKind::Write,
|
||||
address: addr,
|
||||
});
|
||||
}
|
||||
|
||||
self.mem.insert(addr, val);
|
||||
}
|
||||
|
||||
pub(crate) fn get_mem(&mut self, addr: u32) -> u32 {
|
||||
self.trace.mem.push(MemOperation {
|
||||
idx: self.curr_idx / self.reg_len() + 1,
|
||||
kind: MemOperationKind::Read,
|
||||
address: addr,
|
||||
});
|
||||
if let ExecMode::Trace = self.mode {
|
||||
self.trace.mem.push(MemOperation {
|
||||
idx: self.curr_idx / self.reg_len() + 1,
|
||||
kind: MemOperationKind::Read,
|
||||
address: addr,
|
||||
});
|
||||
}
|
||||
|
||||
*self.mem.get(&addr).unwrap_or(&0)
|
||||
}
|
||||
|
||||
pub fn finish(mut self) -> (ExecutionTrace<'a>, MemoryState) {
|
||||
pub fn finish(mut self) -> (ExecutionTrace, MemoryState) {
|
||||
// remove the last row (future row), as it is not part of the trace
|
||||
self.trace.regs.drain((self.curr_idx + self.reg_len())..);
|
||||
(self.trace, self.mem)
|
||||
@@ -438,7 +462,7 @@ fn preprocess_main_function<T: FieldElement>(machine: &Machine<T>) -> Preprocess
|
||||
}
|
||||
|
||||
struct Executor<'a, 'b, F: FieldElement> {
|
||||
proc: TraceBuilder<'a, 'b>,
|
||||
proc: TraceBuilder<'b>,
|
||||
label_map: HashMap<&'a str, Elem>,
|
||||
inputs: HashMap<F, Vec<F>>,
|
||||
bootloader_inputs: &'b [F],
|
||||
@@ -749,12 +773,13 @@ impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_ast<'a, T: FieldElement>(
|
||||
program: &'a AnalysisASMFile<T>,
|
||||
pub fn execute_ast<T: FieldElement>(
|
||||
program: &AnalysisASMFile<T>,
|
||||
inputs: &HashMap<T, Vec<T>>,
|
||||
bootloader_inputs: &[T],
|
||||
max_steps_to_execute: usize,
|
||||
) -> (ExecutionTrace<'a>, MemoryState) {
|
||||
mode: ExecMode,
|
||||
) -> (ExecutionTrace, MemoryState) {
|
||||
let main_machine = get_main_machine(program);
|
||||
let PreprocessedMain {
|
||||
statements,
|
||||
@@ -763,7 +788,8 @@ pub fn execute_ast<'a, T: FieldElement>(
|
||||
debug_files,
|
||||
} = preprocess_main_function(main_machine);
|
||||
|
||||
let proc = match TraceBuilder::new(main_machine, &batch_to_line_map, max_steps_to_execute) {
|
||||
let proc = match TraceBuilder::new(main_machine, &batch_to_line_map, max_steps_to_execute, mode)
|
||||
{
|
||||
Ok(proc) => proc,
|
||||
Err(ret) => return *ret,
|
||||
};
|
||||
@@ -826,6 +852,11 @@ pub fn execute_ast<'a, T: FieldElement>(
|
||||
e.proc.finish()
|
||||
}
|
||||
|
||||
pub enum ExecMode {
|
||||
Fast,
|
||||
Trace,
|
||||
}
|
||||
|
||||
/// Execute a Powdr/RISCV assembly source.
|
||||
///
|
||||
/// Generic argument F is just used by the parser, before everything is
|
||||
@@ -834,7 +865,8 @@ pub fn execute<F: FieldElement>(
|
||||
asm_source: &str,
|
||||
inputs: &HashMap<F, Vec<F>>,
|
||||
bootloader_inputs: &[F],
|
||||
) {
|
||||
mode: ExecMode,
|
||||
) -> (ExecutionTrace, MemoryState) {
|
||||
log::info!("Parsing...");
|
||||
let parsed = parser::parse_asm::<F>(None, asm_source).unwrap();
|
||||
log::info!("Resolving imports...");
|
||||
@@ -843,7 +875,7 @@ pub fn execute<F: FieldElement>(
|
||||
let analyzed = analysis::analyze(resolved, &mut ast::DiffMonitor::default()).unwrap();
|
||||
|
||||
log::info!("Executing...");
|
||||
execute_ast(&analyzed, inputs, bootloader_inputs, usize::MAX);
|
||||
execute_ast(&analyzed, inputs, bootloader_inputs, usize::MAX, mode)
|
||||
}
|
||||
|
||||
fn to_u32<F: FieldElement>(val: &F) -> Option<u32> {
|
||||
|
||||
Reference in New Issue
Block a user