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

feat: read-only array #899

Merged
merged 54 commits into from
Mar 23, 2023
Merged
Changes from 48 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
bcb631d
assign a VM per array
guipublic Feb 13, 2023
4668c72
Merge branch 'master' into gd/dynamic_array4
guipublic Feb 13, 2023
520c2f9
cargo format
guipublic Feb 13, 2023
9764650
Code review
guipublic Feb 14, 2023
50e7d57
code review
guipublic Feb 14, 2023
6dbe6f6
Merge branch 'master' into gd/dynamic_array5
guipublic Feb 14, 2023
528051b
Merge branch 'master' into gd/dynamic_array5
guipublic Feb 20, 2023
8f8d0c5
enable dynamic arrays
guipublic Feb 21, 2023
b01a92c
Merge branch 'master' into gd/dynamic_array5
guipublic Feb 21, 2023
2d51e6f
use trace index as counter
guipublic Feb 21, 2023
cb43e94
restore bad commit
guipublic Feb 21, 2023
c5a0bf9
restore bad commit
guipublic Feb 21, 2023
327fe57
restore bad commit
guipublic Feb 21, 2023
cb6b183
read-only arrays
guipublic Feb 22, 2023
2aa2fb5
Code review
guipublic Feb 22, 2023
3dcf123
Merge branch 'gd/dynamic_array5' into gd/dynamic_array6
guipublic Feb 22, 2023
55afff4
Code review
guipublic Feb 23, 2023
e6eface
Merge branch 'master' into gd/dynamic_array5
guipublic Feb 23, 2023
95192a1
Merge branch 'gd/dynamic_array5' into gd/dynamic_array6
guipublic Feb 23, 2023
c23cefb
Merge branch 'master' into gd/dynamic_array5
guipublic Feb 23, 2023
a027e74
Merge branch 'master' into gd/dynamic_array5
guipublic Feb 24, 2023
7502d9a
Merge branch 'gd/dynamic_array5' into gd/dynamic_array6
guipublic Feb 24, 2023
af17ec6
Code review
guipublic Feb 28, 2023
36ca66c
Merge branch 'master' into gd/dynamic_array5
guipublic Feb 28, 2023
08cedf8
Merge branch 'gd/dynamic_array5' into gd/dynamic_array6
guipublic Feb 28, 2023
0c1bc4e
MemAddress
guipublic Feb 28, 2023
91ea00c
Merge branch 'gd/dynamic_array5' into gd/dynamic_array6
guipublic Feb 28, 2023
1e3861f
Merge branch 'master' into gd/dynamic_array5
guipublic Mar 10, 2023
352b447
Merge branch 'master' into gd/dynamic_array5
guipublic Mar 10, 2023
966b557
merge from master
guipublic Mar 10, 2023
ff90d69
Merge branch 'master' into gd/dynamic_array5
guipublic Mar 10, 2023
af8d748
Merge branch 'gd/dynamic_array5' into gd/dynamic_array6
guipublic Mar 10, 2023
cb93f6e
Remove ilog2 because it is tagged as unstable
guipublic Mar 10, 2023
9002218
Merge branch 'master' into gd/dynamic_array5
guipublic Mar 14, 2023
c79e9dc
clippy
guipublic Mar 14, 2023
5a5f158
Merge branch 'master' into gd/dynamic_array5
guipublic Mar 16, 2023
d171336
Merge branch 'gd/dynamic_array5' into gd/dynamic_array6
guipublic Mar 17, 2023
f13731b
Merge branch 'master' into gd/dynamic_array5
guipublic Mar 17, 2023
aa26c0c
Merge branch 'gd/dynamic_array5' into gd/dynamic_array6
guipublic Mar 17, 2023
e406af7
de-activate dynamic arrays
guipublic Mar 17, 2023
023ab4e
cargo fmt
guipublic Mar 17, 2023
61c24ad
Code review
guipublic Mar 20, 2023
f4e590f
format
guipublic Mar 20, 2023
10e94a4
Merge branch 'gd/dynamic_array5' into gd/dynamic_array6
guipublic Mar 21, 2023
ddd87dc
Merge branch 'master' into gd/dynamic_array6
guipublic Mar 21, 2023
d0d73af
Merge branch 'master' into gd/dynamic_array6
guipublic Mar 21, 2023
6ba6849
document arrayType
guipublic Mar 21, 2023
8f3f56a
Add more comments
guipublic Mar 21, 2023
10281be
Code review
guipublic Mar 22, 2023
823d7d0
Code review - style change
guipublic Mar 23, 2023
fe0ca41
Merge branch 'master' into gd/dynamic_array6
guipublic Mar 23, 2023
2aa5db5
Update crates/noirc_evaluator/src/ssa/acir_gen/acir_mem.rs
guipublic Mar 23, 2023
3b157f6
Code review - style
guipublic Mar 23, 2023
241b2b2
Merge branch 'gd/dynamic_array6' of https://github.com/noir-lang/noir…
guipublic Mar 23, 2023
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
127 changes: 109 additions & 18 deletions crates/noirc_evaluator/src/ssa/acir_gen/acir_mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use acvm::{
};

use iter_extended::vecmap;
use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashSet};

use super::{
constraints::{self, mul_with_witness, subtract},
Expand All @@ -25,21 +25,42 @@ use super::{
/// Represent a memory operation on the ArrayHeap, at the specified index
/// Operation is one for a store and 0 for a load
#[derive(Clone, Debug)]
struct MemOp {
pub(crate) struct MemOp {
operation: Expression,
value: Expression,
index: Expression,
}

type MemAddress = u32;

enum ArrayType {
/// Initialization phase: intializing the array with writes on the 0..array.len range
/// It contains the HashSet of the initialised indexes and the maximum of these indexes
guipublic marked this conversation as resolved.
Show resolved Hide resolved
Init(HashSet<MemAddress>, u32),
guipublic marked this conversation as resolved.
Show resolved Hide resolved
/// Array is only written on, never read
WriteOnly,
/// Initialization phase and then only read, and optionally a bunch of writes at the end
/// The optional usize indicates the position of the ending writes if any: after this position, there are only writes
ReadOnly(Option<usize>),
/// Reads and writes outside the initialization phase
/// The optional usize indicates the position of the ending writes if any: after this position, there are only writes
ReadWrite(Option<usize>),
}

impl Default for ArrayType {
fn default() -> Self {
ArrayType::Init(HashSet::default(), 0)
}
}

#[derive(Default)]
struct ArrayHeap {
// maps memory address to InternalVar
memory_map: BTreeMap<MemAddress, InternalVar>,
trace: Vec<MemOp>,
// maps memory address to (values,operation) that must be committed to the trace
staged: BTreeMap<MemAddress, (Expression, Expression)>,
typ: ArrayType,
}

impl ArrayHeap {
Expand All @@ -55,12 +76,53 @@ impl ArrayHeap {
self.staged.clear();
}

fn push(&mut self, index: Expression, value: Expression, op: Expression) {
let item = MemOp { operation: op, value, index };
fn push(&mut self, item: MemOp) {
let is_load = item.operation == Expression::zero();
let index_const = item.index.to_const();
self.typ = match &self.typ {
ArrayType::Init(init_idx, len) => match (is_load, index_const) {
(false, Some(idx)) => {
let idx: MemAddress = idx.to_u128().try_into().unwrap();
let mut init_idx2 = init_idx.clone();
let mut len2 = *len;
init_idx2.insert(idx);
if idx + 1 > len2 {
len2 = idx + 1;
}
jfecher marked this conversation as resolved.
Show resolved Hide resolved
ArrayType::Init(init_idx2, len2)
}
(false, None) => ArrayType::WriteOnly,
(true, _) => {
if *len as usize == init_idx.len() {
ArrayType::ReadOnly(None)
} else {
ArrayType::ReadWrite(None)
}
}
},
ArrayType::WriteOnly => {
if is_load {
ArrayType::ReadWrite(None)
} else {
ArrayType::WriteOnly
}
}
ArrayType::ReadOnly(last) => match (is_load, last) {
(true, Some(_)) => ArrayType::ReadWrite(None),
jfecher marked this conversation as resolved.
Show resolved Hide resolved
(true, None) => ArrayType::ReadOnly(None),
(false, None) => ArrayType::ReadOnly(Some(self.trace.len())),
(false, Some(_)) => ArrayType::ReadOnly(*last),
},
ArrayType::ReadWrite(last) => match (is_load, last) {
(true, _) => ArrayType::ReadWrite(None),
(false, None) => ArrayType::ReadWrite(Some(self.trace.len())),
(false, Some(_)) => ArrayType::ReadWrite(*last),
},
};
self.trace.push(item);
}

fn stage(&mut self, index: u32, value: Expression, op: Expression) {
fn stage(&mut self, index: MemAddress, value: Expression, op: Expression) {
self.staged.insert(index, (value, op));
}

Expand All @@ -77,8 +139,21 @@ impl ArrayHeap {
}
outputs
}

pub(crate) fn acir_gen(&self, evaluator: &mut Evaluator) {
let len = self.trace.len();
let mut len = self.trace.len();

let mut read_write = true;
match self.typ {
ArrayType::Init(_, _) | ArrayType::WriteOnly => len = 0,
ArrayType::ReadOnly(last) => {
len = last.unwrap_or(len);
read_write = false;
}
ArrayType::ReadWrite(last) => {
len = last.unwrap_or(len);
}
}
jfecher marked this conversation as resolved.
Show resolved Hide resolved
if len == 0 {
return;
}
Expand All @@ -90,29 +165,36 @@ impl ArrayHeap {
let mut in_op = Vec::new();

let mut tuple_expressions = Vec::new();
for (counter, item) in self.trace.iter().enumerate() {
for (counter, item) in self.trace.iter().take(len).enumerate() {
let counter_expr = Expression::from_field(FieldElement::from(counter as i128));
in_counter.push(counter_expr.clone());
in_index.push(item.index.clone());
in_value.push(item.value.clone());
in_op.push(item.operation.clone());
if read_write {
in_op.push(item.operation.clone());
}
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_op = Self::generate_outputs(in_op, &mut bit_counter, evaluator);

let out_op = if read_write {
Self::generate_outputs(in_op, &mut bit_counter, evaluator)
} else {
Vec::new()
};
// sort directive
evaluator.opcodes.push(AcirOpcode::Directive(Directive::PermutationSort {
inputs: tuple_expressions,
tuple: 2,
bits: bit_counter,
sort_by: vec![0, 1],
}));
let init = subtract(&out_op[0], FieldElement::one(), &Expression::one());
evaluator.opcodes.push(AcirOpcode::Arithmetic(init));
if read_write {
let init = subtract(&out_op[0], FieldElement::one(), &Expression::one());
evaluator.opcodes.push(AcirOpcode::Arithmetic(init));
}
for i in 0..len - 1 {
// index sort
let index_sub = subtract(&out_index[i + 1], FieldElement::one(), &out_index[i]);
Expand All @@ -134,11 +216,19 @@ impl ArrayHeap {
);
evaluator.opcodes.push(AcirOpcode::Arithmetic(secondary_order));
// consistency checks
let sub1 = subtract(&Expression::one(), FieldElement::one(), &out_op[i + 1]);
let sub2 = subtract(&out_value[i + 1], FieldElement::one(), &out_value[i]);
let load_on_same_adr = mul_with_witness(evaluator, &sub1, &sub2);
let store_on_new_adr = mul_with_witness(evaluator, &index_sub, &sub1);
evaluator.opcodes.push(AcirOpcode::Arithmetic(store_on_new_adr));
let load_on_same_adr = if read_write {
let sub1 = subtract(&Expression::one(), FieldElement::one(), &out_op[i + 1]);
let store_on_new_adr = mul_with_witness(evaluator, &index_sub, &sub1);
evaluator.opcodes.push(AcirOpcode::Arithmetic(store_on_new_adr));
mul_with_witness(evaluator, &sub1, &sub2)
} else {
subtract(
&mul_with_witness(evaluator, &index_sub, &sub2),
FieldElement::one(),
&sub2,
)
};
evaluator.opcodes.push(AcirOpcode::Arithmetic(load_on_same_adr));
}
}
Expand All @@ -152,7 +242,7 @@ pub(crate) struct AcirMem {

impl AcirMem {
// Returns the memory_map for the array
fn array_map_mut(&mut self, array_id: ArrayId) -> &mut BTreeMap<u32, InternalVar> {
fn array_map_mut(&mut self, array_id: ArrayId) -> &mut BTreeMap<MemAddress, InternalVar> {
&mut self.virtual_memory.entry(array_id).or_default().memory_map
}

Expand Down Expand Up @@ -235,7 +325,8 @@ impl AcirMem {
op: Expression,
) {
self.commit(array_id, op != Expression::zero());
self.array_heap_mut(*array_id).push(index, value, op);
let item = MemOp { operation: op, value, index };
self.array_heap_mut(*array_id).push(item);
}
pub(crate) fn acir_gen(&self, evaluator: &mut Evaluator) {
for mem in &self.virtual_memory {
Expand Down