Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(ci): Add non determinism check and fixes #6847

Merged
merged 7 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
4 changes: 4 additions & 0 deletions compiler/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ pub struct CompileOptions {
/// A lower value keeps the original program if it was smaller, even if it has more jumps.
#[arg(long, hide = true, allow_hyphen_values = true)]
pub max_bytecode_increase_percent: Option<i32>,

/// Used internally to test for non-determinism in the compiler.
#[clap(long, hide = true)]
pub check_non_determinism: bool,
}

pub fn parse_expression_width(input: &str) -> Result<ExpressionWidth, std::io::Error> {
Expand Down
8 changes: 4 additions & 4 deletions compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub(crate) struct GeneratedBrillig<F> {
pub(crate) locations: BTreeMap<OpcodeLocation, CallStack>,
pub(crate) error_types: BTreeMap<ErrorSelector, ErrorType>,
pub(crate) name: String,
pub(crate) procedure_locations: HashMap<ProcedureId, (OpcodeLocation, OpcodeLocation)>,
pub(crate) procedure_locations: BTreeMap<ProcedureId, (OpcodeLocation, OpcodeLocation)>,
}

#[derive(Default, Debug, Clone)]
Expand Down Expand Up @@ -61,13 +61,13 @@ pub(crate) struct BrilligArtifact<F> {
/// This is created as artifacts are linked together and allows us to determine
/// which opcodes originate from reusable procedures.s
/// The range is inclusive for both start and end opcode locations.
pub(crate) procedure_locations: HashMap<ProcedureId, (OpcodeLocation, OpcodeLocation)>,
pub(crate) procedure_locations: BTreeMap<ProcedureId, (OpcodeLocation, OpcodeLocation)>,
}

/// A pointer to a location in the opcode.
pub(crate) type OpcodeLocation = usize;

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
pub(crate) enum LabelType {
/// Labels for the entry point bytecode
Entrypoint,
Expand Down Expand Up @@ -97,7 +97,7 @@ impl std::fmt::Display for LabelType {
///
/// It is assumed that an entity will keep a map
/// of labels to Opcode locations.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
pub(crate) struct Label {
pub(crate) label_type: LabelType,
pub(crate) section: Option<usize>,
Expand Down
6 changes: 4 additions & 2 deletions compiler/noirc_evaluator/src/ssa/opt/unrolling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
//!
//! When unrolling ACIR code, we remove reference count instructions because they are
//! only used by Brillig bytecode.
use std::collections::BTreeSet;

use acvm::{acir::AcirField, FieldElement};
use im::HashSet;

Expand Down Expand Up @@ -117,7 +119,7 @@
back_edge_start: BasicBlockId,

/// All the blocks contained within the loop, including `header` and `back_edge_start`.
pub(super) blocks: HashSet<BasicBlockId>,
pub(super) blocks: BTreeSet<BasicBlockId>,
}

pub(super) struct Loops {
Expand Down Expand Up @@ -238,7 +240,7 @@
back_edge_start: BasicBlockId,
cfg: &ControlFlowGraph,
) -> Self {
let mut blocks = HashSet::default();
let mut blocks = BTreeSet::default();
blocks.insert(header);

let mut insert = |block, stack: &mut Vec<BasicBlockId>| {
Expand Down Expand Up @@ -1008,7 +1010,7 @@
use super::{is_new_size_ok, BoilerplateStats, Loops};

/// Tries to unroll all loops in each SSA function once, calling the `Function` directly,
/// bypassing the iterative loop done by the SSA which does further optimisations.

Check warning on line 1013 in compiler/noirc_evaluator/src/ssa/opt/unrolling.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (optimisations)
///
/// If any loop cannot be unrolled, it is left as-is or in a partially unrolled state.
fn try_unroll_loops(mut ssa: Ssa) -> (Ssa, Vec<RuntimeError>) {
Expand Down
13 changes: 6 additions & 7 deletions compiler/noirc_frontend/src/ast/statement.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::fmt::Display;
use std::sync::atomic::{AtomicU32, Ordering};

use acvm::acir::AcirField;
use acvm::FieldElement;
Expand Down Expand Up @@ -841,10 +840,9 @@ impl ForRange {
block: Expression,
for_loop_span: Span,
) -> Statement {
/// Counter used to generate unique names when desugaring
/// code in the parser requires the creation of fresh variables.
/// The parser is stateless so this is a static global instead.
static UNIQUE_NAME_COUNTER: AtomicU32 = AtomicU32::new(0);
// Counter used to generate unique names when desugaring
// code in the parser requires the creation of fresh variables.
let mut unique_name_counter: u32 = 0;

match self {
ForRange::Range(..) => {
Expand All @@ -855,7 +853,8 @@ impl ForRange {
let start_range = ExpressionKind::integer(FieldElement::zero());
let start_range = Expression::new(start_range, array_span);

let next_unique_id = UNIQUE_NAME_COUNTER.fetch_add(1, Ordering::Relaxed);
let next_unique_id = unique_name_counter;
unique_name_counter += 1;
let array_name = format!("$i{next_unique_id}");
let array_span = array.span;
let array_ident = Ident::new(array_name, array_span);
Expand Down Expand Up @@ -888,7 +887,7 @@ impl ForRange {
}));
let end_range = Expression::new(end_range, array_span);

let next_unique_id = UNIQUE_NAME_COUNTER.fetch_add(1, Ordering::Relaxed);
let next_unique_id = unique_name_counter;
let index_name = format!("$i{next_unique_id}");
let fresh_identifier = Ident::new(index_name.clone(), array_span);

Expand Down
3 changes: 3 additions & 0 deletions tooling/nargo_cli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@
// We need to isolate test cases in the same group, otherwise they overwrite each other's artifacts.
// On CI we use `cargo nextest`, which runs tests in different processes; for this we use a file lock.
// Locally we might be using `cargo test`, which run tests in the same process; in this case the file lock
// wouldn't work, becuase the process itself has the lock, and it looks like it can have N instances without

Check warning on line 183 in tooling/nargo_cli/build.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (becuase)
// any problems; for this reason we also use a `Mutex`.
let mutex_name = format! {"TEST_MUTEX_{}", test_name.to_uppercase()};
write!(
Expand Down Expand Up @@ -215,6 +215,9 @@
// Set the maximum increase so that part of the optimization is exercised (it might fail).
nargo.arg("--max-bytecode-increase-percent");
nargo.arg("50");

// Check whether the test case is non-deterministic
nargo.arg("--check-non-determinism");
}}

{test_content}
Expand Down
14 changes: 14 additions & 0 deletions tooling/nargo_cli/src/cli/compile_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
fn watch_workspace(workspace: &Workspace, compile_options: &CompileOptions) -> notify::Result<()> {
let (tx, rx) = std::sync::mpsc::channel();

// No specific tickrate, max debounce time 1 seconds

Check warning on line 77 in tooling/nargo_cli/src/cli/compile_cmd.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (tickrate)
let mut debouncer = new_debouncer(Duration::from_secs(1), None, tx)?;

// Add a path to be watched. All files and directories at that path and
Expand All @@ -82,7 +82,7 @@
debouncer.watcher().watch(&workspace.root_dir, RecursiveMode::Recursive)?;

let mut screen = std::io::stdout();
write!(screen, "{}", termion::cursor::Save).unwrap();

Check warning on line 85 in tooling/nargo_cli/src/cli/compile_cmd.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (termion)
screen.flush().unwrap();
let _ = compile_workspace_full(workspace, compile_options);
for res in rx {
Expand All @@ -103,7 +103,7 @@
});

if noir_files_modified {
write!(screen, "{}{}", termion::cursor::Restore, termion::clear::AfterCursor).unwrap();

Check warning on line 106 in tooling/nargo_cli/src/cli/compile_cmd.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (termion)

Check warning on line 106 in tooling/nargo_cli/src/cli/compile_cmd.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (termion)
screen.flush().unwrap();
let _ = compile_workspace_full(workspace, compile_options);
}
Expand Down Expand Up @@ -216,6 +216,20 @@
cached_program,
)?;

if compile_options.check_non_determinism {
let (program_two, _) = compile_program(
file_manager,
parsed_files,
workspace,
package,
compile_options,
load_cached_program(package),
)?;
if fxhash::hash64(&program) != fxhash::hash64(&program_two) {
panic!("Non deterministic result compiling {}", package.name);
}
}

// Choose the target width for the final, backend specific transformation.
let target_width =
get_target_width(package.expression_width, compile_options.expression_width);
Expand Down
Loading