diff --git a/powdr_cli/src/main.rs b/powdr_cli/src/main.rs index f5ac6db9c2..393023b1d2 100644 --- a/powdr_cli/src/main.rs +++ b/powdr_cli/src/main.rs @@ -508,7 +508,7 @@ fn handle_riscv_asm( just_execute: bool, ) -> Result<(), Vec> { if just_execute { - riscv_executor::execute::(contents); + riscv_executor::execute::(contents, &inputs); } else { compile_asm_string( file_name, diff --git a/riscv_executor/src/lib.rs b/riscv_executor/src/lib.rs index 75284c6edc..4e3fad10ab 100644 --- a/riscv_executor/src/lib.rs +++ b/riscv_executor/src/lib.rs @@ -8,7 +8,10 @@ //! TODO: perform determinism verification for each instruction independently //! from execution. -use std::collections::HashMap; +use std::{ + collections::HashMap, + io::{self, Write}, +}; use ast::{ asm_analysis::{AnalysisASMFile, CallableSymbol, FunctionStatement, LabelStatement, Machine}, @@ -273,18 +276,16 @@ fn preprocess_main_function( (statements, label_map, batch_to_line_map) } -struct Executor<'a, 'b> { +struct Executor<'a, 'b, F: FieldElement> { proc: TraceBuilder<'a, 'b>, mem: MemoryBuilder, label_map: HashMap<&'a str, Elem>, + inputs: &'b [F], + stdout: io::Stdout, } -impl<'a, 'b> Executor<'a, 'b> { - fn exec_instruction( - &mut self, - name: &str, - args: &[Expression], - ) -> Vec { +impl<'a, 'b, F: FieldElement> Executor<'a, 'b, F> { + fn exec_instruction(&mut self, name: &str, args: &[Expression]) -> Vec { let args = args .iter() .map(|expr| self.eval_expression(expr)[0]) @@ -577,7 +578,7 @@ impl<'a, 'b> Executor<'a, 'b> { } } - fn eval_expression(&mut self, expression: &Expression) -> Vec { + fn eval_expression(&mut self, expression: &Expression) -> Vec { match expression { Expression::Reference(r) => { // an identifier looks like this: @@ -649,13 +650,37 @@ impl<'a, 'b> Executor<'a, 'b> { vec![result.into()] } Expression::FunctionCall(f) => self.exec_instruction(&f.id, &f.arguments), - Expression::FreeInput(_) => todo!(), + Expression::FreeInput(expr) => 'input: { + if let Expression::Tuple(t) = &**expr { + if let Expression::String(name) = &t[0] { + let val = self.eval_expression(&t[1])[0]; + break 'input vec![match name.as_str() { + "input" => { + let idx = val.u() as usize; + to_u32(&self.inputs[idx]).unwrap().into() + } + "print_char" => { + self.stdout.write(&[val.u() as u8]).unwrap(); + // what is print_char supposed to return? + Elem::zero() + } + unk => { + panic!("unknown IO command: {unk}"); + } + }]; + } + }; + panic!("does not matched IO pattern") + } Expression::MatchExpression(_, _) => todo!(), } } } -pub fn execute_ast(program: &AnalysisASMFile) -> ExecutionTrace { +pub fn execute_ast<'a, T: FieldElement>( + program: &'a AnalysisASMFile, + inputs: &[T], +) -> ExecutionTrace<'a> { let main_machine = get_main_machine(program); let (statements, label_map, batch_to_line_map) = preprocess_main_function(main_machine); @@ -663,6 +688,8 @@ pub fn execute_ast(program: &AnalysisASMFile) -> ExecutionTr proc: TraceBuilder::new(main_machine, &batch_to_line_map), mem: MemoryBuilder::new(), label_map, + inputs, + stdout: io::stdout(), }; let mut curr_pc = 0u32; @@ -700,7 +727,7 @@ pub fn execute_ast(program: &AnalysisASMFile) -> ExecutionTr /// /// The FieldElement is just used by the parser, before everything is converted /// to i64, so it is probably not very important. -pub fn execute(asm_source: &str) { +pub fn execute(asm_source: &str, inputs: &[F]) { log::info!("Parsing..."); let parsed = parser::parse_asm::(None, asm_source).unwrap(); log::info!("Resolving imports..."); @@ -709,7 +736,7 @@ pub fn execute(asm_source: &str) { let analyzed = analysis::analyze(resolved, &mut ast::DiffMonitor::default()).unwrap(); log::info!("Executing..."); - execute_ast(&analyzed); + execute_ast(&analyzed, inputs); } fn to_u32(val: &F) -> Option { @@ -744,6 +771,6 @@ mod test { println!("Validating UTF-8..."); let asm_str = std::str::from_utf8(&asm).unwrap(); - execute::(asm_str); + execute::(asm_str, &[]); } }