Skip to content

Commit

Permalink
feat: dynamic arrays for experimental-ssa (#1969)
Browse files Browse the repository at this point in the history
* dynamic arrays for experimental-ssa

* Code review

* switch to new acvm and backend

* panic on non-homogenous arrays
The PR does not supported them.

* switch to new backend

* update cargo.lock

* small cleanup

* small cleanup

* clippy

* remove helper method, doesn't help with readability

* small cleanup

* small cleanup

* typo

* Update crates/noirc_evaluator/src/ssa_refactor/acir_gen/mod.rs

* small cleanup

* clippy

* add function comment

* documentation

* rename AcirArray -> AcirDynamicArray

* use helper method for AcirVar -> Witness

* use Option for optional values

* remove TODO

---------

Co-authored-by: kevaundray <kevtheappdev@gmail.com>
  • Loading branch information
guipublic and kevaundray authored Jul 24, 2023
1 parent 5526c52 commit 08d199a
Show file tree
Hide file tree
Showing 8 changed files with 383 additions and 62 deletions.
34 changes: 18 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ edition = "2021"
rust-version = "1.66"

[workspace.dependencies]
acvm = "0.19.1"
acvm = "0.20.0"
arena = { path = "crates/arena" }
fm = { path = "crates/fm" }
iter-extended = { path = "crates/iter-extended" }
Expand Down
3 changes: 2 additions & 1 deletion crates/nargo_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ color-eyre = "0.6.2"
tokio = { version = "1.0", features = ["io-std"] }

# Backends
acvm-backend-barretenberg = { version = "0.9.0", default-features = false }
acvm-backend-barretenberg = { version = "0.9.1", default-features = false }

[dev-dependencies]
tempdir = "0.3.7"
Expand All @@ -62,3 +62,4 @@ default = ["plonk_bn254"]
plonk_bn254 = ["acvm-backend-barretenberg/native"]
plonk_bn254_wasm = ["acvm-backend-barretenberg/wasm"]
flat_witness = ["acvm-backend-barretenberg/native"]

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
authors = [""]
compiler_version = "0.1"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
x = [104, 101, 108, 108, 111]
z = "59"
t = "10"
index = [0,1,2,3,4]
index2 = [0,1,2,3,4]
offset = 1
sublen = 2


Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

fn main(x: [u32; 5], mut z: u32, t: u32, index: [Field;5], index2: [Field;5], offset: Field, sublen: Field) {
let idx = (z - 5*t - 5) as Field;
//dynamic array test
dyn_array(x, idx, idx - 3);

//regression for issue 1283
let mut s = 0;
let x3 = [246,159,32,176,8];
for i in 0..5 {
s += x3[index[i]];
}
assert(s!=0);

if 3 < (sublen as u32) {
assert(index[offset + 3] == index2[3]);
}
}

fn dyn_array(mut x: [u32; 5], y: Field, z: Field) {
assert(x[y] == 111);
assert(x[z] == 101);
x[z] = 0;
assert(x[y] == 111);
assert(x[1] == 0);
if y as u32 < 10 {
x[y] = x[y] - 2;
} else {
x[y] = 0;
}
assert(x[4] == 109);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use super::{errors::AcirGenError, generated_acir::GeneratedAcir};
use crate::brillig::brillig_gen::brillig_directive;
use crate::ssa_refactor::acir_gen::AcirValue;
use crate::ssa_refactor::acir_gen::{AcirDynamicArray, AcirValue};
use crate::ssa_refactor::ir::types::Type as SsaType;
use crate::ssa_refactor::ir::{instruction::Endian, types::NumericType};
use acvm::acir::circuit::opcodes::{BlockId, MemOp};
use acvm::acir::circuit::Opcode;
use acvm::acir::{
brillig::Opcode as BrilligOpcode,
circuit::brillig::{BrilligInputs, BrilligOutputs},
Expand Down Expand Up @@ -210,7 +212,9 @@ impl AcirContext {
assert_eq!(results.len(), 1);
match results[0] {
AcirValue::Var(var, _) => var,
AcirValue::Array(_) => unreachable!("ICE - expected a variable"),
AcirValue::DynamicArray(_) | AcirValue::Array(_) => {
unreachable!("ICE - expected a variable")
}
}
}

Expand Down Expand Up @@ -789,6 +793,7 @@ impl AcirContext {
Self::flatten_value(acir_vars, value);
}
}
AcirValue::DynamicArray(_) => unreachable!("Cannot flatten a dynamic array"),
}
}

Expand Down Expand Up @@ -826,6 +831,11 @@ impl AcirContext {
}
BrilligInputs::Array(var_expressions)
}
AcirValue::DynamicArray(_) => {
let mut var_expressions = Vec::new();
self.brillig_array_input(&mut var_expressions, i);
BrilligInputs::Array(var_expressions)
}
});

let mut b_outputs = Vec::new();
Expand Down Expand Up @@ -857,7 +867,7 @@ impl AcirContext {
outputs_var
}

fn brillig_array_input(&self, var_expressions: &mut Vec<Expression>, input: AcirValue) {
fn brillig_array_input(&mut self, var_expressions: &mut Vec<Expression>, input: AcirValue) {
match input {
AcirValue::Var(var, _) => {
var_expressions.push(self.vars[&var].to_expression().into_owned());
Expand All @@ -867,6 +877,24 @@ impl AcirContext {
self.brillig_array_input(var_expressions, var);
}
}
AcirValue::DynamicArray(AcirDynamicArray { block_id, len }) => {
for i in 0..len {
// We generate witnesses corresponding to the array values
let index = AcirValue::Var(
self.add_constant(FieldElement::from(i as u128)),
AcirType::NumericType(NumericType::NativeField),
);
let index_var = index.into_var();

let value_read_var = self.read_from_memory(block_id, &index_var);
let value_read = AcirValue::Var(
value_read_var,
AcirType::NumericType(NumericType::NativeField),
);

self.brillig_array_input(var_expressions, value_read);
}
}
}
}

Expand Down Expand Up @@ -901,6 +929,11 @@ impl AcirContext {

Ok(outputs_var)
}
/// Converts an AcirVar to a Witness
fn var_to_witness(&mut self, var: AcirVar) -> Witness {
let var_data = self.vars.get(&var).expect("ICE: undeclared AcirVar");
self.acir_ir.get_or_create_witness(&var_data.to_expression())
}

/// Constrain lhs to be less than rhs
fn less_than_constrain(
Expand All @@ -913,6 +946,72 @@ impl AcirContext {
let lhs_less_than_rhs = self.more_than_eq_var(rhs, lhs, bit_size, predicate)?;
self.maybe_eq_predicate(lhs_less_than_rhs, predicate)
}

/// Returns a Variable that is constrained to be the result of reading
/// from the memory `block_id` at the given `index`.
pub(crate) fn read_from_memory(&mut self, block_id: BlockId, index: &AcirVar) -> AcirVar {
// Fetch the witness corresponding to the index
let index_witness = self.var_to_witness(*index);

// Create a Variable to hold the result of the read and extract the corresponding Witness
let value_read_var = self.add_variable();
let value_read_witness = self.var_to_witness(value_read_var);

// Add the memory read operation to the list of opcodes
let read_op = Expression::zero();
let op = MemOp {
operation: read_op,
index: index_witness.into(),
value: value_read_witness.into(),
};
self.acir_ir.opcodes.push(Opcode::MemoryOp { block_id, op });

value_read_var
}

/// Constrains the Variable `value` to be the new value located at `index` in the memory `block_id`.
pub(crate) fn write_to_memory(&mut self, block_id: BlockId, index: &AcirVar, value: &AcirVar) {
// Fetch the witness corresponding to the index
//
let index_witness = self.var_to_witness(*index);

// Fetch the witness corresponding to the value to be written
let value_write_witness = self.var_to_witness(*value);

// Add the memory write operation to the list of opcodes
let write_op = Expression::one();
let op = MemOp {
operation: write_op,
index: index_witness.into(),
value: value_write_witness.into(),
};
self.acir_ir.opcodes.push(Opcode::MemoryOp { block_id, op });
}

/// Initializes an array in memory with the given values `optional_values`.
/// If `optional_values` is empty, then the array is initialized with zeros.
pub(crate) fn initialize_array(
&mut self,
block_id: BlockId,
len: usize,
optional_values: Option<&[AcirValue]>,
) {
// If the optional values are supplied, then we fill the initialized
// array with those values. If not, then we fill it with zeros.
let initialized_values = match optional_values {
None => {
let zero = self.add_constant(FieldElement::zero());
let zero_witness = self.var_to_witness(zero);
vec![zero_witness; len]
}
Some(optional_values) => vecmap(optional_values, |value| {
let value = value.clone().into_var();
self.var_to_witness(value)
}),
};

self.acir_ir.opcodes.push(Opcode::MemoryInit { block_id, init: initialized_values });
}
}

/// Enum representing the possible values that a
Expand Down
Loading

0 comments on commit 08d199a

Please sign in to comment.