Skip to content

Commit

Permalink
feat: Memory only brillig (AztecProtocol#4215)
Browse files Browse the repository at this point in the history
This PR adds:
 - Memory only brillig noir-lang/noir#4158
 - Calldata and returndata noir-lang/noir#4159

We reserve some memory slots at the start of the memory space. We
reserve 1024 items for the stack frame, the calldata size and the
returndata size.
  • Loading branch information
sirasistant authored Jan 30, 2024
1 parent 9e6605c commit 018177b
Show file tree
Hide file tree
Showing 34 changed files with 1,799 additions and 1,708 deletions.
411 changes: 246 additions & 165 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp

Large diffs are not rendered by default.

371 changes: 217 additions & 154 deletions noir/acvm-repo/acir/codegen/acir.cpp

Large diffs are not rendered by default.

6 changes: 2 additions & 4 deletions noir/acvm-repo/acir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ mod reflection {
path::{Path, PathBuf},
};

use brillig::{
BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode, RegisterOrMemory,
};
use brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode, ValueOrArray};
use serde_reflection::{Tracer, TracerConfig};

use crate::{
Expand Down Expand Up @@ -69,7 +67,7 @@ mod reflection {
tracer.trace_simple_type::<BinaryIntOp>().unwrap();
tracer.trace_simple_type::<BlackBoxOp>().unwrap();
tracer.trace_simple_type::<Directive>().unwrap();
tracer.trace_simple_type::<RegisterOrMemory>().unwrap();
tracer.trace_simple_type::<ValueOrArray>().unwrap();

let registry = tracer.registry().unwrap();

Expand Down
83 changes: 54 additions & 29 deletions noir/acvm-repo/acir/tests/test_program_serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use acir::{
native_types::{Expression, Witness},
};
use acir_field::FieldElement;
use brillig::{HeapArray, RegisterIndex, RegisterOrMemory};
use brillig::{HeapArray, MemoryAddress, ValueOrArray};

#[test]
fn addition_circuit() {
Expand Down Expand Up @@ -173,15 +173,23 @@ fn simple_brillig_foreign_call() {
inputs: vec![
BrilligInputs::Single(w_input.into()), // Input Register 0,
],
// This tells the BrilligSolver which witnesses its output registers correspond to
// This tells the BrilligSolver which witnesses its output values correspond to
outputs: vec![
BrilligOutputs::Simple(w_inverted), // Output Register 1
],
bytecode: vec![brillig::Opcode::ForeignCall {
function: "invert".into(),
destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))],
inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))],
}],
bytecode: vec![
brillig::Opcode::CalldataCopy {
destination_address: MemoryAddress(0),
size: 1,
offset: 0,
},
brillig::Opcode::ForeignCall {
function: "invert".into(),
destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))],
inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))],
},
brillig::Opcode::Stop { return_data_offset: 0, return_data_size: 1 },
],
predicate: None,
};

Expand All @@ -196,10 +204,11 @@ fn simple_brillig_foreign_call() {
let bytes = Circuit::serialize_circuit(&circuit);

let expected_serialization: Vec<u8> = vec![
31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 49, 10, 64, 33, 12, 67, 99, 63, 124, 60, 142,
222, 192, 203, 56, 184, 56, 136, 120, 126, 5, 21, 226, 160, 139, 62, 40, 13, 45, 132, 68,
3, 80, 232, 124, 164, 153, 121, 115, 99, 155, 59, 172, 122, 231, 101, 56, 175, 80, 86, 221,
230, 31, 58, 196, 226, 83, 62, 53, 91, 16, 122, 10, 246, 84, 99, 243, 0, 30, 59, 1, 0, 0,
31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 177, 10, 192, 32, 12, 68, 207, 148, 150, 118,
234, 175, 216, 63, 232, 207, 116, 232, 210, 161, 136, 223, 175, 98, 132, 27, 212, 69, 31,
132, 28, 23, 8, 119, 59, 0, 131, 204, 66, 154, 41, 222, 173, 219, 142, 113, 153, 121, 191,
44, 231, 21, 237, 144, 88, 43, 249, 11, 71, 156, 77, 245, 251, 249, 231, 119, 189, 214,
204, 89, 187, 11, 25, 130, 54, 1, 36, 1, 124, 242, 107, 1, 0, 0,
];

assert_eq!(bytes, expected_serialization)
Expand All @@ -221,39 +230,54 @@ fn complex_brillig_foreign_call() {

let brillig_data = Brillig {
inputs: vec![
// Input Register 0
// Input 0,1,2
BrilligInputs::Array(vec![
Expression::from(a),
Expression::from(b),
Expression::from(c),
]),
// Input Register 1
// Input 3
BrilligInputs::Single(Expression {
mul_terms: vec![],
linear_combinations: vec![(fe_1, a), (fe_1, b), (fe_1, c)],
q_c: fe_0,
}),
],
// This tells the BrilligSolver which witnesses its output registers correspond to
// This tells the BrilligSolver which witnesses its output values correspond to
outputs: vec![
BrilligOutputs::Array(vec![a_times_2, b_times_3, c_times_4]), // Output Register 0
BrilligOutputs::Simple(a_plus_b_plus_c), // Output Register 1
BrilligOutputs::Simple(a_plus_b_plus_c_times_2), // Output Register 2
BrilligOutputs::Array(vec![a_times_2, b_times_3, c_times_4]), // Output 0,1,2
BrilligOutputs::Simple(a_plus_b_plus_c), // Output 3
BrilligOutputs::Simple(a_plus_b_plus_c_times_2), // Output 4
],
bytecode: vec![
brillig::Opcode::CalldataCopy {
destination_address: MemoryAddress(32),
size: 3,
offset: 0,
},
brillig::Opcode::Const {
destination: MemoryAddress(0),
value: brillig::Value::from(32_usize),
},
brillig::Opcode::CalldataCopy {
destination_address: MemoryAddress(1),
size: 1,
offset: 3,
},
// Oracles are named 'foreign calls' in brillig
brillig::Opcode::ForeignCall {
function: "complex".into(),
inputs: vec![
RegisterOrMemory::HeapArray(HeapArray { pointer: 0.into(), size: 3 }),
RegisterOrMemory::RegisterIndex(RegisterIndex::from(1)),
ValueOrArray::HeapArray(HeapArray { pointer: 0.into(), size: 3 }),
ValueOrArray::MemoryAddress(MemoryAddress::from(1)),
],
destinations: vec![
RegisterOrMemory::HeapArray(HeapArray { pointer: 0.into(), size: 3 }),
RegisterOrMemory::RegisterIndex(RegisterIndex::from(1)),
RegisterOrMemory::RegisterIndex(RegisterIndex::from(2)),
ValueOrArray::HeapArray(HeapArray { pointer: 0.into(), size: 3 }),
ValueOrArray::MemoryAddress(MemoryAddress::from(35)),
ValueOrArray::MemoryAddress(MemoryAddress::from(36)),
],
},
brillig::Opcode::Stop { return_data_offset: 32, return_data_size: 5 },
],
predicate: None,
};
Expand All @@ -269,13 +293,14 @@ fn complex_brillig_foreign_call() {
let bytes = Circuit::serialize_circuit(&circuit);

let expected_serialization: Vec<u8> = vec![
31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 219, 10, 128, 48, 8, 117, 174, 139, 159, 179,
254, 160, 127, 137, 222, 138, 122, 236, 243, 19, 114, 32, 22, 244, 144, 131, 118, 64, 156,
178, 29, 14, 59, 74, 0, 16, 224, 66, 228, 64, 57, 7, 169, 53, 242, 189, 81, 114, 250, 134,
33, 248, 113, 165, 82, 26, 177, 2, 141, 177, 128, 198, 60, 15, 63, 245, 219, 211, 23, 215,
255, 139, 15, 251, 211, 112, 180, 28, 157, 212, 189, 100, 82, 179, 64, 170, 63, 109, 235,
190, 204, 135, 166, 178, 150, 216, 62, 154, 252, 250, 70, 147, 35, 220, 119, 93, 227, 4,
182, 131, 81, 25, 36, 4, 0, 0,
31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 65, 10, 132, 48, 12, 76, 218, 237, 174, 123,
242, 11, 130, 62, 160, 250, 2, 255, 34, 222, 20, 61, 250, 124, 11, 78, 49, 4, 193, 131, 21,
52, 16, 210, 132, 105, 50, 77, 210, 140, 136, 152, 54, 177, 65, 13, 206, 12, 95, 74, 196,
181, 176, 254, 154, 212, 156, 46, 151, 191, 139, 163, 121, 1, 71, 123, 3, 199, 184, 15, 15,
157, 119, 202, 185, 36, 237, 159, 61, 248, 63, 159, 160, 46, 232, 23, 254, 15, 54, 67, 156,
96, 11, 213, 119, 82, 248, 116, 179, 104, 188, 163, 125, 15, 89, 213, 253, 139, 154, 221,
52, 206, 67, 191, 88, 5, 213, 52, 75, 113, 174, 96, 205, 201, 157, 24, 207, 197, 211, 157,
6, 50, 18, 233, 158, 72, 89, 1, 53, 215, 75, 175, 196, 4, 0, 0,
];

assert_eq!(bytes, expected_serialization)
Expand Down
56 changes: 25 additions & 31 deletions noir/acvm-repo/acvm/src/pwg/brillig.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use acir::{
brillig::{ForeignCallParam, ForeignCallResult, RegisterIndex, Value},
brillig::{ForeignCallParam, ForeignCallResult, Value},
circuit::{
brillig::{Brillig, BrilligInputs, BrilligOutputs},
OpcodeLocation,
Expand All @@ -8,7 +8,7 @@ use acir::{
FieldElement,
};
use acvm_blackbox_solver::BlackBoxFunctionSolver;
use brillig_vm::{Registers, VMStatus, VM};
use brillig_vm::{VMStatus, VM};

use crate::{pwg::OpcodeNotSolvable, OpcodeResolutionError};

Expand Down Expand Up @@ -69,16 +69,15 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> {
acir_index: usize,
) -> Result<Self, OpcodeResolutionError> {
// Set input values
let mut input_register_values: Vec<Value> = Vec::new();
let mut input_memory: Vec<Value> = Vec::new();
let mut calldata: Vec<Value> = Vec::new();
// Each input represents an expression or array of expressions to evaluate.
// Iterate over each input and evaluate the expression(s) associated with it.
// Push the results into registers and/or memory.
// Push the results into memory.
// If a certain expression is not solvable, we stall the ACVM and do not proceed with Brillig VM execution.
for input in &brillig.inputs {
match input {
BrilligInputs::Single(expr) => match get_value(expr, initial_witness) {
Ok(value) => input_register_values.push(value.into()),
Ok(value) => calldata.push(value.into()),
Err(_) => {
return Err(OpcodeResolutionError::OpcodeNotSolvable(
OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()),
Expand All @@ -87,39 +86,26 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> {
},
BrilligInputs::Array(expr_arr) => {
// Attempt to fetch all array input values
let memory_pointer = input_memory.len();
for expr in expr_arr.iter() {
match get_value(expr, initial_witness) {
Ok(value) => input_memory.push(value.into()),
Ok(value) => calldata.push(value.into()),
Err(_) => {
return Err(OpcodeResolutionError::OpcodeNotSolvable(
OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()),
))
}
}
}

// Push value of the array pointer as a register
input_register_values.push(Value::from(memory_pointer));
}
}
}

// Instantiate a Brillig VM given the solved input registers and memory
// Instantiate a Brillig VM given the solved calldata
// along with the Brillig bytecode.
let input_registers = Registers::load(input_register_values);
let vm = VM::new(input_registers, input_memory, &brillig.bytecode, vec![], bb_solver);
let vm = VM::new(calldata, &brillig.bytecode, vec![], bb_solver);
Ok(Self { vm, acir_index })
}

pub fn get_registers(&self) -> &Registers {
self.vm.get_registers()
}

pub fn set_register(&mut self, register_index: usize, value: Value) {
self.vm.set_register(RegisterIndex(register_index), value);
}

pub fn get_memory(&self) -> &[Value] {
self.vm.get_memory()
}
Expand Down Expand Up @@ -151,7 +137,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> {
// Return the "resolution" to the caller who may choose to make subsequent calls
// (when it gets foreign call results for example).
match vm_status {
VMStatus::Finished => Ok(BrilligSolverStatus::Finished),
VMStatus::Finished { .. } => Ok(BrilligSolverStatus::Finished),
VMStatus::InProgress => Ok(BrilligSolverStatus::InProgress),
VMStatus::Failure { message, call_stack } => {
Err(OpcodeResolutionError::BrilligFunctionFailed {
Expand Down Expand Up @@ -179,8 +165,8 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> {
// Finish the Brillig execution by writing the outputs to the witness map
let vm_status = self.vm.get_status();
match vm_status {
VMStatus::Finished => {
self.write_brillig_outputs(witness, brillig)?;
VMStatus::Finished { return_data_offset, return_data_size } => {
self.write_brillig_outputs(witness, return_data_offset, return_data_size, brillig)?;
Ok(())
}
_ => panic!("Brillig VM has not completed execution"),
Expand All @@ -190,24 +176,32 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> {
fn write_brillig_outputs(
&self,
witness_map: &mut WitnessMap,
return_data_offset: usize,
return_data_size: usize,
brillig: &Brillig,
) -> Result<(), OpcodeResolutionError> {
// Write VM execution results into the witness map
for (i, output) in brillig.outputs.iter().enumerate() {
let register_value = self.vm.get_registers().get(RegisterIndex::from(i));
let memory = self.vm.get_memory();
let mut current_ret_data_idx = return_data_offset;
for output in brillig.outputs.iter() {
match output {
BrilligOutputs::Simple(witness) => {
insert_value(witness, register_value.to_field(), witness_map)?;
insert_value(witness, memory[current_ret_data_idx].to_field(), witness_map)?;
current_ret_data_idx += 1;
}
BrilligOutputs::Array(witness_arr) => {
// Treat the register value as a pointer to memory
for (i, witness) in witness_arr.iter().enumerate() {
let value = &self.vm.get_memory()[register_value.to_usize() + i];
for witness in witness_arr.iter() {
let value = memory[current_ret_data_idx];
insert_value(witness, value.to_field(), witness_map)?;
current_ret_data_idx += 1;
}
}
}
}
assert!(
current_ret_data_idx == return_data_offset + return_data_size,
"Brillig VM did not write the expected number of return values"
);
Ok(())
}

Expand Down
Loading

0 comments on commit 018177b

Please sign in to comment.