diff --git a/tooling/ast_fuzzer/README.md b/tooling/ast_fuzzer/README.md index 26ef2669f7f..9d0b827882a 100644 --- a/tooling/ast_fuzzer/README.md +++ b/tooling/ast_fuzzer/README.md @@ -38,7 +38,7 @@ cargo +nightly fuzz run acir_vs_brillig fuzz/artifacts/acir_vs_brillig/crash-927 Note that `cargo fuzz` requires `nightly` build, which can be either turned on with the `cargo +nightly` flag, or by running `rustup default nightly`. Also note that `cargo fuzz run` automatically creates a `--release` build, there is no need for an explicit flag to be passed. -The `NOIR_AST_FUZZER_SHOW_AST` env var can be used to print the AST before compilation, in case the compiler crashes on the generated program. Otherwise if the execution fails, the output will include the AST, the inputs, and the ACIR/Brillig opcodes. +If the execution fails, the output will include the AST, the inputs, and the ACIR/Brillig opcodes. ## `arbtest` @@ -48,7 +48,11 @@ To get quick feedback about whether there are any easy-to-discover bugs, we can cargo test -p noir_ast_fuzzer_fuzz arbtest ``` -Unlike `cargo fuzz`, these don't "ramp up" the complexity of the code, but go full tilt from the beginning, and only run for a limited amount of time (e.g. 10 seconds). Upon failure they print a hexadecimal `seed`, which can be used with the `NOIR_AST_FUZZER_SEED` env var to replicate the error. +Unlike `cargo fuzz`, these don't "ramp up" the complexity of the code, but go full tilt from the beginning, and only run for a limited amount of time (e.g. 10 seconds). + +Upon failure they print a hexadecimal `seed`, which can be used with the `NOIR_AST_FUZZER_SEED` env var to replicate the error. + +If the compiler crashes during the generation of the SSA artifacts, the problematic program will be printed during attempts to reproduce the issue using a seed. The printing of all inputs can be turned on by setting `RUST_LOG=debug`. ## Minimizing Noir diff --git a/tooling/ast_fuzzer/fuzz/src/lib.rs b/tooling/ast_fuzzer/fuzz/src/lib.rs index 8738c0c75f5..e7fdb96f7e0 100644 --- a/tooling/ast_fuzzer/fuzz/src/lib.rs +++ b/tooling/ast_fuzzer/fuzz/src/lib.rs @@ -20,11 +20,7 @@ fn bool_from_env(key: &str) -> bool { std::env::var(key).map(|s| s == "1" || s == "true").unwrap_or_default() } -// TODO(#7876): Allow specifying options on the command line. -fn show_ast() -> bool { - bool_from_env("NOIR_AST_FUZZER_SHOW_AST") -} - +/// Show all SSA passes during compilation. fn show_ssa() -> bool { bool_from_env("NOIR_AST_FUZZER_SHOW_SSA") } @@ -46,40 +42,51 @@ pub fn default_ssa_options() -> SsaEvaluatorOptions { } } -/// Compile a [Program] into SSA or panic. -/// -/// Prints the AST if `NOIR_AST_FUZZER_SHOW_AST` is set. -pub fn create_ssa_or_die( +/// Compile a monomorphized [Program] into circuit or panic. +pub fn compile_into_circuit_or_die( program: Program, options: &SsaEvaluatorOptions, msg: Option<&str>, ) -> SsaProgramArtifact { - create_ssa_with_passes_or_die(program, options, &primary_passes(options), msg) + compile_into_circuit_with_ssa_passes_or_die(program, options, &primary_passes(options), msg) } -/// Compile a [Program] into SSA using the given primary and secondary passes, or panic. +/// Compile a monomorphized [Program] into circuit using the given SSA passes or panic. /// -/// Prints the AST if `NOIR_AST_FUZZER_SHOW_AST` is set. -pub fn create_ssa_with_passes_or_die( +/// If there is a seed in the environment, then it prints the AST when an error is encountered. +pub fn compile_into_circuit_with_ssa_passes_or_die( program: Program, options: &SsaEvaluatorOptions, primary: &[SsaPass], msg: Option<&str>, ) -> SsaProgramArtifact { - // Unfortunately we can't use `std::panic::catch_unwind` - // and `std::panic::resume_unwind` to catch any panic - // and print the AST, then resume the panic, because - // `Program` has a `RefCell` in it, which is not unwind safe. - if show_ast() { - eprintln!("---\n{}\n---", DisplayAstAsNoir(&program)); - } - - ssa::create_program_with_passes(program, options, primary, None).unwrap_or_else(|e| { - panic!( - "failed to compile program: {}{e}", - msg.map(|s| format!("{s}: ")).unwrap_or_default() + // If we are using a seed, we are probably trying to reproduce some failure; + // in that case let's clone the program for printing if it panicked here, + // otherwise try to keep things faster by not cloning. + let for_print = std::env::var("NOIR_AST_FUZZER_SEED").is_ok().then(|| program.clone()); + + // We expect the programs generated should compile, but sometimes SSA passes panic, or return an error. + // Turn everything into a panic, catch it, print the AST, then resume panicking. + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + ssa::create_program_with_passes(program.clone(), options, primary, None).unwrap_or_else( + |e| { + panic!( + "failed to compile program: {}{e}", + msg.map(|s| format!("{s}: ")).unwrap_or_default() + ) + }, ) - }) + })); + + match result { + Ok(ssa) => ssa, + Err(payload) => { + if let Some(program) = for_print { + eprintln!("--- Failing AST:\n{}\n---", DisplayAstAsNoir(&program)); + } + std::panic::resume_unwind(payload); + } + } } /// Compare the execution result and print the inputs if the result is a failure. @@ -113,11 +120,28 @@ where .serialize(&inputs.input_map, &inputs.abi) .unwrap_or_else(|e| format!("failed to serialize inputs: {e}")) ); + + // Display a Program without the Brillig opcodes, which are unreadable. + fn display_program(artifact: &SsaProgramArtifact) { + for (func_index, function) in artifact.program.functions.iter().enumerate() { + eprintln!("func {func_index}"); + eprintln!("{function}"); + } + for (func_index, function) in + artifact.program.unconstrained_functions.iter().enumerate() + { + eprintln!("unconstrained func {func_index}"); + eprintln!("opcode count: {}", function.bytecode.len()); + } + } + eprintln!("---\nOptions 1:\n{:?}", inputs.ssa1.options); - eprintln!("---\nProgram 1:\n{}", inputs.ssa1.artifact.program); + eprintln!("---\nProgram 1:"); + display_program(&inputs.ssa1.artifact); eprintln!("---\nOptions 2:\n{:?}", inputs.ssa2.options); - eprintln!("---\nProgram 2:\n{}", inputs.ssa2.artifact.program); + eprintln!("---\nProgram 2:"); + display_program(&inputs.ssa1.artifact); // Returning it as-is, so we can see the error message at the bottom as well. Err(report) diff --git a/tooling/ast_fuzzer/fuzz/src/targets/acir_vs_brillig.rs b/tooling/ast_fuzzer/fuzz/src/targets/acir_vs_brillig.rs index 81f9d6f8c8b..3720e6419ad 100644 --- a/tooling/ast_fuzzer/fuzz/src/targets/acir_vs_brillig.rs +++ b/tooling/ast_fuzzer/fuzz/src/targets/acir_vs_brillig.rs @@ -1,7 +1,7 @@ //! Compare the execution of random ASTs between the normal execution //! vs when everything is forced to be Brillig. use crate::targets::default_config; -use crate::{compare_results_compiled, create_ssa_or_die, default_ssa_options}; +use crate::{compare_results_compiled, compile_into_circuit_or_die, default_ssa_options}; use arbitrary::Arbitrary; use arbitrary::Unstructured; use color_eyre::eyre; @@ -16,13 +16,16 @@ pub fn fuzz(u: &mut Unstructured) -> eyre::Result<()> { config, |u, program| { let options = CompareOptions::arbitrary(u)?; - let ssa = - create_ssa_or_die(program, &options.onto(default_ssa_options()), Some("acir")); + let ssa = compile_into_circuit_or_die( + program, + &options.onto(default_ssa_options()), + Some("acir"), + ); Ok((ssa, options)) }, |u, program| { let options = CompareOptions::arbitrary(u)?; - let ssa = create_ssa_or_die( + let ssa = compile_into_circuit_or_die( change_all_functions_into_unconstrained(program), &options.onto(default_ssa_options()), Some("brillig"), @@ -41,7 +44,6 @@ mod tests { /// ```ignore /// NOIR_AST_FUZZER_SEED=0x6819c61400001000 \ - /// NOIR_AST_FUZZER_SHOW_AST=1 \ /// cargo test -p noir_ast_fuzzer_fuzz acir_vs_brillig /// ``` #[test] diff --git a/tooling/ast_fuzzer/fuzz/src/targets/comptime_vs_brillig_direct.rs b/tooling/ast_fuzzer/fuzz/src/targets/comptime_vs_brillig_direct.rs index 5724c0dcb6a..3bea94728ae 100644 --- a/tooling/ast_fuzzer/fuzz/src/targets/comptime_vs_brillig_direct.rs +++ b/tooling/ast_fuzzer/fuzz/src/targets/comptime_vs_brillig_direct.rs @@ -7,7 +7,7 @@ //! through nargo, which speeds up execution but also currently //! has some issues (inability to use prints among others). use crate::targets::default_config; -use crate::{compare_results_comptime, create_ssa_or_die, default_ssa_options}; +use crate::{compare_results_comptime, compile_into_circuit_or_die, default_ssa_options}; use arbitrary::Unstructured; use color_eyre::eyre; use noir_ast_fuzzer::Config; @@ -36,7 +36,7 @@ pub fn fuzz(u: &mut Unstructured) -> eyre::Result<()> { let inputs = CompareComptime::arb(u, config, |program| { let options = CompareOptions::default(); - let ssa = create_ssa_or_die( + let ssa = compile_into_circuit_or_die( change_all_functions_into_unconstrained(program), &options.onto(default_ssa_options()), Some("brillig"), @@ -46,7 +46,7 @@ pub fn fuzz(u: &mut Unstructured) -> eyre::Result<()> { let result = inputs.exec_direct(|program| { let options = CompareOptions::default(); - let ssa = create_ssa_or_die( + let ssa = compile_into_circuit_or_die( program, &options.onto(default_ssa_options()), Some("comptime_result_wrapper"), @@ -62,7 +62,6 @@ mod tests { /// ```ignore /// NOIR_AST_FUZZER_SEED=0x6819c61400001000 \ - /// NOIR_AST_FUZZER_SHOW_AST=1 \ /// cargo test -p noir_ast_fuzzer_fuzz comptime_vs_brillig_direct /// ``` #[test] diff --git a/tooling/ast_fuzzer/fuzz/src/targets/comptime_vs_brillig_nargo.rs b/tooling/ast_fuzzer/fuzz/src/targets/comptime_vs_brillig_nargo.rs index 33866aa666e..c46143b39bf 100644 --- a/tooling/ast_fuzzer/fuzz/src/targets/comptime_vs_brillig_nargo.rs +++ b/tooling/ast_fuzzer/fuzz/src/targets/comptime_vs_brillig_nargo.rs @@ -7,7 +7,7 @@ //! but at the moment is more feature complete than using the interpreter //! directly. use crate::targets::default_config; -use crate::{compare_results_comptime, create_ssa_or_die, default_ssa_options}; +use crate::{compare_results_comptime, compile_into_circuit_or_die, default_ssa_options}; use arbitrary::Unstructured; use color_eyre::eyre; use noir_ast_fuzzer::Config; @@ -33,7 +33,7 @@ pub fn fuzz(u: &mut Unstructured) -> eyre::Result<()> { let inputs = CompareComptime::arb(u, config, |program| { let options = CompareOptions::default(); - let ssa = create_ssa_or_die( + let ssa = compile_into_circuit_or_die( change_all_functions_into_unconstrained(program), &options.onto(default_ssa_options()), Some("brillig"), @@ -51,7 +51,6 @@ mod tests { /// ```ignore /// NOIR_AST_FUZZER_SEED=0x6819c61400001000 \ - /// NOIR_AST_FUZZER_SHOW_AST=1 \ /// cargo test -p noir_ast_fuzzer_fuzz comptime_vs_brillig_nargo /// ``` #[test] diff --git a/tooling/ast_fuzzer/fuzz/src/targets/min_vs_full.rs b/tooling/ast_fuzzer/fuzz/src/targets/min_vs_full.rs index a75118af402..3fb2e16f726 100644 --- a/tooling/ast_fuzzer/fuzz/src/targets/min_vs_full.rs +++ b/tooling/ast_fuzzer/fuzz/src/targets/min_vs_full.rs @@ -3,7 +3,8 @@ //! and the fully optimized version. use crate::targets::default_config; use crate::{ - compare_results_compiled, create_ssa_or_die, create_ssa_with_passes_or_die, default_ssa_options, + compare_results_compiled, compile_into_circuit_or_die, + compile_into_circuit_with_ssa_passes_or_die, default_ssa_options, }; use arbitrary::{Arbitrary, Unstructured}; use color_eyre::eyre; @@ -22,7 +23,7 @@ pub fn fuzz(u: &mut Unstructured) -> eyre::Result<()> { // We want to do the minimum possible amount of SSA passes. Brillig can get away with fewer than ACIR, // because ACIR needs unrolling of loops for example, so we treat everything as Brillig. let options = CompareOptions::default(); - let ssa = create_ssa_with_passes_or_die( + let ssa = compile_into_circuit_with_ssa_passes_or_die( change_all_functions_into_unconstrained(program), &options.onto(default_ssa_options()), &passes, @@ -32,8 +33,11 @@ pub fn fuzz(u: &mut Unstructured) -> eyre::Result<()> { }, |u, program| { let options = CompareOptions::arbitrary(u)?; - let ssa = - create_ssa_or_die(program, &options.onto(default_ssa_options()), Some("final")); + let ssa = compile_into_circuit_or_die( + program, + &options.onto(default_ssa_options()), + Some("final"), + ); Ok((ssa, options)) }, )?; @@ -51,7 +55,6 @@ pub fn fuzz(u: &mut Unstructured) -> eyre::Result<()> { mod tests { /// ```ignore /// NOIR_AST_FUZZER_SEED=0x6819c61400001000 \ - /// NOIR_AST_FUZZER_SHOW_AST=1 \ /// cargo test -p noir_ast_fuzzer_fuzz min_vs_full /// ``` #[test] diff --git a/tooling/ast_fuzzer/fuzz/src/targets/mod.rs b/tooling/ast_fuzzer/fuzz/src/targets/mod.rs index b6f8b520984..dcd8fb0890e 100644 --- a/tooling/ast_fuzzer/fuzz/src/targets/mod.rs +++ b/tooling/ast_fuzzer/fuzz/src/targets/mod.rs @@ -69,7 +69,6 @@ mod tests { /// Run it with for example: /// ```ignore /// NOIR_AST_FUZZER_SEED=0x6819c61400001000 \ - /// NOIR_AST_FUZZER_SHOW_AST=1 \ /// cargo test -p noir_ast_fuzzer_fuzz acir_vs_brillig /// ``` /// diff --git a/tooling/ast_fuzzer/fuzz/src/targets/orig_vs_morph.rs b/tooling/ast_fuzzer/fuzz/src/targets/orig_vs_morph.rs index fc86ff1a62b..0ba20d1908b 100644 --- a/tooling/ast_fuzzer/fuzz/src/targets/orig_vs_morph.rs +++ b/tooling/ast_fuzzer/fuzz/src/targets/orig_vs_morph.rs @@ -4,7 +4,7 @@ use std::cell::{Cell, RefCell}; use crate::targets::default_config; -use crate::{compare_results_compiled, create_ssa_or_die, default_ssa_options}; +use crate::{compare_results_compiled, compile_into_circuit_or_die, default_ssa_options}; use arbitrary::{Arbitrary, Unstructured}; use color_eyre::eyre; use noir_ast_fuzzer::compare::{CompareMorph, CompareOptions}; @@ -28,7 +28,9 @@ pub fn fuzz(u: &mut Unstructured) -> eyre::Result<()> { rewrite_program(u, &mut program, &rules, max_rewrites); Ok((program, options)) }, - |program, options| create_ssa_or_die(program, &options.onto(default_ssa_options()), None), + |program, options| { + compile_into_circuit_or_die(program, &options.onto(default_ssa_options()), None) + }, )?; let result = inputs.exec()?; @@ -820,7 +822,6 @@ mod helpers { mod tests { /// ```ignore /// NOIR_AST_FUZZER_SEED=0xb2fb5f0b00100000 \ - /// NOIR_AST_FUZZER_SHOW_AST=1 \ /// cargo test -p noir_ast_fuzzer_fuzz orig_vs_morph /// ``` #[test] diff --git a/tooling/ast_fuzzer/src/compare/compiled.rs b/tooling/ast_fuzzer/src/compare/compiled.rs index 5743a284378..64c6ce4831a 100644 --- a/tooling/ast_fuzzer/src/compare/compiled.rs +++ b/tooling/ast_fuzzer/src/compare/compiled.rs @@ -11,7 +11,7 @@ use noirc_abi::{Abi, InputMap, input_parser::InputValue}; use noirc_evaluator::{ErrorType, ssa::SsaProgramArtifact}; use noirc_frontend::monomorphization::ast::Program; -use crate::{Config, arb_inputs, arb_program, program_abi}; +use crate::{Config, arb_inputs, arb_program, compare::logging, program_abi}; use super::{Comparable, CompareOptions, CompareResult, FailedOutput, HasPrograms, PassedOutput}; @@ -240,13 +240,6 @@ impl

CompareCompiled

{ let blackbox_solver = Bn254BlackBoxSolver(false); let initial_witness = self.abi.encode(&self.input_map, None).wrap_err("abi::encode")?; - log::debug!( - "ABI input:\n{}", - noirc_abi::input_parser::Format::Toml - .serialize(&self.input_map, &self.abi) - .unwrap_or_else(|e| format!("failed to serialize inputs: {e}")) - ); - let do_exec = |program| { let mut print = Vec::new(); @@ -298,12 +291,17 @@ impl CompareCompiled { ) -> arbitrary::Result { let program = arb_program(u, c)?; let abi = program_abi(&program); + logging::log_program(&program, ""); let ssa1 = CompareArtifact::from(f(u, program.clone())?); let ssa2 = CompareArtifact::from(g(u, program.clone())?); + logging::log_options(&ssa1.options, "1st"); + logging::log_options(&ssa2.options, "2nd"); + let input_program = &ssa1.artifact.program; let input_map = arb_inputs(u, input_program, &abi)?; + logging::log_abi_inputs(&abi, &input_map); Ok(Self { program, abi, input_map, ssa1, ssa2 }) } @@ -327,7 +325,12 @@ impl CompareMorph { g: impl Fn(Program, &CompareOptions) -> SsaProgramArtifact, ) -> arbitrary::Result { let program1 = arb_program(u, c)?; + logging::log_program(&program1, "orig"); + let (program2, options) = f(u, program1.clone())?; + logging::log_program(&program2, "morph"); + logging::log_options(&options, ""); + let abi = program_abi(&program1); let ssa1 = g(program1.clone(), &options); @@ -335,6 +338,7 @@ impl CompareMorph { let input_program = &ssa1.program; let input_map = arb_inputs(u, input_program, &abi)?; + logging::log_abi_inputs(&abi, &input_map); Ok(Self { program: (program1, program2), diff --git a/tooling/ast_fuzzer/src/compare/comptime.rs b/tooling/ast_fuzzer/src/compare/comptime.rs index f2cd9281621..fc56d76d061 100644 --- a/tooling/ast_fuzzer/src/compare/comptime.rs +++ b/tooling/ast_fuzzer/src/compare/comptime.rs @@ -27,6 +27,7 @@ use noirc_frontend::{ use super::{CompareArtifact, CompareCompiledResult, CompareOptions, HasPrograms}; use crate::compare::compiled::ExecResult; +use crate::compare::logging; use crate::{ Config, DisplayAstAsNoirComptime, arb_program_comptime, program_abi, program_wrap_expression, }; @@ -128,8 +129,6 @@ impl CompareComptime { String::from_utf8(output).expect("not UTF-8") }; - // Log source code before interpreting - log::debug!("comptime src:\n{}", self.source); let comptime_expr = match interpret(source.as_str(), output.clone()) { Ok(expr) => expr, Err(e) => { @@ -182,7 +181,6 @@ impl CompareComptime { Self::exec_bytecode(&self.ssa.artifact.program, initial_witness.clone()); // Try to compile the 1st (comptime) version from string. - log::debug!("comptime src:\n{}", self.source); let (program1, output1) = match prepare_and_compile_snippet( self.source.clone(), self.force_brillig, @@ -226,10 +224,13 @@ impl CompareComptime { let force_brillig = c.force_brillig; let program = arb_program_comptime(u, c)?; let abi = program_abi(&program); + logging::log_program(&program, ""); let ssa = CompareArtifact::from(f(program.clone())?); + logging::log_options(&ssa.options, "compiled"); let source = format!("{}", DisplayAstAsNoirComptime(&program)); + logging::log_comptime(&source, ""); Ok(Self { program, abi, source, ssa, force_brillig }) } diff --git a/tooling/ast_fuzzer/src/compare/interpreted.rs b/tooling/ast_fuzzer/src/compare/interpreted.rs index c04d49cbc50..ea52cde1044 100644 --- a/tooling/ast_fuzzer/src/compare/interpreted.rs +++ b/tooling/ast_fuzzer/src/compare/interpreted.rs @@ -16,7 +16,7 @@ use noirc_evaluator::ssa::{ use noirc_frontend::{Shared, monomorphization::ast::Program}; use regex::Regex; -use crate::{Config, arb_program, input::arb_inputs_from_ssa, program_abi}; +use crate::{Config, arb_program, compare::logging, input::arb_inputs_from_ssa, program_abi}; use super::{Comparable, CompareOptions, CompareResult, FailedOutput, PassedOutput}; @@ -70,43 +70,24 @@ impl CompareInterpreted { ) -> arbitrary::Result { let program = arb_program(u, c)?; let abi = program_abi(&program); + logging::log_program(&program, ""); + let (options, ssa1, ssa2) = f(u, program.clone())?; + logging::log_options(&options, ""); + logging::log_ssa(&ssa1.ssa, &format!("after step {} - {}", ssa1.step, ssa1.msg)); + logging::log_ssa(&ssa2.ssa, &format!("after step {} - {}", ssa2.step, ssa2.msg)); + let input_map = arb_inputs_from_ssa(u, &ssa1.ssa, &abi)?; + logging::log_abi_inputs(&abi, &input_map); + let ssa_args = input_values_to_ssa(&abi, &input_map); + logging::log_ssa_inputs(&ssa_args); Ok(Self { program, abi, input_map, ssa_args, options, ssa1, ssa2 }) } pub fn exec(&self) -> eyre::Result { - // Debug prints up front in case the interpreter panics. Turn them on with `RUST_LOG=debug cargo test ...` - log::debug!("Program: \n{}\n", crate::DisplayAstAsNoir(&self.program)); - log::debug!( - "ABI inputs: \n{}\n", - noirc_abi::input_parser::Format::Toml.serialize(&self.input_map, &self.abi).unwrap() - ); - log::debug!( - "SSA inputs:\n{}\n", - self.ssa_args - .iter() - .enumerate() - .map(|(i, v)| format!("{i}: {v}")) - .collect::>() - .join("\n") - ); - log::debug!( - "SSA after step {} ({}):\n{}\n", - self.ssa1.step, - self.ssa1.msg, - self.ssa1.ssa.print_without_locations() - ); - log::debug!( - "SSA after step {} ({}):\n{}\n", - self.ssa2.step, - self.ssa2.msg, - self.ssa2.ssa.print_without_locations() - ); - // Interpret an SSA with a fresh copy of the input values. let interpret = |ssa: &Ssa| { let mut output = Vec::new(); diff --git a/tooling/ast_fuzzer/src/compare/mod.rs b/tooling/ast_fuzzer/src/compare/mod.rs index 5d0da0b063e..9465b25e1e0 100644 --- a/tooling/ast_fuzzer/src/compare/mod.rs +++ b/tooling/ast_fuzzer/src/compare/mod.rs @@ -166,3 +166,56 @@ where } } } + +/// We can turn on the logging of artifacts by setting the `RUST_LOG=debug` env var. +/// +/// This can help reproducing failures. The functions in this module can help cut back some repetition. +/// Log things as soon as they are available in case the next step fails. +mod logging { + use noirc_abi::{Abi, InputMap}; + use noirc_evaluator::ssa::{interpreter::value::Value, ssa_gen::Ssa}; + use noirc_frontend::monomorphization::ast::Program; + + use crate::{DisplayAstAsNoir, compare::CompareOptions}; + + fn format_msg(msg: &str) -> String { + if msg.is_empty() { String::new() } else { format!(" ({msg})") } + } + + pub(super) fn log_program(program: &Program, msg: &str) { + log::debug!("AST{}:\n{}\n", format_msg(msg), DisplayAstAsNoir(program)); + } + + pub(super) fn log_ssa(ssa: &Ssa, msg: &str) { + log::debug!("SSA{}:\n{}\n", format_msg(msg), ssa.print_without_locations()); + } + + pub(super) fn log_comptime(src: &str, msg: &str) { + log::debug!("comptime source{}:\n{}\n", format_msg(msg), src); + } + + pub(super) fn log_options(options: &CompareOptions, msg: &str) { + log::debug!("Options{}:\n{:?}\n", format_msg(msg), options); + } + + pub(super) fn log_abi_inputs(abi: &Abi, input_map: &InputMap) { + log::debug!( + "ABI inputs:\n{}\n", + noirc_abi::input_parser::Format::Toml + .serialize(input_map, abi) + .unwrap_or_else(|e| format!("failed to serialize inputs: {e}")) + ); + } + + pub(super) fn log_ssa_inputs(ssa_args: &[Value]) { + log::debug!( + "SSA inputs:\n{}\n", + ssa_args + .iter() + .enumerate() + .map(|(i, v)| format!("{i}: {v}")) + .collect::>() + .join("\n") + ); + } +}