diff --git a/Cargo.toml b/Cargo.toml index 553d52f..3a2fefd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ members = [ "range", "static_data", "util", - "verifier" + "verifier", ] [workspace.dependencies] diff --git a/basic/Cargo.toml b/basic/Cargo.toml index 9c340ed..121e166 100644 --- a/basic/Cargo.toml +++ b/basic/Cargo.toml @@ -20,6 +20,7 @@ rand = "0.8.5" rand_pcg = "0.3.1" rand_seeder = "0.2.3" tracing = "0.1.37" +reedline-repl-rs = "1.1.1" valida-alu-u32 = { path = "../alu_u32" } valida-assembler = { path = "../assembler" } valida-bus = { path = "../bus" } @@ -51,6 +52,7 @@ p3-merkle-tree = { workspace = true } p3-poseidon = { workspace = true } p3-symmetric = { workspace = true } + [dev-dependencies] ciborium = "0.2.2" p3-challenger = { workspace = true } diff --git a/basic/src/bin/valida.rs b/basic/src/bin/valida.rs index 1232ed8..0687f3b 100644 --- a/basic/src/bin/valida.rs +++ b/basic/src/bin/valida.rs @@ -9,7 +9,8 @@ use p3_baby_bear::BabyBear; use p3_fri::{FriConfig, TwoAdicFriPcs, TwoAdicFriPcsConfig}; use valida_cpu::MachineWithCpuChip; -use valida_machine::{Machine, MachineProof, StdinAdviceProvider}; +use valida_machine::{Machine, MachineProof, ProgramROM, StdinAdviceProvider}; +use valida_memory::MachineWithMemoryChip; use valida_elf::{load_executable_file, Program}; use valida_program::MachineWithProgramChip; @@ -30,9 +31,12 @@ use valida_machine::StarkConfigImpl; use valida_machine::__internal::p3_commit::ExtensionMmcs; use valida_output::MachineWithOutputChip; +use reedline_repl_rs::clap::{Arg, ArgMatches, Command}; +use reedline_repl_rs::{Repl, Result}; + #[derive(Parser)] struct Args { - /// Command option either "run" or "prove" or "verify" + /// Command option either "run" or "prove" or "verify" or "interactive" #[arg(name = "Action Option")] action: String, @@ -49,9 +53,238 @@ struct Args { stack_height: u32, } +struct Context<'a> { + machine_: BasicMachine, + args_: &'a Args, + breakpoints_: Vec, + stopped_: bool, + last_fp_: u32, + recorded_current_fp_: u32, + last_fp_size_: u32, +} + +impl Context<'_> { + fn new(args: &Args) -> Context { + let mut context = Context { + machine_: BasicMachine::::default(), + args_: args.clone(), + breakpoints_: Vec::new(), + stopped_: false, + last_fp_: args.stack_height, + recorded_current_fp_: args.stack_height, + last_fp_size_: 0, + }; + + let rom = match ProgramROM::from_file(&args.program) { + Ok(contents) => contents, + Err(e) => panic!("Failure to load file: {}. {}", &args.program, e), + }; + + context.machine_.program_mut().set_program_rom(&rom); + context.machine_.cpu_mut().fp = args.stack_height; + context.machine_.cpu_mut().save_register_state(); + + context + } + + fn step(&mut self) -> (bool, u32) { + // do not execute if already stopped + if self.stopped_ { + return (true, 0); + } + let state = self.machine_.step(&mut StdinAdviceProvider); + let pc = self.machine_.cpu().pc; + let fp = self.machine_.cpu().fp; + + // check if fp is changed + if fp != self.recorded_current_fp_ { + self.last_fp_size_ = self.recorded_current_fp_ - fp; + self.last_fp_ = self.recorded_current_fp_; + } else if fp == self.last_fp_ { + self.last_fp_size_ = 0; + } + self.recorded_current_fp_ = fp; + + (state, pc) + } +} + +fn init_context(args: ArgMatches, context: &mut Context) -> Result> { + Ok(Some(String::from("created machine"))) +} + +fn status(args: ArgMatches, context: &mut Context) -> Result> { + // construct machine status + let mut status = String::new(); + status.push_str("FP: "); + status.push_str(&context.machine_.cpu().fp.to_string()); + status.push_str(", PC: "); + status.push_str(&context.machine_.cpu().pc.to_string()); + status.push_str(if context.stopped_ { + ", Stopped" + } else { + ", Running" + }); + Ok(Some(status)) +} + +fn show_frame(args: ArgMatches, context: &mut Context) -> Result> { + let size: i32 = match args.contains_id("size") { + true => args + .get_one::("size") + .unwrap() + .parse::() + .unwrap(), + false => 6, + }; + let mut frame = String::new(); + let fp = context.machine_.cpu().fp as i32; + frame.push_str(format!("FP: {:x}\n", fp).as_str()); + for i in 0..size { + let offset = i * -4; + let read_addr = (fp + offset) as u32; + let string_val = context.machine_.mem().examine(read_addr); + let frameslot_addr = format!("{}(fp)", offset); + let frameslot = format!("{:>7}", frameslot_addr); + let frame_str = format!("\n{} : {}", frameslot, string_val); + frame += &frame_str; + } + + Ok(Some(frame)) +} + +fn last_frame(_: ArgMatches, context: &mut Context) -> Result> { + let mut frame = String::new(); + + let lfp = context.last_fp_; + let fp = context.machine_.cpu().fp as i32; + let last_size = context.last_fp_size_ as i32; + frame += &format!("Last FP : 0x{:x}, Frame size: {}\n", lfp, last_size).as_str(); + frame += &format!("Current FP: 0x{:x}\n", fp).as_str(); + + // print last frame + for i in (-5..(last_size / 4) + 1).rev() { + let offset = (i * 4) as i32; + let read_addr = (fp + offset) as u32; + let string_val = context.machine_.mem().examine(read_addr); + let frameslot_addr = format!("{}(fp)", offset); + let frameslot = format!("0x{:<7x} | {:>7}", read_addr, frameslot_addr); + let frame_str = format!("\n{} : {}", frameslot, string_val); + frame += &frame_str; + } + Ok(Some(frame)) +} + +fn list_instrs(_: ArgMatches, context: &mut Context) -> Result> { + let pc = context.machine_.cpu().pc; + + let program_rom = &context.machine_.program().program_rom; + let total_size = program_rom.0.len(); + + let mut formatted = String::new(); + for i in 0..5 { + let cur_pc = pc + i; + if cur_pc >= total_size as u32 { + break; + } + let instruction = program_rom.get_instruction(cur_pc); + formatted.push_str(format!("{:?} : {:?}\n", cur_pc, instruction).as_str()); + } + Ok(Some(formatted)) +} + +fn set_bp(args: ArgMatches, context: &mut Context) -> Result> { + let pc = args + .get_one::("pc") + .unwrap() + .parse::() + .unwrap(); + context.breakpoints_.push(pc); + let message = format!("Breakpoint set at pc: {}", pc); + Ok(Some(message)) +} + +fn run_until(args: ArgMatches, context: &mut Context) -> Result> { + let mut message = String::new(); + loop { + let (stop, pc) = context.step(); + if stop { + message.push_str("Execution stopped"); + break; + } + if context.breakpoints_.contains(&pc) { + let bp_index = context.breakpoints_.iter().position(|&x| x == pc).unwrap(); + message = format!("Execution stopped at breakpoint {}, PC: {}", bp_index, pc); + break; + } + } + Ok(Some(message)) +} + +fn step(args: ArgMatches, context: &mut Context) -> Result> { + let (stop, _) = context.step(); + if stop { + context.stopped_ = true; + Ok(Some(String::from("Execution stopped"))) + } else { + Ok(None) + } +} + +fn repl_run(args: &Args) { + // instantiate repl + let mut repl = Repl::new(Context::new(args)) + .with_name("REPL") + .with_version("v0.1.0") + .with_description("Valida VM REPL") + .with_banner("Start by using keywords") + .with_command(Command::new("x").about("read machine state"), status) + .with_command( + Command::new("s") + .arg(Arg::new("num_steps").required(false)) + .about("step assembly"), + step, + ) + .with_command( + Command::new("f") + .arg(Arg::new("size").required(false)) + .about("show frame"), + show_frame, + ) + .with_command( + Command::new("lf").about("show last frame and current frame"), + last_frame, + ) + .with_command( + Command::new("b") + .arg(Arg::new("pc").required(false)) + .about("set break point at"), + set_bp, + ) + .with_command( + Command::new("r").about("run until stop or breakpoint"), + run_until, + ) + .with_command( + Command::new("l").about("list instruction at current PC"), + list_instrs, + ) + .with_command( + Command::new("reset").about("reset machine state!"), + init_context, + ); + + let _ = repl.run(); +} + fn main() { let args = Args::parse(); + if args.action == "interactive" { + repl_run(&args); + return; + } + let mut machine = BasicMachine::::default(); let Program { code, data } = load_executable_file( fs::read(&args.program) diff --git a/basic/src/lib.rs b/basic/src/lib.rs index 6c07d93..e7522e0 100644 --- a/basic/src/lib.rs +++ b/basic/src/lib.rs @@ -1,4 +1,3 @@ -#![no_std] #![allow(unused)] extern crate alloc; diff --git a/basic/tests/test_static_data.rs b/basic/tests/test_static_data.rs index dada93d..4c53816 100644 --- a/basic/tests/test_static_data.rs +++ b/basic/tests/test_static_data.rs @@ -2,18 +2,14 @@ extern crate core; use p3_baby_bear::BabyBear; use p3_fri::{TwoAdicFriPcs, TwoAdicFriPcsConfig}; -use valida_alu_u32::add::{Add32Instruction, MachineWithAdd32Chip}; use valida_basic::BasicMachine; use valida_cpu::{ BneInstruction, Imm32Instruction, Load32Instruction, MachineWithCpuChip, StopInstruction, }; use valida_machine::{ - FixedAdviceProvider, Instruction, InstructionWord, Machine, MachineProof, Operands, ProgramROM, - Word, + FixedAdviceProvider, Instruction, InstructionWord, Machine, Operands, ProgramROM, Word, }; -use valida_memory::MachineWithMemoryChip; -use valida_opcodes::BYTES_PER_INSTR; use valida_program::MachineWithProgramChip; use valida_static_data::MachineWithStaticDataChip; diff --git a/derive/src/lib.rs b/derive/src/lib.rs index ba8dc38..c148795 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,5 +1,3 @@ -#![no_std] - extern crate alloc; use alloc::format; @@ -76,6 +74,7 @@ fn impl_machine(machine: &syn::DeriveInput) -> TokenStream { let name = &machine.ident; let run = run_method(machine, &instructions, &val, &static_data_chip); + let step = step_method(machine, &instructions, &val); let prove = prove_method(&chips); let verify = verify_method(&chips); @@ -83,6 +82,7 @@ fn impl_machine(machine: &syn::DeriveInput) -> TokenStream { let stream = quote! { impl #impl_generics Machine<#val> for #name #ty_generics #where_clause { + #step #run #prove #verify @@ -173,23 +173,25 @@ fn run_method( #init_static_data loop { - // Fetch - let pc = self.cpu().pc; - let instruction = program.get_instruction(pc); - let opcode = instruction.opcode; - let ops = instruction.operands; - - // Execute - match opcode { - #opcode_arms - _ => panic!("Unrecognized opcode: {}", opcode), - }; - self.read_word(pc as usize); - - // A STOP instruction signals the end of the program - if opcode == >::OPCODE { - break; - } + // Fetch + let pc = self.cpu().pc; + let instruction = program.get_instruction(pc); + let opcode = instruction.opcode; + let ops = instruction.operands; + + // Execute + std::println!("trace: pc = {:?}, instruction = {:?}, ops = {:?}", pc, instruction, ops); + match opcode { + #opcode_arms + _ => panic!("Unrecognized opcode: {}", opcode), + }; + self.read_word(pc as usize); + + //let stop = self.step(&program, &mut advice); + // A STOP instruction signals the end of the program + if opcode == >::OPCODE { + break; + } } // Record padded STOP instructions @@ -201,6 +203,42 @@ fn run_method( } } +fn step_method(machine: &syn::DeriveInput, instructions: &[&Field], val: &Ident) -> TokenStream2 { + // TODO: combine this with run + let name = &machine.ident; + let (_, ty_generics, _) = machine.generics.split_for_impl(); + + let opcode_arms = instructions + .iter() + .map(|inst| { + let ty = &inst.ty; + quote! { + // TODO: Self instead of #name #ty_generics? + <#ty as Instruction<#name #ty_generics, #val>>::OPCODE => + #ty::execute_with_advice::(self, ops, advice), + } + }) + .collect::(); + + quote! { + fn step(&mut self, advice: &mut Adv) -> bool { + let pc = self.cpu().pc; + let instruction = self.program.program_rom.get_instruction(pc); + let opcode = instruction.opcode; + let ops = instruction.operands; + + std::println!("step: pc = {:?}, instruction = {:?}", pc, instruction); + match opcode { + #opcode_arms + _ => panic!("Unrecognized opcode: {}", opcode), + }; + self.read_word(pc as usize); + + opcode == >::OPCODE + } + } +} + fn prove_method(chips: &[&Field]) -> TokenStream2 { let num_chips = chips.len(); let chip_list = chips diff --git a/machine/src/machine.rs b/machine/src/machine.rs index ecd84b7..70d6412 100644 --- a/machine/src/machine.rs +++ b/machine/src/machine.rs @@ -9,6 +9,10 @@ pub trait Machine: Sync { where Adv: AdviceProvider; + fn step(&mut self, advice: &mut Adv) -> bool + where + Adv: AdviceProvider; + fn prove(&self, config: &SC) -> MachineProof where SC: StarkConfig; diff --git a/machine/src/program.rs b/machine/src/program.rs index 5f77e58..666b217 100644 --- a/machine/src/program.rs +++ b/machine/src/program.rs @@ -2,6 +2,15 @@ use crate::{AdviceProvider, Machine, Word, INSTRUCTION_ELEMENTS, OPERAND_ELEMENT use byteorder::{ByteOrder, LittleEndian}; use p3_field::Field; +/* +use derive_more::Display; + +#[derive(Display)] +enum Opcode { + // TODO +} +*/ + pub trait Instruction, F: Field> { const OPCODE: u32; diff --git a/memory/src/lib.rs b/memory/src/lib.rs index 61ac16f..0c9725a 100644 --- a/memory/src/lib.rs +++ b/memory/src/lib.rs @@ -2,8 +2,10 @@ extern crate alloc; +use crate::alloc::string::ToString; use crate::columns::{MemoryCols, MEM_COL_MAP, NUM_MEM_COLS}; use alloc::collections::BTreeMap; +use alloc::string::String; use alloc::vec; use alloc::vec::Vec; use core::mem::transmute; @@ -13,7 +15,7 @@ use p3_matrix::dense::RowMajorMatrix; use p3_maybe_rayon::prelude::*; use valida_bus::MachineWithMemBus; use valida_machine::StarkConfig; -use valida_machine::{BusArgument, Chip, Interaction, Machine, Word}; +use valida_machine::{Chip, Interaction, Machine, Word}; use valida_util::batch_multiplicative_inverse_allowing_zero; pub mod columns; @@ -64,6 +66,19 @@ impl MemoryChip { } } + /// Return "---------------------" if uninitialized, else, return the cell's value. + /// Used in debugger mode + pub fn examine(&self, address: u32) -> String { + let value = self.cells.get(&address.into()); + match value { + Some(raw_value) => { + let u32val: u32 = (*raw_value).into(); + u32val.to_string() + } + None => String::from("--------"), + } + } + pub fn read( &mut self, clk: u32, @@ -161,22 +176,22 @@ where fn local_sends(&self) -> Vec> { return vec![]; // TODO - let sends = Interaction { - fields: vec![VirtualPairCol::single_main(MEM_COL_MAP.diff)], - count: VirtualPairCol::one(), - argument_index: BusArgument::Local(0), - }; - vec![sends] + // let sends = Interaction { + // fields: vec![VirtualPairCol::single_main(MEM_COL_MAP.diff)], + // count: VirtualPairCol::one(), + // argument_index: BusArgument::Local(0), + // }; + // vec![sends] } fn local_receives(&self) -> Vec> { return vec![]; // TODO - let receives = Interaction { - fields: vec![VirtualPairCol::single_main(MEM_COL_MAP.counter)], - count: VirtualPairCol::single_main(MEM_COL_MAP.counter_mult), - argument_index: BusArgument::Local(0), - }; - vec![receives] + // let receives = Interaction { + // fields: vec![VirtualPairCol::single_main(MEM_COL_MAP.counter)], + // count: VirtualPairCol::single_main(MEM_COL_MAP.counter_mult), + // argument_index: BusArgument::Local(0), + // }; + // vec![receives] } fn global_receives(&self, machine: &M) -> Vec> { diff --git a/program/src/lib.rs b/program/src/lib.rs index 145171b..367fe10 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -18,7 +18,7 @@ pub mod stark; #[derive(Default)] pub struct ProgramChip { - program_rom: ProgramROM, + pub program_rom: ProgramROM, pub counts: Vec, }