Skip to content

Commit

Permalink
feat(avm-transpiler): implement tags for SET and others (#4545)
Browse files Browse the repository at this point in the history
Generates tags for
* `BinaryFieldOp`
* `BinaryIntOp`
* `Const` (SET on the AVM)
* CAST on the AVM

Gotcha (1): Brillig seems to only support fields of <= 126 bits. We now support CONST of a field (Brillig) and transpile it to a `SET<u128>` + `CAST<field>`.

It's very difficult to test this e2e in the `avm_test` contract since Noir does a lot of optimizations. Moreover, Noir currently generates code that compares different bit sizes and uses unsupported bit sizes.

As an example
```
#[aztec(public-vm)]
fn setOpcodeUint64() -> pub u64 {
    1 << 60 as u64
}
```

produces (using `nargo compile --package avm_test_contract --show-ssa --print-acir`)

```
Compiled ACIR for AvmTest::avm_setOpcodeUint64 (unoptimized):
current witness index : 0
public parameters indices : []
return value indices : [0]
BRILLIG: inputs: []
outputs: [Simple(Witness(0))]
[Const { destination: MemoryAddress(0), bit_size: 32, value: Value { inner: 1025 } }, CalldataCopy { destination_address: MemoryAddress(1024), size: 0, offset: 0 }, Call { location: 5 }, Mov { destination: MemoryAddress(1024), source: MemoryAddress(2) }, Stop { return_data_offset: 1024, return_data_size: 1 }, Const { destination: MemoryAddress(2), bit_size: 64, value: Value { inner: 2⁶⁰ } }, Mov { destination: MemoryAddress(3), source: MemoryAddress(2) }, Mov { destination: MemoryAddress(2), source: MemoryAddress(3) }, Return]

Initial SSA:
brillig fn avm_setOpcodeUint64 f0 {
  b0():
    call f1()
    return u64 2⁶⁰
}

...
After Dead Instruction Elimination:
brillig fn avm_setOpcodeUint64 f0 {
  b0():
    return u64 2⁶⁰
}
```

Closes #4268.
  • Loading branch information
fcarreiro authored Feb 12, 2024
1 parent a796bef commit 3063bf3
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 97 deletions.
37 changes: 21 additions & 16 deletions avm-transpiler/src/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::fmt::{Debug, Formatter};
use crate::opcodes::AvmOpcode;

/// Common values of the indirect instruction flag
pub const ALL_DIRECT: u8 = 0b00000000;
pub const ZEROTH_OPERAND_INDIRECT: u8 = 0b00000001;
pub const FIRST_OPERAND_INDIRECT: u8 = 0b00000010;
pub const ZEROTH_FIRST_OPERANDS_INDIRECT: u8 = 0b00000011;
Expand All @@ -20,10 +21,9 @@ pub struct AvmInstruction {
/// The 0th bit corresponds to an instruction's 0th offset arg, 1st to 1st, etc...
pub indirect: Option<u8>,

/// Some instructions have a destination or input tag
// TODO(4271): add in_tag alongside its support in TS
//pub in_tag: Option<AvmTypeTag>,
pub dst_tag: Option<AvmTypeTag>,
/// Some instructions have a destination xor input tag
/// Its usage will depend on the instruction.
pub tag: Option<AvmTypeTag>,

/// Different instructions have different numbers of operands
pub operands: Vec<AvmOperand>,
Expand All @@ -35,9 +35,9 @@ impl Display for AvmInstruction {
if let Some(indirect) = self.indirect {
write!(f, ", indirect: {}", indirect)?;
}
// TODO(4271): add in_tag alongside its support in TS
if let Some(dst_tag) = self.dst_tag {
write!(f, ", dst_tag: {}", dst_tag as u8)?;
// This will be either inTag or dstTag depending on the operation
if let Some(dst_tag) = self.tag {
write!(f, ", tag: {}", dst_tag as u8)?;
}
if !self.operands.is_empty() {
write!(f, ", operands: [")?;
Expand All @@ -58,10 +58,9 @@ impl AvmInstruction {
if let Some(indirect) = self.indirect {
bytes.push(indirect);
}
// 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.extend_from_slice(&(dst_tag as u8).to_be_bytes());
// This will be either inTag or dstTag depending on the operation
if let Some(tag) = self.tag {
bytes.extend_from_slice(&(tag as u8).to_be_bytes());
}
for operand in &self.operands {
bytes.extend_from_slice(&operand.to_be_bytes());
Expand All @@ -82,14 +81,14 @@ impl Default for AvmInstruction {
opcode: AvmOpcode::ADD,
// TODO(4266): default to Some(0), since all instructions have indirect flag except jumps
indirect: None,
dst_tag: None,
tag: None,
operands: vec![],
}
}
}

/// AVM instructions may include a type tag
#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug)]
pub enum AvmTypeTag {
UNINITIALIZED,
UINT8,
Expand All @@ -105,16 +104,20 @@ pub enum AvmTypeTag {
/// Constants (as used by the SET instruction) can have size
/// different from 32 bits
pub enum AvmOperand {
U8 { value: u8 },
U16 { value: u16 },
U32 { value: u32 },
// TODO(4267): Support operands of size other than 32 bits (for SET)
U64 { value: u64 },
U128 { value: u128 },
}

impl Display for AvmOperand {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
AvmOperand::U8 { value } => write!(f, " U8:{}", value),
AvmOperand::U16 { value } => write!(f, " U16:{}", value),
AvmOperand::U32 { value } => write!(f, " U32:{}", value),
// TODO(4267): Support operands of size other than 32 bits (for SET)
AvmOperand::U64 { value } => write!(f, " U64:{}", value),
AvmOperand::U128 { value } => write!(f, " U128:{}", value),
}
}
Expand All @@ -123,8 +126,10 @@ impl Display for AvmOperand {
impl AvmOperand {
pub fn to_be_bytes(&self) -> Vec<u8> {
match self {
AvmOperand::U8 { value } => value.to_be_bytes().to_vec(),
AvmOperand::U16 { value } => value.to_be_bytes().to_vec(),
AvmOperand::U32 { value } => value.to_be_bytes().to_vec(),
// TODO(4267): Support operands of size other than 32 bits (for SET)
AvmOperand::U64 { value } => value.to_be_bytes().to_vec(),
AvmOperand::U128 { value } => value.to_be_bytes().to_vec(),
}
}
Expand Down
Loading

0 comments on commit 3063bf3

Please sign in to comment.