Skip to content

Commit 5bb38b1

Browse files
authored
feat(avm)!: make JUMP(I) 16-bit (#8443)
Earns ~2-5%. I did not add an 8bit version because the jump currently rarely fits, and once we move to byte-indexed PC, it will almost never fit.
1 parent dc43306 commit 5bb38b1

File tree

14 files changed

+101
-104
lines changed

14 files changed

+101
-104
lines changed

avm-transpiler/src/bit_traits.rs

+6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ impl BitsQueryable for u128 {
5050
}
5151
}
5252

53+
impl BitsQueryable for usize {
54+
fn num_bits(&self) -> usize {
55+
get_msb(*self as u128)
56+
}
57+
}
58+
5359
pub fn bits_needed_for<T: BitsQueryable>(val: &T) -> usize {
5460
let num_bits = val.num_bits();
5561
if num_bits < 8 {

avm-transpiler/src/opcodes.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ pub enum AvmOpcode {
3636
L2GASLEFT,
3737
DAGASLEFT,
3838
// Control flow
39-
JUMP,
40-
JUMPI,
39+
JUMP_16,
40+
JUMPI_16,
4141
INTERNALCALL,
4242
INTERNALRETURN,
4343
// Memory
@@ -129,8 +129,8 @@ impl AvmOpcode {
129129
AvmOpcode::L2GASLEFT => "L2GASLEFT",
130130
AvmOpcode::DAGASLEFT => "DAGASLEFT",
131131
// Machine State - Internal Control Flow
132-
AvmOpcode::JUMP => "JUMP",
133-
AvmOpcode::JUMPI => "JUMPI",
132+
AvmOpcode::JUMP_16 => "JUMP_16",
133+
AvmOpcode::JUMPI_16 => "JUMPI_16",
134134
AvmOpcode::INTERNALCALL => "INTERNALCALL",
135135
AvmOpcode::INTERNALRETURN => "INTERNALRETURN",
136136
// Machine State - Memory

avm-transpiler/src/transpile.rs

+4-7
Original file line numberDiff line numberDiff line change
@@ -101,20 +101,17 @@ pub fn brillig_to_avm(
101101
BrilligOpcode::Jump { location } => {
102102
let avm_loc = brillig_pcs_to_avm_pcs[*location];
103103
avm_instrs.push(AvmInstruction {
104-
opcode: AvmOpcode::JUMP,
105-
operands: vec![AvmOperand::U32 { value: avm_loc as u32 }],
104+
opcode: AvmOpcode::JUMP_16,
105+
operands: vec![make_operand(16, &avm_loc)],
106106
..Default::default()
107107
});
108108
}
109109
BrilligOpcode::JumpIf { condition, location } => {
110110
let avm_loc = brillig_pcs_to_avm_pcs[*location];
111111
avm_instrs.push(AvmInstruction {
112-
opcode: AvmOpcode::JUMPI,
112+
opcode: AvmOpcode::JUMPI_16,
113113
indirect: Some(ALL_DIRECT),
114-
operands: vec![
115-
AvmOperand::U32 { value: avm_loc as u32 },
116-
AvmOperand::U32 { value: condition.to_usize() as u32 },
117-
],
114+
operands: vec![make_operand(16, &avm_loc), make_operand(16, &condition.0)],
118115
..Default::default()
119116
});
120117
}

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

+54-60
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,6 @@ TEST_F(AvmExecutionTests, nestedInternalCalls)
403403
// 0 1 2 3 4
404404
TEST_F(AvmExecutionTests, jumpAndCalldatacopy)
405405
{
406-
GTEST_SKIP();
407406
std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET
408407
"00" // Indirect flag
409408
"03" // U32
@@ -419,8 +418,8 @@ TEST_F(AvmExecutionTests, jumpAndCalldatacopy)
419418
"00000000" // cd_offset
420419
"00000001" // copy_size
421420
"0000000A" // dst_offset // M[10] = 13, M[11] = 156
422-
+ to_hex(OpCode::JUMP) + // opcode JUMP
423-
"00000005" // jmp_dest (FDIV located at 3)
421+
+ to_hex(OpCode::JUMP_16) + // opcode JUMP
422+
"0005" // jmp_dest (FDIV located at 3)
424423
+ to_hex(OpCode::SUB) + // opcode SUB
425424
"00" // Indirect flag
426425
"06" // FF
@@ -456,14 +455,16 @@ TEST_F(AvmExecutionTests, jumpAndCalldatacopy)
456455

457456
// JUMP
458457
EXPECT_THAT(instructions.at(3),
459-
AllOf(Field(&Instruction::op_code, OpCode::JUMP),
460-
Field(&Instruction::operands, ElementsAre(VariantWith<uint32_t>(3)))));
458+
AllOf(Field(&Instruction::op_code, OpCode::JUMP_16),
459+
Field(&Instruction::operands, ElementsAre(VariantWith<uint16_t>(5)))));
461460

462461
std::vector<FF> returndata;
463462
auto trace = Execution::gen_trace(instructions, returndata, std::vector<FF>{ 13, 156 }, public_inputs_vec);
464463

465464
// Expected sequence of PCs during execution
466-
std::vector<FF> pc_sequence{ 0, 1, 3, 4 };
465+
std::vector<FF> pc_sequence{
466+
0, 1, 2, 3, 4, 6,
467+
};
467468

468469
for (size_t i = 0; i < 4; i++) {
469470
EXPECT_EQ(trace.at(i + 1).main_pc, pc_sequence.at(i));
@@ -492,61 +493,70 @@ TEST_F(AvmExecutionTests, jumpAndCalldatacopy)
492493
// We test this bytecode with two calldatacopy values: 9873123 and 0.
493494
TEST_F(AvmExecutionTests, jumpiAndCalldatacopy)
494495
{
495-
GTEST_SKIP();
496-
std::string bytecode_hex = to_hex(OpCode::CALLDATACOPY) + // opcode CALLDATACOPY (no in tag)
497-
"00" // Indirect flag
498-
"00000000" // cd_offset
499-
"00000001" // copy_size
500-
"0000000A" // dst_offset 10
501-
+ to_hex(OpCode::SET_16) + // opcode SET
502-
"00" // Indirect flag
503-
"02" // U16
504-
"0014" // val 20
505-
"0065" // dst_offset 101
506-
+ to_hex(OpCode::JUMPI) + // opcode JUMPI
507-
"00" // Indirect flag
508-
"00000004" // jmp_dest (MUL located at 4)
509-
"0000000A" // cond_offset 10
510-
+ to_hex(OpCode::ADD) + // opcode ADD
511-
"00" // Indirect flag
512-
"02" // U16
513-
"00000065" // addr 101
514-
"00000065" // addr 101
515-
"00000065" // output addr 101
516-
+ to_hex(OpCode::MUL) + // opcode MUL
517-
"00" // Indirect flag
518-
"02" // U16
519-
"00000065" // addr 101
520-
"00000065" // addr 101
521-
"00000066" // output of MUL addr 102
522-
+ to_hex(OpCode::RETURN) + // opcode RETURN
523-
"00" // Indirect flag
524-
"00000000" // ret offset 0
525-
"00000000" // ret size 0
496+
std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET
497+
"00" // Indirect flag
498+
"03" // U32
499+
"00" // val
500+
"00" // dst_offset
501+
+ to_hex(OpCode::SET_8) + // opcode SET
502+
"00" // Indirect flag
503+
"03" // U32
504+
"01" // val
505+
"01" // dst_offset
506+
+ to_hex(OpCode::CALLDATACOPY) + // opcode CALLDATACOPY (no in tag)
507+
"00" // Indirect flag
508+
"00000000" // cd_offset
509+
"00000001" // copy_size
510+
"0000000A" // dst_offset 10
511+
+ to_hex(OpCode::SET_8) + // opcode SET
512+
"00" // Indirect flag
513+
"02" // U16
514+
"14" // val 20
515+
"65" // dst_offset 101
516+
+ to_hex(OpCode::JUMPI_16) + // opcode JUMPI
517+
"00" // Indirect flag
518+
"0006" // jmp_dest (MUL located at 6)
519+
"000A" // cond_offset 10
520+
+ to_hex(OpCode::ADD) + // opcode ADD
521+
"00" // Indirect flag
522+
"02" // U16
523+
"00000065" // addr 101
524+
"00000065" // addr 101
525+
"00000065" // output addr 101
526+
+ to_hex(OpCode::MUL) + // opcode MUL
527+
"00" // Indirect flag
528+
"02" // U16
529+
"00000065" // addr 101
530+
"00000065" // addr 101
531+
"00000066" // output of MUL addr 102
532+
+ to_hex(OpCode::RETURN) + // opcode RETURN
533+
"00" // Indirect flag
534+
"00000000" // ret offset 0
535+
"00000000" // ret size 0
526536
;
527537

528538
auto bytecode = hex_to_bytes(bytecode_hex);
529539
auto instructions = Deserialization::parse(bytecode);
530540

531-
ASSERT_THAT(instructions, SizeIs(6));
541+
ASSERT_THAT(instructions, SizeIs(8));
532542

533543
// We test parsing of JUMPI.
534544

535545
// JUMPI
536546
EXPECT_THAT(
537-
instructions.at(2),
538-
AllOf(Field(&Instruction::op_code, OpCode::JUMPI),
547+
instructions.at(4),
548+
AllOf(Field(&Instruction::op_code, OpCode::JUMPI_16),
539549
Field(&Instruction::operands,
540-
ElementsAre(VariantWith<uint8_t>(0), VariantWith<uint32_t>(4), VariantWith<uint32_t>(10)))));
550+
ElementsAre(VariantWith<uint8_t>(0), VariantWith<uint16_t>(6), VariantWith<uint16_t>(10)))));
541551

542-
std::vector<FF> returndata{};
552+
std::vector<FF> returndata;
543553
auto trace_jump = Execution::gen_trace(instructions, returndata, std::vector<FF>{ 9873123 }, public_inputs_vec);
544554
auto trace_no_jump = Execution::gen_trace(instructions, returndata, std::vector<FF>{ 0 }, public_inputs_vec);
545555

546556
// Expected sequence of PCs during execution with jump
547-
std::vector<FF> pc_sequence_jump{ 0, 1, 2, 4, 5 };
557+
std::vector<FF> pc_sequence_jump{ 0, 1, 2, 3, 4, 6, 7 };
548558
// Expected sequence of PCs during execution without jump
549-
std::vector<FF> pc_sequence_no_jump{ 0, 1, 2, 3, 4, 5 };
559+
std::vector<FF> pc_sequence_no_jump{ 0, 1, 2, 3, 4, 5, 6, 7 };
550560

551561
for (size_t i = 0; i < 5; i++) {
552562
EXPECT_EQ(trace_jump.at(i + 1).main_pc, pc_sequence_jump.at(i));
@@ -556,22 +566,6 @@ TEST_F(AvmExecutionTests, jumpiAndCalldatacopy)
556566
EXPECT_EQ(trace_no_jump.at(i + 1).main_pc, pc_sequence_no_jump.at(i));
557567
}
558568

559-
// JUMP CASE
560-
// Find the first row enabling the MUL opcode
561-
auto row = std::ranges::find_if(trace_jump.begin(), trace_jump.end(), [](Row r) { return r.main_sel_op_mul == 1; });
562-
EXPECT_EQ(row->main_ic, 400); // 400 = 20 * 20
563-
564-
// Find the first row enabling the addition selector.
565-
row = std::ranges::find_if(trace_jump.begin(), trace_jump.end(), [](Row r) { return r.main_sel_op_add == 1; });
566-
// It must have failed as addition was "jumped over".
567-
EXPECT_EQ(row, trace_jump.end());
568-
569-
// NO JUMP CASE
570-
// Find the first row enabling the MUL opcode
571-
row =
572-
std::ranges::find_if(trace_no_jump.begin(), trace_no_jump.end(), [](Row r) { return r.main_sel_op_mul == 1; });
573-
EXPECT_EQ(row->main_ic, 1600); // 800 = (20 + 20) * (20 + 20)
574-
575569
// traces validation
576570
validate_trace(std::move(trace_jump), public_inputs, { 9873123 });
577571
validate_trace(std::move(trace_no_jump), public_inputs, { 0 });

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ const std::unordered_map<OpCode, std::vector<OperandType>> OPCODE_WIRE_FORMAT =
8181
{ OpCode::DAGASLEFT, getter_format },
8282

8383
// Machine State - Internal Control Flow
84-
{ OpCode::JUMP, { OperandType::UINT32 } },
85-
{ OpCode::JUMPI, { OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32 } },
84+
{ OpCode::JUMP_16, { OperandType::UINT16 } },
85+
{ OpCode::JUMPI_16, { OperandType::INDIRECT, OperandType::UINT16, OperandType::UINT16 } },
8686
{ OpCode::INTERNALCALL, { OperandType::UINT32 } },
8787
{ OpCode::INTERNALRETURN, {} },
8888

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -572,13 +572,13 @@ std::vector<Row> Execution::gen_trace(std::vector<Instruction> const& instructio
572572
break;
573573

574574
// Machine State - Internal Control Flow
575-
case OpCode::JUMP:
576-
trace_builder.op_jump(std::get<uint32_t>(inst.operands.at(0)));
575+
case OpCode::JUMP_16:
576+
trace_builder.op_jump(std::get<uint16_t>(inst.operands.at(0)));
577577
break;
578-
case OpCode::JUMPI:
578+
case OpCode::JUMPI_16:
579579
trace_builder.op_jumpi(std::get<uint8_t>(inst.operands.at(0)),
580-
std::get<uint32_t>(inst.operands.at(1)),
581-
std::get<uint32_t>(inst.operands.at(2)));
580+
std::get<uint16_t>(inst.operands.at(1)),
581+
std::get<uint16_t>(inst.operands.at(2)));
582582
break;
583583
case OpCode::INTERNALCALL:
584584
trace_builder.op_internal_call(std::get<uint32_t>(inst.operands.at(0)));

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ const std::unordered_map<OpCode, FixedGasTable::GasRow> GAS_COST_TABLE = {
4747
{ OpCode::CALLDATACOPY, make_cost(AVM_CALLDATACOPY_BASE_L2_GAS, 0, AVM_CALLDATACOPY_DYN_L2_GAS, 0) },
4848
{ OpCode::L2GASLEFT, make_cost(AVM_L2GASLEFT_BASE_L2_GAS, 0, AVM_L2GASLEFT_DYN_L2_GAS, 0) },
4949
{ OpCode::DAGASLEFT, make_cost(AVM_DAGASLEFT_BASE_L2_GAS, 0, AVM_DAGASLEFT_DYN_L2_GAS, 0) },
50-
{ OpCode::JUMP, make_cost(AVM_JUMP_BASE_L2_GAS, 0, AVM_JUMP_DYN_L2_GAS, 0) },
51-
{ OpCode::JUMPI, make_cost(AVM_JUMPI_BASE_L2_GAS, 0, AVM_JUMPI_DYN_L2_GAS, 0) },
50+
{ OpCode::JUMP_16, make_cost(AVM_JUMP_BASE_L2_GAS, 0, AVM_JUMP_DYN_L2_GAS, 0) },
51+
{ OpCode::JUMPI_16, make_cost(AVM_JUMPI_BASE_L2_GAS, 0, AVM_JUMPI_DYN_L2_GAS, 0) },
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_8, make_cost(AVM_SET_BASE_L2_GAS, 0, AVM_SET_DYN_L2_GAS, 0) },

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,10 @@ std::string to_string(OpCode opcode)
9393
case OpCode::DAGASLEFT:
9494
return "DAGASLEFT";
9595
// Machine State - Internal Control Flow
96-
case OpCode::JUMP:
97-
return "JUMP";
98-
case OpCode::JUMPI:
99-
return "JUMPI";
96+
case OpCode::JUMP_16:
97+
return "JUMP_16";
98+
case OpCode::JUMPI_16:
99+
return "JUMPI_16";
100100
case OpCode::INTERNALCALL:
101101
return "INTERNALCALL";
102102
case OpCode::INTERNALRETURN:

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ enum class OpCode : uint8_t {
6262
L2GASLEFT,
6363
DAGASLEFT,
6464
// Machine State - Internal Control Flow
65-
JUMP,
66-
JUMPI,
65+
JUMP_16,
66+
JUMPI_16,
6767
INTERNALCALL,
6868
INTERNALRETURN,
6969
// Machine State - Memory

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -1563,7 +1563,7 @@ void AvmTraceBuilder::op_jump(uint32_t jmp_dest)
15631563
auto clk = static_cast<uint32_t>(main_trace.size()) + 1;
15641564

15651565
// Constrain gas cost
1566-
gas_trace_builder.constrain_gas(clk, OpCode::JUMP);
1566+
gas_trace_builder.constrain_gas(clk, OpCode::JUMP_16);
15671567

15681568
main_trace.push_back(Row{
15691569
.main_clk = clk,
@@ -1612,7 +1612,7 @@ void AvmTraceBuilder::op_jumpi(uint8_t indirect, uint32_t jmp_dest, uint32_t con
16121612
uint32_t next_pc = !id_zero ? jmp_dest : pc + 1;
16131613

16141614
// Constrain gas cost
1615-
gas_trace_builder.constrain_gas(clk, OpCode::JUMPI);
1615+
gas_trace_builder.constrain_gas(clk, OpCode::JUMPI_16);
16161616

16171617
main_trace.push_back(Row{
16181618
.main_clk = clk,

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ const BaseGasCosts: Record<Opcode, Gas> = {
8686
[Opcode.CALLDATACOPY]: makeCost(c.AVM_CALLDATACOPY_BASE_L2_GAS, 0),
8787
[Opcode.L2GASLEFT]: makeCost(c.AVM_L2GASLEFT_BASE_L2_GAS, 0),
8888
[Opcode.DAGASLEFT]: makeCost(c.AVM_DAGASLEFT_BASE_L2_GAS, 0),
89-
[Opcode.JUMP]: makeCost(c.AVM_JUMP_BASE_L2_GAS, 0),
90-
[Opcode.JUMPI]: makeCost(c.AVM_JUMPI_BASE_L2_GAS, 0),
89+
[Opcode.JUMP_16]: makeCost(c.AVM_JUMP_BASE_L2_GAS, 0),
90+
[Opcode.JUMPI_16]: makeCost(c.AVM_JUMPI_BASE_L2_GAS, 0),
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_8]: makeCost(c.AVM_SET_BASE_L2_GAS, 0),
@@ -157,8 +157,8 @@ const DynamicGasCosts: Record<Opcode, Gas> = {
157157
[Opcode.CALLDATACOPY]: makeCost(c.AVM_CALLDATACOPY_DYN_L2_GAS, 0),
158158
[Opcode.L2GASLEFT]: makeCost(c.AVM_L2GASLEFT_DYN_L2_GAS, 0),
159159
[Opcode.DAGASLEFT]: makeCost(c.AVM_DAGASLEFT_DYN_L2_GAS, 0),
160-
[Opcode.JUMP]: makeCost(c.AVM_JUMP_DYN_L2_GAS, 0),
161-
[Opcode.JUMPI]: makeCost(c.AVM_JUMPI_DYN_L2_GAS, 0),
160+
[Opcode.JUMP_16]: makeCost(c.AVM_JUMP_DYN_L2_GAS, 0),
161+
[Opcode.JUMPI_16]: makeCost(c.AVM_JUMPI_DYN_L2_GAS, 0),
162162
[Opcode.INTERNALCALL]: makeCost(c.AVM_INTERNALCALL_DYN_L2_GAS, 0),
163163
[Opcode.INTERNALRETURN]: makeCost(c.AVM_INTERNALRETURN_DYN_L2_GAS, 0),
164164
[Opcode.SET_8]: makeCost(c.AVM_SET_DYN_L2_GAS, 0),

yarn-project/simulator/src/avm/opcodes/control_flow.test.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ describe('Control Flow Opcodes', () => {
1515
it('Should (de)serialize correctly', () => {
1616
const buf = Buffer.from([
1717
Jump.opcode, // opcode
18-
...Buffer.from('12345678', 'hex'), // loc
18+
...Buffer.from('1234', 'hex'), // loc
1919
]);
20-
const inst = new Jump(/*loc=*/ 0x12345678);
20+
const inst = new Jump(/*loc=*/ 0x1234);
2121

2222
expect(Jump.deserialize(buf)).toEqual(inst);
2323
expect(inst.serialize()).toEqual(buf);
@@ -39,10 +39,10 @@ describe('Control Flow Opcodes', () => {
3939
const buf = Buffer.from([
4040
JumpI.opcode, // opcode
4141
0x01, // indirect
42-
...Buffer.from('12345678', 'hex'), // loc
43-
...Buffer.from('a2345678', 'hex'), // condOffset
42+
...Buffer.from('1234', 'hex'), // loc
43+
...Buffer.from('a234', 'hex'), // condOffset
4444
]);
45-
const inst = new JumpI(/*indirect=*/ 1, /*loc=*/ 0x12345678, /*condOffset=*/ 0xa2345678);
45+
const inst = new JumpI(/*indirect=*/ 1, /*loc=*/ 0x1234, /*condOffset=*/ 0xa234);
4646

4747
expect(JumpI.deserialize(buf)).toEqual(inst);
4848
expect(inst.serialize()).toEqual(buf);

0 commit comments

Comments
 (0)