Skip to content

Commit 5b27fbc

Browse files
authored
feat(avm)!: variants for MOV opcode (#8440)
Yields an 11% to 21% reduction in bytecode size: ``` avm_simulation_bytecode_size_in_bytes (Token:constructor): 10,658 (-18%) avm_simulation_bytecode_size_in_bytes (FPC:constructor): 6,144 (-21%) avm_simulation_bytecode_size_in_bytes (FPC:prepare_fee): 3,089 (-21%) avm_simulation_bytecode_size_in_bytes (Token:transfer_public): 7,401 (-17%) avm_simulation_bytecode_size_in_bytes (FPC:pay_refund): 3,911 (-19%) avm_simulation_bytecode_size_in_bytes (Benchmarking:increment_balance): 2,620 (-16%) avm_simulation_bytecode_size_in_bytes (FPC:pay_refund_with_shielded_rebate): 3,911 (-19%) ``` There are some gas-related things to cleanup later.
1 parent 346502c commit 5b27fbc

21 files changed

+283
-145
lines changed

avm-transpiler/src/bit_traits.rs

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use acvm::{AcirField, FieldElement};
2+
3+
fn get_msb(n: u128) -> usize {
4+
let mut n = n;
5+
let mut msb = 0;
6+
while n > 0 {
7+
n >>= 1;
8+
msb += 1;
9+
}
10+
msb
11+
}
12+
13+
pub trait BitsQueryable {
14+
fn num_bits(&self) -> usize;
15+
}
16+
17+
impl BitsQueryable for FieldElement {
18+
fn num_bits(&self) -> usize {
19+
AcirField::num_bits(self) as usize
20+
}
21+
}
22+
23+
impl BitsQueryable for u8 {
24+
fn num_bits(&self) -> usize {
25+
get_msb(*self as u128)
26+
}
27+
}
28+
29+
impl BitsQueryable for u16 {
30+
fn num_bits(&self) -> usize {
31+
get_msb(*self as u128)
32+
}
33+
}
34+
35+
impl BitsQueryable for u32 {
36+
fn num_bits(&self) -> usize {
37+
get_msb(*self as u128)
38+
}
39+
}
40+
41+
impl BitsQueryable for u64 {
42+
fn num_bits(&self) -> usize {
43+
get_msb(*self as u128)
44+
}
45+
}
46+
47+
impl BitsQueryable for u128 {
48+
fn num_bits(&self) -> usize {
49+
get_msb(*self)
50+
}
51+
}
52+
53+
pub fn bits_needed_for<T: BitsQueryable>(val: &T) -> usize {
54+
let num_bits = val.num_bits();
55+
if num_bits < 8 {
56+
8
57+
} else if num_bits < 16 {
58+
16
59+
} else if num_bits < 32 {
60+
32
61+
} else if num_bits < 64 {
62+
64
63+
} else if num_bits < 128 {
64+
128
65+
} else {
66+
254
67+
}
68+
}

avm-transpiler/src/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::env;
66
use std::fs;
77
use std::path::Path;
88

9+
mod bit_traits;
910
mod instructions;
1011
mod opcodes;
1112
mod transpile;

avm-transpiler/src/opcodes.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/// All AVM opcodes
22
/// Keep updated with TS, cpp, and docs protocol specs!
3-
#[allow(clippy::upper_case_acronyms, dead_code)]
3+
#[allow(clippy::upper_case_acronyms, dead_code, non_camel_case_types)]
44
#[derive(PartialEq, Copy, Clone, Debug, Eq, Hash)]
55
pub enum AvmOpcode {
66
// Compute
@@ -42,7 +42,8 @@ pub enum AvmOpcode {
4242
INTERNALRETURN,
4343
// Memory
4444
SET,
45-
MOV,
45+
MOV_8,
46+
MOV_16,
4647
CMOV,
4748
// World state
4849
SLOAD,
@@ -129,7 +130,8 @@ impl AvmOpcode {
129130
AvmOpcode::INTERNALRETURN => "INTERNALRETURN",
130131
// Machine State - Memory
131132
AvmOpcode::SET => "SET",
132-
AvmOpcode::MOV => "MOV",
133+
AvmOpcode::MOV_8 => "MOV_8",
134+
AvmOpcode::MOV_16 => "MOV_16",
133135
AvmOpcode::CMOV => "CMOV",
134136

135137
// World State

avm-transpiler/src/transpile.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ use acvm::brillig_vm::brillig::{
99
use acvm::{AcirField, FieldElement};
1010
use noirc_errors::debug_info::DebugInfo;
1111

12+
use crate::bit_traits::bits_needed_for;
1213
use crate::instructions::{
1314
AvmInstruction, AvmOperand, AvmTypeTag, ALL_DIRECT, FIRST_OPERAND_INDIRECT,
1415
SECOND_OPERAND_INDIRECT, ZEROTH_OPERAND_INDIRECT,
1516
};
1617
use crate::opcodes::AvmOpcode;
17-
use crate::utils::{dbg_print_avm_program, dbg_print_brillig_program};
18+
use crate::utils::{dbg_print_avm_program, dbg_print_brillig_program, make_operand};
1819

1920
/// Transpile a Brillig program to AVM bytecode
2021
pub fn brillig_to_avm(
@@ -216,7 +217,7 @@ pub fn brillig_to_avm(
216217
// We are adding a MOV instruction that moves a value to itself.
217218
// This should therefore not affect the program's execution.
218219
avm_instrs.push(AvmInstruction {
219-
opcode: AvmOpcode::MOV,
220+
opcode: AvmOpcode::MOV_8,
220221
indirect: Some(ALL_DIRECT),
221222
operands: vec![AvmOperand::U32 { value: 0x18ca }, AvmOperand::U32 { value: 0x18ca }],
222223
..Default::default()
@@ -741,10 +742,18 @@ fn generate_cast_instruction(
741742

742743
/// Generates an AVM MOV instruction.
743744
fn generate_mov_instruction(indirect: Option<u8>, source: u32, dest: u32) -> AvmInstruction {
745+
let bits_needed = [source, dest].iter().map(bits_needed_for).max().unwrap();
746+
747+
let mov_opcode = match bits_needed {
748+
8 => AvmOpcode::MOV_8,
749+
16 => AvmOpcode::MOV_16,
750+
_ => panic!("MOV operands must fit in 16 bits but needed {}", bits_needed),
751+
};
752+
744753
AvmInstruction {
745-
opcode: AvmOpcode::MOV,
754+
opcode: mov_opcode,
746755
indirect,
747-
operands: vec![AvmOperand::U32 { value: source }, AvmOperand::U32 { value: dest }],
756+
operands: vec![make_operand(bits_needed, &source), make_operand(bits_needed, &dest)],
748757
..Default::default()
749758
}
750759
}

avm-transpiler/src/utils.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use log::{debug, info, trace};
77
use acvm::acir::brillig::Opcode as BrilligOpcode;
88
use acvm::acir::circuit::{AssertionPayload, Opcode, Program};
99

10-
use crate::instructions::AvmInstruction;
10+
use crate::instructions::{AvmInstruction, AvmOperand};
1111
use crate::opcodes::AvmOpcode;
1212

1313
/// Extract the Brillig program from its `Program` wrapper.
@@ -90,3 +90,14 @@ pub fn dbg_print_avm_program(avm_program: &[AvmInstruction]) {
9090
debug!("\t{0:?}: {1}", opcode, count);
9191
}
9292
}
93+
94+
pub fn make_operand<T: Into<u128> + Copy>(bits: usize, value: &T) -> AvmOperand {
95+
match bits {
96+
8 => AvmOperand::U8 { value: Into::<u128>::into(*value) as u8 },
97+
16 => AvmOperand::U16 { value: Into::<u128>::into(*value) as u16 },
98+
32 => AvmOperand::U32 { value: Into::<u128>::into(*value) as u32 },
99+
64 => AvmOperand::U64 { value: Into::<u128>::into(*value) as u64 },
100+
128 => AvmOperand::U128 { value: Into::<u128>::into(*value) },
101+
_ => panic!("Invalid operand size for bits: {}", bits),
102+
}
103+
}

barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp

+10-10
Original file line numberDiff line numberDiff line change
@@ -587,10 +587,10 @@ TEST_F(AvmExecutionTests, movOpcode)
587587
"01" // U8
588588
"13" // val 19
589589
"000000AB" // dst_offset 171
590-
+ to_hex(OpCode::MOV) + // opcode MOV
590+
+ to_hex(OpCode::MOV_8) + // opcode MOV
591591
"00" // Indirect flag
592-
"000000AB" // src_offset 171
593-
"00000021" // dst_offset 33
592+
"AB" // src_offset 171
593+
"21" // dst_offset 33
594594
+ to_hex(OpCode::RETURN) + // opcode RETURN
595595
"00" // Indirect flag
596596
"00000000" // ret offset 0
@@ -613,9 +613,9 @@ TEST_F(AvmExecutionTests, movOpcode)
613613
// MOV
614614
EXPECT_THAT(
615615
instructions.at(1),
616-
AllOf(Field(&Instruction::op_code, OpCode::MOV),
616+
AllOf(Field(&Instruction::op_code, OpCode::MOV_8),
617617
Field(&Instruction::operands,
618-
ElementsAre(VariantWith<uint8_t>(0), VariantWith<uint32_t>(171), VariantWith<uint32_t>(33)))));
618+
ElementsAre(VariantWith<uint8_t>(0), VariantWith<uint8_t>(171), VariantWith<uint8_t>(33)))));
619619

620620
auto trace = gen_trace_from_instr(instructions);
621621

@@ -701,10 +701,10 @@ TEST_F(AvmExecutionTests, indMovOpcode)
701701
"01" // U8
702702
"FF" // val 255
703703
"0000000A" // dst_offset 10
704-
+ to_hex(OpCode::MOV) + // opcode MOV
704+
+ to_hex(OpCode::MOV_8) + // opcode MOV
705705
"01" // Indirect flag
706-
"00000001" // src_offset 1 --> direct offset 10
707-
"00000002" // dst_offset 2 --> direct offset 11
706+
"01" // src_offset 1 --> direct offset 10
707+
"02" // dst_offset 2 --> direct offset 11
708708
+ to_hex(OpCode::RETURN) + // opcode RETURN
709709
"00" // Indirect flag
710710
"00000000" // ret offset 0
@@ -717,9 +717,9 @@ TEST_F(AvmExecutionTests, indMovOpcode)
717717

718718
// MOV
719719
EXPECT_THAT(instructions.at(3),
720-
AllOf(Field(&Instruction::op_code, OpCode::MOV),
720+
AllOf(Field(&Instruction::op_code, OpCode::MOV_8),
721721
Field(&Instruction::operands,
722-
ElementsAre(VariantWith<uint8_t>(1), VariantWith<uint32_t>(1), VariantWith<uint32_t>(2)))));
722+
ElementsAre(VariantWith<uint8_t>(1), VariantWith<uint8_t>(1), VariantWith<uint8_t>(2)))));
723723

724724
auto trace = gen_trace_from_instr(instructions);
725725

barretenberg/cpp/src/barretenberg/vm/avm/trace/deserialization.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ const std::unordered_map<OpCode, std::vector<OperandType>> OPCODE_WIRE_FORMAT =
8888

8989
// Machine State - Memory
9090
// OpCode::SET is handled differently
91-
{ OpCode::MOV, { OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32 } },
91+
{ OpCode::MOV_8, { OperandType::INDIRECT, OperandType::UINT8, OperandType::UINT8 } },
92+
{ OpCode::MOV_16, { OperandType::INDIRECT, OperandType::UINT16, OperandType::UINT16 } },
9293
{ OpCode::CMOV,
9394
{ OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } },
9495

barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -616,10 +616,15 @@ std::vector<Row> Execution::gen_trace(std::vector<Instruction> const& instructio
616616
std::get<uint8_t>(inst.operands.at(0)), val, std::get<uint32_t>(inst.operands.at(3)), in_tag);
617617
break;
618618
}
619-
case OpCode::MOV:
619+
case OpCode::MOV_8:
620620
trace_builder.op_mov(std::get<uint8_t>(inst.operands.at(0)),
621-
std::get<uint32_t>(inst.operands.at(1)),
622-
std::get<uint32_t>(inst.operands.at(2)));
621+
std::get<uint8_t>(inst.operands.at(1)),
622+
std::get<uint8_t>(inst.operands.at(2)));
623+
break;
624+
case OpCode::MOV_16:
625+
trace_builder.op_mov(std::get<uint8_t>(inst.operands.at(0)),
626+
std::get<uint16_t>(inst.operands.at(1)),
627+
std::get<uint16_t>(inst.operands.at(2)));
623628
break;
624629
case OpCode::CMOV:
625630
trace_builder.op_cmov(std::get<uint8_t>(inst.operands.at(0)),

barretenberg/cpp/src/barretenberg/vm/avm/trace/fixed_gas.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ const std::unordered_map<OpCode, FixedGasTable::GasRow> GAS_COST_TABLE = {
5252
{ OpCode::INTERNALCALL, make_cost(AVM_INTERNALCALL_BASE_L2_GAS, 0, AVM_INTERNALCALL_DYN_L2_GAS, 0) },
5353
{ OpCode::INTERNALRETURN, make_cost(AVM_INTERNALRETURN_BASE_L2_GAS, 0, AVM_INTERNALRETURN_DYN_L2_GAS, 0) },
5454
{ OpCode::SET, make_cost(AVM_SET_BASE_L2_GAS, 0, AVM_SET_DYN_L2_GAS, 0) },
55-
{ OpCode::MOV, make_cost(AVM_MOV_BASE_L2_GAS, 0, AVM_MOV_DYN_L2_GAS, 0) },
55+
{ OpCode::MOV_8, make_cost(AVM_MOV_BASE_L2_GAS, 0, AVM_MOV_DYN_L2_GAS, 0) },
56+
{ OpCode::MOV_16, make_cost(AVM_MOV_BASE_L2_GAS, 0, AVM_MOV_DYN_L2_GAS, 0) },
5657
{ OpCode::CMOV, make_cost(AVM_CMOV_BASE_L2_GAS, 0, AVM_CMOV_DYN_L2_GAS, 0) },
5758
{ OpCode::SLOAD, make_cost(AVM_SLOAD_BASE_L2_GAS, 0, AVM_SLOAD_DYN_L2_GAS, 0) },
5859
{ OpCode::SSTORE, make_cost(AVM_SSTORE_BASE_L2_GAS, 0, AVM_SSTORE_DYN_L2_GAS, 0) },

barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,10 @@ std::string to_string(OpCode opcode)
104104
// Machine State - Memory
105105
case OpCode::SET:
106106
return "SET";
107-
case OpCode::MOV:
108-
return "MOV";
107+
case OpCode::MOV_8:
108+
return "MOV_8";
109+
case OpCode::MOV_16:
110+
return "MOV_16";
109111
case OpCode::CMOV:
110112
return "CMOV";
111113
// World State

barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ enum class OpCode : uint8_t {
6868
INTERNALRETURN,
6969
// Machine State - Memory
7070
SET,
71-
MOV,
71+
MOV_8,
72+
MOV_16,
7273
CMOV,
7374

7475
// World State

barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1817,7 +1817,8 @@ void AvmTraceBuilder::op_mov(uint8_t indirect, uint32_t src_offset, uint32_t dst
18171817
mem_trace_builder.write_into_memory(call_ptr, clk, IntermRegister::IC, direct_dst_offset, val, tag, tag);
18181818

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

18221823
main_trace.push_back(Row{
18231824
.main_clk = clk,

yarn-project/simulator/src/avm/avm_gas.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ const BaseGasCosts: Record<Opcode, Gas> = {
9191
[Opcode.INTERNALCALL]: makeCost(c.AVM_INTERNALCALL_BASE_L2_GAS, 0),
9292
[Opcode.INTERNALRETURN]: makeCost(c.AVM_INTERNALRETURN_BASE_L2_GAS, 0),
9393
[Opcode.SET]: makeCost(c.AVM_SET_BASE_L2_GAS, 0),
94-
[Opcode.MOV]: makeCost(c.AVM_MOV_BASE_L2_GAS, 0),
94+
[Opcode.MOV_8]: makeCost(c.AVM_MOV_BASE_L2_GAS, 0),
95+
[Opcode.MOV_16]: makeCost(c.AVM_MOV_BASE_L2_GAS, 0),
9596
[Opcode.CMOV]: makeCost(c.AVM_CMOV_BASE_L2_GAS, 0),
9697
[Opcode.SLOAD]: makeCost(c.AVM_SLOAD_BASE_L2_GAS, 0),
9798
[Opcode.SSTORE]: makeCost(c.AVM_SSTORE_BASE_L2_GAS, 0),
@@ -156,7 +157,8 @@ const DynamicGasCosts: Record<Opcode, Gas> = {
156157
[Opcode.INTERNALCALL]: makeCost(c.AVM_INTERNALCALL_DYN_L2_GAS, 0),
157158
[Opcode.INTERNALRETURN]: makeCost(c.AVM_INTERNALRETURN_DYN_L2_GAS, 0),
158159
[Opcode.SET]: makeCost(c.AVM_SET_DYN_L2_GAS, 0),
159-
[Opcode.MOV]: makeCost(c.AVM_MOV_DYN_L2_GAS, 0),
160+
[Opcode.MOV_8]: makeCost(c.AVM_MOV_DYN_L2_GAS, 0),
161+
[Opcode.MOV_16]: makeCost(c.AVM_MOV_DYN_L2_GAS, 0),
160162
[Opcode.CMOV]: makeCost(c.AVM_CMOV_DYN_L2_GAS, 0),
161163
[Opcode.SLOAD]: makeCost(c.AVM_SLOAD_DYN_L2_GAS, 0),
162164
[Opcode.SSTORE]: makeCost(c.AVM_SSTORE_DYN_L2_GAS, 0),

yarn-project/simulator/src/avm/bytecode_utils.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { promisify } from 'util';
22
import { gunzip } from 'zlib';
33

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

66
const AVM_MAGIC_SUFFIX = Buffer.from([
7-
Mov.opcode, // opcode
7+
Opcode.MOV_8, // opcode
88
0x00, // indirect
99
...Buffer.from('000018ca', 'hex'), // srcOffset
1010
...Buffer.from('000018ca', 'hex'), // dstOffset

0 commit comments

Comments
 (0)