Skip to content

Commit

Permalink
feat: use RAM/ROM opcode when supported by the backend (#1282)
Browse files Browse the repository at this point in the history
* assign a VM per array

* cargo format

* Code review

* code review

* enable dynamic arrays

* use trace index as counter

* restore bad commit

* restore bad commit

* restore bad commit

* Code review

* Code review

* Code review

* MemAddress

* merge from master

* Remove ilog2 because it is tagged as unstable

* clippy

* *WIP* handle block opcode

* **WIP** support RAM/ROM/BLOCK opcodes

* fikx the build after merge

* clean-up

* missed this one

* code review

* code review

* Fix issue with array_len

* clarify comment

* code review

* code review

* Code review

* Hardcode BB support for memory gates

* Fix clippy error

* Code review

* Check for array length before adding a memory gate

* avoid empty memory constraints

* Use correct trace length

* Create a witness for memory operations

* use barretenberg with dynamic array fixes

* Update to last commit of bb noir branch, through flake.lock
  • Loading branch information
guipublic authored Jun 14, 2023
1 parent 825679b commit 242f07b
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 12 deletions.
126 changes: 117 additions & 9 deletions crates/noirc_evaluator/src/ssa/acir_gen/acir_mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,95 @@ impl ArrayHeap {
self.staged.insert(index, (value, op));
}

/// This helper function transforms an expression into a single witness representing the expression
fn normalize_expression(expr: &Expression, evaluator: &mut Evaluator) -> Expression {
expr.to_witness()
.unwrap_or_else(|| evaluator.create_intermediate_variable(expr.clone()))
.into()
}

/// Decide which opcode to use, depending on the backend support
fn acir_gen(
&self,
evaluator: &mut Evaluator,
array_id: ArrayId,
array_len: u32,
is_opcode_supported: OpcodeSupported,
) {
//sanity check
if array_len == 0 || self.trace.is_empty() {
return;
}
let dummy = MemoryBlock { id: AcirBlockId(0), len: 0, trace: Vec::new() };
let trace_len = match self.typ {
ArrayType::ReadOnly(Some(len)) | ArrayType::ReadWrite(Some(len)) => len,
_ => self.trace.len(),
};

if is_opcode_supported(&AcirOpcode::ROM(dummy.clone())) {
// If the backend support ROM and the array is read-only, we generate the ROM opcode
if matches!(self.typ, ArrayType::ReadOnly(_)) {
self.add_rom_opcode(evaluator, array_id, array_len, trace_len);
return;
}
}
if is_opcode_supported(&AcirOpcode::Block(dummy.clone())) {
self.add_block_opcode(evaluator, array_id, array_len);
} else if is_opcode_supported(&AcirOpcode::RAM(dummy)) {
self.add_ram_opcode(evaluator, array_id, array_len, trace_len);
} else {
self.generate_permutation_constraints(evaluator, array_id, array_len);
}
}

fn add_block_opcode(&self, evaluator: &mut Evaluator, array_id: ArrayId, array_len: u32) {
evaluator.opcodes.push(AcirOpcode::Block(MemoryBlock {
id: AcirBlockId(array_id.as_u32()),
len: array_len,
trace: self.trace.clone(),
}));
}

fn add_rom_opcode(
&self,
evaluator: &mut Evaluator,
array_id: ArrayId,
array_len: u32,
trace_len: usize,
) {
let mut trace = Vec::with_capacity(trace_len);
for op in self.trace.iter().take(trace_len) {
let index = Self::normalize_expression(&op.index, evaluator);
let value = Self::normalize_expression(&op.value, evaluator);
trace.push(MemOp { operation: op.operation.clone(), index, value });
}
evaluator.opcodes.push(AcirOpcode::ROM(MemoryBlock {
id: AcirBlockId(array_id.as_u32()),
len: array_len,
trace,
}));
}

fn add_ram_opcode(
&self,
evaluator: &mut Evaluator,
array_id: ArrayId,
array_len: u32,
trace_len: usize,
) {
let mut trace = Vec::with_capacity(trace_len);
for op in self.trace.iter().take(trace_len) {
let index = Self::normalize_expression(&op.index, evaluator);
let value = Self::normalize_expression(&op.value, evaluator);
trace.push(MemOp { operation: op.operation.clone(), index, value });
}
evaluator.opcodes.push(AcirOpcode::RAM(MemoryBlock {
id: AcirBlockId(array_id.as_u32()),
len: array_len,
trace,
}));
}

fn generate_outputs(
inputs: Vec<Expression>,
bits: &mut Vec<Witness>,
Expand All @@ -130,22 +219,21 @@ impl ArrayHeap {
}
outputs
}

pub(crate) fn acir_gen(&self, evaluator: &mut Evaluator, array_id: ArrayId, array_len: u32) {
fn generate_permutation_constraints(
&self,
evaluator: &mut Evaluator,
array_id: ArrayId,
array_len: u32,
) {
let (len, read_write) = match self.typ {
ArrayType::Init(_, _) | ArrayType::WriteOnly => (0, true),
ArrayType::ReadOnly(last) => (last.unwrap_or(self.trace.len()), false),
ArrayType::ReadWrite(last) => (last.unwrap_or(self.trace.len()), true),
};

if len == 0 {
return;
}
evaluator.opcodes.push(AcirOpcode::Block(MemoryBlock {
id: AcirBlockId(array_id.as_u32()),
len: array_len,
trace: self.trace.clone(),
}));
self.add_block_opcode(evaluator, array_id, array_len);
let len_bits = AcirMem::bits(len);
// permutations
let mut in_counter = Vec::new();
Expand Down Expand Up @@ -262,6 +350,17 @@ impl AcirMem {
}
}

//Ensure we do not optimise writes when the array is returned
pub(crate) fn return_array(&mut self, array_id: ArrayId) {
let heap = self.array_heap_mut(array_id);
match heap.typ {
ArrayType::ReadOnly(_) | ArrayType::ReadWrite(_) => {
heap.typ = ArrayType::ReadWrite(None);
}
_ => (),
}
}

// Load array values into InternalVars
pub(super) fn load_array(
&mut self,
Expand Down Expand Up @@ -327,9 +426,18 @@ impl AcirMem {
self.array_heap_mut(*array_id).push(item);
}
pub(crate) fn acir_gen(&self, evaluator: &mut Evaluator, ctx: &SsaContext) {
//Temporary hack - We hardcode Barretenberg support here.
//TODO: to remove once opcodesupported usage is clarified
let is_opcode_supported: OpcodeSupported = |o| match o {
AcirOpcode::Block(_) => false,
AcirOpcode::ROM(_) | AcirOpcode::RAM(_) => true,
_ => unreachable!(),
};
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, is_opcode_supported);
}
}
}

type OpcodeSupported = fn(&AcirOpcode) -> bool;
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub(crate) fn evaluate(
let objects = match Memory::deref(ctx, *node_id) {
Some(a) => {
let array = &ctx.mem[a];
memory_map.return_array(a);
memory_map.load_array(array, evaluator)
}
None => {
Expand Down
6 changes: 3 additions & 3 deletions flake.lock

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

0 comments on commit 242f07b

Please sign in to comment.