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

chore: generate brillig opcodes for an empty function #1448

Merged
merged 14 commits into from
Jun 2, 2023
155 changes: 155 additions & 0 deletions crates/noirc_evaluator/src/brillig/acvm_brillig.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
use acvm::FieldElement;
kevaundray marked this conversation as resolved.
Show resolved Hide resolved
//THIS IS A TEMPORARY FILE, all warnings are disabled.
#[allow(clippy::all, unreachable_code, unreachable_pub, rustdoc::all, dead_code)]
guipublic marked this conversation as resolved.
Show resolved Hide resolved

pub type Label = usize;
#[allow(clippy::all, unreachable_code, unreachable_pub, rustdoc::all)]
pub type BrilligOpcode = Opcode;
#[allow(clippy::all, unreachable_code, unreachable_pub, rustdoc::all, dead_code)]
pub type BrilligType = Typ;
guipublic marked this conversation as resolved.
Show resolved Hide resolved
#[allow(clippy::all, unreachable_code, unreachable_pub, rustdoc::all, dead_code)]
pub type BrilligValue = Value;

#[allow(clippy::all, unreachable_code, unreachable_pub, rustdoc::all, dead_code)]
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum RegisterValueOrArray {
RegisterIndex(RegisterIndex),
HeapArray(RegisterIndex, usize),
}
#[allow(clippy::all, unreachable_code, unreachable_pub, rustdoc::all, dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Opcode {
jfecher marked this conversation as resolved.
Show resolved Hide resolved
/// Takes the fields in registers `lhs` and `rhs`
/// Performs the specified binary operation
/// and stores the value in the `result` register.
BinaryFieldOp {
destination: RegisterIndex,
op: BinaryFieldOp,
lhs: RegisterIndex,
rhs: RegisterIndex,
},
/// Takes the `bit_size` size integers in registers `lhs` and `rhs`
/// Performs the specified binary operation
/// and stores the value in the `result` register.
BinaryIntOp {
destination: RegisterIndex,
op: BinaryIntOp,
bit_size: u32,
lhs: RegisterIndex,
rhs: RegisterIndex,
},
JumpIfNot {
condition: RegisterIndex,
location: Label,
},
/// Sets the program counter to the value located at `destination`
/// If the value at `condition` is non-zero
JumpIf {
condition: RegisterIndex,
location: Label,
},
/// Sets the program counter to the label.
Jump {
location: Label,
},
/// We don't support dynamic jumps or calls
/// See https://github.com/ethereum/aleth/issues/3404 for reasoning
Call {
location: Label,
},
Const {
destination: RegisterIndex,
value: Value,
},
Return,
/// Used to get data from an outside source.
/// Also referred to as an Oracle. However, we don't use that name as
/// this is intended for things like state tree reads, and shouldn't be confused
/// with e.g. blockchain price oracles.
ForeignCall {
/// Interpreted by caller context, ie this will have different meanings depending on
/// who the caller is.
function: String,
/// Destination register (may be a memory pointer).
destination: RegisterValueOrArray,
/// Input register (may be a memory pointer).
input: RegisterValueOrArray,
},
Mov {
destination: RegisterIndex,
source: RegisterIndex,
},
Load {
destination: RegisterIndex,
source_pointer: RegisterIndex,
},
Store {
destination_pointer: RegisterIndex,
source: RegisterIndex,
},
/// Used to denote execution failure
Trap,
/// Stop execution
Stop,
}

#[allow(clippy::all, unreachable_code, unreachable_pub, rustdoc::all, dead_code)]
/// Binary fixed-length field expressions
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinaryFieldOp {
Add,
Sub,
Mul,
Div,
Cmp(Comparison),
}
#[allow(clippy::all, unreachable_code, unreachable_pub, rustdoc::all, dead_code)]
/// Binary fixed-length integer expressions
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinaryIntOp {
Add,
Sub,
Mul,
SignedDiv,
UnsignedDiv,
Cmp(Comparison),
/// (&) Bitwise AND
And,
/// (|) Bitwise OR
Or,
/// (^) Bitwise XOR
Xor,
/// (<<) Shift left
Shl,
/// (>>) Shift right
Shr,
}
#[allow(clippy::all, unreachable_code, unreachable_pub, rustdoc::all, dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Comparison {
/// (==) equal
Eq,
/// (<) Field less than
Lt,
/// (<=) field less or equal
Lte,
}

#[allow(clippy::all, unreachable_code, unreachable_pub, rustdoc::all, dead_code)]
/// `RegisterIndex` refers to the index of a register in the VM.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RegisterIndex(pub usize);
#[allow(clippy::all, unreachable_code, unreachable_pub, rustdoc::all, dead_code)]
/// Types of values allowed in the VM
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Typ {
guipublic marked this conversation as resolved.
Show resolved Hide resolved
Field,
Unsigned { bit_size: u32 },
Signed { bit_size: u32 },
}
#[allow(clippy::all, unreachable_code, unreachable_pub, rustdoc::all, dead_code)]
/// `Value` represents the base descriptor for a value in the VM.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Value {
pub inner: FieldElement,
}
30 changes: 30 additions & 0 deletions crates/noirc_evaluator/src/brillig/artefact.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use super::acvm_brillig::BrilligOpcode;

const PREFIX_LEN: usize = 3;

#[derive(Default, Debug, Clone)]
pub(crate) struct Artefact {
pub(crate) byte_code: Vec<BrilligOpcode>,
}

impl Artefact {
pub(crate) fn link(&mut self, obj: &Artefact) -> Vec<BrilligOpcode> {
self.link_with(obj);
self.byte_code.clone()
}

fn link_with(&mut self, obj: &Artefact) {
if obj.byte_code.is_empty() {
panic!("ICE: unresolved symbol");
}
if self.byte_code.is_empty() {
//prefix
self.byte_code.push(BrilligOpcode::Jump { location: PREFIX_LEN });
self.byte_code.push(BrilligOpcode::Trap);
self.byte_code.push(BrilligOpcode::Stop);
//assert prefic length is as expected
assert_eq!(self.byte_code.len(), PREFIX_LEN);
guipublic marked this conversation as resolved.
Show resolved Hide resolved
}
self.byte_code.extend_from_slice(&obj.byte_code);
}
}
19 changes: 19 additions & 0 deletions crates/noirc_evaluator/src/brillig/brillig_gen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use super::{acvm_brillig::BrilligOpcode, artefact::Artefact};

#[derive(Default)]
pub(crate) struct BrilligGen {
obj: Artefact,
}

impl BrilligGen {
/// Adds a brillig instruction to the brillig code base
fn push_code(&mut self, code: BrilligOpcode) {
self.obj.byte_code.push(code);
}

pub(crate) fn compile() -> Artefact {
let mut brillig = BrilligGen::default();
brillig.push_code(BrilligOpcode::Stop);
brillig.obj
}
}
3 changes: 3 additions & 0 deletions crates/noirc_evaluator/src/brillig/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub(crate) mod acvm_brillig;
pub(crate) mod artefact;
pub(crate) mod brillig_gen;
2 changes: 2 additions & 0 deletions crates/noirc_evaluator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ mod ssa;
// for functions and execute different optimizations.
pub mod ssa_refactor;

pub mod brillig;

use acvm::{
acir::circuit::{opcodes::Opcode as AcirOpcode, Circuit, PublicInputs},
acir::native_types::{Expression, Witness},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::brillig::acvm_brillig::BrilligOpcode;

use super::generated_acir::GeneratedAcir;
use acvm::{
acir::native_types::{Expression, Witness},
Expand Down Expand Up @@ -262,6 +264,10 @@ impl AcirContext {

id
}

pub(crate) fn brillig(&mut self, _code: Vec<BrilligOpcode>) {
todo!();
}
}

/// Enum representing the possible values that a
Expand Down
28 changes: 26 additions & 2 deletions crates/noirc_evaluator/src/ssa_refactor/acir_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

use std::collections::HashMap;

use crate::brillig::artefact::Artefact;

use self::acir_ir::acir_variable::{AcirContext, AcirVar};
use super::{
abi_gen::collate_array_lengths,
ir::{
dfg::DataFlowGraph,
function::RuntimeType,
instruction::{Binary, BinaryOp, Instruction, InstructionId, TerminatorInstruction},
map::Id,
types::Type,
Expand Down Expand Up @@ -60,7 +63,7 @@ impl Context {
}

for instruction_id in entry_block.instructions() {
self.convert_ssa_instruction(*instruction_id, dfg);
self.convert_ssa_instruction(*instruction_id, dfg, &ssa);
}

self.convert_ssa_return(entry_block.terminator().unwrap(), dfg);
Expand Down Expand Up @@ -90,7 +93,12 @@ impl Context {
}

/// Converts an SSA instruction into its ACIR representation
fn convert_ssa_instruction(&mut self, instruction_id: InstructionId, dfg: &DataFlowGraph) {
fn convert_ssa_instruction(
&mut self,
instruction_id: InstructionId,
dfg: &DataFlowGraph,
ssa: &Ssa,
) {
let instruction = &dfg[instruction_id];
match instruction {
Instruction::Binary(binary) => {
Expand All @@ -99,6 +107,22 @@ impl Context {
assert_eq!(result_ids.len(), 1, "Binary ops have a single result");
self.ssa_value_to_acir_var.insert(result_ids[0], result_acir_var);
}
Instruction::Call { func, arguments: _ } => {
match &dfg[*func] {
Value::Function(id) => {
let func = &ssa.functions[id];
match func.runtime() {
RuntimeType::Normal => unreachable!(),
guipublic marked this conversation as resolved.
Show resolved Hide resolved
RuntimeType::Unsafe => {
// Generate the brillig code of the function
let code = Artefact::default().link(func.dfg.obj());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why create a default object then link it with another instead of just using the other directly?

Copy link
Contributor Author

@guipublic guipublic May 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the object is mutable and generates its own bytecode while the other is not modified.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is preventing us from doing func.dfg.obj().clone()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a new artefact makes it clearer that we start from a new one, although for now it does not really makes a difference, but link will do more than cloning.

self.acir_context.brillig(code);
}
}
}
_ => unreachable!("expected calling a function"),
}
}
_ => todo!(),
}
}
Expand Down
8 changes: 5 additions & 3 deletions crates/noirc_evaluator/src/ssa_refactor/ir/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,16 @@ impl ControlFlowGraph {

#[cfg(test)]
mod tests {
use crate::ssa_refactor::ir::{instruction::TerminatorInstruction, map::Id, types::Type};
use crate::ssa_refactor::ir::{
function::RuntimeType, instruction::TerminatorInstruction, map::Id, types::Type,
};

use super::{super::function::Function, ControlFlowGraph};

#[test]
fn empty() {
let func_id = Id::test_new(0);
let mut func = Function::new("func".into(), func_id);
let mut func = Function::new("func".into(), func_id, RuntimeType::Normal);
guipublic marked this conversation as resolved.
Show resolved Hide resolved
let block_id = func.entry_block();
func.dfg[block_id].set_terminator(TerminatorInstruction::Return { return_values: vec![] });

Expand All @@ -153,7 +155,7 @@ mod tests {
// return ()
// }
let func_id = Id::test_new(0);
let mut func = Function::new("func".into(), func_id);
let mut func = Function::new("func".into(), func_id, RuntimeType::Normal);
let block0_id = func.entry_block();
let cond = func.dfg.add_block_parameter(block0_id, Type::unsigned(1));
let block1_id = func.dfg.make_block();
Expand Down
14 changes: 13 additions & 1 deletion crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashMap;

use crate::ssa_refactor::ir::instruction::SimplifyResult;
use crate::{brillig::artefact::Artefact, ssa_refactor::ir::instruction::SimplifyResult};

use super::{
basic_block::{BasicBlock, BasicBlockId},
Expand Down Expand Up @@ -61,6 +61,9 @@ pub(crate) struct DataFlowGraph {

/// All blocks in a function
blocks: DenseMap<BasicBlock>,

/// Brillig compilation artefact of the funtion
obj: Artefact,
guipublic marked this conversation as resolved.
Show resolved Hide resolved
}

impl DataFlowGraph {
Expand Down Expand Up @@ -91,6 +94,15 @@ impl DataFlowGraph {
new_block
}

/// Brillig compilation artefact
pub(crate) fn obj(&self) -> &Artefact {
&self.obj
}

pub(crate) fn brillig_gen(&mut self, obj: Artefact) {
self.obj = obj;
}
guipublic marked this conversation as resolved.
Show resolved Hide resolved

/// Get an iterator over references to each basic block within the dfg, paired with the basic
/// block's id.
///
Expand Down
Loading