diff --git a/crates/noirc_evaluator/src/lib.rs b/crates/noirc_evaluator/src/lib.rs index 506b0c2ceba..33fbdb5a5f3 100644 --- a/crates/noirc_evaluator/src/lib.rs +++ b/crates/noirc_evaluator/src/lib.rs @@ -15,6 +15,7 @@ use acvm::{ acir::native_types::{Expression, Witness}, compiler::optimizers::simplify::CircuitSimplifier, Language, + //compiler::optimizers::simplify, }; use errors::{RuntimeError, RuntimeErrorKind}; use iter_extended::btree_map; @@ -23,7 +24,9 @@ use noirc_frontend::monomorphization::ast::*; use ssa::{node::ObjectType, ssa_gen::IrGenerator}; use std::collections::{BTreeMap, BTreeSet}; -#[derive(Default)] +static _UNSASTISFIED_CONSTRAIN_ERR: &str = "Cannot satisfy constraint"; + +//#[derive(Default)] pub struct Evaluator { // Why is this not u64? // @@ -56,8 +59,23 @@ pub struct Evaluator { return_is_distinct: bool, opcodes: Vec, + simplifier: CircuitSimplifier, } +impl Default for Evaluator { + fn default() -> Self { + Evaluator { + current_witness_index: u32::default(), + num_witnesses_abi_len: usize::default(), + param_witnesses: BTreeMap::default(), + public_parameters: BTreeSet::default(), + return_values: Vec::default(), + return_is_distinct: bool::default(), + opcodes: Vec::default(), + simplifier: CircuitSimplifier::new(0), + } + } +} /// Compiles the Program into ACIR and applies optimizations to the arithmetic gates // XXX: We return the num_witnesses, but this is the max number of witnesses // Some of these could have been removed due to optimizations. We need this number because the @@ -83,7 +101,7 @@ pub fn create_circuit( opcodes, .. } = evaluator; - let simplifier = CircuitSimplifier::new(current_witness_index); + let optimized_circuit = acvm::compiler::compile( Circuit { current_witness_index, @@ -93,7 +111,7 @@ pub fn create_circuit( }, np_language, is_opcode_supported, - &simplifier, + &evaluator.simplifier, ) .map_err(|_| RuntimeErrorKind::Spanless(String::from("produced an acvm compile error")))?; @@ -141,8 +159,18 @@ impl Evaluator { self.current_witness_index } - pub fn push_opcode(&mut self, gate: AcirOpcode) { + pub fn push_opcode(&mut self, gate: AcirOpcode) -> Result<(), RuntimeErrorKind> { self.opcodes.push(gate); + // TODO uncomment to activate the simplification + // if let simplify::SimplifyResult::UnsatisfiedConstrain(_g) = + // self.simplifier.simplify(&mut self.opcodes) + // { + // //TODO add location + // return Err(RuntimeErrorKind::UnstructuredError { + // message: UNSASTISFIED_CONSTRAIN_ERR.to_string(), + // }); + // } + Ok(()) } /// Compiles the AST into the intermediate format by evaluating the main function @@ -161,6 +189,7 @@ impl Evaluator { ir_gen.ssa_gen_main()?; //Generates ACIR representation: + self.simplifier = CircuitSimplifier::new(self.num_witnesses_abi_len as u32); ir_gen.context.ir_to_acir(self, enable_logging, show_output)?; Ok(()) } diff --git a/crates/noirc_evaluator/src/ssa/acir_gen.rs b/crates/noirc_evaluator/src/ssa/acir_gen.rs index 22b5390e2fa..1d15057a62d 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen.rs @@ -46,7 +46,7 @@ impl Acir { //TODO we should rather follow the jumps current_block = block.left.map(|block_id| &ctx[block_id]); } - self.memory.acir_gen(evaluator, ctx); + self.memory.acir_gen(evaluator, ctx)?; Ok(()) } @@ -72,9 +72,11 @@ impl Acir { Operation::Constrain(value, ..) => { constrain::evaluate(value, var_cache, evaluator, ctx) } - Operation::Not(value) => not::evaluate(value, ins.res_type, var_cache, evaluator, ctx), + Operation::Not(value) => { + Ok(not::evaluate(value, ins.res_type, var_cache, evaluator, ctx)) + } Operation::Cast(value) => { - self.var_cache.get_or_compute_internal_var(*value, evaluator, ctx) + Ok(self.var_cache.get_or_compute_internal_var(*value, evaluator, ctx)) } Operation::Truncate { value, bit_size, max_bit_size } => { truncate::evaluate(value, *bit_size, *max_bit_size, var_cache, evaluator, ctx) @@ -92,18 +94,18 @@ impl Acir { intrinsics::evaluate(args, ins, opcode, self, ctx, evaluator) } Operation::Return(node_ids) => { - r#return::evaluate(node_ids, acir_mem, var_cache, evaluator, ctx)? + Ok(r#return::evaluate(node_ids, acir_mem, var_cache, evaluator, ctx)?) } Operation::Cond { condition, val_true: lhs, val_false: rhs } => { - condition::evaluate(*condition, *lhs, *rhs, var_cache, evaluator, ctx) + Ok(condition::evaluate(*condition, *lhs, *rhs, var_cache, evaluator, ctx)) } - Operation::Load { array_id, index, location } => Some(load::evaluate( + Operation::Load { array_id, index, location } => Ok(Some(load::evaluate( *array_id, *index, acir_mem, var_cache, *location, evaluator, ctx, - )?), + )?)), Operation::Store { .. } => { - store::evaluate(&ins.operation, acir_mem, var_cache, evaluator, ctx)? + store::evaluate(&ins.operation, acir_mem, var_cache, evaluator, ctx) } - Operation::Nop => None, + Operation::Nop => Ok(None), i @ Operation::Jne(..) | i @ Operation::Jeq(..) | i @ Operation::Jmp(_) @@ -112,7 +114,7 @@ impl Acir { | i @ Operation::Result { .. } => { unreachable!("Invalid instruction: {:?}", i); } - }; + }?; // If the operation returned an `InternalVar` // then we add it to the `InternalVar` cache diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/acir_mem.rs b/crates/noirc_evaluator/src/ssa/acir_gen/acir_mem.rs index ac3395d9411..68eeca1bb03 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/acir_mem.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/acir_mem.rs @@ -1,4 +1,5 @@ use crate::{ + errors::RuntimeError, ssa::{ acir_gen::InternalVar, context::SsaContext, @@ -121,17 +122,22 @@ impl ArrayHeap { inputs: Vec, bits: &mut Vec, evaluator: &mut Evaluator, - ) -> Vec { + ) -> Result, RuntimeError> { let outputs = vecmap(0..inputs.len(), |_| evaluator.add_witness_to_cs().into()); if bits.is_empty() { - *bits = operations::sort::evaluate_permutation(&inputs, &outputs, evaluator); + *bits = operations::sort::evaluate_permutation(&inputs, &outputs, evaluator)?; } else { operations::sort::evaluate_permutation_with_witness(&inputs, &outputs, bits, evaluator); } - outputs + Ok(outputs) } - pub(crate) fn acir_gen(&self, evaluator: &mut Evaluator, array_id: ArrayId, array_len: u32) { + pub(crate) fn acir_gen( + &self, + evaluator: &mut Evaluator, + array_id: ArrayId, + array_len: u32, + ) -> Result<(), RuntimeError> { let (len, read_write) = match self.typ { ArrayType::Init(_, _) | ArrayType::WriteOnly => (0, true), ArrayType::ReadOnly(last) => (last.unwrap_or(self.trace.len()), false), @@ -139,7 +145,7 @@ impl ArrayHeap { }; if len == 0 { - return; + return Ok(()); } evaluator.opcodes.push(AcirOpcode::Block(MemoryBlock { id: AcirBlockId(array_id.as_u32()), @@ -165,11 +171,11 @@ impl ArrayHeap { tuple_expressions.push(vec![item.index.clone(), counter_expr.clone()]); } let mut bit_counter = Vec::new(); - let out_counter = Self::generate_outputs(in_counter, &mut bit_counter, evaluator); - let out_index = Self::generate_outputs(in_index, &mut bit_counter, evaluator); - let out_value = Self::generate_outputs(in_value, &mut bit_counter, evaluator); + let out_counter = Self::generate_outputs(in_counter, &mut bit_counter, evaluator)?; + let out_index = Self::generate_outputs(in_index, &mut bit_counter, evaluator)?; + let out_value = Self::generate_outputs(in_value, &mut bit_counter, evaluator)?; let out_op = if read_write { - Self::generate_outputs(in_op, &mut bit_counter, evaluator) + Self::generate_outputs(in_op, &mut bit_counter, evaluator)? } else { Vec::new() }; @@ -196,7 +202,7 @@ impl ArrayHeap { len_bits, false, evaluator, - ); + )?; let sub_cmp = subtract(&cmp, FieldElement::one(), &Expression::one()); let secondary_order = subtract( &mul_with_witness(evaluator, &index_sub, &sub_cmp), @@ -220,6 +226,7 @@ impl ArrayHeap { }; evaluator.opcodes.push(AcirOpcode::Arithmetic(load_on_same_adr)); } + Ok(()) } } @@ -317,10 +324,15 @@ impl AcirMem { let item = MemOp { operation: op, value, index }; self.array_heap_mut(*array_id).push(item); } - pub(crate) fn acir_gen(&self, evaluator: &mut Evaluator, ctx: &SsaContext) { + pub(crate) fn acir_gen( + &self, + evaluator: &mut Evaluator, + ctx: &SsaContext, + ) -> Result<(), RuntimeError> { for mem in &self.virtual_memory { let array = &ctx.mem[*mem.0]; - mem.1.acir_gen(evaluator, array.id, array.len); + mem.1.acir_gen(evaluator, array.id, array.len)?; } + Ok(()) } } diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/constraints.rs b/crates/noirc_evaluator/src/ssa/acir_gen/constraints.rs index 0900c34ce36..ddb40275ced 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/constraints.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/constraints.rs @@ -1,5 +1,5 @@ use crate::{ - errors::RuntimeErrorKind, + errors::{RuntimeError, RuntimeErrorKind}, ssa::{acir_gen::expression_to_witness, builtin::Endian}, Evaluator, }; @@ -250,7 +250,7 @@ pub(crate) fn range_constraint( if num_bits == 1 { // Add a bool gate let bool_constraint = boolean(witness); - evaluator.push_opcode(AcirOpcode::Arithmetic(bool_constraint)); + evaluator.push_opcode(AcirOpcode::Arithmetic(bool_constraint))?; } else if num_bits == FieldElement::max_num_bits() { // Don't apply any constraints if the range is for the maximum number of bits let message = format!( @@ -271,7 +271,7 @@ pub(crate) fn range_constraint( q: b_witness, r: r_witness, predicate: None, - }))); + })))?; try_range_constraint(r_witness, num_bits - 1, evaluator); try_range_constraint(b_witness, 1, evaluator); @@ -281,12 +281,12 @@ pub(crate) fn range_constraint( f = f.pow(&FieldElement::from((num_bits - 1) as i128)); let res = add(&r_witness.into(), f, &b_witness.into()); let my_constraint = add(&res, -FieldElement::one(), &witness.into()); - evaluator.push_opcode(AcirOpcode::Arithmetic(my_constraint)); + evaluator.push_opcode(AcirOpcode::Arithmetic(my_constraint))?; } else { let gate = AcirOpcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input: FunctionInput { witness, num_bits }, }); - evaluator.push_opcode(gate); + evaluator.push_opcode(gate)?; } Ok(()) @@ -298,7 +298,7 @@ pub(crate) fn bound_check( b: &Expression, max_bits: u32, evaluator: &mut Evaluator, -) -> Witness { +) -> Result { assert!(max_bits + 1 < FieldElement::max_num_bits()); //n.b what we really need is 2^{max_bits+1}

Result<(), RuntimeError> { assert!( bits < FieldElement::max_num_bits(), "range check with bit size of the prime field is not implemented yet" @@ -356,21 +356,23 @@ pub(crate) fn bound_constraint_with_offset( if f < 3 { match f { - 0 => evaluator.push_opcode(AcirOpcode::Arithmetic(aof)), + 0 => { + evaluator.push_opcode(AcirOpcode::Arithmetic(aof))?; + } 1 => { let expr = boolean_expr(&aof, evaluator); - evaluator.push_opcode(AcirOpcode::Arithmetic(expr)); + evaluator.push_opcode(AcirOpcode::Arithmetic(expr))?; } 2 => { let y = expression_to_witness(boolean_expr(&aof, evaluator), evaluator); let two = FieldElement::from(2_i128); let y_expr = y.into(); let eee = subtract(&mul_with_witness(evaluator, &aof, &y_expr), two, &y_expr); - evaluator.push_opcode(AcirOpcode::Arithmetic(eee)); + evaluator.push_opcode(AcirOpcode::Arithmetic(eee))?; } _ => unreachable!(), } - return; + return Ok(()); } let bit_size = bit_size_u128(f); if bit_size < 128 { @@ -379,13 +381,14 @@ pub(crate) fn bound_constraint_with_offset( let aor = add(&aof, FieldElement::from(r), &Expression::one()); let witness = expression_to_witness(aor, evaluator); try_range_constraint(witness, bit_size, evaluator); - return; + return Ok(()); } } let sub_expression = subtract(b, FieldElement::one(), &aof); //b-(a+offset) let w = expression_to_witness(sub_expression, evaluator); try_range_constraint(w, bits, evaluator); + Ok(()) } pub(crate) fn try_range_constraint(w: Witness, bits: u32, evaluator: &mut Evaluator) { @@ -401,44 +404,48 @@ pub(crate) fn to_radix_base( limb_size: u32, endianness: Endian, evaluator: &mut Evaluator, -) -> Vec { +) -> Result, RuntimeError> { // ensure there is no overflow let rad = BigUint::from(radix); let max = rad.pow(limb_size) - BigUint::one(); if max < FieldElement::modulus() { - let (mut result, bytes) = to_radix_little(radix, limb_size, evaluator); + let (mut result, bytes) = to_radix_little(radix, limb_size, evaluator)?; evaluator.push_opcode(AcirOpcode::Directive(Directive::ToLeRadix { a: lhs.clone(), b: result.clone(), radix, - })); + }))?; if endianness == Endian::Big { result.reverse(); } - evaluator.push_opcode(AcirOpcode::Arithmetic(subtract(lhs, FieldElement::one(), &bytes))); - result + evaluator.push_opcode(AcirOpcode::Arithmetic(subtract( + lhs, + FieldElement::one(), + &bytes, + )))?; + Ok(result) } else { let min = rad.pow(limb_size - 1) - BigUint::one(); assert!(min < FieldElement::modulus()); let max_bits = max.bits() as u32; - let a = evaluate_constant_modulo(lhs, radix, max_bits, evaluator) + let a = evaluate_constant_modulo(lhs, radix, max_bits, evaluator)? .to_witness() .expect("Constant expressions should already be simplified"); let y = subtract(lhs, FieldElement::one(), &Expression::from(a)); let radix_f = FieldElement::from(radix as i128); let y = Expression::default().add_mul(FieldElement::one() / radix_f, &y); - let mut b = to_radix_base(&y, radix, limb_size - 1, endianness, evaluator); + let mut b = to_radix_base(&y, radix, limb_size - 1, endianness, evaluator)?; match endianness { Endian::Little => b.insert(0, a), Endian::Big => b.push(a), } - b + Ok(b) } } @@ -450,7 +457,7 @@ pub(crate) fn to_radix_little( radix: u32, num_limbs: u32, evaluator: &mut Evaluator, -) -> (Vec, Expression) { +) -> Result<(Vec, Expression), RuntimeError> { let mut digits = Expression::default(); let mut radix_pow = FieldElement::one(); @@ -473,9 +480,9 @@ pub(crate) fn to_radix_little( &Expression::one(), bit_size, evaluator, - ); + )?; } - (result, digits) + Ok((result, digits)) } //Returns 1 if lhs < rhs @@ -485,17 +492,17 @@ pub(crate) fn evaluate_cmp( bit_size: u32, signed: bool, evaluator: &mut Evaluator, -) -> Expression { +) -> Result { if signed { //TODO use range_constraints instead of bit decomposition, like in the unsigned case let mut sub_expr = subtract(lhs, FieldElement::one(), rhs); let two_pow = BigUint::one() << (bit_size + 1); sub_expr.q_c += FieldElement::from_be_bytes_reduce(&two_pow.to_bytes_be()); - let bits = to_radix_base(&sub_expr, 2, bit_size + 2, Endian::Little, evaluator); - bits[(bit_size - 1) as usize].into() + let bits = to_radix_base(&sub_expr, 2, bit_size + 2, Endian::Little, evaluator)?; + Ok(bits[(bit_size - 1) as usize].into()) } else { - let is_greater = bound_check(lhs, rhs, bit_size, evaluator); - subtract(&Expression::one(), FieldElement::one(), &is_greater.into()) + let is_greater = bound_check(lhs, rhs, bit_size, evaluator)?; + Ok(subtract(&Expression::one(), FieldElement::one(), &is_greater.into())) } } @@ -505,7 +512,7 @@ pub(crate) fn evaluate_truncate( rhs: u32, max_bits: u32, evaluator: &mut Evaluator, -) -> Expression { +) -> Result { assert!(max_bits > rhs, "max_bits = {max_bits}, rhs = {rhs}"); let exp_big = BigUint::from(2_u32).pow(rhs); @@ -513,7 +520,7 @@ pub(crate) fn evaluate_truncate( if let Some(a_c) = lhs.to_const() { let mut a_big = BigUint::from_bytes_be(&a_c.to_be_bytes()); a_big %= exp_big; - return Expression::from(FieldElement::from_be_bytes_reduce(&a_big.to_bytes_be())); + return Ok(Expression::from(FieldElement::from_be_bytes_reduce(&a_big.to_bytes_be()))); } let exp = FieldElement::from_be_bytes_reduce(&exp_big.to_bytes_be()); @@ -526,7 +533,7 @@ pub(crate) fn evaluate_truncate( q: c_witness, r: b_witness, predicate: None, - }))); + })))?; try_range_constraint(b_witness, rhs, evaluator); //TODO propagate the error using ? try_range_constraint(c_witness, max_bits - rhs, evaluator); @@ -538,9 +545,9 @@ pub(crate) fn evaluate_truncate( let c_arith = c_witness.into(); let res = add(&b_arith, f, &c_arith); //b+2^Nc let my_constraint = add(&res, -FieldElement::one(), lhs); - evaluator.push_opcode(AcirOpcode::Arithmetic(my_constraint)); + evaluator.push_opcode(AcirOpcode::Arithmetic(my_constraint))?; - Expression::from(b_witness) + Ok(Expression::from(b_witness)) } //Returns b such that lhs (a number whose value requires max_bits) mod rhs is b @@ -549,7 +556,7 @@ pub(crate) fn evaluate_constant_modulo( rhs: u32, max_bits: u32, evaluator: &mut Evaluator, -) -> Expression { +) -> Result { let modulus = FieldElement::from(rhs as i128); let modulus_exp = Expression::from_field(modulus); assert_ne!(rhs, 0); @@ -559,7 +566,7 @@ pub(crate) fn evaluate_constant_modulo( if let Some(a_c) = lhs.to_const() { let mut a_big = BigUint::from_bytes_be(&a_c.to_be_bytes()); a_big %= BigUint::from_bytes_be(&modulus.to_be_bytes()); - return Expression::from(FieldElement::from_be_bytes_reduce(&a_big.to_bytes_be())); + return Ok(Expression::from(FieldElement::from_be_bytes_reduce(&a_big.to_bytes_be()))); } //1. Generate witnesses b,c @@ -571,14 +578,14 @@ pub(crate) fn evaluate_constant_modulo( q: c_witness, r: b_witness, predicate: None, - }))); + })))?; bound_constraint_with_offset( &Expression::from(b_witness), &modulus_exp, &Expression::one(), modulus_bits, evaluator, - ); + )?; //if rhs is a power of 2, then we avoid this range check as it is redundant with the previous one. if rhs & (rhs - 1) != 0 { try_range_constraint(b_witness, modulus_bits, evaluator); @@ -591,9 +598,9 @@ pub(crate) fn evaluate_constant_modulo( let c_arith = c_witness.into(); let res = add(&b_arith, modulus, &c_arith); let my_constraint = add(&res, -FieldElement::one(), lhs); - evaluator.push_opcode(AcirOpcode::Arithmetic(my_constraint)); + evaluator.push_opcode(AcirOpcode::Arithmetic(my_constraint))?; - Expression::from(b_witness) + Ok(Expression::from(b_witness)) } pub(crate) fn evaluate_udiv( @@ -602,7 +609,7 @@ pub(crate) fn evaluate_udiv( bit_size: u32, predicate: &Expression, evaluator: &mut Evaluator, -) -> (Witness, Witness) { +) -> Result<(Witness, Witness), RuntimeError> { let q_witness = evaluator.add_witness_to_cs(); let r_witness = evaluator.add_witness_to_cs(); let pa = mul_with_witness(evaluator, lhs, predicate); @@ -612,12 +619,12 @@ pub(crate) fn evaluate_udiv( q: q_witness, r: r_witness, predicate: Some(predicate.clone()), - }))); + })))?; //r Witness { +) -> Result { // Create a fresh witness - n.b we could check if x is constant or not let inverse_witness = evaluator.add_witness_to_cs(); evaluator.push_opcode(AcirOpcode::Directive(Directive::Invert { x: x_witness, result: inverse_witness, - })); + }))?; //x*inverse = 1 let one = mul(&x_witness.into(), &inverse_witness.into()); let lhs = mul_with_witness(evaluator, &one, predicate); - evaluator.push_opcode(AcirOpcode::Arithmetic(subtract(&lhs, FieldElement::one(), predicate))); - inverse_witness + evaluator.push_opcode(AcirOpcode::Arithmetic(subtract( + &lhs, + FieldElement::one(), + predicate, + )))?; + Ok(inverse_witness) } //Zero Equality gate: returns 1 if x is not null and 0 else -pub(crate) fn evaluate_zero_equality(x_witness: Witness, evaluator: &mut Evaluator) -> Witness { +pub(crate) fn evaluate_zero_equality( + x_witness: Witness, + evaluator: &mut Evaluator, +) -> Result { let m = evaluator.add_witness_to_cs(); //'inverse' of x - evaluator.push_opcode(AcirOpcode::Directive(Directive::Invert { x: x_witness, result: m })); + evaluator.push_opcode(AcirOpcode::Directive(Directive::Invert { x: x_witness, result: m }))?; //y=x*m y is 1 if x is not null, and 0 else let y_witness = evaluator.add_witness_to_cs(); @@ -661,7 +675,7 @@ pub(crate) fn evaluate_zero_equality(x_witness: Witness, evaluator: &mut Evaluat mul_terms: vec![(FieldElement::one(), x_witness, m)], linear_combinations: vec![(-FieldElement::one(), y_witness)], q_c: FieldElement::zero(), - })); + }))?; //x=y*x let xy = mul(&x_witness.into(), &y_witness.into()); @@ -669,8 +683,8 @@ pub(crate) fn evaluate_zero_equality(x_witness: Witness, evaluator: &mut Evaluat &xy, FieldElement::one(), &x_witness.into(), - ))); - y_witness + )))?; + Ok(y_witness) } // Given two lists, `A` and `B` of `Expression`s @@ -684,7 +698,7 @@ pub(crate) fn arrays_eq_predicate( a_values: &[Expression], b_values: &[Expression], evaluator: &mut Evaluator, -) -> Expression { +) -> Result { let mut sum = Expression::default(); for (a_iter, b_iter) in a_values.iter().zip(b_values) { @@ -696,12 +710,12 @@ pub(crate) fn arrays_eq_predicate( &diff_expr, FieldElement::one(), &diff_witness.into(), - ))); + )))?; //TODO: avoid creating witnesses for diff - sum = - add(&sum, FieldElement::one(), &evaluate_zero_equality(diff_witness, evaluator).into()); + let comp = evaluate_zero_equality(diff_witness, evaluator)?; + sum = add(&sum, FieldElement::one(), &comp.into()); } - sum + Ok(sum) } // TODO: An issue should be created for this diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/operations/binary.rs b/crates/noirc_evaluator/src/ssa/acir_gen/operations/binary.rs index 166a55b0d52..a54ff135069 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/operations/binary.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/operations/binary.rs @@ -1,4 +1,5 @@ use crate::{ + errors::RuntimeError, ssa::{ acir_gen::{ constraints, internal_var_cache::InternalVarCache, operations, Acir, InternalVar, @@ -31,12 +32,12 @@ pub(crate) fn evaluate( acir_gen: &mut Acir, evaluator: &mut Evaluator, ctx: &SsaContext, -) -> Option { +) -> Result, RuntimeError> { let r_size = ctx[binary.rhs].size_in_bits(); let l_size = ctx[binary.lhs].size_in_bits(); let max_size = u32::max(r_size, l_size); if binary.predicate == Some(ctx.zero()) { - return None; + return Ok(None); } let binary_output = match &binary.operator { @@ -108,7 +109,7 @@ pub(crate) fn evaluate( max_size, predicate.expression(), evaluator, - ); + )?; InternalVar::from(q_wit) } BinaryOp::Sdiv(_) => { @@ -128,7 +129,7 @@ pub(crate) fn evaluate( max_size, predicate.expression(), evaluator, - ); + )?; InternalVar::from(r_wit) } BinaryOp::Srem(_) => { @@ -153,7 +154,7 @@ pub(crate) fn evaluate( let x_witness = acir_gen.var_cache.get_or_compute_witness(r_c, evaluator).expect("unexpected constant expression"); let inverse = Expression::from(constraints::evaluate_inverse( x_witness, &predicate, evaluator, - )); + )?); InternalVar::from(constraints::mul_with_witness( evaluator, l_c.expression(), @@ -165,13 +166,13 @@ pub(crate) fn evaluate( let l_c = acir_gen.var_cache.get_or_compute_internal_var(binary.lhs, evaluator, ctx); let r_c = acir_gen.var_cache.get_or_compute_internal_var(binary.rhs, evaluator, ctx); InternalVar::from( - operations::cmp::evaluate_eq(acir_gen,binary.lhs, binary.rhs, l_c, r_c, ctx, evaluator), + operations::cmp::evaluate_eq(acir_gen,binary.lhs, binary.rhs, l_c, r_c, ctx, evaluator)?, )}, BinaryOp::Ne => { let l_c = acir_gen.var_cache.get_or_compute_internal_var(binary.lhs, evaluator, ctx); let r_c = acir_gen.var_cache.get_or_compute_internal_var(binary.rhs, evaluator, ctx); InternalVar::from( - operations::cmp::evaluate_neq(acir_gen,binary.lhs, binary.rhs, l_c, r_c, ctx, evaluator), + operations::cmp::evaluate_neq(acir_gen,binary.lhs, binary.rhs, l_c, r_c, ctx, evaluator)?, )}, BinaryOp::Ult => { let l_c = acir_gen.var_cache.get_or_compute_internal_var_unwrap(binary.lhs, evaluator, ctx); @@ -183,7 +184,7 @@ pub(crate) fn evaluate( size, false, evaluator, - ) + )? .into() } BinaryOp::Ule => { @@ -196,14 +197,14 @@ pub(crate) fn evaluate( size, false, evaluator, - ); + )?; constraints::subtract(&Expression::one(), FieldElement::one(), &e).into() } BinaryOp::Slt => { let l_c = acir_gen.var_cache.get_or_compute_internal_var_unwrap(binary.lhs, evaluator, ctx); let r_c = acir_gen.var_cache.get_or_compute_internal_var_unwrap(binary.rhs, evaluator, ctx); let s = ctx[binary.lhs].get_type().bits(); - constraints::evaluate_cmp(l_c.expression(), r_c.expression(), s, true, evaluator) + constraints::evaluate_cmp(l_c.expression(), r_c.expression(), s, true, evaluator)? .into() } BinaryOp::Sle => { @@ -216,7 +217,7 @@ pub(crate) fn evaluate( s, true, evaluator, - ); + )?; constraints::subtract(&Expression::one(), FieldElement::one(), &e).into() } BinaryOp::Lt | BinaryOp::Lte => { @@ -241,5 +242,5 @@ pub(crate) fn evaluate( BinaryOp::Shl | BinaryOp::Shr(_) => todo!("ShiftLeft and ShiftRight operations with shifts which are only known at runtime are not yet implemented."), i @ BinaryOp::Assign => unreachable!("Invalid Instruction: {:?}", i), }; - Some(binary_output) + Ok(Some(binary_output)) } diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/operations/cmp.rs b/crates/noirc_evaluator/src/ssa/acir_gen/operations/cmp.rs index 0f8091e2f6f..48047fbd585 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/operations/cmp.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/operations/cmp.rs @@ -1,4 +1,5 @@ use crate::{ + errors::RuntimeError, ssa::{ acir_gen::{acir_mem::AcirMem, constraints, Acir, InternalVar}, context::SsaContext, @@ -33,10 +34,10 @@ pub(super) fn evaluate_neq( r_c: Option, ctx: &SsaContext, evaluator: &mut Evaluator, -) -> Expression { +) -> Result { // Check whether the `lhs` and `rhs` are trivially equal if lhs == rhs { - return Expression::zero(); + return Ok(Expression::zero()); } // Check whether the `lhs` and `rhs` are Arrays @@ -60,7 +61,7 @@ pub(super) fn evaluate_neq( ) } - let x = InternalVar::from(array_eq(&mut acir_gen.memory, array_a, array_b, evaluator)); + let x = InternalVar::from(array_eq(&mut acir_gen.memory, array_a, array_b, evaluator)?); // TODO we need a witness because of the directive, but we should use an expression // TODO if we change the Invert directive to take an `Expression`, then we // TODO can get rid of this extra gate. @@ -69,7 +70,7 @@ pub(super) fn evaluate_neq( .get_or_compute_witness(x, evaluator) .expect("unexpected constant expression"); - return Expression::from(constraints::evaluate_zero_equality(x_witness, evaluator)); + return Ok(Expression::from(constraints::evaluate_zero_equality(x_witness, evaluator)?)); } // Arriving here means that `lhs` and `rhs` are not Arrays @@ -85,9 +86,9 @@ pub(super) fn evaluate_neq( // it is zero at compile time. if let Some(x_const) = x.to_const() { if x_const.is_zero() { - Expression::zero() + Ok(Expression::zero()) } else { - Expression::one() + Ok(Expression::one()) } } else { //todo we need a witness because of the directive, but we should use an expression @@ -95,7 +96,7 @@ pub(super) fn evaluate_neq( .var_cache .get_or_compute_witness(x, evaluator) .expect("unexpected constant expression"); - Expression::from(constraints::evaluate_zero_equality(x_witness, evaluator)) + Ok(Expression::from(constraints::evaluate_zero_equality(x_witness, evaluator)?)) } } @@ -107,9 +108,9 @@ pub(super) fn evaluate_eq( r_c: Option, ctx: &SsaContext, evaluator: &mut Evaluator, -) -> Expression { - let neq = evaluate_neq(acir_gen, lhs, rhs, l_c, r_c, ctx, evaluator); - constraints::subtract(&Expression::one(), FieldElement::one(), &neq) +) -> Result { + let neq = evaluate_neq(acir_gen, lhs, rhs, l_c, r_c, ctx, evaluator)?; + Ok(constraints::subtract(&Expression::one(), FieldElement::one(), &neq)) } // Given two `MemArray`s, generate constraints that check whether @@ -122,7 +123,7 @@ fn array_eq( a: &MemArray, b: &MemArray, evaluator: &mut Evaluator, -) -> Expression { +) -> Result { // Fetch the elements in both `MemArrays`s, these are `InternalVar`s // We then convert these to `Expressions` let internal_var_to_expr = |internal_var: InternalVar| internal_var.expression().clone(); diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/operations/constrain.rs b/crates/noirc_evaluator/src/ssa/acir_gen/operations/constrain.rs index 1906c0c7f50..ae564f0f6b2 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/operations/constrain.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/operations/constrain.rs @@ -3,6 +3,7 @@ use acvm::{ }; use crate::{ + errors::RuntimeError, ssa::{ acir_gen::{constraints, internal_var_cache::InternalVarCache, InternalVar}, context::SsaContext, @@ -16,10 +17,10 @@ pub(crate) fn evaluate( var_cache: &mut InternalVarCache, evaluator: &mut Evaluator, ctx: &SsaContext, -) -> Option { +) -> Result, RuntimeError> { let value = var_cache.get_or_compute_internal_var_unwrap(*value, evaluator, ctx); let subtract = constraints::subtract(&Expression::one(), FieldElement::one(), value.expression()); - evaluator.push_opcode(AcirOpcode::Arithmetic(subtract)); - Some(value) + evaluator.push_opcode(AcirOpcode::Arithmetic(subtract))?; + Ok(Some(value)) } diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/operations/intrinsics.rs b/crates/noirc_evaluator/src/ssa/acir_gen/operations/intrinsics.rs index 01d5fecc897..fb7d9135228 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/operations/intrinsics.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/operations/intrinsics.rs @@ -1,4 +1,5 @@ use crate::{ + errors::RuntimeError, ssa::{ acir_gen::{ constraints::{bound_constraint_with_offset, to_radix_base}, @@ -37,7 +38,7 @@ pub(crate) fn evaluate( acir_gen: &mut Acir, ctx: &SsaContext, evaluator: &mut Evaluator, -) -> Option { +) -> Result, RuntimeError> { use builtin::Opcode; let instruction_id = instruction.id; @@ -50,7 +51,7 @@ pub(crate) fn evaluate( let bit_size = ctx.get_as_constant(args[1]).unwrap().to_u128() as u32; let l_c = acir_gen.var_cache.get_or_compute_internal_var_unwrap(args[0], evaluator, ctx); - outputs = to_radix_base(l_c.expression(), 2, bit_size, endianness, evaluator); + outputs = to_radix_base(l_c.expression(), 2, bit_size, endianness, evaluator)?; if let ObjectType::ArrayPointer(a) = res_type { acir_gen.memory.map_array(a, &outputs, ctx); } @@ -61,7 +62,7 @@ pub(crate) fn evaluate( let limb_size = ctx.get_as_constant(args[2]).unwrap().to_u128() as u32; let l_c = acir_gen.var_cache.get_or_compute_internal_var_unwrap(args[0], evaluator, ctx); - outputs = to_radix_base(l_c.expression(), radix, limb_size, endianness, evaluator); + outputs = to_radix_base(l_c.expression(), radix, limb_size, endianness, evaluator)?; if let ObjectType::ArrayPointer(a) = res_type { acir_gen.memory.map_array(a, &outputs, ctx); } @@ -171,16 +172,16 @@ pub(crate) fn evaluate( &Expression::zero(), num_bits, evaluator, - ); + )?; } - let bits = evaluate_permutation(&in_expr, &out_expr, evaluator); + let bits = evaluate_permutation(&in_expr, &out_expr, evaluator)?; let inputs = in_expr.iter().map(|a| vec![a.clone()]).collect(); evaluator.push_opcode(AcirOpcode::Directive(Directive::PermutationSort { inputs, tuple: 1, bits, sort_by: vec![0], - })); + }))?; if let node::ObjectType::ArrayPointer(a) = res_type { acir_gen.memory.map_array(a, &outputs, ctx); } else { @@ -192,7 +193,7 @@ pub(crate) fn evaluate( // If more than witness is returned, // the result is inside the result type of `Instruction` // as a pointer to an array - (outputs.len() == 1).then(|| InternalVar::from(outputs[0])) + Ok((outputs.len() == 1).then(|| InternalVar::from(outputs[0]))) } fn resolve_variable( diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/operations/sort.rs b/crates/noirc_evaluator/src/ssa/acir_gen/operations/sort.rs index ccea9caac4c..210934230be 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/operations/sort.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/operations/sort.rs @@ -5,6 +5,7 @@ use acvm::{ }; use crate::{ + errors::RuntimeError, ssa::acir_gen::constraints::{add, mul_with_witness, subtract}, Evaluator, }; @@ -15,14 +16,14 @@ pub(crate) fn evaluate_permutation( in_expr: &[Expression], out_expr: &[Expression], evaluator: &mut Evaluator, -) -> Vec { +) -> Result, RuntimeError> { let bits = Vec::new(); let (w, b) = permutation_layer(in_expr, &bits, true, evaluator); // we constrain the network output to out_expr for (b, o) in b.iter().zip(out_expr) { - evaluator.push_opcode(AcirOpcode::Arithmetic(subtract(b, FieldElement::one(), o))); + evaluator.push_opcode(AcirOpcode::Arithmetic(subtract(b, FieldElement::one(), o)))?; } - w + Ok(w) } // Same as evaluate_permutation() but uses the provided witness as network control bits @@ -268,7 +269,7 @@ mod test { output.push(w.into()); } //generate constraints for the inputs - let w = evaluate_permutation(&input, &output, &mut eval); + let w = evaluate_permutation(&input, &output, &mut eval).unwrap(); //checks that it generate the same witness let (w1, _) = permutation_layer(&input, &w, false, &mut eval); assert_eq!(w, w1); diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/operations/truncate.rs b/crates/noirc_evaluator/src/ssa/acir_gen/operations/truncate.rs index 1f99df2ca17..e76305d72e5 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/operations/truncate.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/operations/truncate.rs @@ -1,4 +1,5 @@ use crate::{ + errors::RuntimeError, ssa::{ acir_gen::{constraints, internal_var_cache::InternalVarCache, InternalVar}, context::SsaContext, @@ -14,12 +15,12 @@ pub(crate) fn evaluate( var_cache: &mut InternalVarCache, evaluator: &mut Evaluator, ctx: &SsaContext, -) -> Option { +) -> Result, RuntimeError> { let value = var_cache.get_or_compute_internal_var_unwrap(*value, evaluator, ctx); - Some(InternalVar::from_expression(constraints::evaluate_truncate( + Ok(Some(InternalVar::from_expression(constraints::evaluate_truncate( value.expression(), bit_size, max_bit_size, evaluator, - ))) + )?))) }