Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
fcarreiro committed Sep 7, 2024
1 parent e79ac20 commit 4d23561
Show file tree
Hide file tree
Showing 20 changed files with 241 additions and 146 deletions.
5 changes: 5 additions & 0 deletions avm-transpiler/src/instructions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::fmt::{self, Display};
use std::fmt::{Debug, Formatter};

use acvm::{AcirField, FieldElement};

use crate::opcodes::AvmOpcode;

/// Common values of the indirect instruction flag
Expand Down Expand Up @@ -110,6 +112,7 @@ pub enum AvmOperand {
U32 { value: u32 },
U64 { value: u64 },
U128 { value: u128 },
FF { value: FieldElement },
}

impl Display for AvmOperand {
Expand All @@ -120,6 +123,7 @@ impl Display for AvmOperand {
AvmOperand::U32 { value } => write!(f, " U32:{}", value),
AvmOperand::U64 { value } => write!(f, " U64:{}", value),
AvmOperand::U128 { value } => write!(f, " U128:{}", value),
AvmOperand::FF { value } => write!(f, " FF:{}", value),
}
}
}
Expand All @@ -132,6 +136,7 @@ impl AvmOperand {
AvmOperand::U32 { value } => value.to_be_bytes().to_vec(),
AvmOperand::U64 { value } => value.to_be_bytes().to_vec(),
AvmOperand::U128 { value } => value.to_be_bytes().to_vec(),
AvmOperand::FF { value } => value.to_be_bytes(),
}
}
}
8 changes: 5 additions & 3 deletions avm-transpiler/src/opcodes.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// All AVM opcodes
/// Keep updated with TS, cpp, and docs protocol specs!
#[allow(clippy::upper_case_acronyms, dead_code)]
#[allow(clippy::upper_case_acronyms, dead_code, non_camel_case_types)]
#[derive(PartialEq, Copy, Clone, Debug, Eq, Hash)]
pub enum AvmOpcode {
// Compute
Expand Down Expand Up @@ -42,7 +42,8 @@ pub enum AvmOpcode {
INTERNALRETURN,
// Memory
SET,
MOV,
MOV_8,
MOV_16,
CMOV,
// World state
SLOAD,
Expand Down Expand Up @@ -129,7 +130,8 @@ impl AvmOpcode {
AvmOpcode::INTERNALRETURN => "INTERNALRETURN",
// Machine State - Memory
AvmOpcode::SET => "SET",
AvmOpcode::MOV => "MOV",
AvmOpcode::MOV_8 => "MOV_8",
AvmOpcode::MOV_16 => "MOV_16",
AvmOpcode::CMOV => "CMOV",

// World State
Expand Down
21 changes: 17 additions & 4 deletions avm-transpiler/src/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use crate::instructions::{
SECOND_OPERAND_INDIRECT, ZEROTH_OPERAND_INDIRECT,
};
use crate::opcodes::AvmOpcode;
use crate::utils::{dbg_print_avm_program, dbg_print_brillig_program};
use crate::utils::{
bits_needed_for, dbg_print_avm_program, dbg_print_brillig_program, make_operand,
};

/// Transpile a Brillig program to AVM bytecode
pub fn brillig_to_avm(
Expand Down Expand Up @@ -216,7 +218,7 @@ pub fn brillig_to_avm(
// We are adding a MOV instruction that moves a value to itself.
// This should therefore not affect the program's execution.
avm_instrs.push(AvmInstruction {
opcode: AvmOpcode::MOV,
opcode: AvmOpcode::MOV_8,
indirect: Some(ALL_DIRECT),
operands: vec![AvmOperand::U32 { value: 0x18ca }, AvmOperand::U32 { value: 0x18ca }],
..Default::default()
Expand Down Expand Up @@ -741,10 +743,21 @@ fn generate_cast_instruction(

/// Generates an AVM MOV instruction.
fn generate_mov_instruction(indirect: Option<u8>, source: u32, dest: u32) -> AvmInstruction {
let bits_needed = [source.into(), dest.into()].iter().map(bits_needed_for).max().unwrap();

let mov_opcode = match bits_needed {
8 => AvmOpcode::MOV_8,
16 => AvmOpcode::MOV_16,
_ => panic!("MOV operands must fit in 16 bits but needed {}", bits_needed),
};

AvmInstruction {
opcode: AvmOpcode::MOV,
opcode: mov_opcode,
indirect,
operands: vec![AvmOperand::U32 { value: source }, AvmOperand::U32 { value: dest }],
operands: vec![
make_operand(bits_needed, &source.into()),
make_operand(bits_needed, &dest.into()),
],
..Default::default()
}
}
Expand Down
32 changes: 30 additions & 2 deletions avm-transpiler/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use fxhash::FxHashMap as HashMap;

use acvm::acir::circuit::brillig::BrilligFunctionId;
use acvm::FieldElement;
use acvm::{AcirField, FieldElement};
use log::{debug, info, trace};

use acvm::acir::brillig::Opcode as BrilligOpcode;
use acvm::acir::circuit::{AssertionPayload, Opcode, Program};

use crate::instructions::AvmInstruction;
use crate::instructions::{AvmInstruction, AvmOperand};
use crate::opcodes::AvmOpcode;

/// Extract the Brillig program from its `Program` wrapper.
Expand Down Expand Up @@ -90,3 +90,31 @@ pub fn dbg_print_avm_program(avm_program: &[AvmInstruction]) {
debug!("\t{0:?}: {1}", opcode, count);
}
}

pub fn bits_needed_for(val: &FieldElement) -> u8 {
if val.num_bits() < 8 {
8
} else if val.num_bits() < 16 {
16
} else if val.num_bits() < 32 {
32
} else if val.num_bits() < 64 {
64
} else if val.num_bits() < 128 {
128
} else {
254
}
}

pub fn make_operand(bits: u8, value: &FieldElement) -> AvmOperand {
match bits {
8 => AvmOperand::U8 { value: value.to_u128() as u8 },
16 => AvmOperand::U16 { value: value.to_u128() as u16 },
32 => AvmOperand::U32 { value: value.to_u128() as u32 },
64 => AvmOperand::U64 { value: value.to_u128() as u64 },
128 => AvmOperand::U128 { value: value.to_u128() },
254 => AvmOperand::FF { value: value.clone() },
_ => panic!("Invalid operand size for bits: {}", bits),
}
}
20 changes: 10 additions & 10 deletions barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,10 +574,10 @@ TEST_F(AvmExecutionTests, movOpcode)
"01" // U8
"13" // val 19
"000000AB" // dst_offset 171
+ to_hex(OpCode::MOV) + // opcode MOV
+ to_hex(OpCode::MOV_8) + // opcode MOV
"00" // Indirect flag
"000000AB" // src_offset 171
"00000021" // dst_offset 33
"AB" // src_offset 171
"21" // dst_offset 33
+ to_hex(OpCode::RETURN) + // opcode RETURN
"00" // Indirect flag
"00000000" // ret offset 0
Expand All @@ -600,9 +600,9 @@ TEST_F(AvmExecutionTests, movOpcode)
// MOV
EXPECT_THAT(
instructions.at(1),
AllOf(Field(&Instruction::op_code, OpCode::MOV),
AllOf(Field(&Instruction::op_code, OpCode::MOV_8),
Field(&Instruction::operands,
ElementsAre(VariantWith<uint8_t>(0), VariantWith<uint32_t>(171), VariantWith<uint32_t>(33)))));
ElementsAre(VariantWith<uint8_t>(0), VariantWith<uint8_t>(171), VariantWith<uint8_t>(33)))));

auto trace = gen_trace_from_instr(instructions);

Expand Down Expand Up @@ -688,10 +688,10 @@ TEST_F(AvmExecutionTests, indMovOpcode)
"01" // U8
"FF" // val 255
"0000000A" // dst_offset 10
+ to_hex(OpCode::MOV) + // opcode MOV
+ to_hex(OpCode::MOV_8) + // opcode MOV
"01" // Indirect flag
"00000001" // src_offset 1 --> direct offset 10
"00000002" // dst_offset 2 --> direct offset 11
"01" // src_offset 1 --> direct offset 10
"02" // dst_offset 2 --> direct offset 11
+ to_hex(OpCode::RETURN) + // opcode RETURN
"00" // Indirect flag
"00000000" // ret offset 0
Expand All @@ -704,9 +704,9 @@ TEST_F(AvmExecutionTests, indMovOpcode)

// MOV
EXPECT_THAT(instructions.at(3),
AllOf(Field(&Instruction::op_code, OpCode::MOV),
AllOf(Field(&Instruction::op_code, OpCode::MOV_8),
Field(&Instruction::operands,
ElementsAre(VariantWith<uint8_t>(1), VariantWith<uint32_t>(1), VariantWith<uint32_t>(2)))));
ElementsAre(VariantWith<uint8_t>(1), VariantWith<uint8_t>(1), VariantWith<uint8_t>(2)))));

auto trace = gen_trace_from_instr(instructions);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ const std::unordered_map<OpCode, std::vector<OperandType>> OPCODE_WIRE_FORMAT =

// Machine State - Memory
// OpCode::SET is handled differently
{ OpCode::MOV, { OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32 } },
{ OpCode::MOV_8, { OperandType::INDIRECT, OperandType::UINT8, OperandType::UINT8 } },
{ OpCode::MOV_16, { OperandType::INDIRECT, OperandType::UINT16, OperandType::UINT16 } },
{ OpCode::CMOV,
{ OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } },

Expand Down
11 changes: 8 additions & 3 deletions barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -617,10 +617,15 @@ std::vector<Row> Execution::gen_trace(std::vector<Instruction> const& instructio
std::get<uint8_t>(inst.operands.at(0)), val, std::get<uint32_t>(inst.operands.at(3)), in_tag);
break;
}
case OpCode::MOV:
case OpCode::MOV_8:
trace_builder.op_mov(std::get<uint8_t>(inst.operands.at(0)),
std::get<uint32_t>(inst.operands.at(1)),
std::get<uint32_t>(inst.operands.at(2)));
std::get<uint8_t>(inst.operands.at(1)),
std::get<uint8_t>(inst.operands.at(2)));
break;
case OpCode::MOV_16:
trace_builder.op_mov(std::get<uint8_t>(inst.operands.at(0)),
std::get<uint16_t>(inst.operands.at(1)),
std::get<uint16_t>(inst.operands.at(2)));
break;
case OpCode::CMOV:
trace_builder.op_cmov(std::get<uint8_t>(inst.operands.at(0)),
Expand Down
3 changes: 2 additions & 1 deletion barretenberg/cpp/src/barretenberg/vm/avm/trace/fixed_gas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ const std::unordered_map<OpCode, FixedGasTable::GasRow> GAS_COST_TABLE = {
{ OpCode::INTERNALCALL, make_cost(AVM_INTERNALCALL_BASE_L2_GAS, 0, AVM_INTERNALCALL_DYN_L2_GAS, 0) },
{ OpCode::INTERNALRETURN, make_cost(AVM_INTERNALRETURN_BASE_L2_GAS, 0, AVM_INTERNALRETURN_DYN_L2_GAS, 0) },
{ OpCode::SET, make_cost(AVM_SET_BASE_L2_GAS, 0, AVM_SET_DYN_L2_GAS, 0) },
{ OpCode::MOV, make_cost(AVM_MOV_BASE_L2_GAS, 0, AVM_MOV_DYN_L2_GAS, 0) },
{ OpCode::MOV_8, make_cost(AVM_MOV_BASE_L2_GAS, 0, AVM_MOV_DYN_L2_GAS, 0) },
{ OpCode::MOV_16, make_cost(AVM_MOV_BASE_L2_GAS, 0, AVM_MOV_DYN_L2_GAS, 0) },
{ OpCode::CMOV, make_cost(AVM_CMOV_BASE_L2_GAS, 0, AVM_CMOV_DYN_L2_GAS, 0) },
{ OpCode::SLOAD, make_cost(AVM_SLOAD_BASE_L2_GAS, 0, AVM_SLOAD_DYN_L2_GAS, 0) },
{ OpCode::SSTORE, make_cost(AVM_SSTORE_BASE_L2_GAS, 0, AVM_SSTORE_DYN_L2_GAS, 0) },
Expand Down
6 changes: 4 additions & 2 deletions barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,10 @@ std::string to_string(OpCode opcode)
// Machine State - Memory
case OpCode::SET:
return "SET";
case OpCode::MOV:
return "MOV";
case OpCode::MOV_8:
return "MOV_8";
case OpCode::MOV_16:
return "MOV_16";
case OpCode::CMOV:
return "CMOV";
// World State
Expand Down
3 changes: 2 additions & 1 deletion barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ enum class OpCode : uint8_t {
INTERNALRETURN,
// Machine State - Memory
SET,
MOV,
MOV_8,
MOV_16,
CMOV,

// World State
Expand Down
3 changes: 2 additions & 1 deletion barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1807,7 +1807,8 @@ void AvmTraceBuilder::op_mov(uint8_t indirect, uint32_t src_offset, uint32_t dst
mem_trace_builder.write_into_memory(call_ptr, clk, IntermRegister::IC, direct_dst_offset, val, tag, tag);

// Constrain gas cost
gas_trace_builder.constrain_gas(clk, OpCode::MOV);
// FIXME: not great that we are having to choose one specific opcode here!
gas_trace_builder.constrain_gas(clk, OpCode::MOV_8);

main_trace.push_back(Row{
.main_clk = clk,
Expand Down
6 changes: 4 additions & 2 deletions yarn-project/simulator/src/avm/avm_gas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ const BaseGasCosts: Record<Opcode, Gas> = {
[Opcode.INTERNALCALL]: makeCost(c.AVM_INTERNALCALL_BASE_L2_GAS, 0),
[Opcode.INTERNALRETURN]: makeCost(c.AVM_INTERNALRETURN_BASE_L2_GAS, 0),
[Opcode.SET]: makeCost(c.AVM_SET_BASE_L2_GAS, 0),
[Opcode.MOV]: makeCost(c.AVM_MOV_BASE_L2_GAS, 0),
[Opcode.MOV_8]: makeCost(c.AVM_MOV_BASE_L2_GAS, 0),
[Opcode.MOV_16]: makeCost(c.AVM_MOV_BASE_L2_GAS, 0),
[Opcode.CMOV]: makeCost(c.AVM_CMOV_BASE_L2_GAS, 0),
[Opcode.SLOAD]: makeCost(c.AVM_SLOAD_BASE_L2_GAS, 0),
[Opcode.SSTORE]: makeCost(c.AVM_SSTORE_BASE_L2_GAS, 0),
Expand Down Expand Up @@ -156,7 +157,8 @@ const DynamicGasCosts: Record<Opcode, Gas> = {
[Opcode.INTERNALCALL]: makeCost(c.AVM_INTERNALCALL_DYN_L2_GAS, 0),
[Opcode.INTERNALRETURN]: makeCost(c.AVM_INTERNALRETURN_DYN_L2_GAS, 0),
[Opcode.SET]: makeCost(c.AVM_SET_DYN_L2_GAS, 0),
[Opcode.MOV]: makeCost(c.AVM_MOV_DYN_L2_GAS, 0),
[Opcode.MOV_8]: makeCost(c.AVM_MOV_DYN_L2_GAS, 0),
[Opcode.MOV_16]: makeCost(c.AVM_MOV_DYN_L2_GAS, 0),
[Opcode.CMOV]: makeCost(c.AVM_CMOV_DYN_L2_GAS, 0),
[Opcode.SLOAD]: makeCost(c.AVM_SLOAD_DYN_L2_GAS, 0),
[Opcode.SSTORE]: makeCost(c.AVM_SSTORE_DYN_L2_GAS, 0),
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/simulator/src/avm/bytecode_utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { promisify } from 'util';
import { gunzip } from 'zlib';

import { Mov } from '../avm/opcodes/memory.js';
import { Opcode } from './serialization/instruction_serialization.js';

const AVM_MAGIC_SUFFIX = Buffer.from([
Mov.opcode, // opcode
Opcode.MOV_8, // opcode
0x00, // indirect
...Buffer.from('000018ca', 'hex'), // srcOffset
...Buffer.from('000018ca', 'hex'), // dstOffset
Expand Down
60 changes: 41 additions & 19 deletions yarn-project/simulator/src/avm/opcodes/instruction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import type { AvmContext } from '../avm_context.js';
import { getBaseGasCost, getDynamicGasCost, mulGas, sumGas } from '../avm_gas.js';
import { type MemoryOperations } from '../avm_memory_types.js';
import { type BufferCursor } from '../serialization/buffer_cursor.js';
import { Opcode, type OperandType, deserialize, serialize } from '../serialization/instruction_serialization.js';
import { type Serializable } from '../serialization/bytecode_serialization.js';
import { Opcode, type OperandType, deserialize, serializeAs } from '../serialization/instruction_serialization.js';

type InstructionConstructor = {
new (...args: any[]): Instruction;
wireFormat?: OperandType[];
};

/**
Expand Down Expand Up @@ -37,29 +37,51 @@ export abstract class Instruction {
return instructionStr;
}

// Default deserialization which uses Class.opcode and Class.wireFormat.
public static deserialize(
this: InstructionConstructor & { wireFormat: OperandType[]; as: any },
buf: BufferCursor | Buffer,
): Instruction {
return this.as(this.wireFormat).deserialize(buf);
}

// Default serialization which uses Class.opcode and Class.wireFormat.
public serialize(): Buffer {
const klass = this.constructor as any;
assert(klass.opcode !== undefined && klass.opcode !== null);
assert(klass.wireFormat !== undefined && klass.wireFormat !== null);
return this.as(klass.opcode, klass.wireFormat).serialize();
}

/**
* Serialize the instruction to a Buffer according to its wire format specified in its subclass.
* If you want to use this, your subclass should specify a {@code static wireFormat: OperandType[]}.
* @param this - The instruction to serialize.
* @returns The serialized instruction.
* Returns a new instruction instance that can be serialized with the given opcode and wire format.
* @param opcode The opcode of the instruction.
* @param wireFormat The wire format of the instruction.
* @returns The new instruction instance.
*/
public serialize(this: any): Buffer {
assert(!!this.constructor.wireFormat, 'wireFormat must be defined on the class');
return serialize(this.constructor.wireFormat, this);
public as(opcode: Opcode, wireFormat: OperandType[]): Instruction & Serializable {
return Object.defineProperty(this, 'serialize', {
value: (): Buffer => {
return serializeAs(wireFormat, opcode, this);
},
enumerable: false,
});
}

/**
* Deserializes a subclass of Instruction from a Buffer.
* If you want to use this, your subclass should specify a {@code static wireFormat: OperandType[]}.
* @param this Class object to deserialize to.
* @param buf Buffer to read from.
* @returns Constructed instance of Class.
* Returns a new instruction class that can be deserialized with the given opcode and wire format.
* @param opcode The opcode of the instruction.
* @param wireFormat The wire format of the instruction.
* @returns The new instruction class.
*/
public static deserialize(this: InstructionConstructor, buf: BufferCursor | Buffer): Instruction {
assert(!!this.wireFormat, 'wireFormat must be defined on the instruction class');
const res = deserialize(buf, this.wireFormat);
const args = res.slice(1); // Remove opcode.
return new this(...args);
public static as(this: InstructionConstructor, wireFormat: OperandType[]) {
return Object.assign(this, {
deserialize: (buf: BufferCursor | Buffer): Instruction => {
const res = deserialize(buf, wireFormat);
const args = res.slice(1); // Remove opcode.
return new this(...args);
},
});
}

/**
Expand Down
Loading

0 comments on commit 4d23561

Please sign in to comment.