Skip to content

Commit

Permalink
chore: abstract away brillig execution from creating AcirValues
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench committed Aug 5, 2023
1 parent da47fb2 commit d58d84c
Showing 1 changed file with 136 additions and 98 deletions.
234 changes: 136 additions & 98 deletions crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -938,110 +938,39 @@ impl AcirContext {
inputs: &[BrilligInputs],
outputs: &[AcirType],
) -> Option<Vec<AcirValue>> {
struct NullBbSolver;

impl BlackBoxFunctionSolver for NullBbSolver {
fn schnorr_verify(
&self,
_public_key_x: &FieldElement,
_public_key_y: &FieldElement,
_signature: &[u8],
_message: &[u8],
) -> Result<bool, BlackBoxResolutionError> {
Err(BlackBoxResolutionError::Unsupported(BlackBoxFunc::SchnorrVerify))
}
fn pedersen(
&self,
_inputs: &[FieldElement],
_domain_separator: u32,
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> {
Err(BlackBoxResolutionError::Unsupported(BlackBoxFunc::Pedersen))
}
fn fixed_base_scalar_mul(
&self,
_input: &FieldElement,
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> {
Err(BlackBoxResolutionError::Unsupported(BlackBoxFunc::FixedBaseScalarMul))
}
}

// Set input values
let mut input_register_values: Vec<Value> = Vec::new();
let mut input_memory: Vec<Value> = Vec::new();
// Each input represents a constant or array of constants.
// Iterate over each input and push it into registers and/or memory.
for input in inputs {
match input {
BrilligInputs::Single(expr) => {
input_register_values.push(expr.to_const().unwrap().into());
let results = execute_constant_brillig(code, inputs, outputs)?;

let mut b_outputs: Vec<AcirValue> = Vec::new();
for (output, result) in outputs.iter().zip(results) {
match (output, result) {
(typ @ AcirType::NumericType(_), BrilligOutputValue::Simple(value)) => {
let var = self.add_data(AcirVarData::Const(value));
b_outputs.push(AcirValue::Var(var, typ.clone()));
}
BrilligInputs::Array(expr_arr) => {
// Attempt to fetch all array input values
let memory_pointer = input_memory.len();
for expr in expr_arr.iter() {
input_memory.push(expr.to_const().unwrap().into());
(AcirType::Array(elem_types, len), BrilligOutputValue::Array(values)) => {
assert_eq!(values.len(), *len);
assert_eq!(elem_types.len(), 1, "Nested arrays are not supported");
let elem_type = &elem_types[0];
assert!(
matches!(elem_type, AcirType::NumericType(_)),
"Nested arrays are not supported",
);

let mut array_outputs = Vec::new();

for value in values {
let var = self.add_data(AcirVarData::Const(value));
array_outputs.push(AcirValue::Var(var, elem_type.clone()));
}

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

// Instantiate a Brillig VM given the solved input registers and memory, along with the Brillig bytecode.
let input_registers = Registers::load(input_register_values);
let mut vm = VM::new(input_registers, input_memory, code, Vec::new(), &NullBbSolver);

// Run the Brillig VM on these inputs, bytecode, etc!
let vm_status = vm.process_opcodes();

// Check the status of the Brillig VM.
// It may be finished, in-progress, failed, or may be waiting for results of a foreign call.
// If it's finished then we can omit the opcode and just write in the return values.
match vm_status {
VMStatus::Finished => {
let mut b_outputs: Vec<AcirValue> = Vec::new();
for (index, output) in outputs.iter().enumerate() {
let register_values = vm.get_registers();
let register_value = register_values.get(RegisterIndex::from(index));

match output {
typ @ AcirType::NumericType(_) => {
let var = self.add_data(AcirVarData::Const(register_value.to_field()));
b_outputs.push(AcirValue::Var(var, typ.clone()));
}
AcirType::Array(elem_types, len) => {
let elem_type = &elem_types[0];

let mut array_outputs = Vec::new();
for i in 0..*len {
match elem_type {
typ @ AcirType::NumericType(_) => {
let value = vm.get_memory()[register_value.to_usize() + i]
.to_field();

let var = self.add_data(AcirVarData::Const(value));
array_outputs.push(AcirValue::Var(var, typ.clone()));
}
_ => todo!("support for nested array return values"),
}
}
b_outputs.push(AcirValue::Array(array_outputs.into()));
}
};
}
Some(b_outputs)
}
VMStatus::InProgress => unreachable!("Brillig VM has not completed execution"),
VMStatus::Failure { .. } => {
// TODO: Return an error stating that the brillig function failed.
None
}
VMStatus::ForeignCallWait { .. } => {
// If execution can't complete then keep the opcode
None
}
_ => unreachable!("Invalid type combination"),
};
}

Some(b_outputs)
}

fn brillig_array_input(
Expand Down Expand Up @@ -1279,3 +1208,112 @@ impl AcirVarData {
/// A Reference to an `AcirVarData`
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) struct AcirVar(usize);

enum BrilligOutputValue {
Simple(FieldElement),
Array(Vec<FieldElement>),
}

fn execute_constant_brillig(
code: Vec<BrilligOpcode>,
inputs: &[BrilligInputs],
outputs: &[AcirType],
) -> Option<Vec<BrilligOutputValue>> {
struct NullBbSolver;

impl BlackBoxFunctionSolver for NullBbSolver {
fn schnorr_verify(
&self,
_public_key_x: &FieldElement,
_public_key_y: &FieldElement,
_signature: &[u8],
_message: &[u8],
) -> Result<bool, BlackBoxResolutionError> {
Err(BlackBoxResolutionError::Unsupported(BlackBoxFunc::SchnorrVerify))
}
fn pedersen(
&self,
_inputs: &[FieldElement],
_domain_separator: u32,
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> {
Err(BlackBoxResolutionError::Unsupported(BlackBoxFunc::Pedersen))
}
fn fixed_base_scalar_mul(
&self,
_input: &FieldElement,
) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> {
Err(BlackBoxResolutionError::Unsupported(BlackBoxFunc::FixedBaseScalarMul))
}
}

// Set input values
let mut input_register_values: Vec<Value> = Vec::new();
let mut input_memory: Vec<Value> = Vec::new();
// Each input represents a constant or array of constants.
// Iterate over each input and push it into registers and/or memory.
for input in inputs {
match input {
BrilligInputs::Single(expr) => {
input_register_values.push(expr.to_const().unwrap().into());
}
BrilligInputs::Array(expr_arr) => {
// Attempt to fetch all array input values
let memory_pointer = input_memory.len();
for expr in expr_arr.iter() {
input_memory.push(expr.to_const().unwrap().into());
}

// 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, along with the Brillig bytecode.
let input_registers = Registers::load(input_register_values);
let mut vm = VM::new(input_registers, input_memory, code, Vec::new(), &NullBbSolver);

// Run the Brillig VM on these inputs, bytecode, etc!
let vm_status = vm.process_opcodes();

// Check the status of the Brillig VM.
// It may be finished, in-progress, failed, or may be waiting for results of a foreign call.
// If it's finished then we can omit the opcode and just write in the return values.
match vm_status {
VMStatus::Finished => {
let mut b_outputs: Vec<BrilligOutputValue> = Vec::new();
for (index, output) in outputs.iter().enumerate() {
let register_values = vm.get_registers();
let register_value = register_values.get(RegisterIndex::from(index));

match output {
AcirType::NumericType(_) => {
b_outputs.push(BrilligOutputValue::Simple(register_value.to_field()));
}
AcirType::Array(elem_types, len) => {
assert_eq!(elem_types.len(), 1, "Nested arrays are not supported");
assert!(
matches!(&elem_types[0], AcirType::NumericType(_)),
"Nested arrays are not supported",
);

let array_outputs = vecmap(0..*len, |i| {
vm.get_memory()[register_value.to_usize() + i].to_field()
});
b_outputs.push(BrilligOutputValue::Array(array_outputs));
}
}
}
Some(b_outputs)
}
VMStatus::InProgress => unreachable!("Brillig VM has not completed execution"),
VMStatus::Failure { .. } => {
// TODO: Return an error stating that the brillig function failed.
None
}
VMStatus::ForeignCallWait { .. } => {
// If execution can't complete then keep the opcode
None
}
}
}

0 comments on commit d58d84c

Please sign in to comment.