Skip to content

Commit

Permalink
Merge branch 'master' into gd/issue_1445
Browse files Browse the repository at this point in the history
  • Loading branch information
guipublic committed Jul 31, 2023
2 parents f9b141b + 910f482 commit 76a8d73
Show file tree
Hide file tree
Showing 36 changed files with 573 additions and 384 deletions.
8 changes: 4 additions & 4 deletions crates/nargo/src/ops/foreign_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use acvm::{
pwg::ForeignCallWaitInfo,
};
use iter_extended::vecmap;
use noirc_abi::{decode_string_value, decode_value, input_parser::json::JsonTypes, AbiType};
use noirc_abi::{decode_string_value, input_parser::InputValueDisplay, AbiType};

use crate::errors::ForeignCallError;

Expand Down Expand Up @@ -68,11 +68,11 @@ impl ForeignCall {
// We must use a flat map here as each value in a struct will be in a separate input value
let mut input_values_as_fields =
input_values.iter().flat_map(|values| values.iter().map(|value| value.to_field()));
let decoded_value = decode_value(&mut input_values_as_fields, &abi_type)?;

let json_value = JsonTypes::try_from_input_value(&decoded_value, &abi_type)?;
let input_value_display =
InputValueDisplay::try_from_fields(&mut input_values_as_fields, abi_type)?;

println!("{json_value}");
println!("{input_value_display}");
Ok(())
}
}
Expand Down
6 changes: 3 additions & 3 deletions crates/nargo_cli/tests/test_data/array_len/src/main.nr
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use dep::std;

fn len_plus_1<T>(array: [T]) -> Field {
fn len_plus_1<T, N>(array: [T; N]) -> Field {
array.len() + 1
}

fn add_lens<T>(a: [T], b: [Field]) -> Field {
fn add_lens<T, N, M>(a: [T; N], b: [Field; M]) -> Field {
a.len() + b.len()
}

fn nested_call(b: [Field]) -> Field {
fn nested_call<N>(b: [Field; N]) -> Field {
len_plus_1(b)
}

Expand Down
98 changes: 51 additions & 47 deletions crates/nargo_cli/tests/test_data/brillig_slices/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,75 @@ use dep::std::slice;
use dep::std;

unconstrained fn main(x: Field, y: Field) {
// Mark it as mut so the compiler doesn't simplify the following operations
// But don't reuse the mut slice variable until this is fixed https://github.com/noir-lang/noir/issues/1931
let slice: [Field] = [y, x];
let mut slice: [Field] = [y, x];
assert(slice.len() == 2);

let mut pushed_back_slice = slice.push_back(7);
assert(pushed_back_slice.len() == 3);
assert(pushed_back_slice[0] == y);
assert(pushed_back_slice[1] == x);
assert(pushed_back_slice[2] == 7);
slice = slice.push_back(7);
assert(slice.len() == 3);
assert(slice[0] == y);
assert(slice[1] == x);
assert(slice[2] == 7);

// Array set on slice target
pushed_back_slice[0] = x;
pushed_back_slice[1] = y;
pushed_back_slice[2] = 1;

assert(pushed_back_slice[0] == x);
assert(pushed_back_slice[1] == y);
assert(pushed_back_slice[2] == 1);

assert(slice.len() == 2);

let pushed_front_slice = pushed_back_slice.push_front(2);
assert(pushed_front_slice.len() == 4);
assert(pushed_front_slice[0] == 2);
assert(pushed_front_slice[1] == x);
assert(pushed_front_slice[2] == y);
assert(pushed_front_slice[3] == 1);

let (item, popped_front_slice) = pushed_front_slice.pop_front();
slice[0] = x;
slice[1] = y;
slice[2] = 1;

assert(slice[0] == x);
assert(slice[1] == y);
assert(slice[2] == 1);

slice = push_front_to_slice(slice, 2);
assert(slice.len() == 4);
assert(slice[0] == 2);
assert(slice[1] == x);
assert(slice[2] == y);
assert(slice[3] == 1);

let (item, popped_front_slice) = slice.pop_front();
slice = popped_front_slice;
assert(item == 2);

assert(popped_front_slice.len() == 3);
assert(popped_front_slice[0] == x);
assert(popped_front_slice[1] == y);
assert(popped_front_slice[2] == 1);
assert(slice.len() == 3);
assert(slice[0] == x);
assert(slice[1] == y);
assert(slice[2] == 1);

let (popped_back_slice, another_item) = popped_front_slice.pop_back();
let (popped_back_slice, another_item) = slice.pop_back();
slice = popped_back_slice;
assert(another_item == 1);

assert(popped_back_slice.len() == 2);
assert(popped_back_slice[0] == x);
assert(popped_back_slice[1] == y);
assert(slice.len() == 2);
assert(slice[0] == x);
assert(slice[1] == y);

let inserted_slice = popped_back_slice.insert(1, 2);
assert(inserted_slice.len() == 3);
assert(inserted_slice[0] == x);
assert(inserted_slice[1] == 2);
assert(inserted_slice[2] == y);
slice = slice.insert(1, 2);
assert(slice.len() == 3);
assert(slice[0] == x);
assert(slice[1] == 2);
assert(slice[2] == y);

let (removed_slice, should_be_2) = inserted_slice.remove(1);
let (removed_slice, should_be_2) = slice.remove(1);
slice = removed_slice;
assert(should_be_2 == 2);

assert(removed_slice.len() == 2);
assert(removed_slice[0] == x);
assert(removed_slice[1] == y);
assert(slice.len() == 2);
assert(slice[0] == x);
assert(slice[1] == y);

let (slice_with_only_x, should_be_y) = removed_slice.remove(1);
let (slice_with_only_x, should_be_y) = slice.remove(1);
slice = slice_with_only_x;
assert(should_be_y == y);

assert(slice_with_only_x.len() == 1);
assert(removed_slice[0] == x);
assert(slice.len() == 1);
assert(slice[0] == x);

let (empty_slice, should_be_x) = slice_with_only_x.remove(0);
let (empty_slice, should_be_x) = slice.remove(0);
assert(should_be_x == x);
assert(empty_slice.len() == 0);
}

// Tests slice passing to/from functions
unconstrained fn push_front_to_slice<T>(slice: [T], item: T) -> [T] {
slice.push_front(item)
}
4 changes: 2 additions & 2 deletions crates/nargo_cli/tests/test_data/slices/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ fn main(x : Field, y : pub Field) {
/// TODO(#1889): Using slices in if statements where the condition is a witness
/// is not yet supported
let mut slice: [Field] = [0; 2];
let mut slice = [0; 2];
assert(slice[0] == 0);
assert(slice[0] != 1);
slice[0] = x;
Expand All @@ -15,7 +15,7 @@ fn main(x : Field, y : pub Field) {
assert(slice_plus_10[2] != 8);
assert(slice_plus_10.len() == 3);

let mut new_slice: [Field] = [];
let mut new_slice = [];
for i in 0..5 {
new_slice = new_slice.push_back(i);
}
Expand Down
4 changes: 2 additions & 2 deletions crates/noirc_abi/src/input_parser/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub(crate) fn serialize_to_json(

#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub enum JsonTypes {
pub(super) enum JsonTypes {
// This is most likely going to be a hex string
// But it is possible to support UTF-8
String(String),
Expand All @@ -78,7 +78,7 @@ pub enum JsonTypes {
}

impl JsonTypes {
pub fn try_from_input_value(
pub(super) fn try_from_input_value(
value: &InputValue,
abi_type: &AbiType,
) -> Result<JsonTypes, InputParserError> {
Expand Down
35 changes: 32 additions & 3 deletions crates/noirc_abi/src/input_parser/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
pub mod json;
mod json;
mod toml;

use std::collections::BTreeMap;

use acvm::FieldElement;
use serde::Serialize;

use crate::errors::InputParserError;
use crate::{Abi, AbiType};
use crate::errors::{AbiError, InputParserError};
use crate::{decode_value, Abi, AbiType};
/// This is what all formats eventually transform into
/// For example, a toml file will parse into TomlTypes
/// and those TomlTypes will be mapped to Value
Expand Down Expand Up @@ -67,6 +67,35 @@ impl InputValue {
}
}

/// In order to display an `InputValue` we need an `AbiType` to accurately
/// convert the value into a human-readable format.
pub struct InputValueDisplay {
input_value: InputValue,
abi_type: AbiType,
}

impl InputValueDisplay {
pub fn try_from_fields(
field_iterator: &mut impl Iterator<Item = FieldElement>,
abi_type: AbiType,
) -> Result<Self, AbiError> {
let input_value = decode_value(field_iterator, &abi_type)?;
Ok(InputValueDisplay { input_value, abi_type })
}
}

impl std::fmt::Display for InputValueDisplay {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// From the docs: https://doc.rust-lang.org/std/fmt/struct.Error.html
// This type does not support transmission of an error other than that an error
// occurred. Any extra information must be arranged to be transmitted through
// some other means.
let json_value = json::JsonTypes::try_from_input_value(&self.input_value, &self.abi_type)
.map_err(|_| std::fmt::Error)?;
write!(f, "{}", serde_json::to_string(&json_value).map_err(|_| std::fmt::Error)?)
}
}

/// The different formats that are supported when parsing
/// the initial witness values
#[cfg_attr(test, derive(strum_macros::EnumIter))]
Expand Down
2 changes: 1 addition & 1 deletion crates/noirc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ impl Abi {
}
}

pub fn decode_value(
fn decode_value(
field_iterator: &mut impl Iterator<Item = FieldElement>,
value_type: &AbiType,
) -> Result<InputValue, AbiError> {
Expand Down
31 changes: 23 additions & 8 deletions crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -800,22 +800,37 @@ impl<'block> BrilligBlock<'block> {
value_id,
dfg,
);
let heap_array = self.function_context.extract_heap_array(new_variable);

self.brillig_context
.allocate_fixed_length_array(heap_array.pointer, heap_array.size);
// Initialize the variable
let pointer = match new_variable {
RegisterOrMemory::HeapArray(heap_array) => {
self.brillig_context
.allocate_fixed_length_array(heap_array.pointer, array.len());

heap_array.pointer
}
RegisterOrMemory::HeapVector(heap_vector) => {
self.brillig_context
.const_instruction(heap_vector.size, array.len().into());
self.brillig_context
.allocate_array_instruction(heap_vector.pointer, heap_vector.size);

heap_vector.pointer
}
_ => unreachable!(
"ICE: Cannot initialize array value created as {new_variable:?}"
),
};

// Write the items

// Allocate a register for the iterator
let iterator_register = self.brillig_context.make_constant(0_usize.into());

for element_id in array.iter() {
let element_variable = self.convert_ssa_value(*element_id, dfg);
// Store the item in memory
self.store_variable_in_array(
heap_array.pointer,
iterator_register,
element_variable,
);
self.store_variable_in_array(pointer, iterator_register, element_variable);
// Increment the iterator
self.brillig_context.usize_op_in_place(iterator_register, BinaryIntOp::Add, 1);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,8 @@ impl AcirContext {
}

/// Returns an `AcirVar` which will be constrained to be lhs mod 2^{rhs}
/// In order to do this, we simply perform euclidian division of lhs by 2^{rhs}
/// The remainder of the division is then lhs mod 2^{rhs}
pub(crate) fn truncate_var(
&mut self,
lhs: AcirVar,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,16 +248,19 @@ impl GeneratedAcir {
) -> Expression {
let max_power_of_two =
FieldElement::from(2_i128).pow(&FieldElement::from(max_bit_size as i128 - 1));
let inter = &(&Expression::from_field(max_power_of_two) - lhs) * &leading.into();
lhs.add_mul(FieldElement::from(2_i128), &inter.unwrap())

let intermediate =
self.mul_with_witness(&(&Expression::from(max_power_of_two) - lhs), &leading.into());

lhs.add_mul(FieldElement::from(2_i128), &intermediate)
}

/// Returns an expression which represents `lhs * rhs`
///
/// If one has multiplicative term and the other is of degree one or more,
/// the function creates [intermediate variables][`Witness`] accordingly.
/// There are two cases where we can optimize the multiplication between two expressions:
/// 1. If both expressions have at most a total degree of 1 in each term, then we can just multiply them
/// 1. If the sum of the degrees of both expressions is at most 2, then we can just multiply them
/// as each term in the result will be degree-2.
/// 2. If one expression is a constant, then we can just multiply the constant with the other expression
///
Expand All @@ -268,10 +271,14 @@ impl GeneratedAcir {
let lhs_is_linear = lhs.is_linear();
let rhs_is_linear = rhs.is_linear();

// Case 1: Both expressions have at most a total degree of 1 in each term
if lhs_is_linear && rhs_is_linear {
return (lhs * rhs)
.expect("one of the expressions is a constant and so this should not fail");
// Case 1: The sum of the degrees of both expressions is at most 2.
//
// If one of the expressions is constant then it does not increase the degree when multiplying by another expression.
// If both of the expressions are linear (degree <=1) then the product will be at most degree 2.
let both_are_linear = lhs_is_linear && rhs_is_linear;
let either_is_const = lhs.is_const() || rhs.is_const();
if both_are_linear || either_is_const {
return (lhs * rhs).expect("Both expressions are degree <= 1");
}

// Case 2: One or both of the sides needs to be reduced to a degree-1 univariate polynomial
Expand All @@ -285,7 +292,7 @@ impl GeneratedAcir {
// rhs, we only need to square the lhs.
if lhs == rhs {
return (&*lhs_reduced * &*lhs_reduced)
.expect("Both expressions are reduced to be degree<=1");
.expect("Both expressions are reduced to be degree <= 1");
};

let rhs_reduced = if rhs_is_linear {
Expand All @@ -294,7 +301,7 @@ impl GeneratedAcir {
Cow::Owned(self.get_or_create_witness(rhs).into())
};

(&*lhs_reduced * &*rhs_reduced).expect("Both expressions are reduced to be degree<=1")
(&*lhs_reduced * &*rhs_reduced).expect("Both expressions are reduced to be degree <= 1")
}

/// Signed division lhs / rhs
Expand Down
3 changes: 2 additions & 1 deletion crates/noirc_evaluator/src/ssa_refactor/acir_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1057,7 +1057,8 @@ mod tests {
let one = builder.field_constant(FieldElement::one());

let element_type = Rc::new(vec![Type::field()]);
let array = builder.array_constant(im::Vector::unit(one), element_type);
let array_type = Type::Array(element_type, 1);
let array = builder.array_constant(im::Vector::unit(one), array_type);

builder.terminate_with_return(vec![array]);

Expand Down
Loading

0 comments on commit 76a8d73

Please sign in to comment.