diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index 66062c055..7734bfbbf 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -5,6 +5,7 @@ use std::fs; use std::io::BufWriter; use std::io::Write; use std::path::Path; +use std::path::PathBuf; use std::time::Instant; use ast::analyzed::Analyzed; @@ -35,9 +36,9 @@ pub fn compile_pil_or_asm( output_dir: &Path, force_overwrite: bool, prove_with: Option, -) { +) -> PathBuf { if file_name.ends_with(".asm") { - compile_asm(file_name, inputs, output_dir, force_overwrite, prove_with); + compile_asm(file_name, inputs, output_dir, force_overwrite, prove_with) } else { compile_pil( Path::new(file_name), @@ -45,6 +46,7 @@ pub fn compile_pil_or_asm( Some(inputs_to_query_callback(inputs)), prove_with, ); + PathBuf::from(file_name) } } @@ -103,7 +105,7 @@ pub fn compile_asm( output_dir: &Path, force_overwrite: bool, prove_with: Option, -) { +) -> PathBuf { let contents = fs::read_to_string(file_name).unwrap(); compile_asm_string( file_name, @@ -112,7 +114,7 @@ pub fn compile_asm( output_dir, force_overwrite, prove_with, - ); + ) } /// Compiles the contents of a .asm file, outputs the PIL on stdout and tries to generate @@ -126,7 +128,7 @@ pub fn compile_asm_string( output_dir: &Path, force_overwrite: bool, prove_with: Option, -) -> String { +) -> PathBuf { let parsed = parser::parse_asm(Some(file_name), contents).unwrap_or_else(|err| { eprintln!("Error parsing .asm file:"); err.output_to_stderr(); @@ -139,13 +141,13 @@ pub fn compile_asm_string( Path::new(file_name).file_stem().unwrap().to_str().unwrap() ); - let pil_file_path = output_dir.join(&pil_file_name); + let pil_file_path = output_dir.join(pil_file_name); if pil_file_path.exists() && !force_overwrite { eprint!( "Target file {} already exists. Not overwriting.", pil_file_path.to_str().unwrap() ); - return pil_file_name; + return pil_file_path; } fs::write(pil_file_path.clone(), format!("{pil}")).unwrap(); @@ -157,7 +159,7 @@ pub fn compile_asm_string( prove_with, ); - pil_file_name + pil_file_path } fn compile( diff --git a/compiler/src/verify.rs b/compiler/src/verify.rs index daf5c1932..bdbd4e3d4 100644 --- a/compiler/src/verify.rs +++ b/compiler/src/verify.rs @@ -5,7 +5,8 @@ use crate::compile_asm_string; pub fn verify_asm_string(file_name: &str, contents: &str, inputs: Vec) { let temp_dir = mktemp::Temp::new_dir().unwrap(); - let pil_file_name = compile_asm_string(file_name, contents, inputs, &temp_dir, true, None); + let pil_file_path = compile_asm_string(file_name, contents, inputs, &temp_dir, true, None); + let pil_file_name = pil_file_path.file_name().unwrap().to_string_lossy(); verify(&pil_file_name, &temp_dir); } diff --git a/powdr_cli/src/main.rs b/powdr_cli/src/main.rs index 4e2f8cb40..1b2d82677 100644 --- a/powdr_cli/src/main.rs +++ b/powdr_cli/src/main.rs @@ -9,7 +9,7 @@ use env_logger::{Builder, Target}; use log::LevelFilter; use number::{Bn254Field, FieldElement, GoldilocksField}; use riscv::{compile_riscv_asm, compile_rust}; -use std::{borrow::Cow, fs, io::Write, path::Path}; +use std::{borrow::Cow, collections::HashSet, fs, io::Write, path::Path}; use strum::{Display, EnumString, EnumVariantNames}; use std::io::{BufWriter, Cursor}; @@ -74,6 +74,17 @@ enum Commands { #[arg(short, long)] #[arg(value_parser = clap_enum_variants!(Backend))] prove_with: Option, + + /// Generate a CSV file containing the fixed and witness column values. Useful for debugging purposes. + #[arg(long)] + #[arg(default_value_t = false)] + export_csv: bool, + + /// How to render field elements in the csv file + #[arg(long)] + #[arg(default_value_t = CsvRenderMode::Hex)] + #[arg(value_parser = clap_enum_variants!(CsvRenderMode))] + csv_mode: CsvRenderMode, }, /// Compiles (no-std) rust code to riscv assembly, then to powdr assembly /// and finally to PIL and generates fixed and witness columns. @@ -164,11 +175,11 @@ enum Commands { backend: Backend, /// File containing previously generated proof for aggregation. - #[arg(short, long)] + #[arg(long)] proof: Option, /// File containing previously generated setup parameters. - #[arg(short, long)] + #[arg(long)] params: Option, }, @@ -210,29 +221,6 @@ enum Commands { #[arg(value_parser = clap_enum_variants!(FieldArgument))] field: FieldArgument, }, - - /// Exports witness and fixed columns to a csv file. - ExportCsv { - /// Input PIL file - file: String, - - /// Directory to find the committed and fixed values - #[arg(short, long)] - #[arg(default_value_t = String::from("."))] - dir: String, - - /// The field to use - #[arg(long)] - #[arg(default_value_t = FieldArgument::Gl)] - #[arg(value_parser = clap_enum_variants!(FieldArgument))] - field: FieldArgument, - - /// How to render field elements in the csv file - #[arg(long)] - #[arg(default_value_t = CsvRenderMode::Hex)] - #[arg(value_parser = clap_enum_variants!(CsvRenderMode))] - render_mode: CsvRenderMode, - }, } fn split_inputs(inputs: &str) -> Vec { @@ -310,13 +298,26 @@ fn main() { inputs, force, prove_with, - } => call_with_field!(compile_pil_or_asm::( - &file, - split_inputs(&inputs), - Path::new(&output_directory), - force, - prove_with - )), + export_csv, + csv_mode, + } => { + let pil_filename = call_with_field!(compile_pil_or_asm::( + &file, + split_inputs(&inputs), + Path::new(&output_directory), + force, + prove_with + )); + + if export_csv { + let pil = Path::new(&pil_filename); + let dir = Path::new(&output_directory); + let csv_path = dir.join("columns.csv"); + call_with_field!(export_columns_to_csv::( + pil, dir, &csv_path, csv_mode + )); + } + } Commands::Prove { file, dir, @@ -344,24 +345,6 @@ fn main() { log::info!("Wrote {proof_filename}."); } } - - Commands::ExportCsv { - file, - dir, - field, - render_mode, - } => { - let pil = Path::new(&file); - let dir = Path::new(&dir); - let csv_path = dir.join("columns.csv"); - - call_with_field!(export_columns_to_csv::( - pil, - dir, - &csv_path, - render_mode - )); - } Commands::Setup { size, dir, @@ -413,11 +396,26 @@ fn export_columns_to_csv( let mut csv_file = fs::File::create(csv_path).unwrap(); let mut csv_writer = BufWriter::new(&mut csv_file); - // Write the column headers + // Remove prefixes (e.g. "Assembly.") if column names are still unique after let headers = columns .iter() - .map(|(header, _)| header.clone()) + .map(|(header, _)| header.to_owned()) .collect::>(); + let headers_without_prefix = headers + .iter() + .map(|header| { + let suffix_start = header.rfind('.').map(|i| i + 1).unwrap_or(0); + header[suffix_start..].to_owned() + }) + .collect::>(); + + let unique_elements = headers_without_prefix.iter().collect::>(); + let headers = if unique_elements.len() == headers.len() { + headers_without_prefix + } else { + headers + }; + writeln!(csv_writer, "Row,{}", headers.join(",")).unwrap(); // Write the column values