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
2 changes: 1 addition & 1 deletion compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
Ok(ssa)
}

pub(crate) fn validate_ssa(ssa: &Ssa) {
pub fn validate_ssa(ssa: &Ssa) {
for function in ssa.functions.values() {
validate_function(function);
}
Expand Down Expand Up @@ -530,7 +530,7 @@
/// br loop_entry(v0)
/// loop_entry(i: Field):
/// v2 = lt i v1
/// brif v2, then: loop_body, else: loop_end

Check warning on line 533 in compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (brif)
/// loop_body():
/// v3 = ... codegen body ...
/// v4 = add 1, i
Expand Down Expand Up @@ -682,7 +682,7 @@
///
/// ```text
/// v0 = ... codegen cond ...
/// brif v0, then: then_block, else: else_block

Check warning on line 685 in compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (brif)
/// then_block():
/// v1 = ... codegen a ...
/// br end_if(v1)
Expand All @@ -697,7 +697,7 @@
///
/// ```text
/// v0 = ... codegen cond ...
/// brif v0, then: then_block, else: end_if

Check warning on line 700 in compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (brif)
/// then_block:
/// v1 = ... codegen a ...
/// br end_if()
Expand Down
21 changes: 12 additions & 9 deletions tooling/ssa_executor/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ use noirc_evaluator::{
errors::{InternalError, RuntimeError},
ssa::{
ArtifactsAndWarnings, SsaBuilder, SsaCircuitArtifact, SsaEvaluatorOptions, SsaLogging,
SsaProgramArtifact, ir::instruction::ErrorType, optimize_ssa_builder_into_acir,
primary_passes, secondary_passes, ssa_gen::Ssa,
SsaProgramArtifact,
ir::instruction::ErrorType,
optimize_ssa_builder_into_acir, primary_passes, secondary_passes,
ssa_gen::{Ssa, validate_ssa},
},
};
use std::collections::{BTreeMap, BTreeSet, HashMap};
Expand All @@ -27,13 +29,6 @@ pub fn optimize_ssa_into_acir(
ssa: Ssa,
options: SsaEvaluatorOptions,
) -> Result<ArtifactsAndWarnings, RuntimeError> {
let builder = SsaBuilder {
ssa,
ssa_logging: options.ssa_logging.clone(),
print_codegen_timings: options.print_codegen_timings,
passed: HashMap::new(),
skip_passes: vec![],
};
let previous_hook = std::panic::take_hook();
let panic_message = std::sync::Arc::new(std::sync::Mutex::new(String::new()));
let hook_message = panic_message.clone();
Expand All @@ -55,6 +50,14 @@ pub fn optimize_ssa_into_acir(
}
}));
let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
validate_ssa(&ssa);
let builder = SsaBuilder {
ssa,
ssa_logging: options.ssa_logging.clone(),
print_codegen_timings: options.print_codegen_timings,
passed: HashMap::new(),
skip_passes: vec![],
};
optimize_ssa_builder_into_acir(
builder,
&options,
Expand Down
5 changes: 3 additions & 2 deletions tooling/ssa_fuzzer/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ use std::collections::BTreeMap;
/// Optimizes the given FunctionBuilder into ACIR
/// its taken from noirc_evaluator::ssa::optimize_all, but modified to accept FunctionBuilder
/// and to catch panics... It cannot be caught with just catch_unwind.
fn optimize_into_acir(
/// This function will also run Ssa validation to make sure that the hand written Ssa has been well formed.
fn optimize_into_acir_and_validate(
builder: FunctionBuilder,
options: SsaEvaluatorOptions,
) -> Result<ArtifactsAndWarnings, RuntimeError> {
Expand All @@ -24,7 +25,7 @@ pub fn compile_from_builder(
builder: FunctionBuilder,
options: &CompileOptions,
) -> Result<CompiledProgram, CompileError> {
let artifacts = optimize_into_acir(builder, evaluator_options(options))?;
let artifacts = optimize_into_acir_and_validate(builder, evaluator_options(options))?;
compile_from_artifacts(
artifacts,
Abi { parameters: vec![], return_type: None, error_types: BTreeMap::new() },
Expand Down
159 changes: 157 additions & 2 deletions tooling/ssa_fuzzer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@

#[cfg(test)]
mod tests {
use crate::builder::{FuzzerBuilder, InstructionWithTwoArgs};
use crate::builder::{FuzzerBuilder, FuzzerBuilderError, InstructionWithTwoArgs};
use crate::runner::{CompareResults, run_and_compare};
use crate::typed_value::{TypedValue, ValueType};
use acvm::FieldElement;
use acvm::acir::native_types::{Witness, WitnessMap};
use noirc_driver::CompileOptions;
use noirc_driver::{CompileOptions, CompiledProgram};
use rand::RngCore;

use noirc_evaluator::ssa::ir::instruction::BinaryOp;
use noirc_evaluator::ssa::ir::types::NumericType;

const NUMBER_OF_VARIABLES_INITIAL: u32 = 7;

struct TestHelper {
Expand Down Expand Up @@ -80,7 +83,7 @@
}

/// Runs the given instruction with the given values and returns the results of the ACIR and Brillig programs
/// Instruction runned with first and second witness given

Check warning on line 86 in tooling/ssa_fuzzer/src/lib.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (runned)
fn run_instruction_double_arg(
instruction: InstructionWithTwoArgs,
values: Vec<u64>,
Expand Down Expand Up @@ -224,4 +227,156 @@
run_instruction_double_arg(FuzzerBuilder::insert_shr_instruction, values.clone());
compare_results(values[0] >> values[1], noir_res);
}

fn check_expected_validation_error(
compilation_result: Result<CompiledProgram, FuzzerBuilderError>,
expected_message: &str,
) {
match compilation_result {
Ok(_) => panic!("Expected an SSA validation failure"),
Err(FuzzerBuilderError::RuntimeError(error)) => {
assert!(error.contains(expected_message));
}
}
}

#[test]
fn regression_multiplication_without_range_check() {
let mut acir_builder = FuzzerBuilder::new_acir();
let mut brillig_builder = FuzzerBuilder::new_brillig();

let field_acir_var = acir_builder.insert_variable(ValueType::Field.to_ssa_type()).value_id;
let field_brillig_var =
brillig_builder.insert_variable(ValueType::Field.to_ssa_type()).value_id;

let truncated_acir = acir_builder.builder.insert_truncate(field_acir_var, 16, 254);
let truncated_brillig = brillig_builder.builder.insert_truncate(field_brillig_var, 16, 254);

let field_casted_i16_acir =
acir_builder.builder.insert_cast(truncated_acir, NumericType::Signed { bit_size: 16 });
let field_casted_i16_brillig = brillig_builder
.builder
.insert_cast(truncated_brillig, NumericType::Signed { bit_size: 16 });

let casted_pow_2_acir = acir_builder.builder.insert_binary(
field_casted_i16_acir,
BinaryOp::Mul { unchecked: false },
field_casted_i16_acir,
);
let casted_pow_2_brillig = brillig_builder.builder.insert_binary(
field_casted_i16_brillig,
BinaryOp::Mul { unchecked: false },
field_casted_i16_brillig,
);

let last_var = acir_builder.builder.insert_binary(
casted_pow_2_acir,
BinaryOp::Div,
field_casted_i16_acir,
);
let last_var_brillig = brillig_builder.builder.insert_binary(
casted_pow_2_brillig,
BinaryOp::Div,
field_casted_i16_brillig,
);

acir_builder.builder.terminate_with_return(vec![last_var]);
brillig_builder.builder.terminate_with_return(vec![last_var_brillig]);

let acir_result = acir_builder.compile(CompileOptions::default());
check_expected_validation_error(
acir_result,
"Signed binary operation does not follow overflow pattern",
);

let brillig_result = brillig_builder.compile(CompileOptions::default());
check_expected_validation_error(
brillig_result,
"Signed binary operation does not follow overflow pattern",
);
}

#[test]
fn regression_cast_without_truncate() {
let mut acir_builder = FuzzerBuilder::new_acir();
let mut brillig_builder = FuzzerBuilder::new_brillig();

let field_var_acir_id_1 =
acir_builder.insert_variable(ValueType::Field.to_ssa_type()).value_id;
let u64_var_acir_id_2 = acir_builder.insert_variable(ValueType::U64.to_ssa_type()).value_id;
let field_var_brillig_id_1 =
brillig_builder.insert_variable(ValueType::Field.to_ssa_type()).value_id;
let u64_var_brillig_id_2 =
brillig_builder.insert_variable(ValueType::U64.to_ssa_type()).value_id;

let casted_acir = acir_builder
.builder
.insert_cast(field_var_acir_id_1, NumericType::Unsigned { bit_size: 64 });
let casted_brillig = brillig_builder
.builder
.insert_cast(field_var_brillig_id_1, NumericType::Unsigned { bit_size: 64 });

let mul_acir = acir_builder.builder.insert_binary(
casted_acir,
BinaryOp::Mul { unchecked: false },
u64_var_acir_id_2,
);
let mul_brillig = brillig_builder.builder.insert_binary(
casted_brillig,
BinaryOp::Mul { unchecked: false },
u64_var_brillig_id_2,
);

acir_builder.builder.terminate_with_return(vec![mul_acir]);
brillig_builder.builder.terminate_with_return(vec![mul_brillig]);

let acir_result = acir_builder.compile(CompileOptions::default());
check_expected_validation_error(
acir_result,
"Invalid cast from Field, not preceded by valid truncation or known safe value",
);
let brillig_result = brillig_builder.compile(CompileOptions::default());
check_expected_validation_error(
brillig_result,
"Invalid cast from Field, not preceded by valid truncation or known safe value",
);
}

#[test]
fn regression_signed_sub() {
let mut acir_builder = FuzzerBuilder::new_acir();
let mut brillig_builder = FuzzerBuilder::new_brillig();

let i16_acir_var_1 = acir_builder.insert_variable(ValueType::I16.to_ssa_type()).value_id;
let i16_acir_var_2 = acir_builder.insert_variable(ValueType::I16.to_ssa_type()).value_id;
let i16_brillig_var_1 =
brillig_builder.insert_variable(ValueType::I16.to_ssa_type()).value_id;
let i16_brillig_var_2 =
brillig_builder.insert_variable(ValueType::I16.to_ssa_type()).value_id;

let sub_acir = acir_builder.builder.insert_binary(
i16_acir_var_1,
BinaryOp::Sub { unchecked: false },
i16_acir_var_2,
);
let sub_brillig = brillig_builder.builder.insert_binary(
i16_brillig_var_1,
BinaryOp::Sub { unchecked: false },
i16_brillig_var_2,
);

acir_builder.builder.terminate_with_return(vec![sub_acir]);
brillig_builder.builder.terminate_with_return(vec![sub_brillig]);

let acir_result = acir_builder.compile(CompileOptions::default());
check_expected_validation_error(
acir_result,
"Signed binary operation does not follow overflow pattern",
);
let brillig_result = brillig_builder.compile(CompileOptions::default());
check_expected_validation_error(
brillig_result,
"Signed binary operation does not follow overflow pattern",
);
}
}
Loading