Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 78 additions & 78 deletions Cargo.lock

Large diffs are not rendered by default.

96 changes: 95 additions & 1 deletion compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::{
graph::CrateId,
hir::{
Context,
comptime::ComptimeError,
comptime::{ComptimeError, InterpreterError},
def_collector::{
dc_crate::{
CollectedItems, CompilationError, ImplMap, UnresolvedEnum, UnresolvedFunctions,
Expand Down Expand Up @@ -2409,3 +2409,97 @@ impl Visitor for RemoveGenericsAppearingInTypeVisitor<'_> {
}
}
}

/// The possible errors of interpreting given code
/// into a monomorphized AST expression.
#[cfg(feature = "test_utils")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ElaboratorError {
Parse(Vec<ParserError>),
Compile(Vec<CompilationError>),
Interpret(InterpreterError),
HIRConvert(InterpreterError),
}

/// Interpret source code using the elaborator, without
/// parsing and compiling it with nargo, converting
/// the result into a monomorphized AST expression.
#[cfg(feature = "test_utils")]
pub fn interpret(src: &str) -> Result<crate::monomorphization::ast::Expression, ElaboratorError> {
use crate::hir::{
Context, ParsedFiles,
def_collector::{dc_crate::DefCollector, dc_mod::collect_defs},
def_map::{CrateDefMap, ModuleData},
};
use crate::monomorphization::{Monomorphizer, debug_types::DebugTypeTracker};
use crate::parse_program;
use fm::{FileId, FileManager};
use std::path::PathBuf;

let file = FileId::default();

let location = Location::new(Default::default(), file);
let root_module = ModuleData::new(
None,
location,
Vec::new(),
Vec::new(),
false, // is contract
false, // is struct
);

let file_manager = FileManager::new(&PathBuf::new());
let parsed_files = ParsedFiles::new();
let mut context = Context::new(file_manager, parsed_files);
context.def_interner.populate_dummy_operator_traits();

let krate = context.crate_graph.add_crate_root(FileId::dummy());

let (module, errors) = parse_program(src, file);
// Skip parser warnings
let errors: Vec<_> = errors.iter().filter(|e| !e.is_warning()).cloned().collect();
if !errors.is_empty() {
return Err(ElaboratorError::Parse(errors));
}

let ast = module.into_sorted();

let def_map = CrateDefMap::new(krate, root_module);
let root_module_id = def_map.root();
let mut collector = DefCollector::new(def_map);

collect_defs(&mut collector, ast, FileId::dummy(), root_module_id, krate, &mut context);
context.def_maps.insert(krate, collector.def_map);

let main = context.get_main_function(&krate).expect("Expected 'main' function");

let mut elaborator = Elaborator::elaborate_and_return_self(
&mut context,
krate,
collector.items,
ElaboratorOptions::test_default(),
);

// Skip the elaborator's compilation warnings
let errors: Vec<_> = elaborator.errors.iter().filter(|&e| e.is_error()).cloned().collect();
if !errors.is_empty() {
return Err(ElaboratorError::Compile(errors));
}

let mut interpreter = elaborator.setup_interpreter();

// The most straightforward way to convert the interpreter result into
// an acceptable monomorphized AST expression seems to be converting it
// into HIR first and then processing it with the monomorphizer
let expr_id =
match interpreter.call_function(main, Vec::new(), Default::default(), Location::dummy()) {
Err(e) => return Err(ElaboratorError::Interpret(e)),
Ok(value) => match value.into_hir_expression(elaborator.interner, Location::dummy()) {
Err(e) => return Err(ElaboratorError::HIRConvert(e)),
Ok(expr_id) => expr_id,
},
};

let mut monomorphizer = Monomorphizer::new(elaborator.interner, DebugTypeTracker::default());
Ok(monomorphizer.expr(expr_id).expect("monomorphization error while converting interpreter execution result, should not be possible"))
}
7 changes: 5 additions & 2 deletions compiler/noirc_frontend/src/monomorphization/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,10 @@ pub fn monomorphize_debug(
}

impl<'interner> Monomorphizer<'interner> {
fn new(interner: &'interner mut NodeInterner, debug_type_tracker: DebugTypeTracker) -> Self {
pub(crate) fn new(
interner: &'interner mut NodeInterner,
debug_type_tracker: DebugTypeTracker,
) -> Self {
Monomorphizer {
functions: HashMap::default(),
locals: HashMap::default(),
Expand Down Expand Up @@ -481,7 +484,7 @@ impl<'interner> Monomorphizer<'interner> {
Ok(())
}

fn expr(
pub(crate) fn expr(
&mut self,
expr: node_interner::ExprId,
) -> Result<ast::Expression, MonomorphizationError> {
Expand Down
2 changes: 1 addition & 1 deletion tooling/ast_fuzzer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ nargo.workspace = true
noirc_abi.workspace = true
noirc_driver.workspace = true
noirc_evaluator.workspace = true
noirc_frontend.workspace = true
noirc_frontend = { workspace = true, features = ["test_utils"] }
noir_greybox_fuzzer.workspace = true

[dev-dependencies]
Expand Down
11 changes: 9 additions & 2 deletions tooling/ast_fuzzer/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,15 @@ doc = false
bench = false

[[bin]]
name = "comptime_vs_brillig"
path = "fuzz_targets/comptime_vs_brillig.rs"
name = "comptime_vs_brillig_nargo"
path = "fuzz_targets/comptime_vs_brillig_nargo.rs"
test = false
doc = false
bench = false

[[bin]]
name = "comptime_vs_brillig_direct"
path = "fuzz_targets/comptime_vs_brillig_direct.rs"
test = false
doc = false
bench = false
Expand Down
12 changes: 0 additions & 12 deletions tooling/ast_fuzzer/fuzz/fuzz_targets/comptime_vs_brillig.rs

This file was deleted.

12 changes: 12 additions & 0 deletions tooling/ast_fuzzer/fuzz/fuzz_targets/comptime_vs_brillig_direct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! ```text
//! cargo +nightly fuzz run comptime_vs_brillig_direct
//! ```
#![no_main]

use libfuzzer_sys::arbitrary::Unstructured;
use libfuzzer_sys::fuzz_target;
use noir_ast_fuzzer_fuzz::targets::comptime_vs_brillig_direct;

fuzz_target!(|data: &[u8]| {
comptime_vs_brillig_direct::fuzz(&mut Unstructured::new(data)).unwrap();
});
12 changes: 12 additions & 0 deletions tooling/ast_fuzzer/fuzz/fuzz_targets/comptime_vs_brillig_nargo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! ```text
//! cargo +nightly fuzz run comptime_vs_brillig_nargo
//! ```
#![no_main]

use libfuzzer_sys::arbitrary::Unstructured;
use libfuzzer_sys::fuzz_target;
use noir_ast_fuzzer_fuzz::targets::comptime_vs_brillig_nargo;

fuzz_target!(|data: &[u8]| {
comptime_vs_brillig_nargo::fuzz(&mut Unstructured::new(data)).unwrap();
});
77 changes: 77 additions & 0 deletions tooling/ast_fuzzer/fuzz/src/targets/comptime_vs_brillig_direct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//! Compare the execution of random ASTs between the comptime execution
//! (after converting the AST to Noir and running it through the comptime
//! interpreter) vs when everything is forced to be Brillig.
//! We choose Brillig here because it mostly matches comptime feature set
//! (e.g. `loop`, `while`, `break` and `continue` are possible)
//! This variant accesses the interpreter directly instead of going
//! through nargo, which speeds up execution but also currently
//! has some issues (inability to use prints among others).
use crate::{compare_results_comptime, create_ssa_or_die, default_ssa_options};
use arbitrary::Unstructured;
use color_eyre::eyre;
use noir_ast_fuzzer::Config;
use noir_ast_fuzzer::compare::CompareComptime;
use noir_ast_fuzzer::compare::CompareOptions;
use noir_ast_fuzzer::rewrite::change_all_functions_into_unconstrained;

pub fn fuzz(u: &mut Unstructured) -> eyre::Result<()> {
let config = Config {
// Avoid using large integers in for loops that the frontend would reject.
avoid_large_int_literals: true,
// Also avoid negative integers, because the frontend rejects them for loops.
avoid_negative_int_literals: true,
// Avoid break/continue
avoid_loop_control: true,
// Has to only use expressions valid in comptime
comptime_friendly: true,
// Force brillig, to generate loops that the interpreter can do but ACIR cannot.
force_brillig: true,
// Avoid overflows, divisions by zero and constraints for now, as we currently
// don't catch errors issued by the elaborator
avoid_overflow: true,
avoid_err_by_zero: true,
avoid_constrain: true,
// At the moment prints aren't recognized by elaborator
avoid_print: true,
// Use lower limits because of the interpreter, to avoid stack overflow
max_loop_size: 5,
max_recursive_calls: 5,
..Default::default()
};

let inputs = CompareComptime::arb(u, config, |program| {
let options = CompareOptions::default();
let ssa = create_ssa_or_die(
change_all_functions_into_unconstrained(program),
&options.onto(default_ssa_options()),
Some("brillig"),
);
Ok((ssa, options))
})?;

let result = inputs.exec_direct(|program| {
let options = CompareOptions::default();
let ssa = create_ssa_or_die(
program,
&options.onto(default_ssa_options()),
Some("comptime_result_wrapper"),
);
Ok((ssa, options))
})?;

compare_results_comptime(&inputs, &result)
}

#[cfg(test)]
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]
fn fuzz_with_arbtest() {
crate::targets::tests::fuzz_with_arbtest(super::fuzz, 500);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
//! interpreter) vs when everything is forced to be Brillig.
//! We choose Brillig here because it mostly matches comptime feature set
//! (e.g. `loop`, `while`, `break` and `continue` are possible)
//! This variant lets nargo parse the resulting source code which is slow
//! but at the moment is more feature complete than using the interpreter
//! directly.
use crate::{compare_results_comptime, create_ssa_or_die, default_ssa_options};
use arbitrary::Unstructured;
use color_eyre::eyre;
Expand Down Expand Up @@ -31,7 +34,7 @@ pub fn fuzz(u: &mut Unstructured) -> eyre::Result<()> {
..Default::default()
};

let inputs = CompareComptime::arb(u, config, |_, program| {
let inputs = CompareComptime::arb(u, config, |program| {
let options = CompareOptions::default();
let ssa = create_ssa_or_die(
change_all_functions_into_unconstrained(program),
Expand All @@ -52,7 +55,7 @@ mod tests {
/// ```ignore
/// NOIR_AST_FUZZER_SEED=0x6819c61400001000 \
/// NOIR_AST_FUZZER_SHOW_AST=1 \
/// cargo test -p noir_ast_fuzzer_fuzz comptime_vs_brillig
/// cargo test -p noir_ast_fuzzer_fuzz comptime_vs_brillig_nargo
/// ```
#[test]
fn fuzz_with_arbtest() {
Expand Down
3 changes: 2 additions & 1 deletion tooling/ast_fuzzer/fuzz/src/targets/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod acir_vs_brillig;
pub mod comptime_vs_brillig;
pub mod comptime_vs_brillig_direct;
pub mod comptime_vs_brillig_nargo;
pub mod min_vs_full;
pub mod orig_vs_morph;
pub mod pass_vs_prev;
Expand Down
2 changes: 1 addition & 1 deletion tooling/ast_fuzzer/src/compare/compiled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl From<(SsaProgramArtifact, CompareOptions)> for CompareArtifact {
type SsaErrorTypes = BTreeMap<acir::circuit::ErrorSelector, ErrorType>;

/// The execution result is the value returned from the circuit and any output from `println`.
type ExecResult = (Result<WitnessStack<FieldElement>, NargoError<FieldElement>>, String);
pub(crate) type ExecResult = (Result<WitnessStack<FieldElement>, NargoError<FieldElement>>, String);

#[derive(Debug)]
pub struct NargoErrorWithTypes(NargoError<FieldElement>, SsaErrorTypes);
Expand Down
Loading
Loading