diff --git a/avm-transpiler/src/instructions.rs b/avm-transpiler/src/instructions.rs index f2e56b451eb..0fc44228f44 100644 --- a/avm-transpiler/src/instructions.rs +++ b/avm-transpiler/src/instructions.rs @@ -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 @@ -110,6 +112,7 @@ pub enum AvmOperand { U32 { value: u32 }, U64 { value: u64 }, U128 { value: u128 }, + FF { value: FieldElement }, } impl Display for AvmOperand { @@ -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), } } } @@ -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(), } } } diff --git a/avm-transpiler/src/opcodes.rs b/avm-transpiler/src/opcodes.rs index 88c1330b296..86537a38c88 100644 --- a/avm-transpiler/src/opcodes.rs +++ b/avm-transpiler/src/opcodes.rs @@ -41,7 +41,12 @@ pub enum AvmOpcode { INTERNALCALL, INTERNALRETURN, // Memory - SET, + SET_8, + SET_16, + SET_32, + SET_64, + SET_128, + SET_FF, MOV_8, MOV_16, CMOV, @@ -129,7 +134,12 @@ impl AvmOpcode { AvmOpcode::INTERNALCALL => "INTERNALCALL", AvmOpcode::INTERNALRETURN => "INTERNALRETURN", // Machine State - Memory - AvmOpcode::SET => "SET", + AvmOpcode::SET_8 => "SET_8", + AvmOpcode::SET_16 => "SET_16", + AvmOpcode::SET_32 => "SET_32", + AvmOpcode::SET_64 => "SET_64", + AvmOpcode::SET_128 => "SET_128", + AvmOpcode::SET_FF => "SET_FF", AvmOpcode::MOV_8 => "MOV_8", AvmOpcode::MOV_16 => "MOV_16", AvmOpcode::CMOV => "CMOV", diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 420efbab480..c4800a2c552 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -6,7 +6,7 @@ use acvm::acir::circuit::BrilligOpcodeLocation; use acvm::brillig_vm::brillig::{ BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapArray, HeapVector, MemoryAddress, ValueOrArray, }; -use acvm::{AcirField, FieldElement}; +use acvm::FieldElement; use noirc_errors::debug_info::DebugInfo; use crate::bit_traits::bits_needed_for; @@ -674,45 +674,38 @@ fn handle_const( ) { let tag = tag_from_bit_size(*bit_size); let dest = destination.to_usize() as u32; - - if !matches!(tag, AvmTypeTag::FIELD) { - avm_instrs.push(generate_set_instruction(tag, dest, value.to_u128(), indirect)); - } else { - // We can't fit a field in an instruction. This should've been handled in Brillig. - let field = value; - if field.num_bits() > 128 { - panic!("SET: Field value doesn't fit in 128 bits, that's not supported!"); - } - avm_instrs.extend([ - generate_set_instruction(AvmTypeTag::UINT128, dest, field.to_u128(), indirect), - generate_cast_instruction(dest, indirect, dest, indirect, AvmTypeTag::FIELD), - ]); - } + avm_instrs.push(generate_set_instruction(tag, dest, value, indirect)); } /// Generates an AVM SET instruction. fn generate_set_instruction( tag: AvmTypeTag, dest: u32, - value: u128, + value: &FieldElement, indirect: bool, ) -> AvmInstruction { + let bits_needed_val = bits_needed_for(value); + let bits_needed_mem = if bits_needed_val >= 16 { 16 } else { bits_needed_for(&dest) }; + assert!(bits_needed_mem <= 16); + let bits_needed_opcode = bits_needed_val.max(bits_needed_mem); + + let set_opcode = match bits_needed_opcode { + 8 => AvmOpcode::SET_8, + 16 => AvmOpcode::SET_16, + 32 => AvmOpcode::SET_32, + 64 => AvmOpcode::SET_64, + 128 => AvmOpcode::SET_128, + 254 => AvmOpcode::SET_FF, + _ => panic!("Invalid bits needed for opcode: {}", bits_needed_opcode), + }; + AvmInstruction { - opcode: AvmOpcode::SET, + opcode: set_opcode, indirect: if indirect { Some(ZEROTH_OPERAND_INDIRECT) } else { Some(ALL_DIRECT) }, tag: Some(tag), operands: vec![ - // const - match tag { - AvmTypeTag::UINT8 => AvmOperand::U8 { value: value as u8 }, - AvmTypeTag::UINT16 => AvmOperand::U16 { value: value as u16 }, - AvmTypeTag::UINT32 => AvmOperand::U32 { value: value as u32 }, - AvmTypeTag::UINT64 => AvmOperand::U64 { value: value as u64 }, - AvmTypeTag::UINT128 => AvmOperand::U128 { value }, - _ => panic!("Invalid type tag {:?} for set", tag), - }, - // dest offset - AvmOperand::U32 { value: dest }, + make_operand(bits_needed_opcode, value), + make_operand(bits_needed_mem, &dest), ], } } @@ -1137,8 +1130,6 @@ pub fn map_brillig_pcs_to_avm_pcs(brillig_bytecode: &[BrilligOpcode 2, - BrilligOpcode::IndirectConst { bit_size: BitSize::Field, .. } => 2, BrilligOpcode::Cast { bit_size: BitSize::Integer(IntegerBitSize::U1), .. } => 3, _ => 1, }; diff --git a/avm-transpiler/src/utils.rs b/avm-transpiler/src/utils.rs index cc440feda05..3f5269f5daa 100644 --- a/avm-transpiler/src/utils.rs +++ b/avm-transpiler/src/utils.rs @@ -1,7 +1,7 @@ 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; @@ -91,13 +91,15 @@ pub fn dbg_print_avm_program(avm_program: &[AvmInstruction]) { } } -pub fn make_operand + Copy>(bits: usize, value: &T) -> AvmOperand { +pub fn make_operand + Clone>(bits: usize, value: &T) -> AvmOperand { + let field: FieldElement = value.clone().into(); match bits { - 8 => AvmOperand::U8 { value: Into::::into(*value) as u8 }, - 16 => AvmOperand::U16 { value: Into::::into(*value) as u16 }, - 32 => AvmOperand::U32 { value: Into::::into(*value) as u32 }, - 64 => AvmOperand::U64 { value: Into::::into(*value) as u64 }, - 128 => AvmOperand::U128 { value: Into::::into(*value) }, + 8 => AvmOperand::U8 { value: field.try_to_u32().unwrap() as u8 }, + 16 => AvmOperand::U16 { value: field.try_to_u32().unwrap() as u16 }, + 32 => AvmOperand::U32 { value: field.try_to_u32().unwrap() }, + 64 => AvmOperand::U64 { value: field.try_to_u64().unwrap() }, + 128 => AvmOperand::U128 { value: field.try_into_u128().unwrap() }, + 254 => AvmOperand::FF { value: field }, _ => panic!("Invalid operand size for bits: {}", bits), } } diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/arithmetic.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/arithmetic.test.cpp index f61c3826042..a3be8b38028 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/arithmetic.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/arithmetic.test.cpp @@ -221,8 +221,8 @@ class AvmArithmeticTests : public ::testing::Test { } // Generate a trace with an EQ opcode operation. - std::vector gen_trace_eq(uint128_t const& a, - uint128_t const& b, + std::vector gen_trace_eq(uint256_t const& a, + uint256_t const& b, uint32_t const& addr_a, uint32_t const& addr_b, uint32_t const& addr_c, @@ -240,8 +240,8 @@ class AvmArithmeticTests : public ::testing::Test { // and the memory and alu trace are created consistently with the wrong value c_mutated. std::vector gen_mutated_trace_add(FF const& a, FF const& b, FF const& c_mutated, avm_trace::AvmMemoryTag tag) { - trace_builder.op_set(0, uint128_t{ a }, 0, tag); - trace_builder.op_set(0, uint128_t{ b }, 1, tag); + trace_builder.op_set(0, a, 0, tag); + trace_builder.op_set(0, b, 1, tag); trace_builder.op_add(0, 0, 1, 2, tag); trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); @@ -257,8 +257,8 @@ class AvmArithmeticTests : public ::testing::Test { // and the memory and alu trace are created consistently with the wrong value c_mutated. std::vector gen_mutated_trace_sub(FF const& a, FF const& b, FF const& c_mutated, avm_trace::AvmMemoryTag tag) { - trace_builder.op_set(0, uint128_t{ a }, 0, tag); - trace_builder.op_set(0, uint128_t{ b }, 1, tag); + trace_builder.op_set(0, a, 0, tag); + trace_builder.op_set(0, b, 1, tag); trace_builder.op_sub(0, 0, 1, 2, tag); trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); @@ -274,8 +274,8 @@ class AvmArithmeticTests : public ::testing::Test { // and the memory and alu trace are created consistently with the wrong value c_mutated. std::vector gen_mutated_trace_mul(FF const& a, FF const& b, FF const& c_mutated, avm_trace::AvmMemoryTag tag) { - trace_builder.op_set(0, uint128_t{ a }, 0, tag); - trace_builder.op_set(0, uint128_t{ b }, 1, tag); + trace_builder.op_set(0, a, 0, tag); + trace_builder.op_set(0, b, 1, tag); trace_builder.op_mul(0, 0, 1, 2, tag); trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); @@ -294,8 +294,8 @@ class AvmArithmeticTests : public ::testing::Test { std::vector gen_mutated_trace_eq( FF const& a, FF const& b, FF const& c_mutated, FF const& mutated_inv_diff, avm_trace::AvmMemoryTag tag) { - trace_builder.op_set(0, uint128_t{ a }, 0, tag); - trace_builder.op_set(0, uint128_t{ b }, 1, tag); + trace_builder.op_set(0, a, 0, tag); + trace_builder.op_set(0, b, 1, tag); trace_builder.op_eq(0, 0, 1, 2, tag); trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); @@ -671,8 +671,8 @@ TEST_P(AvmArithmeticTestsDiv, division) { const auto [operands, mem_tag] = GetParam(); const auto [a, b, output] = operands; - trace_builder.op_set(0, uint128_t(a), 0, mem_tag); - trace_builder.op_set(0, uint128_t(b), 1, mem_tag); + trace_builder.op_set(0, a, 0, mem_tag); + trace_builder.op_set(0, b, 1, mem_tag); trace_builder.op_div(0, 0, 1, 2, mem_tag); trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); @@ -1077,7 +1077,7 @@ TEST_F(AvmArithmeticTestsU32, subtractionCarry) { // trace_builder trace_builder.op_set(0, UINT32_MAX - 99, 8, AvmMemoryTag::U32); - trace_builder.op_set(0, 3210987654, 9, AvmMemoryTag::U32); + trace_builder.op_set(0, uint256_t(3210987654), 9, AvmMemoryTag::U32); trace_builder.op_sub(0, 9, 8, 0, AvmMemoryTag::U32); trace_builder.op_return(0, 0, 0); @@ -1335,9 +1335,9 @@ TEST_F(AvmArithmeticTestsU64, nonEquality) // Test on basic addition over u128 type. TEST_F(AvmArithmeticTestsU128, addition) { - uint128_t const a = (uint128_t{ 0x5555222233334444LLU } << 64) + uint128_t{ 0x88889999AAAABBBBLLU }; - uint128_t const b = (uint128_t{ 0x3333222233331111LLU } << 64) + uint128_t{ 0x5555111155553333LLU }; - uint128_t const c = (uint128_t{ 0x8888444466665555LLU } << 64) + uint128_t{ 0xDDDDAAAAFFFFEEEELLU }; + const FF a = (uint256_t{ 0x5555222233334444LLU } << 64) + uint256_t{ 0x88889999AAAABBBBLLU }; + const FF b = (uint256_t{ 0x3333222233331111LLU } << 64) + uint256_t{ 0x5555111155553333LLU }; + const FF c = (uint256_t{ 0x8888444466665555LLU } << 64) + uint256_t{ 0xDDDDAAAAFFFFEEEELLU }; // trace_builder trace_builder.op_set(0, a, 8, AvmMemoryTag::U128); @@ -1347,14 +1347,7 @@ TEST_F(AvmArithmeticTestsU128, addition) trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); - auto alu_row = common_validate_add(trace, - FF(uint256_t::from_uint128(a)), - FF(uint256_t::from_uint128(b)), - FF(uint256_t::from_uint128(c)), - FF(8), - FF(9), - FF(9), - AvmMemoryTag::U128); + auto alu_row = common_validate_add(trace, a, b, c, FF(8), FF(9), FF(9), AvmMemoryTag::U128); EXPECT_EQ(alu_row.alu_u128_tag, FF(1)); EXPECT_EQ(alu_row.alu_cf, FF(0)); @@ -1365,10 +1358,10 @@ TEST_F(AvmArithmeticTestsU128, addition) // Test on basic addition over u128 type with carry. TEST_F(AvmArithmeticTestsU128, additionCarry) { - uint128_t const a = (uint128_t{ UINT64_MAX } << 64) + uint128_t{ UINT64_MAX } - uint128_t{ 72948899 }; - uint128_t const b = (uint128_t{ UINT64_MAX } << 64) + uint128_t{ UINT64_MAX } - uint128_t{ 36177344 }; - uint128_t const c = - (uint128_t{ UINT64_MAX } << 64) + uint128_t{ UINT64_MAX } - uint128_t{ 36177345 } - uint128_t{ 72948899 }; + const FF a = (uint256_t{ UINT64_MAX } << 64) + uint256_t{ UINT64_MAX } - uint256_t{ 72948899 }; + const FF b = (uint256_t{ UINT64_MAX } << 64) + uint256_t{ UINT64_MAX } - uint256_t{ 36177344 }; + const FF c = + (uint256_t{ UINT64_MAX } << 64) + uint256_t{ UINT64_MAX } - uint256_t{ 36177345 } - uint256_t{ 72948899 }; // trace_builder trace_builder.op_set(0, a, 8, AvmMemoryTag::U128); @@ -1378,14 +1371,7 @@ TEST_F(AvmArithmeticTestsU128, additionCarry) trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); - auto alu_row = common_validate_add(trace, - FF(uint256_t::from_uint128(a)), - FF(uint256_t::from_uint128(b)), - FF(uint256_t::from_uint128(c)), - FF(8), - FF(9), - FF(9), - AvmMemoryTag::U128); + auto alu_row = common_validate_add(trace, a, b, c, FF(8), FF(9), FF(9), AvmMemoryTag::U128); EXPECT_EQ(alu_row.alu_u128_tag, FF(1)); EXPECT_EQ(alu_row.alu_cf, FF(1)); @@ -1396,9 +1382,9 @@ TEST_F(AvmArithmeticTestsU128, additionCarry) // Test on basic subtraction over u128 type. TEST_F(AvmArithmeticTestsU128, subtraction) { - uint128_t const a = (uint128_t{ UINT64_MAX } << 64) + uint128_t{ UINT64_MAX } - uint128_t{ 36177344 }; - uint128_t const b = (uint128_t{ UINT64_MAX } << 64) + uint128_t{ UINT64_MAX } - uint128_t{ 72948899 }; - uint128_t const c = 36771555; // 72948899 - 36177344 + const FF a = (uint256_t{ UINT64_MAX } << 64) + uint256_t{ UINT64_MAX } - uint256_t{ 36177344 }; + const FF b = (uint256_t{ UINT64_MAX } << 64) + uint256_t{ UINT64_MAX } - uint256_t{ 72948899 }; + const FF c = 36771555; // 72948899 - 36177344 // trace_builder trace_builder.op_set(0, a, 8, AvmMemoryTag::U128); @@ -1408,14 +1394,7 @@ TEST_F(AvmArithmeticTestsU128, subtraction) trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); - auto alu_row = common_validate_sub(trace, - FF(uint256_t::from_uint128(a)), - FF(uint256_t::from_uint128(b)), - FF(uint256_t::from_uint128(c)), - FF(8), - FF(9), - FF(9), - AvmMemoryTag::U128); + auto alu_row = common_validate_sub(trace, a, b, c, FF(8), FF(9), FF(9), AvmMemoryTag::U128); EXPECT_EQ(alu_row.alu_u128_tag, FF(1)); EXPECT_EQ(alu_row.alu_cf, FF(0)); @@ -1426,9 +1405,9 @@ TEST_F(AvmArithmeticTestsU128, subtraction) // Test on basic subtraction over u128 type with carry. TEST_F(AvmArithmeticTestsU128, subtractionCarry) { - uint128_t const a = (uint128_t{ 0x5555222233334444LLU } << 64) + uint128_t{ 0x88889999AAAABBBBLLU }; - uint128_t const b = (uint128_t{ 0x3333222233331111LLU } << 64) + uint128_t{ 0x5555111155553333LLU }; - uint128_t const c = (uint128_t{ 0x2222000000003333LLU } << 64) + uint128_t{ 0x3333888855558888LLU }; + const FF a = (uint256_t{ 0x5555222233334444LLU } << 64) + uint256_t{ 0x88889999AAAABBBBLLU }; + const FF b = (uint256_t{ 0x3333222233331111LLU } << 64) + uint256_t{ 0x5555111155553333LLU }; + const FF c = (uint256_t{ 0x2222000000003333LLU } << 64) + uint256_t{ 0x3333888855558888LLU }; // trace_builder trace_builder.op_set(0, a, 8, AvmMemoryTag::U128); @@ -1438,14 +1417,7 @@ TEST_F(AvmArithmeticTestsU128, subtractionCarry) trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); - auto alu_row = common_validate_sub(trace, - FF(uint256_t::from_uint128(a)), - FF(uint256_t::from_uint128(b)), - FF(uint256_t::from_uint128(c)), - FF(8), - FF(9), - FF(9), - AvmMemoryTag::U128); + auto alu_row = common_validate_sub(trace, a, b, c, FF(8), FF(9), FF(9), AvmMemoryTag::U128); EXPECT_EQ(alu_row.alu_u128_tag, FF(1)); EXPECT_EQ(alu_row.alu_cf, FF(0)); @@ -1480,8 +1452,8 @@ TEST_F(AvmArithmeticTestsU128, multiplicationOverflow) { // (2^128 - 2) * (2^128 - 4) = 2^256 - 2^130 - 2^129 + 2^3 // The above modulo 2^128 = 8 - uint128_t const a = (uint128_t{ UINT64_MAX } << 64) + uint128_t{ UINT64_MAX - 1 }; - uint128_t const b = (uint128_t{ UINT64_MAX } << 64) + uint128_t{ UINT64_MAX - 3 }; + const FF a = (uint256_t{ UINT64_MAX } << 64) + uint256_t{ UINT64_MAX - 1 }; + const FF b = (uint256_t{ UINT64_MAX } << 64) + uint256_t{ UINT64_MAX - 3 }; // trace_builder trace_builder.op_set(0, a, 0, AvmMemoryTag::U128); @@ -1491,14 +1463,7 @@ TEST_F(AvmArithmeticTestsU128, multiplicationOverflow) trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); - auto alu_row_index = common_validate_mul(trace, - FF{ uint256_t::from_uint128(a) }, - FF{ uint256_t::from_uint128(b) }, - FF{ 8 }, - FF(0), - FF(1), - FF(2), - AvmMemoryTag::U128); + auto alu_row_index = common_validate_mul(trace, a, b, FF{ 8 }, FF(0), FF(1), FF(2), AvmMemoryTag::U128); auto alu_row_first = trace.at(alu_row_index); EXPECT_EQ(alu_row_first.alu_u128_tag, FF(1)); @@ -1508,17 +1473,10 @@ TEST_F(AvmArithmeticTestsU128, multiplicationOverflow) TEST_F(AvmArithmeticTestsU128, equality) { - uint128_t const elem = (uint128_t{ 0x5555222233334444LLU } << 64) + uint128_t{ 0x88889999AAAABBBBLLU }; + const FF elem = (uint256_t{ 0x5555222233334444LLU } << 64) + uint256_t{ 0x88889999AAAABBBBLLU }; auto trace = gen_trace_eq(elem, elem, 0, 1, 2, AvmMemoryTag::U128); - auto alu_row_index = common_validate_eq(trace, - FF(uint256_t::from_uint128(elem)), - FF(uint256_t::from_uint128(elem)), - FF(1), - FF(0), - FF(1), - FF(2), - AvmMemoryTag::U128); + auto alu_row_index = common_validate_eq(trace, elem, elem, FF(1), FF(0), FF(1), FF(2), AvmMemoryTag::U128); auto alu_row = trace.at(alu_row_index); EXPECT_EQ(alu_row.alu_u128_tag, FF(1)); @@ -1529,18 +1487,11 @@ TEST_F(AvmArithmeticTestsU128, equality) // Test correct non-equality of U128 elements TEST_F(AvmArithmeticTestsU128, nonEquality) { - uint128_t const a = (uint128_t{ 0x5555222233334444LLU } << 64) + uint128_t{ 0x88889999AAAABBBBLLU }; - uint128_t const b = a - (0xdeadbeefLLU << 32); + const FF a = (uint256_t{ 0x5555222233334444LLU } << 64) + uint256_t{ 0x88889999AAAABBBBLLU }; + const FF b = a - (0xdeadbeefLLU << 32); auto trace = gen_trace_eq(a, b, 0, 1, 2, AvmMemoryTag::U128); - auto alu_row_index = common_validate_eq(trace, - FF(uint256_t::from_uint128(a)), - FF(uint256_t::from_uint128(b)), - FF(0), - FF(0), - FF(1), - FF(2), - AvmMemoryTag::U128); + auto alu_row_index = common_validate_eq(trace, a, b, FF(0), FF(0), FF(1), FF(2), AvmMemoryTag::U128); auto alu_row = trace.at(alu_row_index); EXPECT_EQ(alu_row.alu_u128_tag, FF(1)); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/bitwise.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/bitwise.test.cpp index deab35a0efc..ecb51b50a7d 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/bitwise.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/bitwise.test.cpp @@ -357,7 +357,7 @@ class AvmBitwiseTests : public ::testing::Test { std::vector gen_mutated_trace_not(FF const& a, FF const& c_mutated, avm_trace::AvmMemoryTag tag) { - trace_builder.op_set(0, uint128_t{ a }, 0, tag); + trace_builder.op_set(0, a, 0, tag); trace_builder.op_not(0, 0, 1, tag); trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); @@ -375,21 +375,21 @@ class AvmBitwiseTests : public ::testing::Test { * ******************************************************************************/ -using TwoOpParamRow = std::tuple, AvmMemoryTag>; +using TwoOpParamRow = std::tuple, AvmMemoryTag>; std::vector mem_tags{ { AvmMemoryTag::U8, AvmMemoryTag::U16, AvmMemoryTag::U32, AvmMemoryTag::U64, AvmMemoryTag::U128 } }; -std::vector> positive_op_not_test_values = { { { 1, 254 }, - { 512, 65'023 }, - { 131'072, 4'294'836'223LLU }, - { 0x100000000LLU, 0xfffffffeffffffffLLU }, - { uint128_t{ 0x4000000000000 } << 64, - (uint128_t{ 0xfffbffffffffffff } << 64) + - uint128_t{ 0xffffffffffffffff } } } }; +std::vector> positive_op_not_test_values = { { { 1, 254 }, + { 512, 65'023 }, + { 131'072, 4'294'836'223LLU }, + { 0x100000000LLU, 0xfffffffeffffffffLLU }, + { uint256_t{ 0x4000000000000 } << 64, + (uint256_t{ 0xfffbffffffffffff } << 64) + + uint256_t{ 0xffffffffffffffff } } } }; // This is essentially a zip while we wait for C++23 -std::vector gen_two_op_params(std::vector> operands, +std::vector gen_two_op_params(std::vector> operands, std::vector mem_tags) { std::vector params; @@ -470,9 +470,7 @@ TEST_P(AvmBitwiseTestsNot, ParamTest) trace_builder.op_not(0, 0, 1, mem_tag); // [1,254,0,0,....] trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); - FF ff_a = FF(uint256_t::from_uint128(a)); - FF ff_output = FF(uint256_t::from_uint128(output)); - common_validate_op_not(trace, ff_a, ff_output, FF(0), FF(1), mem_tag); + common_validate_op_not(trace, a, output, FF(0), FF(1), mem_tag); validate_trace(std::move(trace), public_inputs); } @@ -484,8 +482,8 @@ TEST_P(AvmBitwiseTestsAnd, AllAndTest) { const auto [operands, mem_tag] = GetParam(); const auto [a, b, output] = operands; - trace_builder.op_set(0, uint128_t(a), 0, mem_tag); - trace_builder.op_set(0, uint128_t(b), 1, mem_tag); + trace_builder.op_set(0, a, 0, mem_tag); + trace_builder.op_set(0, b, 1, mem_tag); trace_builder.op_and(0, 0, 1, 2, mem_tag); trace_builder.op_return(0, 2, 1); @@ -501,8 +499,8 @@ TEST_P(AvmBitwiseTestsOr, AllOrTest) { const auto [operands, mem_tag] = GetParam(); const auto [a, b, output] = operands; - trace_builder.op_set(0, uint128_t(a), 0, mem_tag); - trace_builder.op_set(0, uint128_t(b), 1, mem_tag); + trace_builder.op_set(0, a, 0, mem_tag); + trace_builder.op_set(0, b, 1, mem_tag); trace_builder.op_or(0, 0, 1, 2, mem_tag); trace_builder.op_return(0, 2, 1); auto trace = trace_builder.finalize(); @@ -518,8 +516,8 @@ TEST_P(AvmBitwiseTestsXor, AllXorTest) { const auto [operands, mem_tag] = GetParam(); const auto [a, b, output] = operands; - trace_builder.op_set(0, uint128_t(a), 0, mem_tag); - trace_builder.op_set(0, uint128_t(b), 1, mem_tag); + trace_builder.op_set(0, a, 0, mem_tag); + trace_builder.op_set(0, b, 1, mem_tag); trace_builder.op_xor(0, 0, 1, 2, mem_tag); trace_builder.op_return(0, 2, 1); auto trace = trace_builder.finalize(); @@ -536,8 +534,8 @@ TEST_P(AvmBitwiseTestsShr, AllShrTest) { const auto [operands, mem_tag] = GetParam(); const auto [a, b, output] = operands; - trace_builder.op_set(0, uint128_t(a), 0, mem_tag); - trace_builder.op_set(0, uint128_t(b), 1, mem_tag); + trace_builder.op_set(0, a, 0, mem_tag); + trace_builder.op_set(0, b, 1, mem_tag); trace_builder.op_shr(0, 0, 1, 2, mem_tag); trace_builder.op_return(0, 2, 1); auto trace = trace_builder.finalize(); @@ -553,8 +551,8 @@ TEST_P(AvmBitwiseTestsShl, AllShlTest) { const auto [operands, mem_tag] = GetParam(); const auto [a, b, output] = operands; - trace_builder.op_set(0, uint128_t(a), 0, mem_tag); - trace_builder.op_set(0, uint128_t(b), 1, mem_tag); + trace_builder.op_set(0, a, 0, mem_tag); + trace_builder.op_set(0, b, 1, mem_tag); trace_builder.op_shl(0, 0, 1, 2, mem_tag); trace_builder.op_return(0, 2, 1); auto trace = trace_builder.finalize(); @@ -653,8 +651,8 @@ TEST_P(AvmBitwiseNegativeTestsAnd, AllNegativeTests) const auto [failure_string, failure_mode] = failure; const auto [operands, mem_tag] = params; const auto [a, b, output] = operands; - trace_builder.op_set(0, uint128_t{ a }, 0, mem_tag); - trace_builder.op_set(0, uint128_t{ b }, 1, mem_tag); + trace_builder.op_set(0, a, 0, mem_tag); + trace_builder.op_set(0, b, 1, mem_tag); trace_builder.op_and(0, 0, 1, 2, mem_tag); trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); @@ -672,8 +670,8 @@ TEST_P(AvmBitwiseNegativeTestsOr, AllNegativeTests) const auto [failure_string, failure_mode] = failure; const auto [operands, mem_tag] = params; const auto [a, b, output] = operands; - trace_builder.op_set(0, uint128_t{ a }, 0, mem_tag); - trace_builder.op_set(0, uint128_t{ b }, 1, mem_tag); + trace_builder.op_set(0, a, 0, mem_tag); + trace_builder.op_set(0, b, 1, mem_tag); trace_builder.op_or(0, 0, 1, 2, mem_tag); trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); @@ -690,8 +688,8 @@ TEST_P(AvmBitwiseNegativeTestsXor, AllNegativeTests) const auto [failure_string, failure_mode] = failure; const auto [operands, mem_tag] = params; const auto [a, b, output] = operands; - trace_builder.op_set(0, uint128_t{ a }, 0, mem_tag); - trace_builder.op_set(0, uint128_t{ b }, 1, mem_tag); + trace_builder.op_set(0, a, 0, mem_tag); + trace_builder.op_set(0, b, 1, mem_tag); trace_builder.op_xor(0, 0, 1, 2, mem_tag); trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); @@ -708,8 +706,8 @@ TEST_P(AvmBitwiseNegativeTestsShr, AllNegativeTests) const auto [failure, params] = GetParam(); const auto [operands, mem_tag] = params; const auto [a, b, output] = operands; - trace_builder.op_set(0, uint128_t{ a }, 0, mem_tag); - trace_builder.op_set(0, uint128_t{ b }, 1, mem_tag); + trace_builder.op_set(0, a, 0, mem_tag); + trace_builder.op_set(0, b, 1, mem_tag); trace_builder.op_shr(0, 0, 1, 2, mem_tag); trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); @@ -727,8 +725,8 @@ TEST_P(AvmBitwiseNegativeTestsShl, AllNegativeTests) const auto [failure, params] = GetParam(); const auto [operands, mem_tag] = params; const auto [a, b, output] = operands; - trace_builder.op_set(0, uint128_t{ a }, 0, mem_tag); - trace_builder.op_set(0, uint128_t{ b }, 1, mem_tag); + trace_builder.op_set(0, a, 0, mem_tag); + trace_builder.op_set(0, b, 1, mem_tag); trace_builder.op_shl(0, 0, 1, 2, mem_tag); trace_builder.op_return(0, 0, 0); auto trace = trace_builder.finalize(); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/cast.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/cast.test.cpp index c074ebf46e8..b9985009ba3 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/cast.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/cast.test.cpp @@ -1,3 +1,4 @@ +#include "barretenberg/numeric/uint256/uint256.hpp" #include "barretenberg/vm/avm/tests/helpers.test.hpp" #include "barretenberg/vm/avm/trace/common.hpp" #include "common.test.hpp" @@ -33,7 +34,7 @@ class AvmCastTests : public ::testing::Test { void gen_trace( uint128_t const& a, uint32_t src_address, uint32_t dst_address, AvmMemoryTag src_tag, AvmMemoryTag dst_tag) { - trace_builder.op_set(0, a, src_address, src_tag); + trace_builder.op_set(0, uint256_t::from_uint128(a), src_address, src_tag); trace_builder.op_cast(0, src_address, dst_address, dst_tag); trace_builder.op_return(0, 0, 0); trace = trace_builder.finalize(); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/comparison.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/comparison.test.cpp index 36ec0f9bee9..b1315b0bb97 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/comparison.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/comparison.test.cpp @@ -113,8 +113,8 @@ TEST_P(AvmCmpTestsLT, ParamTest) trace_builder = AvmTraceBuilder(public_inputs, {}, 0, calldata); trace_builder.op_calldata_copy(0, 0, 2, 0); } else { - trace_builder.op_set(0, uint128_t(a), 0, mem_tag); - trace_builder.op_set(0, uint128_t(b), 1, mem_tag); + trace_builder.op_set(0, a, 0, mem_tag); + trace_builder.op_set(0, b, 1, mem_tag); } trace_builder.op_lt(0, 0, 1, 2, mem_tag); trace_builder.op_return(0, 0, 0); @@ -149,8 +149,8 @@ TEST_P(AvmCmpTestsLTE, ParamTest) trace_builder = AvmTraceBuilder(public_inputs, {}, 0, calldata); trace_builder.op_calldata_copy(0, 0, 2, 0); } else { - trace_builder.op_set(0, uint128_t(a), 0, mem_tag); - trace_builder.op_set(0, uint128_t(b), 1, mem_tag); + trace_builder.op_set(0, a, 0, mem_tag); + trace_builder.op_set(0, b, 1, mem_tag); } trace_builder.op_lte(0, 0, 1, 2, mem_tag); trace_builder.op_return(0, 0, 0); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp index 5956687061d..268c42e7e85 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp @@ -108,16 +108,16 @@ TEST_F(AvmExecutionTests, basicAddReturn) // Positive test for SET and SUB opcodes TEST_F(AvmExecutionTests, setAndSubOpcodes) { - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + std::string bytecode_hex = to_hex(OpCode::SET_16) + // opcode SET "00" // Indirect flag "02" // U16 "B813" // val 47123 - "000000AA" // dst_offset 170 - + to_hex(OpCode::SET) + // opcode SET + "00AA" // dst_offset 170 + + to_hex(OpCode::SET_16) + // opcode SET "00" // Indirect flag "02" // U16 "9103" // val 37123 - "00000033" // dst_offset 51 + "0033" // dst_offset 51 + to_hex(OpCode::SUB) + // opcode SUB "00" // Indirect flag "02" // U16 @@ -136,21 +136,21 @@ TEST_F(AvmExecutionTests, setAndSubOpcodes) // SET EXPECT_THAT(instructions.at(0), - AllOf(Field(&Instruction::op_code, OpCode::SET), + AllOf(Field(&Instruction::op_code, OpCode::SET_16), Field(&Instruction::operands, ElementsAre(VariantWith(0), VariantWith(AvmMemoryTag::U16), VariantWith(47123), - VariantWith(170))))); + VariantWith(170))))); // SET EXPECT_THAT(instructions.at(1), - AllOf(Field(&Instruction::op_code, OpCode::SET), + AllOf(Field(&Instruction::op_code, OpCode::SET_16), Field(&Instruction::operands, ElementsAre(VariantWith(0), VariantWith(AvmMemoryTag::U16), VariantWith(37123), - VariantWith(51))))); + VariantWith(51))))); // SUB EXPECT_THAT(instructions.at(2), @@ -178,18 +178,16 @@ TEST_F(AvmExecutionTests, setAndSubOpcodes) // the result at offset 1. TEST_F(AvmExecutionTests, powerWithMulOpcodes) { - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "04" // U64 - "00000000" // val 5 higher 32 bits - "00000005" // val 5 lower 32 bits - "00000000" // dst_offset 0 - + to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "04" // U64 - "00000000" // val 1 higher 32 bits - "00000001" // val 1 lower 32 bits - "00000001"; // dst_offset 1 + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "04" // U64 + "05" // val + "00" // dst_offset 0 + + to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "04" // U64 + "01" // val + "01"; // dst_offset 1 std::string const mul_hex = to_hex(OpCode::MUL) + // opcode MUL "00" // Indirect flag @@ -261,11 +259,11 @@ TEST_F(AvmExecutionTests, powerWithMulOpcodes) // 0 1 2 3 4 5 TEST_F(AvmExecutionTests, simpleInternalCall) { - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + std::string bytecode_hex = to_hex(OpCode::SET_32) + // opcode SET "00" // Indirect flag "03" // U32 "0D3D2518" // val 222111000 = 0xD3D2518 - "00000004" // dst_offset 4 + "0004" // dst_offset 4 + to_hex(OpCode::INTERNALCALL) + // opcode INTERNALCALL "00000004" // jmp_dest + to_hex(OpCode::ADD) + // opcode ADD @@ -278,11 +276,11 @@ TEST_F(AvmExecutionTests, simpleInternalCall) "00" // Indirect flag "00000000" // ret offset 0 "00000000" // ret size 0 - + to_hex(OpCode::SET) + // opcode SET + + to_hex(OpCode::SET_32) + // opcode SET "00" // Indirect flag "03" // U32 "075BCD15" // val 123456789 = 0x75BCD15 - "00000007" // dst_offset 7 + "0007" // dst_offset 7 + to_hex(OpCode::INTERNALRETURN) // opcode INTERNALRETURN ; @@ -337,10 +335,11 @@ TEST_F(AvmExecutionTests, nestedInternalCalls) }; auto setInstructionHex = [](std::string const& val, std::string const& dst_offset) { - return to_hex(OpCode::SET) // opcode SET - + "00" // Indirect flag - + "01" // U8 - + val + "000000" + dst_offset; + // val and dst_offset is assumed to be 2 bytes + return to_hex(OpCode::SET_32) // opcode SET + + "00" // Indirect flag + + "01" // U8 + + "000000" + val + "00" + dst_offset; }; const std::string tag_address_arguments = "00" // Indirect Flag @@ -369,12 +368,11 @@ TEST_F(AvmExecutionTests, nestedInternalCalls) ASSERT_THAT(instructions, SizeIs(12)); // Expected sequence of opcodes - std::vector const opcode_sequence{ OpCode::SET, OpCode::SET, - OpCode::INTERNALCALL, OpCode::RETURN, - OpCode::MUL, OpCode::INTERNALRETURN, - OpCode::ADD, OpCode::INTERNALRETURN, - OpCode::INTERNALCALL, OpCode::SET, - OpCode::INTERNALCALL, OpCode::INTERNALRETURN }; + std::vector const opcode_sequence{ + OpCode::SET_32, OpCode::SET_32, OpCode::INTERNALCALL, OpCode::RETURN, + OpCode::MUL, OpCode::INTERNALRETURN, OpCode::ADD, OpCode::INTERNALRETURN, + OpCode::INTERNALCALL, OpCode::SET_32, OpCode::INTERNALCALL, OpCode::INTERNALRETURN + }; for (size_t i = 0; i < 12; i++) { EXPECT_EQ(instructions.at(i).op_code, opcode_sequence.at(i)); @@ -406,16 +404,16 @@ TEST_F(AvmExecutionTests, nestedInternalCalls) TEST_F(AvmExecutionTests, jumpAndCalldatacopy) { GTEST_SKIP(); - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "03" // U32 - "00000000" // val - "00000000" // dst_offset 101 - + to_hex(OpCode::SET) + // opcode SET + "00" // val + "00" // dst_offset 101 + + to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "03" // U32 - "00000002" // val - "00000001" // dst_offset 101 + "02" // val + "01" // dst_offset 101 + to_hex(OpCode::CALLDATACOPY) + // opcode CALLDATACOPY (no in tag) "00" // Indirect flag "00000000" // cd_offset @@ -500,11 +498,11 @@ TEST_F(AvmExecutionTests, jumpiAndCalldatacopy) "00000000" // cd_offset "00000001" // copy_size "0000000A" // dst_offset 10 - + to_hex(OpCode::SET) + // opcode SET + + to_hex(OpCode::SET_16) + // opcode SET "00" // Indirect flag "02" // U16 "0014" // val 20 - "00000065" // dst_offset 101 + "0065" // dst_offset 101 + to_hex(OpCode::JUMPI) + // opcode JUMPI "00" // Indirect flag "00000004" // jmp_dest (MUL located at 4) @@ -582,11 +580,11 @@ TEST_F(AvmExecutionTests, jumpiAndCalldatacopy) // Positive test with MOV. TEST_F(AvmExecutionTests, movOpcode) { - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "01" // U8 "13" // val 19 - "000000AB" // dst_offset 171 + "AB" // dst_offset 171 + to_hex(OpCode::MOV_8) + // opcode MOV "00" // Indirect flag "AB" // src_offset 171 @@ -603,12 +601,12 @@ TEST_F(AvmExecutionTests, movOpcode) // SET EXPECT_THAT(instructions.at(0), - AllOf(Field(&Instruction::op_code, OpCode::SET), + AllOf(Field(&Instruction::op_code, OpCode::SET_8), Field(&Instruction::operands, ElementsAre(VariantWith(0), VariantWith(AvmMemoryTag::U8), VariantWith(19), - VariantWith(171))))); + VariantWith(171))))); // MOV EXPECT_THAT( @@ -630,21 +628,21 @@ TEST_F(AvmExecutionTests, movOpcode) // Positive test with CMOV. TEST_F(AvmExecutionTests, cmovOpcode) { - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "01" // U8 "03" // val 3 - "00000010" // a_offset 16 - + to_hex(OpCode::SET) + // opcode SET + "10" // a_offset 16 + + to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "02" // U16 - "0004" // val 4 - "00000011" // b_offset 17 - + to_hex(OpCode::SET) + // opcode SET + "04" // val 4 + "11" // b_offset 17 + + to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "03" // U32 - "00000005" // val 5 - "00000020" // cond_offset 32 + "05" // val 5 + "20" // cond_offset 32 + to_hex(OpCode::CMOV) + // opcode CMOV "00" // Indirect flag "00000010" // a_offset 16 @@ -686,21 +684,21 @@ TEST_F(AvmExecutionTests, cmovOpcode) // Positive test with indirect MOV. TEST_F(AvmExecutionTests, indMovOpcode) { - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "03" // U32 - "0000000A" // val 10 - "00000001" // dst_offset 1 - + to_hex(OpCode::SET) + // opcode SET + "0A" // val 10 + "01" // dst_offset 1 + + to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "03" // U32 - "0000000B" // val 11 - "00000002" // dst_offset 2 - + to_hex(OpCode::SET) + // opcode SET + "0B" // val 11 + "02" // dst_offset 2 + + to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "01" // U8 "FF" // val 255 - "0000000A" // dst_offset 10 + "0A" // dst_offset 10 + to_hex(OpCode::MOV_8) + // opcode MOV "01" // Indirect flag "01" // src_offset 1 --> direct offset 10 @@ -734,11 +732,11 @@ TEST_F(AvmExecutionTests, indMovOpcode) // Positive test for SET and CAST opcodes TEST_F(AvmExecutionTests, setAndCastOpcodes) { - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + std::string bytecode_hex = to_hex(OpCode::SET_16) + // opcode SET "00" // Indirect flag "02" // U16 "B813" // val 47123 - "00000011" // dst_offset 17 + "0011" // dst_offset 17 + to_hex(OpCode::CAST) + // opcode CAST "00" // Indirect flag "01" // U8 @@ -775,31 +773,31 @@ TEST_F(AvmExecutionTests, setAndCastOpcodes) // Positive test with TO_RADIX_LE. TEST_F(AvmExecutionTests, toRadixLeOpcode) { - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "03" // U32 - "00000000" // val - "00000000" // dst_offset - + to_hex(OpCode::SET) + // opcode SET + "00" // val + "00" // dst_offset + + to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "03" // U32 - "00000001" // val - "00000001" // dst_offset + "01" // val + "01" // dst_offset + to_hex(OpCode::CALLDATACOPY) + // opcode CALLDATACOPY "00" // Indirect flag "00000000" // cd_offset "00000001" // copy_size "00000001" // dst_offset - + to_hex(OpCode::SET) + // opcode SET for indirect src + + to_hex(OpCode::SET_8) + // opcode SET for indirect src "00" // Indirect flag "03" // U32 - "00000001" // value 1 (i.e. where the src from calldata is copied) - "00000011" // dst_offset 17 - + to_hex(OpCode::SET) + // opcode SET for indirect dst + "01" // value 1 (i.e. where the src from calldata is copied) + "11" // dst_offset 17 + + to_hex(OpCode::SET_8) + // opcode SET for indirect dst "00" // Indirect flag "03" // U32 - "00000005" // value 5 (i.e. where the dst will be written to) - "00000015" // dst_offset 21 + "05" // value 5 (i.e. where the dst will be written to) + "15" // dst_offset 21 + to_hex(OpCode::TORADIXLE) + // opcode TO_RADIX_LE "03" // Indirect flag "00000011" // src_offset 17 (indirect) @@ -839,39 +837,39 @@ TEST_F(AvmExecutionTests, sha256CompressionOpcode) // Set operations for sha256 state // Test vectors taken from noir black_box_solver // State = Uint32Array.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]), - for (uint32_t i = 1; i <= 8; i++) { - bytecode_preamble += to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" + // U32 - to_hex(i) + // val i - to_hex(i); // val i + for (uint8_t i = 1; i <= 8; i++) { + bytecode_preamble += to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" + // U32 + to_hex(i) + // val i + to_hex(i); // val i } // Set operations for sha256 input // Test vectors taken from noir black_box_solver // Input = Uint32Array.from([1, 2, 3, 4, 5, 6, 7, 8]), - for (uint32_t i = 1; i <= 16; i++) { - bytecode_preamble += to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" + // U32 - to_hex(i) + // val i - to_hex(i + 8); // val i + for (uint8_t i = 1; i <= 16; i++) { + bytecode_preamble += to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" + // U32 + to_hex(i) + // val i + to_hex(i + 8); // val i } - std::string bytecode_hex = bytecode_preamble // Initial SET operations to store state and input - + to_hex(OpCode::SET) + // opcode SET for indirect dst (output) - "00" // Indirect flag - "03" // U32 - "00000100" // value 256 (i.e. where the dst will be written to) - "00000024" // dst_offset 36 - + to_hex(OpCode::SET) + // opcode SET for indirect state - "00" // Indirect flag - "03" // U32 - "00000001" // value 1 (i.e. where the state will be read from) - "00000022" // dst_offset 34 - + to_hex(OpCode::SET) + // opcode SET for indirect input - "00" // Indirect flag - "03" // U32 - "00000009" // value 9 (i.e. where the input will be read from) - "00000023" // dst_offset 35 + std::string bytecode_hex = bytecode_preamble // Initial SET operations to store state and input + + to_hex(OpCode::SET_16) + // opcode SET for indirect dst (output) + "00" // Indirect flag + "03" // U32 + "0100" // value 256 (i.e. where the dst will be written to) + "0024" // dst_offset 36 + + to_hex(OpCode::SET_8) + // opcode SET for indirect state + "00" // Indirect flag + "03" // U32 + "01" // value 1 (i.e. where the state will be read from) + "22" // dst_offset 34 + + to_hex(OpCode::SET_8) + // opcode SET for indirect input + "00" // Indirect flag + "03" // U32 + "09" // value 9 (i.e. where the input will be read from) + "23" // dst_offset 35 + to_hex(OpCode::SHA256COMPRESSION) + // opcode SHA256COMPRESSION "07" // Indirect flag (first 3 operands indirect) "00000024" // output offset (indirect 36) @@ -915,36 +913,36 @@ TEST_F(AvmExecutionTests, sha256Opcode) FF(0xde), FF(0x5d), FF(0xae), FF(0x22), FF(0x23), FF(0xb0), FF(0x03), FF(0x61), FF(0xa3), FF(0x96), FF(0x17), FF(0x7a), FF(0x9c), FF(0xb4), FF(0x10), FF(0xff), FF(0x61), FF(0xf2), FF(0x00), FF(0x15), FF(0xad), }; - std::string bytecode_hex = to_hex(OpCode::SET) + // Initial SET operations to store state and input + std::string bytecode_hex = to_hex(OpCode::SET_8) + // Initial SET operations to store state and input "00" // Indirect Flag "01" // U8 "61" // val 97 - "00000001" // dst_offset 1 - + to_hex(OpCode::SET) + // opcode SET for indirect src (input) + "01" // dst_offset 1 + + to_hex(OpCode::SET_8) + // opcode SET for indirect src (input) "00" // Indirect flag "01" // U8 "62" // value 98 (i.e. where the src will be read from)A - "00000002" // input_offset 2 - + to_hex(OpCode::SET) + // opcode SET for indirect src (input) + "02" // input_offset 2 + + to_hex(OpCode::SET_8) + // opcode SET for indirect src (input) "00" // Indirect flag "01" // U32 "63" // value 99 (i.e. where the src will be read from) - "00000003" // input_offset 36 - + to_hex(OpCode::SET) + // opcode SET for indirect src (input) + "03" // input_offset 36 + + to_hex(OpCode::SET_8) + // opcode SET for indirect src (input) "00" // Indirect flag "03" // U32 - "00000001" // value 1 (i.e. where the src will be read from) - "00000024" // input_offset 36 - + to_hex(OpCode::SET) + // + "01" // value 1 (i.e. where the src will be read from) + "24" // input_offset 36 + + to_hex(OpCode::SET_8) + // "00" // Indirect flag "03" // U8 - "00000003" // value 3 (i.e. where the length parameter is stored) - "00000025" // input_offset 37 - + to_hex(OpCode::SET) + // opcode SET for indirect dst (output) + "03" // value 3 (i.e. where the length parameter is stored) + "25" // input_offset 37 + + to_hex(OpCode::SET_16) + // opcode SET for indirect dst (output) "00" // Indirect flag "03" // U32 - "00000100" // value 256 (i.e. where the ouput will be written to) - "00000023" // dst_offset 35 + "0100" // value 256 (i.e. where the ouput will be written to) + "0023" // dst_offset 35 + to_hex(OpCode::SHA256) + // opcode SHA256 "03" // Indirect flag (first 2 operands indirect) "00000023" // output offset (indirect 35) @@ -977,31 +975,31 @@ TEST_F(AvmExecutionTests, poseidon2PermutationOpCode) FF(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789")), FF(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789")) }; - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "03" // U32 - "00000000" // val - "00000000" // dst_offset - + to_hex(OpCode::SET) + // opcode SET + "00" // val + "00" // dst_offset + + to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "03" // U32 - "00000004" // val - "00000001" // dst_offset + "04" // val + "01" // dst_offset + to_hex(OpCode::CALLDATACOPY) + // opcode CALL DATA COPY "00" // Indirect Flag "00000000" // cd_offset "00000001" // copy_size "00000001" // dst_offset 1 - + to_hex(OpCode::SET) + // opcode SET for indirect src (input) + + to_hex(OpCode::SET_8) + // opcode SET for indirect src (input) "00" // Indirect flag "03" // U32 - "00000001" // value 1 (i.e. where the src will be read from) - "00000024" // dst_offset 36 - + to_hex(OpCode::SET) + // opcode SET for indirect dst (output) + "01" // value 1 (i.e. where the src will be read from) + "24" // dst_offset 36 + + to_hex(OpCode::SET_8) + // opcode SET for indirect dst (output) "00" // Indirect flag "03" // U32 - "00000009" // value 9 (i.e. where the ouput will be written to) - "00000023" // dst_offset 35 + "09" // value 9 (i.e. where the ouput will be written to) + "23" // dst_offset 35 + to_hex(OpCode::POSEIDON2) + // opcode POSEIDON2 "03" // Indirect flag (first 2 operands indirect) "00000024" // input offset (indirect 36) @@ -1055,31 +1053,31 @@ TEST_F(AvmExecutionTests, keccakf1600OpCode) std::string bytecode_preamble; // Set operations for keccak state - for (uint32_t i = 0; i < 25; i++) { - bytecode_preamble += to_hex(OpCode::SET) + // opcode SET + for (uint8_t i = 0; i < 25; i++) { + bytecode_preamble += to_hex(OpCode::SET_64) + // opcode SET "00" // Indirect flag "04" + // U64 to_hex(state[i]) + // val i - to_hex(i + 1); // dst offset + to_hex(i + 1); // dst offset } // We use calldatacopy twice because we need to set up 4 inputs std::string bytecode_hex = bytecode_preamble + // Initial SET operations to store state and input - to_hex(OpCode::SET) + // opcode SET for indirect src (input) + to_hex(OpCode::SET_8) + // opcode SET for indirect src (input) "00" // Indirect flag "03" // U32 - "00000001" // value 1 (i.e. where the src will be read from) - "00000024" // input_offset 36 - + to_hex(OpCode::SET) + // + "01" // value 1 (i.e. where the src will be read from) + "24" // input_offset 36 + + to_hex(OpCode::SET_8) + // "00" // Indirect flag "03" // U32 - "00000019" // value 25 (i.e. where the length parameter is stored) - "00000025" // input_offset 37 - + to_hex(OpCode::SET) + // opcode SET for indirect dst (output) + "19" // value 25 (i.e. where the length parameter is stored) + "25" // input_offset 37 + + to_hex(OpCode::SET_16) + // opcode SET for indirect dst (output) "00" // Indirect flag "03" // U32 - "00000100" // value 256 (i.e. where the ouput will be written to) - "00000023" // dst_offset 35 + "0100" // value 256 (i.e. where the ouput will be written to) + "0023" // dst_offset 35 + to_hex(OpCode::KECCAKF1600) + // opcode KECCAKF1600 "03" // Indirect flag (first 2 operands indirect) "00000023" // output offset (indirect 35) @@ -1117,26 +1115,26 @@ TEST_F(AvmExecutionTests, keccakOpCode) FF(0x33), FF(0x65), FF(0x19), FF(0x37), FF(0xe8), FF(0x05), FF(0x27), FF(0x0c), FF(0xa3), FF(0xf3), FF(0xaf), FF(0x1c), FF(0x0d), FF(0xd2), FF(0x46), FF(0x2d), FF(0xca), FF(0x4b), FF(0x3b), FF(0x1a), FF(0xbf) }; - std::string bytecode_hex = to_hex(OpCode::SET) + // Initial SET operations to store state and input + std::string bytecode_hex = to_hex(OpCode::SET_8) + // Initial SET operations to store state and input "00" // Indirect Flag "01" // U8 "BD" // val 189 - "00000001" // dst_offset 1 - + to_hex(OpCode::SET) + // opcode SET for indirect src (input) + "01" // dst_offset 1 + + to_hex(OpCode::SET_8) + // opcode SET for indirect src (input) "00" // Indirect flag "03" // U32 - "00000001" // value 1 (i.e. where the src will be read from) - "00000024" // input_offset 36 - + to_hex(OpCode::SET) + // + "01" // value 1 (i.e. where the src will be read from) + "24" // input_offset 36 + + to_hex(OpCode::SET_8) + // "00" // Indirect flag "03" // U8 - "00000001" // value 1 (i.e. where the length parameter is stored) - "00000025" // input_offset 37 - + to_hex(OpCode::SET) + // opcode SET for indirect dst (output) + "01" // value 1 (i.e. where the length parameter is stored) + "25" // input_offset 37 + + to_hex(OpCode::SET_16) + // opcode SET for indirect dst (output) "00" // Indirect flag "03" // U32 - "00000100" // value 256 (i.e. where the ouput will be written to) - "00000023" // dst_offset 35 + "0100" // value 256 (i.e. where the ouput will be written to) + "0023" // dst_offset 35 + to_hex(OpCode::KECCAK) + // opcode KECCAK "03" // Indirect flag (first 2 operands indirect) "00000023" // output offset (indirect 35) @@ -1168,36 +1166,36 @@ TEST_F(AvmExecutionTests, pedersenHashOpCode) // output = 0x1c446df60816b897cda124524e6b03f36df0cec333fad87617aab70d7861daa6 // hash_index = 5; FF expected_output = FF("0x1c446df60816b897cda124524e6b03f36df0cec333fad87617aab70d7861daa6"); - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "03" // U32 - "00000000" // val - "00000000" // dst_offset - + to_hex(OpCode::SET) + // opcode SET + "00" // val + "00" // dst_offset + + to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "03" // U32 - "00000002" // val - "00000001" // dst_offset + "02" // val + "01" // dst_offset + to_hex(OpCode::CALLDATACOPY) + // Calldatacopy "00" // Indirect flag "00000000" // cd_offset "00000001" // copy_size "00000000" // dst_offset - + to_hex(OpCode::SET) + // opcode SET for direct hash index offset + + to_hex(OpCode::SET_8) + // opcode SET for direct hash index offset "00" // Indirect flag "03" // U32 - "00000005" // value 5 - "00000002" // input_offset 2 - + to_hex(OpCode::SET) + // opcode SET for indirect src + "05" // value 5 + "02" // input_offset 2 + + to_hex(OpCode::SET_8) + // opcode SET for indirect src "00" // Indirect flag "03" // U32 - "00000000" // value 0 (i.e. where the src will be read from) - "00000004" // dst_offset 4 - + to_hex(OpCode::SET) + // opcode SET for direct src_length + "00" // value 0 (i.e. where the src will be read from) + "04" // dst_offset 4 + + to_hex(OpCode::SET_8) + // opcode SET for direct src_length "00" // Indirect flag "03" // U32 - "00000002" // value 2 - "00000005" // dst_offset + "02" // value 2 + "05" // dst_offset + to_hex(OpCode::PEDERSEN) + // opcode PEDERSEN "04" // Indirect flag (3rd operand indirect) "00000002" // hash_index offset (direct) @@ -1232,49 +1230,49 @@ TEST_F(AvmExecutionTests, embeddedCurveAddOpCode) auto b_is_inf = b.is_point_at_infinity(); grumpkin::g1::affine_element res = a + b; auto expected_output = std::vector{ res.x, res.y, res.is_point_at_infinity() }; - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" // U32 - "00000000" // val - "00000000" // dst_offset - + to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" // U32 - "00000006" // val - "00000001" + - to_hex(OpCode::CALLDATACOPY) + // Calldatacopy - "00" // Indirect flag - "00000000" // cd_offset - "00000001" // copy_size - "00000000" // dst_offset - + to_hex(OpCode::CAST) + // opcode CAST inf to U8 - "00" // Indirect flag - "01" // U8 tag field - "00000002" // a_is_inf - "00000002" // a_is_inf - + to_hex(OpCode::CAST) + // opcode CAST inf to U8 - "00" // Indirect flag - "01" // U8 tag field - "00000005" // b_is_inf - "00000005" // b_is_inf - + to_hex(OpCode::SET) + // opcode SET for direct src_length - "00" // Indirect flag - "03" // U32 - "00000007" // value - "00000006" // dst_offset - + to_hex(OpCode::ECADD) + // opcode ECADD - "40" // Indirect flag (sixth operand indirect) - "00000000" // hash_index offset (direct) - "00000001" // dest offset (direct) - "00000002" // input offset (indirect) - "00000003" // length offset (direct) - "00000004" // length offset (direct) - "00000005" // length offset (direct) - "00000006" // length offset (direct) - + to_hex(OpCode::RETURN) + // opcode RETURN - "00" // Indirect flag - "00000007" // ret offset 3 - "00000003"; // ret size 1 + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" // U32 + "00" // val + "00" // dst_offset + + to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" // U32 + "06" // val + "01" // dst_offset + + to_hex(OpCode::CALLDATACOPY) + // Calldatacopy + "00" // Indirect flag + "00000000" // cd_offset + "00000001" // copy_size + "00000000" // dst_offset + + to_hex(OpCode::CAST) + // opcode CAST inf to U8 + "00" // Indirect flag + "01" // U8 tag field + "00000002" // a_is_inf + "00000002" // a_is_inf + + to_hex(OpCode::CAST) + // opcode CAST inf to U8 + "00" // Indirect flag + "01" // U8 tag field + "00000005" // b_is_inf + "00000005" // b_is_inf + + to_hex(OpCode::SET_8) + // opcode SET for direct src_length + "00" // Indirect flag + "03" // U32 + "07" // value + "06" // dst_offset + + to_hex(OpCode::ECADD) + // opcode ECADD + "40" // Indirect flag (sixth operand indirect) + "00000000" // hash_index offset (direct) + "00000001" // dest offset (direct) + "00000002" // input offset (indirect) + "00000003" // length offset (direct) + "00000004" // length offset (direct) + "00000005" // length offset (direct) + "00000006" // length offset (direct) + + to_hex(OpCode::RETURN) + // opcode RETURN + "00" // Indirect flag + "00000007" // ret offset 3 + "00000003"; // ret size 1 auto bytecode = hex_to_bytes(bytecode_hex); auto instructions = Deserialization::parse(bytecode); @@ -1308,16 +1306,16 @@ TEST_F(AvmExecutionTests, msmOpCode) // Send all the input as Fields and cast them to U8 later std::vector calldata = { FF(a.x), FF(a.y), a_is_inf, FF(b.x), FF(b.y), b_is_inf, scalar_a_lo, scalar_a_hi, scalar_b_lo, scalar_b_hi }; - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" // U32 - "00000000" // val - "00000000" // dst_offset - + to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" // U32 - "0000000A" // val - "00000001" + + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" // U32 + "00" // val + "00" // dst_offset + + to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" // U32 + "0A" // val + "01" + to_hex(OpCode::CALLDATACOPY) + // Calldatacopy "00" // Indirect flag "00000000" // cd_offset 0 @@ -1333,26 +1331,26 @@ TEST_F(AvmExecutionTests, msmOpCode) "01" // U8 tag field "00000005" // b_is_inf "00000005" // - + to_hex(OpCode::SET) + // opcode SET for length + + to_hex(OpCode::SET_8) + // opcode SET for length "00" // Indirect flag "03" // U32 - "00000006" // Length of point elements (6) - "0000000b" // dst offset (11) - + to_hex(OpCode::SET) + // SET Indirects + "06" // Length of point elements (6) + "0b" // dst offset (11) + + to_hex(OpCode::SET_8) + // SET Indirects "00" // Indirect flag "03" // U32 - "00000000" // points offset - "0000000d" // dst offset + - + to_hex(OpCode::SET) + // SET Indirects + "00" // points offset + "0d" // dst offset + + + to_hex(OpCode::SET_8) + // SET Indirects "00" // Indirect flag "03" // U32 - "00000006" // scalars offset - "0000000e" + // dst offset - to_hex(OpCode::SET) + // SET Indirects + "06" // scalars offset + "0e" + // dst offset + to_hex(OpCode::SET_8) + // SET Indirects "00" // Indirect flag "03" // U32 - "0000000c" // output offset - "0000000f" + // dst offset + "0c" // output offset + "0f" + // dst offset to_hex(OpCode::MSM) + // opcode MSM "07" // Indirect flag (first 3 indirect) "0000000d" // points offset @@ -1391,41 +1389,41 @@ TEST_F(AvmExecutionTests, pedersenCommitmentOpcode) std::vector expected_output = { expected_result.x, expected_result.y, expected_result.is_point_at_infinity() }; // Send all the input as Fields and cast them to U8 later std::vector calldata = { scalar_a, scalar_b }; - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" // U32 - "00000000" // val - "00000000" // dst_offset - + to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" // U32 - "00000002" // val - "00000001" + + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" // U32 + "00" // val + "00" // dst_offset + + to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" // U32 + "02" // val + "01" + to_hex(OpCode::CALLDATACOPY) + // Calldatacopy "00" // Indirect flag "00000000" // cd_offset 0 "00000001" // copy_size (2 elements) "00000000" // dst_offset 0 - + to_hex(OpCode::SET) + // opcode SET for indirect input + + to_hex(OpCode::SET_8) + // opcode SET for indirect input "00" // Indirect flag "03" // U32 - "00000000" // Input stored at memory 0 - "0000000b" // dst offset (11) - + to_hex(OpCode::SET) + // opcode SET for indirect output + "00" // Input stored at memory 0 + "0b" // dst offset (11) + + to_hex(OpCode::SET_8) + // opcode SET for indirect output "00" // Indirect flag "03" // U32 - "00000020" // output offset - "0000000d" // dst offset - + to_hex(OpCode::SET) + // opcode SET for input length + "20" // output offset + "0d" // dst offset + + to_hex(OpCode::SET_8) + // opcode SET for input length "00" // Indirect flag "03" // U32 - "00000002" // scalars length (2) - "00000002" + // dst offset (2) - to_hex(OpCode::SET) + // opcode SET for ctx index + "02" // scalars length (2) + "02" + // dst offset (2) + to_hex(OpCode::SET_8) + // opcode SET for ctx index "00" // Indirect flag "03" // U32 - "00000000" // ctx index (0) - "0000000f" + // dst offset + "00" // ctx index (0) + "0f" + // dst offset to_hex(OpCode::PEDERSENCOMMITMENT) + // opcode MSM "03" // Indirect flag (first 2 indirect) "0000000b" // inputs offset @@ -1660,11 +1658,11 @@ TEST_F(AvmExecutionTests, kernelInputOpcodes) // Positive test for L2GASLEFT opcode TEST_F(AvmExecutionTests, l2GasLeft) { - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + std::string bytecode_hex = to_hex(OpCode::SET_16) + // opcode SET "00" // Indirect flag "03" // U32 - "00000101" // val 257 - "00000011" // dst_offset 17 + "0101" // val 257 + "0011" // dst_offset 17 + to_hex(OpCode::L2GASLEFT) + // opcode L2GASLEFT "01" // Indirect flag "00000011" // dst_offset (indirect addr: 17) @@ -1689,7 +1687,7 @@ TEST_F(AvmExecutionTests, l2GasLeft) auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.main_sel_op_l2gasleft == 1; }); uint32_t expected_rem_gas = DEFAULT_INITIAL_L2_GAS - - static_cast(GAS_COST_TABLE.at(OpCode::SET).base_l2_gas_fixed_table) - + static_cast(GAS_COST_TABLE.at(OpCode::SET_8).base_l2_gas_fixed_table) - static_cast(GAS_COST_TABLE.at(OpCode::L2GASLEFT).base_l2_gas_fixed_table); EXPECT_EQ(row->main_ia, expected_rem_gas); @@ -1761,11 +1759,11 @@ TEST_F(AvmExecutionTests, ExecutorThrowsWithIncorrectNumberOfPublicInputs) TEST_F(AvmExecutionTests, kernelOutputEmitOpcodes) { // Set values into the first register to emit - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode Set - "00" // Indirect flag - "03" // U32 - "00000001" // value 1 - "00000001" // dst_offset 1 + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode Set + "00" // Indirect flag + "03" // U32 + "01" // value 1 + "01" // dst_offset 1 // Cast set to field + to_hex(OpCode::CAST) + // opcode CAST "00" // Indirect flag @@ -1863,11 +1861,11 @@ TEST_F(AvmExecutionTests, kernelOutputEmitOpcodes) TEST_F(AvmExecutionTests, kernelOutputStorageLoadOpcodeSimple) { // Sload from a value that has not previously been written to will require a hint to process - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET "00" // Indirect flag "03" // U32 - "00000009" // value 9 - "00000001" // dst_offset 1 + "09" // value 9 + "01" // dst_offset 1 + to_hex(OpCode::CAST) + // opcode CAST (Cast set to field) "00" // Indirect flag "06" // tag field @@ -1919,16 +1917,16 @@ TEST_F(AvmExecutionTests, kernelOutputStorageStoreOpcodeSimple) { // SSTORE, write 2 elements of calldata to dstOffset 1 and 2. std::vector calldata = { 42, 123, 9, 10 }; - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" // U32 - "00000000" // val - "00000000" // dst_offset - + to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" // U32 - "00000004" // val - "00000001" + + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" // U32 + "00" // val + "00" // dst_offset + + to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" // U32 + "04" // val + "01" + to_hex(OpCode::CALLDATACOPY) + // opcode CALLDATACOPY "00" // Indirect flag "00000000" // cd_offset @@ -1975,11 +1973,11 @@ TEST_F(AvmExecutionTests, kernelOutputStorageStoreOpcodeSimple) TEST_F(AvmExecutionTests, kernelOutputStorageOpcodes) { // Sload from a value that has not previously been written to will require a hint to process - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" // U32 - "00000009" // value 9 - "00000001" // dst_offset 1 + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" // U32 + "09" // value 9 + "01" // dst_offset 1 // Cast set to field + to_hex(OpCode::CAST) + // opcode CAST "00" // Indirect flag @@ -2050,11 +2048,11 @@ TEST_F(AvmExecutionTests, kernelOutputStorageOpcodes) TEST_F(AvmExecutionTests, kernelOutputHashExistsOpcodes) { // hash exists from a value that has not previously been written to will require a hint to process - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" // U32 - "00000001" // value 1 - "00000001" // dst_offset 1 + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" // U32 + "01" // value 1 + "01" // dst_offset 1 // Cast set to field + to_hex(OpCode::CAST) + // opcode CAST "00" // Indirect flag @@ -2149,52 +2147,52 @@ TEST_F(AvmExecutionTests, opCallOpcodes) std::vector calldata = { 17, 10, 34802342, 1, 2, 3, 4 }; std::string bytecode_preamble; // Set up Gas offsets - bytecode_preamble += to_hex(OpCode::SET) + // opcode SET for gas offset indirect - "00" // Indirect flag - "03" // U32 - "00000000" // val 0 (address where gas tuple is located) - "00000011"; // dst_offset 17 + bytecode_preamble += to_hex(OpCode::SET_8) + // opcode SET for gas offset indirect + "00" // Indirect flag + "03" // U32 + "00" // val 0 (address where gas tuple is located) + "11"; // dst_offset 17 // Set up contract address offset - bytecode_preamble += to_hex(OpCode::SET) + // opcode SET for args offset indirect - "00" // Indirect flag - "03" // U32 - "00000002" // val 2 (where contract address is located) - "00000012"; // dst_offset 18 + bytecode_preamble += to_hex(OpCode::SET_8) + // opcode SET for args offset indirect + "00" // Indirect flag + "03" // U32 + "02" // val 2 (where contract address is located) + "12"; // dst_offset 18 // Set up args offset - bytecode_preamble += to_hex(OpCode::SET) + // opcode SET for ret offset indirect - "00" // Indirect flag - "03" // U32 - "00000003" // val 3 (the start of the args array) - "00000013"; // dst_offset 19 + bytecode_preamble += to_hex(OpCode::SET_8) + // opcode SET for ret offset indirect + "00" // Indirect flag + "03" // U32 + "03" // val 3 (the start of the args array) + "13"; // dst_offset 19 // Set up args size offset - bytecode_preamble += to_hex(OpCode::SET) + // opcode SET for ret offset indirect - "00" // Indirect flag - "03" // U32 - "00000004" // val 4 (the length of the args array) - "00000014"; // dst_offset 20 + bytecode_preamble += to_hex(OpCode::SET_8) + // opcode SET for ret offset indirect + "00" // Indirect flag + "03" // U32 + "04" // val 4 (the length of the args array) + "14"; // dst_offset 20 // Set up the ret offset - bytecode_preamble += to_hex(OpCode::SET) + // opcode SET for ret offset indirect - "00" // Indirect flag - "03" // U32 - "00000100" // val 256 (the start of where to write the return data) - "00000015"; // dst_offset 21 + bytecode_preamble += to_hex(OpCode::SET_16) + // opcode SET for ret offset indirect + "00" // Indirect flag + "03" // U32 + "0100" // val 256 (the start of where to write the return data) + "0015"; // dst_offset 21 // Set up the success offset - bytecode_preamble += to_hex(OpCode::SET) + // opcode SET for ret offset indirect - "00" // Indirect flag - "03" // U32 - "00000102" // val 258 (write the success flag at ret_offset + ret_size) - "00000016"; // dst_offset 22 - - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" // U32 - "00000000" // val - "00000000" // dst_offset - + to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" // U32 - "00000007" // val - "00000001" + + bytecode_preamble += to_hex(OpCode::SET_16) + // opcode SET for ret offset indirect + "00" // Indirect flag + "03" // U32 + "0102" // val 258 (write the success flag at ret_offset + ret_size) + "0016"; // dst_offset 22 + + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" // U32 + "00" // val + "00" // dst_offset + + to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" // U32 + "07" // val + "01" + to_hex(OpCode::CALLDATACOPY) + // opcode CALLDATACOPY "00" // Indirect flag "00000000" // cd_offset @@ -2238,26 +2236,26 @@ TEST_F(AvmExecutionTests, opCallOpcodes) TEST_F(AvmExecutionTests, opGetContractInstanceOpcodes) { - std::string bytecode_hex = to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" // U32 - "00000000" // val - "00000000" // dst_offset - + to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "03" // U32 - "00000001" // val - "00000001" + + std::string bytecode_hex = to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" // U32 + "00" // val + "00" // dst_offset + + to_hex(OpCode::SET_8) + // opcode SET + "00" // Indirect flag + "03" // U32 + "01" // val + "01" + to_hex(OpCode::CALLDATACOPY) + // opcode CALLDATACOPY for addr "00" // Indirect flag "00000000" // cd_offset "00000001" // copy_size "00000001" // dst_offset, (i.e. where we store the addr) - + to_hex(OpCode::SET) + // opcode SET for the indirect dst offset + + to_hex(OpCode::SET_8) + // opcode SET for the indirect dst offset "00" // Indirect flag "03" // U32 - "00000003" // val i - "00000002" + // dst_offset 2 + "03" // val i + "02" + // dst_offset 2 to_hex(OpCode::GETCONTRACTINSTANCE) + // opcode CALL "02" // Indirect flag "00000001" // address offset @@ -2319,40 +2317,6 @@ TEST_F(AvmExecutionTests, invalidInstructionTag) EXPECT_THROW_WITH_MESSAGE(Deserialization::parse(bytecode), "Instruction tag is invalid"); } -// Negative test detecting SET opcode with instruction memory tag set to FF. -TEST_F(AvmExecutionTests, ffInstructionTagSetOpcode) -{ - std::string bytecode_hex = "00" // ADD - "00" // Indirect flag - "05" // U128 - "00000007" // addr a 7 - "00000009" // addr b 9 - "00000001" // addr c 1 - + to_hex(OpCode::SET) + // opcode SET - "00" // Indirect flag - "06" // tag FF - "00002344"; // - - auto bytecode = hex_to_bytes(bytecode_hex); - EXPECT_THROW_WITH_MESSAGE(Deserialization::parse(bytecode), "Instruction tag for SET opcode is invalid"); -} - -// Negative test detecting SET opcode without any operand. -TEST_F(AvmExecutionTests, SetOpcodeNoOperand) -{ - std::string bytecode_hex = "00" // ADD - "00" // Indirect flag - "05" // U128 - "00000007" // addr a 7 - "00000009" // addr b 9 - "00000001" // addr c 1 - + to_hex(OpCode::SET) + // opcode SET - "00"; // Indirect flag - - auto bytecode = hex_to_bytes(bytecode_hex); - EXPECT_THROW_WITH_MESSAGE(Deserialization::parse(bytecode), "Operand for SET opcode is missing"); -} - // Negative test detecting an incomplete instruction: missing instruction tag TEST_F(AvmExecutionTests, truncatedInstructionNoTag) { diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/inter_table.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/inter_table.test.cpp index d20103006f2..98aa04b7e66 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/inter_table.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/inter_table.test.cpp @@ -148,8 +148,7 @@ class AvmRangeCheckNegativeTests : public AvmInterTableTests { void SetUp() override { GTEST_SKIP(); } - void genTraceAdd( - uint128_t const& a, uint128_t const& b, uint128_t const& c, AvmMemoryTag tag, uint32_t min_trace_size = 0) + void genTraceAdd(FF const& a, FF const& b, FF const& c, AvmMemoryTag tag, uint32_t min_trace_size = 0) { trace_builder.op_set(0, a, 0, tag); trace_builder.op_set(0, b, 1, tag); @@ -161,9 +160,9 @@ class AvmRangeCheckNegativeTests : public AvmInterTableTests { auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.main_sel_op_add == FF(1); }); ASSERT_TRUE(row != trace.end()); - ASSERT_EQ(row->main_ia, FF(uint256_t::from_uint128(a))); - ASSERT_EQ(row->main_ib, FF(uint256_t::from_uint128(b))); - ASSERT_EQ(row->main_ic, FF(uint256_t::from_uint128(c))); + ASSERT_EQ(row->main_ia, a); + ASSERT_EQ(row->main_ib, b); + ASSERT_EQ(row->main_ic, c); auto clk = row->main_clk; // Find the corresponding Alu trace row @@ -400,7 +399,7 @@ class AvmPermMainMemNegativeTests : public AvmInterTableTests { // Helper function to generate a trace with a subtraction // for c = a - b at arbitray chosen addresses 52 (a), 11 (b), 55 (c). - void executeSub(uint128_t const a, uint128_t const b) + void executeSub(FF const a, FF const b) { trace_builder.op_set(0, a, 52, AvmMemoryTag::U8); trace_builder.op_set(0, b, 11, AvmMemoryTag::U8); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/kernel.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/kernel.test.cpp index e0b277b344d..b18553f1f2c 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/kernel.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/kernel.test.cpp @@ -1081,7 +1081,7 @@ TEST_F(AvmKernelOutputPositiveTests, kernelSload) auto execution_hints = ExecutionHints().with_storage_value_hints({ { 0, value } }); auto apply_opcodes = [=](AvmTraceBuilder& trace_builder) { - trace_builder.op_set(0, static_cast(slot), slot_offset, AvmMemoryTag::FF); + trace_builder.op_set(0, slot, slot_offset, AvmMemoryTag::FF); trace_builder.op_sload(indirect, slot_offset, size, dest_offset); }; auto checks = [=]([[maybe_unused]] bool indirect, const std::vector& trace) { @@ -1121,8 +1121,8 @@ TEST_F(AvmKernelOutputPositiveTests, kernelSstore) uint32_t output_offset = START_SSTORE_WRITE_OFFSET; auto apply_opcodes = [=](AvmTraceBuilder& trace_builder) { - trace_builder.op_set(0, static_cast(value), value_offset, AvmMemoryTag::FF); - trace_builder.op_set(0, static_cast(slot), metadata_offset, AvmMemoryTag::FF); + trace_builder.op_set(0, value, value_offset, AvmMemoryTag::FF); + trace_builder.op_set(0, slot, metadata_offset, AvmMemoryTag::FF); trace_builder.op_sstore(indirect, value_offset, size, metadata_offset); }; auto checks = [=]([[maybe_unused]] bool indirect, const std::vector& trace) { @@ -1165,13 +1165,13 @@ TEST_F(AvmKernelOutputPositiveTests, kernelNoteHashExists) auto execution_hints = ExecutionHints().with_note_hash_exists_hints({ { 0, exists } }); auto direct_apply_opcodes = [=](AvmTraceBuilder& trace_builder) { - trace_builder.op_set(0, static_cast(value), value_offset, AvmMemoryTag::FF); + trace_builder.op_set(0, value, value_offset, AvmMemoryTag::FF); // TODO(#8287): Leaf index isnt constrained properly so we just set it to 0 trace_builder.op_note_hash_exists(/*indirect*/ false, value_offset, 0, metadata_offset); }; // TODO: fix auto indirect_apply_opcodes = [=](AvmTraceBuilder& trace_builder) { - trace_builder.op_set(0, static_cast(value), value_offset, AvmMemoryTag::FF); + trace_builder.op_set(0, value, value_offset, AvmMemoryTag::FF); trace_builder.op_set(0, value_offset, indirect_value_offset, AvmMemoryTag::U32); trace_builder.op_set(0, metadata_offset, indirect_metadata_offset, AvmMemoryTag::U32); // TODO(#8287): Leaf index isnt constrained properly so we just set it to 0 @@ -1214,7 +1214,7 @@ TEST_F(AvmKernelOutputPositiveTests, kernelNullifierExists) auto execution_hints = ExecutionHints().with_nullifier_exists_hints({ { 0, exists } }); auto apply_opcodes = [=](AvmTraceBuilder& trace_builder) { - trace_builder.op_set(0, static_cast(value), value_offset, AvmMemoryTag::FF); + trace_builder.op_set(0, value, value_offset, AvmMemoryTag::FF); trace_builder.op_nullifier_exists(/*indirect=*/0, value_offset, metadata_offset); }; auto checks = [=](bool indirect, const std::vector& trace) { @@ -1253,7 +1253,7 @@ TEST_F(AvmKernelOutputPositiveTests, kernelNullifierNonExists) auto execution_hints = ExecutionHints().with_nullifier_exists_hints({ { 0, exists } }); auto apply_opcodes = [=](AvmTraceBuilder& trace_builder) { - trace_builder.op_set(0, static_cast(value), value_offset, AvmMemoryTag::FF); + trace_builder.op_set(0, value, value_offset, AvmMemoryTag::FF); trace_builder.op_nullifier_exists(/*indirect=*/0, value_offset, metadata_offset); }; auto checks = [=](bool indirect, const std::vector& trace) { @@ -1293,7 +1293,7 @@ TEST_F(AvmKernelOutputPositiveTests, kernelL1ToL2MsgExists) auto execution_hints = ExecutionHints().with_l1_to_l2_message_exists_hints({ { 0, exists } }); auto apply_opcodes = [=](AvmTraceBuilder& trace_builder) { - trace_builder.op_set(0, static_cast(value), value_offset, AvmMemoryTag::FF); + trace_builder.op_set(0, value, value_offset, AvmMemoryTag::FF); // TODO(#8287): Leaf index isnt constrained properly so we just set it to 0 trace_builder.op_l1_to_l2_msg_exists(/*indirect*/ false, value_offset, 0, metadata_offset); }; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/mem_opcodes.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/mem_opcodes.test.cpp index cfd90a2db00..84eed13b773 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/tests/mem_opcodes.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/mem_opcodes.test.cpp @@ -48,9 +48,9 @@ class AvmMemOpcodeTests : public ::testing::Test { if (indirect) { trace_builder.op_set(0, dir_src_offset, src_offset, AvmMemoryTag::U32); trace_builder.op_set(0, dir_dst_offset, dst_offset, AvmMemoryTag::U32); - trace_builder.op_set(0, val, dir_src_offset, tag); + trace_builder.op_set(0, uint256_t::from_uint128(val), dir_src_offset, tag); } else { - trace_builder.op_set(0, val, src_offset, tag); + trace_builder.op_set(0, uint256_t::from_uint128(val), src_offset, tag); } trace_builder.op_mov(indirect ? 3 : 0, src_offset, dst_offset); diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/deserialization.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/deserialization.cpp index 25c92733338..799ce6219ae 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/deserialization.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/deserialization.cpp @@ -87,7 +87,12 @@ const std::unordered_map> OPCODE_WIRE_FORMAT = { OpCode::INTERNALRETURN, {} }, // Machine State - Memory - // OpCode::SET is handled differently + { OpCode::SET_8, { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT8, OperandType::UINT8 } }, + { OpCode::SET_16, { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT16, OperandType::UINT16 } }, + { OpCode::SET_32, { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT32, OperandType::UINT16 } }, + { OpCode::SET_64, { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT64, OperandType::UINT16 } }, + { OpCode::SET_128, { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT128, OperandType::UINT16 } }, + { OpCode::SET_FF, { OperandType::INDIRECT, OperandType::TAG, OperandType::FF, OperandType::UINT16 } }, { OpCode::MOV_8, { OperandType::INDIRECT, OperandType::UINT8, OperandType::UINT8 } }, { OpCode::MOV_16, { OperandType::INDIRECT, OperandType::UINT16, OperandType::UINT16 } }, { OpCode::CMOV, @@ -177,7 +182,7 @@ const std::unordered_map> OPCODE_WIRE_FORMAT = const std::unordered_map OPERAND_TYPE_SIZE = { { OperandType::INDIRECT, 1 }, { OperandType::TAG, 1 }, { OperandType::UINT8, 1 }, { OperandType::UINT16, 2 }, - { OperandType::UINT32, 4 }, { OperandType::UINT64, 8 }, { OperandType::UINT128, 16 }, + { OperandType::UINT32, 4 }, { OperandType::UINT64, 8 }, { OperandType::UINT128, 16 }, { OperandType::FF, 32 } }; } // Anonymous namespace @@ -208,58 +213,11 @@ std::vector Deserialization::parse(std::vector const& byte pos++; auto const opcode = static_cast(opcode_byte); - std::vector inst_format; - - if (opcode == OpCode::SET) { - // Small hack here because of the structure of SET (where Indirect is the first flag). - // Right now pos is pointing to the indirect flag, but we want it to point to the memory tag. - // We cannot increment pos again because we need to read from pos later when parsing the SET opcode - // So we effectively peek at the next pos - if (pos + 1 == length) { - throw_or_abort("Operand for SET opcode is missing at position " + std::to_string(pos)); - } - - std::set const valid_tags = { static_cast(AvmMemoryTag::U8), - static_cast(AvmMemoryTag::U16), - static_cast(AvmMemoryTag::U32), - static_cast(AvmMemoryTag::U64), - static_cast(AvmMemoryTag::U128) }; - // Peek again here for the mem tag - uint8_t set_tag_u8 = bytecode.at(pos + 1); - - if (!valid_tags.contains(set_tag_u8)) { - throw_or_abort("Instruction tag for SET opcode is invalid at position " + std::to_string(pos + 1) + - " value: " + std::to_string(set_tag_u8)); - } - - auto in_tag = static_cast(set_tag_u8); - switch (in_tag) { - case AvmMemoryTag::U8: - inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT8, OperandType::UINT32 }; - break; - case AvmMemoryTag::U16: - inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT16, OperandType::UINT32 }; - break; - case AvmMemoryTag::U32: - inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT32, OperandType::UINT32 }; - break; - case AvmMemoryTag::U64: - inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT64, OperandType::UINT32 }; - break; - case AvmMemoryTag::U128: - inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT128, OperandType::UINT32 }; - break; - default: // This branch is guarded above. - throw_or_abort("Error processing wire format of SET opcode."); - } - } else { - auto const iter = OPCODE_WIRE_FORMAT.find(opcode); - if (iter == OPCODE_WIRE_FORMAT.end()) { - throw_or_abort("Opcode not found in OPCODE_WIRE_FORMAT: " + to_hex(opcode) + " name " + - to_string(opcode)); - } - inst_format = iter->second; + auto const iter = OPCODE_WIRE_FORMAT.find(opcode); + if (iter == OPCODE_WIRE_FORMAT.end()) { + throw_or_abort("Opcode not found in OPCODE_WIRE_FORMAT: " + to_hex(opcode) + " name " + to_string(opcode)); } + std::vector inst_format = iter->second; std::vector operands; for (OperandType const& opType : inst_format) { @@ -315,6 +273,12 @@ std::vector Deserialization::parse(std::vector const& byte operands.emplace_back(operand_u128); break; } + case OperandType::FF: { + FF operand_ff; + uint8_t const* pos_ptr = &bytecode.at(pos); + read(pos_ptr, operand_ff); + operands.emplace_back(operand_ff); + } } pos += OPERAND_TYPE_SIZE.at(opType); } diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/deserialization.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/deserialization.hpp index 42238d7c7b1..2a38e836f03 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/deserialization.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/deserialization.hpp @@ -11,7 +11,7 @@ namespace bb::avm_trace { // See avm/serialization/instruction_serialization.ts). // Note that the TAG enum value is not supported in TS and is parsed as UINT8. // INDIRECT is parsed as UINT8 where the bits represent the operands that have indirect mem access. -enum class OperandType : uint8_t { INDIRECT, TAG, UINT8, UINT16, UINT32, UINT64, UINT128 }; +enum class OperandType : uint8_t { INDIRECT, TAG, UINT8, UINT16, UINT32, UINT64, UINT128, FF }; class Deserialization { public: diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp index 468329ff80b..6c0b8b01d6f 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/execution.cpp @@ -588,32 +588,46 @@ std::vector Execution::gen_trace(std::vector const& instructio break; // Machine State - Memory - case OpCode::SET: { - uint128_t val = 0; - AvmMemoryTag in_tag = std::get(inst.operands.at(1)); - - switch (in_tag) { - case AvmMemoryTag::U8: - val = std::get(inst.operands.at(2)); - break; - case AvmMemoryTag::U16: - val = std::get(inst.operands.at(2)); - break; - case AvmMemoryTag::U32: - val = std::get(inst.operands.at(2)); - break; - case AvmMemoryTag::U64: - val = std::get(inst.operands.at(2)); - break; - case AvmMemoryTag::U128: - val = std::get(inst.operands.at(2)); - break; - default: - break; - } - - trace_builder.op_set( - std::get(inst.operands.at(0)), val, std::get(inst.operands.at(3)), in_tag); + case OpCode::SET_8: { + trace_builder.op_set(std::get(inst.operands.at(0)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(1))); + break; + } + case OpCode::SET_16: { + trace_builder.op_set(std::get(inst.operands.at(0)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(1))); + break; + } + case OpCode::SET_32: { + trace_builder.op_set(std::get(inst.operands.at(0)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(1))); + break; + } + case OpCode::SET_64: { + trace_builder.op_set(std::get(inst.operands.at(0)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(1))); + break; + } + case OpCode::SET_128: { + trace_builder.op_set(std::get(inst.operands.at(0)), + uint256_t::from_uint128(std::get(inst.operands.at(2))), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(1))); + break; + } + case OpCode::SET_FF: { + trace_builder.op_set(std::get(inst.operands.at(0)), + std::get(inst.operands.at(2)), + std::get(inst.operands.at(3)), + std::get(inst.operands.at(1))); break; } case OpCode::MOV_8: diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/fixed_gas.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/fixed_gas.cpp index fa4dd2f515e..76f472d6400 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/fixed_gas.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/fixed_gas.cpp @@ -51,7 +51,12 @@ const std::unordered_map GAS_COST_TABLE = { { OpCode::JUMPI, make_cost(AVM_JUMPI_BASE_L2_GAS, 0, AVM_JUMPI_DYN_L2_GAS, 0) }, { 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::SET_8, make_cost(AVM_SET_BASE_L2_GAS, 0, AVM_SET_DYN_L2_GAS, 0) }, + { OpCode::SET_16, make_cost(AVM_SET_BASE_L2_GAS, 0, AVM_SET_DYN_L2_GAS, 0) }, + { OpCode::SET_32, make_cost(AVM_SET_BASE_L2_GAS, 0, AVM_SET_DYN_L2_GAS, 0) }, + { OpCode::SET_64, make_cost(AVM_SET_BASE_L2_GAS, 0, AVM_SET_DYN_L2_GAS, 0) }, + { OpCode::SET_128, make_cost(AVM_SET_BASE_L2_GAS, 0, AVM_SET_DYN_L2_GAS, 0) }, + { OpCode::SET_FF, make_cost(AVM_SET_BASE_L2_GAS, 0, AVM_SET_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) }, diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/instructions.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/instructions.hpp index 78679099bb0..5c003ece969 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/instructions.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/instructions.hpp @@ -11,7 +11,7 @@ namespace bb::avm_trace { -using Operand = std::variant; +using Operand = std::variant; class Instruction { public: @@ -40,6 +40,8 @@ class Instruction { str += std::to_string(std::get(operand)); } else if (std::holds_alternative(operand)) { str += "someu128"; + } else if (std::holds_alternative(operand)) { + str += "someff"; } else { str += "unknown operand type"; } diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.cpp index f359fdc1e05..5c9721a25eb 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.cpp @@ -102,8 +102,18 @@ std::string to_string(OpCode opcode) case OpCode::INTERNALRETURN: return "INTERNALRETURN"; // Machine State - Memory - case OpCode::SET: - return "SET"; + case OpCode::SET_8: + return "SET_8"; + case OpCode::SET_16: + return "SET_16"; + case OpCode::SET_32: + return "SET_32"; + case OpCode::SET_64: + return "SET_64"; + case OpCode::SET_128: + return "SET_128"; + case OpCode::SET_FF: + return "SET_FF"; case OpCode::MOV_8: return "MOV_8"; case OpCode::MOV_16: diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.hpp index 963fd23831b..711376e6e09 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/opcode.hpp @@ -67,7 +67,12 @@ enum class OpCode : uint8_t { INTERNALCALL, INTERNALRETURN, // Machine State - Memory - SET, + SET_8, + SET_16, + SET_32, + SET_64, + SET_128, + SET_FF, MOV_8, MOV_16, CMOV, diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp index b08a1bec6d4..cc3a48260d9 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp @@ -217,10 +217,10 @@ FF AvmTraceBuilder::unconstrained_read_from_memory(AddressWithMode addr) void AvmTraceBuilder::write_to_memory(AddressWithMode addr, FF val, AvmMemoryTag w_tag) { - // op_set_internal increments the pc, so we need to store the current pc and then jump back to it + // op_set increments the pc, so we need to store the current pc and then jump back to it // to legaly reset the pc. auto current_pc = pc; - op_set_internal(static_cast(addr.mode), val, addr.offset, w_tag); + op_set(static_cast(addr.mode), val, addr.offset, w_tag); op_jump(current_pc); } @@ -1744,13 +1744,7 @@ void AvmTraceBuilder::op_internal_return() * @param dst_offset Memory destination offset where val is written to * @param in_tag The instruction memory tag */ -void AvmTraceBuilder::op_set(uint8_t indirect, uint128_t val, uint32_t dst_offset, AvmMemoryTag in_tag) -{ - auto const val_ff = FF{ uint256_t::from_uint128(val) }; - op_set_internal(indirect, val_ff, dst_offset, in_tag); -} - -void AvmTraceBuilder::op_set_internal(uint8_t indirect, FF val_ff, uint32_t dst_offset, AvmMemoryTag in_tag) +void AvmTraceBuilder::op_set(uint8_t indirect, FF val_ff, uint32_t dst_offset, AvmMemoryTag in_tag) { auto const clk = static_cast(main_trace.size()) + 1; auto [resolved_c] = unpack_indirects<1>(indirect, { dst_offset }); @@ -1759,7 +1753,8 @@ void AvmTraceBuilder::op_set_internal(uint8_t indirect, FF val_ff, uint32_t dst_ constrained_write_to_memory(call_ptr, clk, resolved_c, val_ff, AvmMemoryTag::U0, in_tag, IntermRegister::IC); // Constrain gas cost - gas_trace_builder.constrain_gas(clk, OpCode::SET); + // FIXME: not great that we are having to choose one specific opcode here! + gas_trace_builder.constrain_gas(clk, OpCode::SET_8); main_trace.push_back(Row{ .main_clk = clk, diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp index e97d8b8662b..2267006c7fe 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.hpp @@ -112,9 +112,7 @@ class AvmTraceBuilder { void op_internal_return(); // Machine State - Memory - void op_set(uint8_t indirect, uint128_t val, uint32_t dst_offset, AvmMemoryTag in_tag); - // TODO: only used for write_slice_to_memory. Remove. - void op_set_internal(uint8_t indirect, FF val_ff, uint32_t dst_offset, AvmMemoryTag in_tag); + void op_set(uint8_t indirect, FF val, uint32_t dst_offset, AvmMemoryTag in_tag); void op_mov(uint8_t indirect, uint32_t src_offset, uint32_t dst_offset); void op_cmov(uint8_t indirect, uint32_t a_offset, uint32_t b_offset, uint32_t cond_offset, uint32_t dst_offset); diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr index 81ef861d364..f621ba153ae 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -22,6 +22,7 @@ contract AvmTest { global big_field_128_bits: Field = 0x001234567890abcdef1234567890abcdef; global big_field_136_bits: Field = 0x991234567890abcdef1234567890abcdef; + global big_field_254_bits: Field = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; // Libs use std::embedded_curve_ops::{multi_scalar_mul, EmbeddedCurvePoint}; @@ -125,6 +126,11 @@ contract AvmTest { big_field_136_bits } + #[aztec(public)] + fn set_opcode_really_big_field() -> Field { + big_field_254_bits + } + #[aztec(public)] fn add_u128(a: U128, b: U128) -> U128 { a + b diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs index c728b36c193..fab43041e65 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs @@ -394,43 +394,7 @@ impl BrilligContext< constant, bit_size ); - if bit_size > 128 && constant.num_bits() > 128 { - let high = F::from_be_bytes_reduce( - constant.to_be_bytes().get(0..16).expect("FieldElement::to_be_bytes() too short!"), - ); - let low = F::from(constant.to_u128()); - let high_register = SingleAddrVariable::new(self.allocate_register(), 254); - let low_register = SingleAddrVariable::new(self.allocate_register(), 254); - let intermediate_register = SingleAddrVariable::new(self.allocate_register(), 254); - - self.constant(high_register.address, high_register.bit_size, high, false); - self.constant(low_register.address, low_register.bit_size, low, false); - // I want to multiply high by 2^128, but I can't get that big constant in. - // So I'll multiply by 2^64 twice. - self.constant( - intermediate_register.address, - intermediate_register.bit_size, - F::from(1_u128 << 64), - false, - ); - self.binary(high_register, intermediate_register, high_register, BrilligBinaryOp::Mul); - self.binary(high_register, intermediate_register, high_register, BrilligBinaryOp::Mul); - // Now we can add. - self.binary(high_register, low_register, intermediate_register, BrilligBinaryOp::Add); - if indirect { - self.cast( - SingleAddrVariable::new(intermediate_register.address, bit_size), - intermediate_register, - ); - self.store_instruction(result, intermediate_register.address); - } else { - self.cast(SingleAddrVariable::new(result, bit_size), intermediate_register); - } - - self.deallocate_single_addr(high_register); - self.deallocate_single_addr(low_register); - self.deallocate_single_addr(intermediate_register); - } else if indirect { + if indirect { self.push_opcode(BrilligOpcode::IndirectConst { destination_pointer: result, value: constant, diff --git a/yarn-project/simulator/src/avm/avm_gas.ts b/yarn-project/simulator/src/avm/avm_gas.ts index ef854cd1f53..598c5d58e65 100644 --- a/yarn-project/simulator/src/avm/avm_gas.ts +++ b/yarn-project/simulator/src/avm/avm_gas.ts @@ -90,7 +90,12 @@ const BaseGasCosts: Record = { [Opcode.JUMPI]: makeCost(c.AVM_JUMPI_BASE_L2_GAS, 0), [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.SET_8]: makeCost(c.AVM_SET_BASE_L2_GAS, 0), + [Opcode.SET_16]: makeCost(c.AVM_SET_BASE_L2_GAS, 0), + [Opcode.SET_32]: makeCost(c.AVM_SET_BASE_L2_GAS, 0), + [Opcode.SET_64]: makeCost(c.AVM_SET_BASE_L2_GAS, 0), + [Opcode.SET_128]: makeCost(c.AVM_SET_BASE_L2_GAS, 0), + [Opcode.SET_FF]: makeCost(c.AVM_SET_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), @@ -156,7 +161,12 @@ const DynamicGasCosts: Record = { [Opcode.JUMPI]: makeCost(c.AVM_JUMPI_DYN_L2_GAS, 0), [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.SET_8]: makeCost(c.AVM_SET_DYN_L2_GAS, 0), + [Opcode.SET_16]: makeCost(c.AVM_SET_DYN_L2_GAS, 0), + [Opcode.SET_32]: makeCost(c.AVM_SET_DYN_L2_GAS, 0), + [Opcode.SET_64]: makeCost(c.AVM_SET_DYN_L2_GAS, 0), + [Opcode.SET_128]: makeCost(c.AVM_SET_DYN_L2_GAS, 0), + [Opcode.SET_FF]: makeCost(c.AVM_SET_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), diff --git a/yarn-project/simulator/src/avm/avm_memory_types.ts b/yarn-project/simulator/src/avm/avm_memory_types.ts index 59eb9a98553..c85f920b7be 100644 --- a/yarn-project/simulator/src/avm/avm_memory_types.ts +++ b/yarn-project/simulator/src/avm/avm_memory_types.ts @@ -350,7 +350,7 @@ export class TaggedMemory implements TaggedMemoryInterface { } // Truncates the value to fit the type. - public static integralFromTag(v: bigint | number, tag: TypeTag): IntegralValue { + public static buildFromTagTruncating(v: bigint | number, tag: TypeTag): MemoryValue { v = BigInt(v); switch (tag) { case TypeTag.UINT8: @@ -363,8 +363,10 @@ export class TaggedMemory implements TaggedMemoryInterface { return new Uint64(v & ((1n << 64n) - 1n)); case TypeTag.UINT128: return new Uint128(v & ((1n << 128n) - 1n)); + case TypeTag.FIELD: + return new Field(v); default: - throw new Error(`${TypeTag[tag]} is not a valid integral type.`); + throw new Error(`${TypeTag[tag]} is not a valid tag.`); } } diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index ad2b51b22aa..2918db3c119 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -34,6 +34,7 @@ import { type HostStorage } from './journal/host_storage.js'; import { type AvmPersistableStateManager } from './journal/journal.js'; import { Add, CalldataCopy, Return, Set } from './opcodes/index.js'; import { encodeToBytecode } from './serialization/bytecode_serialization.js'; +import { Opcode } from './serialization/instruction_serialization.js'; import { mockGetBytecode, mockGetContractInstance, @@ -52,8 +53,11 @@ describe('AVM simulator: injected bytecode', () => { beforeAll(() => { calldata = [new Fr(1), new Fr(2)]; bytecode = encodeToBytecode([ - new Set(/*indirect*/ 0, TypeTag.UINT32, /*value*/ adjustCalldataIndex(0), /*dstOffset*/ 0), - new Set(/*indirect*/ 0, TypeTag.UINT32, /*value*/ 2, /*dstOffset*/ 1), + new Set(/*indirect*/ 0, TypeTag.UINT32, /*value*/ adjustCalldataIndex(0), /*dstOffset*/ 0).as( + Opcode.SET_8, + Set.wireFormat8, + ), + new Set(/*indirect*/ 0, TypeTag.UINT32, /*value*/ 2, /*dstOffset*/ 1).as(Opcode.SET_8, Set.wireFormat8), new CalldataCopy(/*indirect=*/ 0, /*cdOffset=*/ 0, /*copySize=*/ 1, /*dstOffset=*/ 0), new Add(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), new Return(/*indirect=*/ 0, /*returnOffset=*/ 2, /*copySize=*/ 1), @@ -227,6 +231,7 @@ describe('AVM simulator: transpiled Noir contracts', () => { ['set_opcode_u64', 1n << 60n], ['set_opcode_small_field', 0x001234567890abcdef1234567890abcdefn], ['set_opcode_big_field', 0x991234567890abcdef1234567890abcdefn], + ['set_opcode_really_big_field', 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn], ])('SET functions', (name: string, res: bigint) => { it(`function '${name}'`, async () => { const context = initContext(); diff --git a/yarn-project/simulator/src/avm/opcodes/external_calls.test.ts b/yarn-project/simulator/src/avm/opcodes/external_calls.test.ts index 48f703c8185..8cde0f61cb4 100644 --- a/yarn-project/simulator/src/avm/opcodes/external_calls.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/external_calls.test.ts @@ -10,6 +10,7 @@ import { adjustCalldataIndex, initContext, initHostStorage, initPersistableState import { type HostStorage } from '../journal/host_storage.js'; import { type AvmPersistableStateManager } from '../journal/journal.js'; import { encodeToBytecode } from '../serialization/bytecode_serialization.js'; +import { Opcode } from '../serialization/instruction_serialization.js'; import { mockGetBytecode, mockTraceFork } from '../test_utils.js'; import { L2GasLeft } from './context_getters.js'; import { Call, Return, Revert, StaticCall } from './external_calls.js'; @@ -83,8 +84,11 @@ describe('External Calls', () => { // const otherContextInstructionsL2GasCost = 780; // Includes the cost of the call itself const otherContextInstructionsBytecode = markBytecodeAsAvm( encodeToBytecode([ - new Set(/*indirect=*/ 0, TypeTag.UINT32, adjustCalldataIndex(0), /*dstOffset=*/ 0), - new Set(/*indirect=*/ 0, TypeTag.UINT32, argsSize, /*dstOffset=*/ 1), + new Set(/*indirect=*/ 0, TypeTag.UINT32, adjustCalldataIndex(0), /*dstOffset=*/ 0).as( + Opcode.SET_8, + Set.wireFormat8, + ), + new Set(/*indirect=*/ 0, TypeTag.UINT32, argsSize, /*dstOffset=*/ 1).as(Opcode.SET_8, Set.wireFormat8), new CalldataCopy(/*indirect=*/ 0, /*csOffsetAddress=*/ 0, /*copySizeOffset=*/ 1, /*dstOffset=*/ 0), new SStore(/*indirect=*/ 0, /*srcOffset=*/ valueOffset, /*slotOffset=*/ slotOffset), new Return(/*indirect=*/ 0, /*retOffset=*/ 0, /*size=*/ 2), diff --git a/yarn-project/simulator/src/avm/opcodes/memory.test.ts b/yarn-project/simulator/src/avm/opcodes/memory.test.ts index 80e60b4ca5d..99967748062 100644 --- a/yarn-project/simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/memory.test.ts @@ -2,7 +2,6 @@ import { Fr } from '@aztec/foundation/fields'; import { type AvmContext } from '../avm_context.js'; import { Field, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js'; -import { InstructionExecutionError } from '../errors.js'; import { adjustCalldataIndex, initContext, initExecutionEnvironment } from '../fixtures/index.js'; import { Opcode } from '../serialization/instruction_serialization.js'; import { Addressing, AddressingMode } from './addressing_mode.js'; @@ -18,86 +17,111 @@ describe('Memory instructions', () => { describe('SET', () => { it('Should (de)serialize correctly [tag=u8]', () => { const buf = Buffer.from([ - Set.opcode, // opcode + Opcode.SET_8, // opcode 0x01, // indirect TypeTag.UINT8, // inTag ...Buffer.from('12', 'hex'), - ...Buffer.from('3456789a', 'hex'), // dstOffset + ...Buffer.from('56', 'hex'), // dstOffset ]); - const inst = new Set(/*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT8, /*value=*/ 0x12, /*dstOffset=*/ 0x3456789a); + const inst = new Set(/*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT8, /*value=*/ 0x12, /*dstOffset=*/ 0x56).as( + Opcode.SET_8, + Set.wireFormat8, + ); - expect(Set.deserialize(buf)).toEqual(inst); + expect(Set.as(Set.wireFormat8).deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); }); it('Should (de)serialize correctly [tag=u16]', () => { const buf = Buffer.from([ - Set.opcode, // opcode + Opcode.SET_16, // opcode 0x01, // indirect TypeTag.UINT16, // inTag ...Buffer.from('1234', 'hex'), - ...Buffer.from('3456789a', 'hex'), // dstOffset + ...Buffer.from('3456', 'hex'), // dstOffset ]); - const inst = new Set(/*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT16, /*value=*/ 0x1234, /*dstOffset=*/ 0x3456789a); + const inst = new Set(/*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT16, /*value=*/ 0x1234, /*dstOffset=*/ 0x3456).as( + Opcode.SET_16, + Set.wireFormat16, + ); - expect(Set.deserialize(buf)).toEqual(inst); + expect(Set.as(Set.wireFormat16).deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); }); it('Should (de)serialize correctly [tag=u32]', () => { const buf = Buffer.from([ - Set.opcode, // opcode + Opcode.SET_32, // opcode 0x01, // indirect TypeTag.UINT32, // inTag ...Buffer.from('12345678', 'hex'), - ...Buffer.from('3456789a', 'hex'), // dstOffset + ...Buffer.from('3456', 'hex'), // dstOffset ]); const inst = new Set( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT32, /*value=*/ 0x12345678, - /*dstOffset=*/ 0x3456789a, - ); + /*dstOffset=*/ 0x3456, + ).as(Opcode.SET_32, Set.wireFormat32); - expect(Set.deserialize(buf)).toEqual(inst); + expect(Set.as(Set.wireFormat32).deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); }); it('Should (de)serialize correctly [tag=u64]', () => { const buf = Buffer.from([ - Set.opcode, // opcode + Opcode.SET_64, // opcode 0x01, // indirect TypeTag.UINT64, // inTag ...Buffer.from('1234567812345678', 'hex'), - ...Buffer.from('3456789a', 'hex'), // dstOffset + ...Buffer.from('34567', 'hex'), // dstOffset ]); const inst = new Set( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, /*value=*/ 0x1234567812345678n, - /*dstOffset=*/ 0x3456789a, - ); + /*dstOffset=*/ 0x3456, + ).as(Opcode.SET_64, Set.wireFormat64); - expect(Set.deserialize(buf)).toEqual(inst); + expect(Set.as(Set.wireFormat64).deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); }); it('Should (de)serialize correctly [tag=u128]', () => { const buf = Buffer.from([ - Set.opcode, // opcode + Opcode.SET_128, // opcode 0x01, // indirect TypeTag.UINT128, // inTag ...Buffer.from('12345678123456781234567812345678', 'hex'), // const (will be 128 bit) - ...Buffer.from('3456789a', 'hex'), // dstOffset + ...Buffer.from('3456', 'hex'), // dstOffset ]); const inst = new Set( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT128, /*value=*/ 0x12345678123456781234567812345678n, - /*dstOffset=*/ 0x3456789a, - ); + /*dstOffset=*/ 0x3456, + ).as(Opcode.SET_128, Set.wireFormat128); - expect(Set.deserialize(buf)).toEqual(inst); + expect(Set.as(Set.wireFormat128).deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); + }); + + it('Should (de)serialize correctly [tag=ff]', () => { + const buf = Buffer.from([ + Opcode.SET_FF, // opcode + 0x01, // indirect + TypeTag.UINT128, // inTag + ...Buffer.from('1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', 'hex'), // const (will be 32 bytes) + ...Buffer.from('3456', 'hex'), // dstOffset + ]); + const inst = new Set( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT128, + /*value=*/ 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn, + /*dstOffset=*/ 0x3456, + ).as(Opcode.SET_FF, Set.wireFormatFF); + + expect(Set.as(Set.wireFormatFF).deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); }); @@ -132,14 +156,6 @@ describe('Memory instructions', () => { expect(actual).toEqual(new Uint16(0x5678)); expect(tag).toEqual(TypeTag.UINT16); }); - - it('should throw if tag is FIELD, UNINITIALIZED, INVALID', async () => { - for (const tag of [TypeTag.FIELD, TypeTag.UNINITIALIZED, TypeTag.INVALID]) { - await expect( - async () => await new Set(/*indirect=*/ 0, /*inTag=*/ tag, /*value=*/ 1234n, /*offset=*/ 1).execute(context), - ).rejects.toThrow(InstructionExecutionError); - } - }); }); describe('CAST', () => { diff --git a/yarn-project/simulator/src/avm/opcodes/memory.ts b/yarn-project/simulator/src/avm/opcodes/memory.ts index 91d85884ed5..66a04494af2 100644 --- a/yarn-project/simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/simulator/src/avm/opcodes/memory.ts @@ -1,39 +1,57 @@ import type { AvmContext } from '../avm_context.js'; -import { Field, TaggedMemory, TypeTag } from '../avm_memory_types.js'; -import { InstructionExecutionError } from '../errors.js'; -import { BufferCursor } from '../serialization/buffer_cursor.js'; -import { Opcode, OperandType, deserialize, serializeAs } from '../serialization/instruction_serialization.js'; +import { Field, TaggedMemory } from '../avm_memory_types.js'; +import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Addressing } from './addressing_mode.js'; import { Instruction } from './instruction.js'; import { TwoOperandInstruction } from './instruction_impl.js'; -const TAG_TO_OPERAND_TYPE = new Map([ - [TypeTag.UINT8, OperandType.UINT8], - [TypeTag.UINT16, OperandType.UINT16], - [TypeTag.UINT32, OperandType.UINT32], - [TypeTag.UINT64, OperandType.UINT64], - [TypeTag.UINT128, OperandType.UINT128], -]); - -function getOperandTypeFromInTag(inTag: number | bigint): OperandType { - inTag = inTag as number; - const tagOperandType = TAG_TO_OPERAND_TYPE.get(inTag); - if (tagOperandType === undefined) { - throw new Error(`Invalid tag ${inTag} for SET.`); - } - return tagOperandType; -} - export class Set extends Instruction { static readonly type: string = 'SET'; - static readonly opcode: Opcode = Opcode.SET; - - private static readonly wireFormatBeforeConst: OperandType[] = [ - OperandType.UINT8, - OperandType.UINT8, - OperandType.UINT8, + // Required for gas. + static readonly opcode: Opcode = Opcode.SET_8; + + public static readonly wireFormat8: OperandType[] = [ + OperandType.UINT8, // opcode + OperandType.UINT8, // indirect + OperandType.UINT8, // tag + OperandType.UINT8, // const (value) + OperandType.UINT8, // dstOffset + ]; + public static readonly wireFormat16: OperandType[] = [ + OperandType.UINT8, // opcode + OperandType.UINT8, // indirect + OperandType.UINT8, // tag + OperandType.UINT16, // const (value) + OperandType.UINT16, // dstOffset + ]; + public static readonly wireFormat32: OperandType[] = [ + OperandType.UINT8, // opcode + OperandType.UINT8, // indirect + OperandType.UINT8, // tag + OperandType.UINT32, // const (value) + OperandType.UINT16, // dstOffset + ]; + public static readonly wireFormat64: OperandType[] = [ + OperandType.UINT8, // opcode + OperandType.UINT8, // indirect + OperandType.UINT8, // tag + OperandType.UINT64, // const (value) + OperandType.UINT16, // dstOffset + ]; + public static readonly wireFormat128: OperandType[] = [ + OperandType.UINT8, // opcode + OperandType.UINT8, // indirect + OperandType.UINT8, // tag + OperandType.UINT128, // const (value) + OperandType.UINT16, // dstOffset + ]; + public static readonly wireFormatFF: OperandType[] = [ + OperandType.UINT8, // opcode + OperandType.UINT8, // indirect + OperandType.UINT8, // tag + OperandType.FF, // const (value) + OperandType.UINT16, // dstOffset ]; - private static readonly wireFormatAfterConst: OperandType[] = [OperandType.UINT32]; constructor( private indirect: number, @@ -44,42 +62,13 @@ export class Set extends Instruction { super(); } - /** We need to use a custom serialize function because of the variable length of the value. */ - public override serialize(): Buffer { - const format: OperandType[] = [ - ...Set.wireFormatBeforeConst, - getOperandTypeFromInTag(this.inTag), - ...Set.wireFormatAfterConst, - ]; - return serializeAs(format, this.opcode, this); - } - - /** We need to use a custom deserialize function because of the variable length of the value. */ - public static override deserialize(buf: BufferCursor | Buffer): Set { - if (buf instanceof Buffer) { - buf = new BufferCursor(buf); - } - const beforeConst = deserialize(buf, Set.wireFormatBeforeConst); - const tag = beforeConst[beforeConst.length - 1]; - const val = deserialize(buf, [getOperandTypeFromInTag(tag)]); - const afterConst = deserialize(buf, Set.wireFormatAfterConst); - const res = [...beforeConst, ...val, ...afterConst]; - const args = res.slice(1) as ConstructorParameters; // Remove opcode. - return new Set(...args); - } - public async execute(context: AvmContext): Promise { const memoryOperations = { writes: 1, indirect: this.indirect }; const memory = context.machineState.memory.track(this.type); context.machineState.consumeGas(this.gasCost(memoryOperations)); - // Per the YP, the tag cannot be a field. - if ([TypeTag.FIELD, TypeTag.UNINITIALIZED, TypeTag.INVALID].includes(this.inTag)) { - throw new InstructionExecutionError(`Invalid tag ${TypeTag[this.inTag]} for SET.`); - } const [dstOffset] = Addressing.fromWire(this.indirect).resolve([this.dstOffset], memory); - - const res = TaggedMemory.integralFromTag(this.value, this.inTag); + const res = TaggedMemory.buildFromTagTruncating(this.value, this.inTag); memory.set(dstOffset, res); memory.assert(memoryOperations); @@ -148,9 +137,7 @@ export class Cast extends TwoOperandInstruction { const [srcOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve([this.aOffset, this.dstOffset], memory); const a = memory.get(srcOffset); - - const casted = - this.inTag == TypeTag.FIELD ? new Field(a.toBigInt()) : TaggedMemory.integralFromTag(a.toBigInt(), this.inTag); + const casted = TaggedMemory.buildFromTagTruncating(a.toBigInt(), this.inTag); memory.set(dstOffset, casted); diff --git a/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts b/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts index cad761b80cc..099357c888d 100644 --- a/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts +++ b/yarn-project/simulator/src/avm/serialization/bytecode_serialization.ts @@ -113,7 +113,12 @@ const INSTRUCTION_SET = () => [JumpI.opcode, Instruction.deserialize.bind(JumpI)], [InternalCall.opcode, Instruction.deserialize.bind(InternalCall)], [InternalReturn.opcode, Instruction.deserialize.bind(InternalReturn)], - [Set.opcode, Set.deserialize.bind(Set)], + [Opcode.SET_8, Set.as(Set.wireFormat8).deserialize], + [Opcode.SET_16, Set.as(Set.wireFormat16).deserialize], + [Opcode.SET_32, Set.as(Set.wireFormat32).deserialize], + [Opcode.SET_64, Set.as(Set.wireFormat64).deserialize], + [Opcode.SET_128, Set.as(Set.wireFormat128).deserialize], + [Opcode.SET_FF, Set.as(Set.wireFormatFF).deserialize], [Opcode.MOV_8, Mov.as(Mov.wireFormat8).deserialize], [Opcode.MOV_16, Mov.as(Mov.wireFormat16).deserialize], [CMov.opcode, Instruction.deserialize.bind(CMov)], diff --git a/yarn-project/simulator/src/avm/serialization/instruction_serialization.ts b/yarn-project/simulator/src/avm/serialization/instruction_serialization.ts index 436d4893a2a..cdc1b463bbf 100644 --- a/yarn-project/simulator/src/avm/serialization/instruction_serialization.ts +++ b/yarn-project/simulator/src/avm/serialization/instruction_serialization.ts @@ -45,7 +45,12 @@ export enum Opcode { INTERNALCALL, INTERNALRETURN, // Memory - SET, + SET_8, + SET_16, + SET_32, + SET_64, + SET_128, + SET_FF, MOV_8, MOV_16, CMOV, @@ -93,6 +98,7 @@ export enum OperandType { UINT32, UINT64, UINT128, + FF, } type OperandNativeType = number | bigint; @@ -105,8 +111,27 @@ const OPERAND_SPEC = new Map OperandNativeType, Oper [OperandType.UINT32, [4, Buffer.prototype.readUint32BE, Buffer.prototype.writeUint32BE]], [OperandType.UINT64, [8, Buffer.prototype.readBigInt64BE, Buffer.prototype.writeBigInt64BE]], [OperandType.UINT128, [16, readBigInt128BE, writeBigInt128BE]], + [OperandType.FF, [32, readBigInt254BE, writeBigInt254BE]], ]); +function readBigInt254BE(this: Buffer): bigint { + const totalBytes = 32; + let ret: bigint = 0n; + for (let i = 0; i < totalBytes; ++i) { + ret <<= 8n; + ret |= BigInt(this.readUint8(i)); + } + return ret; +} + +function writeBigInt254BE(this: Buffer, value: bigint): void { + const totalBytes = 32; + for (let offset = totalBytes - 1; offset >= 0; --offset) { + this.writeUint8(Number(value & 0xffn), offset); + value >>= 8n; + } +} + function readBigInt128BE(this: Buffer): bigint { const totalBytes = 16; let ret: bigint = 0n;