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(aztec-nr): initial work for aztec public vm macro #4400

Merged
merged 5 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
18 changes: 14 additions & 4 deletions avm-transpiler/src/instructions.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::fmt::{Formatter, Debug};
use std::fmt;

use crate::opcodes::AvmOpcode;

/// Common values of the indirect instruction flag
Expand Down Expand Up @@ -55,15 +58,21 @@ impl AvmInstruction {
// TODO(4271): add in_tag alongside its support in TS
if let Some(dst_tag) = self.dst_tag {
// TODO(4271): make 8 bits when TS supports deserialization of 8 bit flags
//bytes.push(dst_tag as u8);
bytes.extend_from_slice(&(dst_tag as u32).to_be_bytes());
bytes.extend_from_slice(&(dst_tag as u8).to_be_bytes());
}
for operand in &self.operands {
bytes.extend_from_slice(&operand.to_be_bytes());
}
bytes
}
}

impl Debug for AvmInstruction {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_string())
}
}

impl Default for AvmInstruction {
fn default() -> Self {
AvmInstruction {
Expand Down Expand Up @@ -95,21 +104,22 @@ pub enum AvmTypeTag {
pub enum AvmOperand {
U32 { value: u32 },
// TODO(4267): Support operands of size other than 32 bits (for SET)
//U128 { value: u128 },
U128 { value: u128 },
}
impl AvmOperand {
pub fn to_string(&self) -> String {
match self {
AvmOperand::U32 { value } => format!(" U32:{}", value),
// TODO(4267): Support operands of size other than 32 bits (for SET)
//AvmOperand::U128 { value } => format!("U128:{}", value),
AvmOperand::U128 { value } => format!(" U128:{}", value),
Maddiaa0 marked this conversation as resolved.
Show resolved Hide resolved
}
}
pub fn to_be_bytes(&self) -> Vec<u8> {
match self {
AvmOperand::U32 { value } => value.to_be_bytes().to_vec(),
// TODO(4267): Support operands of size other than 32 bits (for SET)
//AvmOperand::U128 { value } => value.to_be_bytes().to_vec(),
AvmOperand::U128 { value } => value.to_be_bytes().to_vec(),
}
}
}
16 changes: 14 additions & 2 deletions avm-transpiler/src/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec<u8> {
// TODO(4268): set in_tag to `field`
avm_instrs.push(AvmInstruction {
opcode: avm_opcode,
indirect: Some(0),
// TEMPORARY - instruction set currently expects this
dst_tag: Some(AvmTypeTag::UINT32),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Start this comment with TODO(4268) as it should be handled properly by #4268

operands: vec![
AvmOperand::U32 {
value: lhs.to_usize() as u32,
Expand Down Expand Up @@ -80,6 +83,7 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec<u8> {
// TODO(4268): support u8..u128 and use in_tag
avm_instrs.push(AvmInstruction {
opcode: avm_opcode,
indirect: Some(0),
operands: vec![
AvmOperand::U32 {
value: lhs.to_usize() as u32,
Expand All @@ -97,6 +101,7 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec<u8> {
BrilligOpcode::CalldataCopy { destination_address, size, offset } => {
avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::CALLDATACOPY,
indirect: Some(0),
operands: vec![
AvmOperand::U32 {
value: *offset as u32, // cdOffset (calldata offset)
Expand Down Expand Up @@ -125,6 +130,7 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec<u8> {
let avm_loc = brillig_pcs_to_avm_pcs[*location];
avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::JUMPI,
indirect: Some(0),
operands: vec![
AvmOperand::U32 {
value: avm_loc as u32,
Expand All @@ -139,12 +145,15 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec<u8> {
BrilligOpcode::Const { destination, value } => {
avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::SET,
indirect: Some(0),
dst_tag: Some(AvmTypeTag::UINT32),
operands: vec![
// TODO(4267): support u8..u128 and use dst_tag
AvmOperand::U32 {
value: value.to_usize() as u32,
// value - temporarily as u128
AvmOperand::U128 {
value: value.to_usize() as u128,
},
// dest offset
AvmOperand::U32 {
value: destination.to_usize() as u32,
},
Expand All @@ -158,6 +167,7 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec<u8> {
} => {
avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::MOV,
indirect: Some(0),
operands: vec![
AvmOperand::U32 {
value: source.to_usize() as u32,
Expand Down Expand Up @@ -223,6 +233,7 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec<u8> {
BrilligOpcode::Stop { return_data_offset, return_data_size } => {
avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::RETURN,
indirect: Some(0),
operands: vec![
AvmOperand::U32 { value: *return_data_offset as u32},
AvmOperand::U32 { value: *return_data_size as u32},
Expand All @@ -234,6 +245,7 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec<u8> {
// TODO(https://github.com/noir-lang/noir/issues/3113): Trap should support return data
avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::REVERT,
indirect: Some(0),
operands: vec![
//AvmOperand::U32 { value: *return_data_offset as u32},
//AvmOperand::U32 { value: *return_data_size as u32},
Expand Down
6 changes: 4 additions & 2 deletions avm-transpiler/src/transpile_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,12 @@ pub enum AvmOrAcirContractFunction {
impl From<CompiledAcirContract> for TranspiledContract {
fn from(contract: CompiledAcirContract) -> Self {
let mut functions = Vec::new();

// Note, in aztec_macros/lib.rs, avm_ prefix is pushed to function names with the #[aztec(public-vm)] tag
let re = Regex::new(r"avm_.*$").unwrap();
for function in contract.functions {
// TODO(4269): once functions are tagged for transpilation to AVM, check tag
let re = Regex::new(r"avm_.*$").unwrap();
if function.function_type == ContractFunctionType::Unconstrained
if function.function_type == ContractFunctionType::Open
&& re.is_match(function.name.as_str())
{
info!(
Expand Down
15 changes: 15 additions & 0 deletions noir/aztec_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,10 @@ fn transform_module(
transform_function("Public", func, storage_defined)
.map_err(|err| (err, crate_graph.root_file_id))?;
has_transformed_module = true;
} else if is_custom_attribute(&secondary_attribute, "aztec(public-vm)") {
transform_vm_function(func, storage_defined)
.map_err(|err| (err, crate_graph.root_file_id))?;
has_transformed_module = true;
}
}
// Add the storage struct to the beginning of the function if it is unconstrained in an aztec contract
Expand Down Expand Up @@ -585,6 +589,17 @@ fn generate_storage_implementation(module: &mut SortedModule) -> Result<(), Azte
Ok(())
}

// Transform a function to work with AVM bytecode
fn transform_vm_function(func: &mut NoirFunction, _storage_defined: bool) -> Result<(), AztecMacroError> {
// We want the function to be seen as a public function
func.def.is_open = true;

// NOTE: the line below is a temporary hack to trigger external transpilation tools
// It will be removed once the transpiler is integrated into the Noir compiler
func.def.name.0.contents = format!("avm_{}", func.def.name.0.contents);
Ok(())
}

/// If it does, it will insert the following things:
/// - A new Input that is provided for a kernel app circuit, named: {Public/Private}ContextInputs
/// - Hashes all of the function input variables
Expand Down
5 changes: 3 additions & 2 deletions yarn-project/acir-simulator/src/avm/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,16 @@ describe('avm', () => {

describe('testing transpiled Noir contracts', () => {
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/4361): sync wire format w/transpiler.
it.skip('Should execute contract function that performs addition', async () => {
it('Should execute contract function that performs addition', async () => {
const calldata: Fr[] = [new Fr(1), new Fr(2)];
const journal = mock<AvmJournal>();

// Get contract function artifact
const addArtifact = AvmTestContractArtifact.functions.find(f => f.name === 'avm_addArgsReturn')!;

// Decode bytecode into instructions
const instructions = decodeFromBytecode(Buffer.from(addArtifact.bytecode, 'base64'));
const instructionsBytecode = Buffer.from(addArtifact.bytecode, 'base64');
const instructions = decodeFromBytecode(instructionsBytecode);

// Execute instructions
const context = new AvmMachineState(initExecutionEnvironment({ calldata }));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ const INSTRUCTION_SET: InstructionSet = new Map<Opcode, DeserializableInstructio
[
[Add.opcode, Add],
[Sub.opcode, Sub],
[Sub.opcode, Sub],
Copy link
Collaborator

Choose a reason for hiding this comment

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

👀

[Mul.opcode, Mul],
[Div.opcode, Div],
[Eq.opcode, Eq],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ contract AvmTest {
fn constructor() {}

// Function name prefix "avm_" flags it for transpilation
unconstrained fn avm_addArgsReturn(argA: Field, argB: Field) -> pub Field {
#[aztec(public-vm)]
fn addArgsReturn(argA: Field, argB: Field) -> pub Field {
argA + argB
}

Expand Down
Loading