Skip to content

Commit

Permalink
feat: indirect mem flag deserialisation (#4877)
Browse files Browse the repository at this point in the history
Brings the cpp avm code in sync with the instruction layout +
serialisation format of the ts simulator by adding the Indirect flag.

Note: The flag isnt yet used in the witgen/prover or in generating the
trace
  • Loading branch information
IlyasRidhuan authored Mar 5, 2024
1 parent ff4110e commit 4c6820f
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ namespace bb::avm_trace {
namespace {

const std::vector<OperandType> three_operand_format = {
OperandType::TAG,
OperandType::UINT32,
OperandType::UINT32,
OperandType::UINT32,
OperandType::INDIRECT, OperandType::TAG, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32,
};

// Contrary to TS, the format does not contain the opcode byte which prefixes any instruction.
Expand All @@ -32,22 +29,22 @@ const std::unordered_map<OpCode, std::vector<OperandType>> OPCODE_WIRE_FORMAT =
// Compute - Comparators
{ OpCode::EQ, three_operand_format },
// Compute - Bitwise
{ OpCode::NOT, { OperandType::TAG, OperandType::UINT32, OperandType::UINT32 } },
{ OpCode::NOT, { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT32, OperandType::UINT32 } },
// Execution Environment - Calldata
{ OpCode::CALLDATACOPY, { OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } },
{ OpCode::CALLDATACOPY, { OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32, OperandType::UINT32 } },
// Machine State - Internal Control Flow
{ OpCode::JUMP, { OperandType::UINT32 } },
{ OpCode::INTERNALCALL, { OperandType::UINT32 } },
{ OpCode::INTERNALRETURN, {} },
// Machine State - Memory
// OpCode::SET is handled differently
// Control Flow - Contract Calls
{ OpCode::RETURN, { OperandType::UINT32, OperandType::UINT32 } },
{ OpCode::RETURN, { OperandType::INDIRECT, OperandType::UINT32, OperandType::UINT32 } },
};

const std::unordered_map<OperandType, size_t> OPERAND_TYPE_SIZE = {
{ OperandType::TAG, 1 }, { OperandType::UINT8, 1 }, { OperandType::UINT16, 2 },
{ OperandType::UINT32, 4 }, { OperandType::UINT64, 8 }, { OperandType::UINT128, 16 },
{ OperandType::INDIRECT, 1 }, { OperandType::TAG, 1 }, { OperandType::UINT8, 1 }, { OperandType::UINT16, 2 },
{ OperandType::UINT32, 4 }, { OperandType::UINT64, 8 }, { OperandType::UINT128, 16 },
};

} // Anonymous namespace
Expand Down Expand Up @@ -80,7 +77,11 @@ std::vector<Instruction> Deserialization::parse(std::vector<uint8_t> const& byte
std::vector<OperandType> inst_format;

if (opcode == OpCode::SET) {
if (pos == length) {
// 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));
}

Expand All @@ -89,29 +90,30 @@ std::vector<Instruction> Deserialization::parse(std::vector<uint8_t> const& byte
static_cast<uint8_t>(AvmMemoryTag::U32),
static_cast<uint8_t>(AvmMemoryTag::U64),
static_cast<uint8_t>(AvmMemoryTag::U128) };
uint8_t set_tag_u8 = bytecode.at(pos);
// 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) +
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<AvmMemoryTag>(set_tag_u8);
switch (in_tag) {
case AvmMemoryTag::U8:
inst_format = { OperandType::TAG, OperandType::UINT8, OperandType::UINT32 };
inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT8, OperandType::UINT32 };
break;
case AvmMemoryTag::U16:
inst_format = { OperandType::TAG, OperandType::UINT16, OperandType::UINT32 };
inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT16, OperandType::UINT32 };
break;
case AvmMemoryTag::U32:
inst_format = { OperandType::TAG, OperandType::UINT32, OperandType::UINT32 };
inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT32, OperandType::UINT32 };
break;
case AvmMemoryTag::U64:
inst_format = { OperandType::TAG, OperandType::UINT64, OperandType::UINT32 };
inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT64, OperandType::UINT32 };
break;
case AvmMemoryTag::U128:
inst_format = { OperandType::TAG, OperandType::UINT128, OperandType::UINT32 };
inst_format = { OperandType::INDIRECT, OperandType::TAG, OperandType::UINT128, OperandType::UINT32 };
break;
default: // This branch is guarded above.
std::cerr << "This code branch must have been guarded by the tag validation. \n";
Expand All @@ -130,6 +132,10 @@ std::vector<Instruction> Deserialization::parse(std::vector<uint8_t> const& byte
}

switch (opType) {
case OperandType::INDIRECT: {
operands.emplace_back(bytecode.at(pos));
break;
}
case OperandType::TAG: {
uint8_t tag_u8 = bytecode.at(pos);
if (tag_u8 == static_cast<uint8_t>(AvmMemoryTag::U0) || tag_u8 > MAX_MEM_TAG) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ namespace bb::avm_trace {
// Possible types for an instruction's operand in its wire format. (Keep in sync with TS code.
// See avm/serialization/instruction_serialization.ts).
// Note that the TAG enum value is not supported in TS and is parsed as UINT8.
enum class OperandType : uint8_t { TAG, UINT8, UINT16, UINT32, UINT64, UINT128 };
// 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 };

class Deserialization {
public:
Expand All @@ -24,4 +25,4 @@ class Deserialization {
static std::vector<Instruction> parse(std::vector<uint8_t> const& bytecode);
};

} // namespace bb::avm_trace
} // namespace bb::avm_trace
54 changes: 29 additions & 25 deletions barretenberg/cpp/src/barretenberg/vm/avm_trace/avm_execution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,44 +58,46 @@ std::vector<Row> Execution::gen_trace(std::vector<Instruction> const& instructio
while ((pc = trace_builder.getPc()) < inst_size) {
auto inst = instructions.at(pc);

// TODO: We do not yet support the indirect flag. Therefore we do not extract
// inst.operands(0) (i.e. the indirect flag) when processiing the instructions.
switch (inst.op_code) {
// Compute
// Compute - Arithmetic
case OpCode::ADD:
trace_builder.op_add(std::get<uint32_t>(inst.operands.at(1)),
std::get<uint32_t>(inst.operands.at(2)),
trace_builder.op_add(std::get<uint32_t>(inst.operands.at(2)),
std::get<uint32_t>(inst.operands.at(3)),
std::get<AvmMemoryTag>(inst.operands.at(0)));
std::get<uint32_t>(inst.operands.at(4)),
std::get<AvmMemoryTag>(inst.operands.at(1)));
break;
case OpCode::SUB:
trace_builder.op_sub(std::get<uint32_t>(inst.operands.at(1)),
std::get<uint32_t>(inst.operands.at(2)),
trace_builder.op_sub(std::get<uint32_t>(inst.operands.at(2)),
std::get<uint32_t>(inst.operands.at(3)),
std::get<AvmMemoryTag>(inst.operands.at(0)));
std::get<uint32_t>(inst.operands.at(4)),
std::get<AvmMemoryTag>(inst.operands.at(1)));
break;
case OpCode::MUL:
trace_builder.op_mul(std::get<uint32_t>(inst.operands.at(1)),
std::get<uint32_t>(inst.operands.at(2)),
trace_builder.op_mul(std::get<uint32_t>(inst.operands.at(2)),
std::get<uint32_t>(inst.operands.at(3)),
std::get<AvmMemoryTag>(inst.operands.at(0)));
std::get<uint32_t>(inst.operands.at(4)),
std::get<AvmMemoryTag>(inst.operands.at(1)));
break;
case OpCode::DIV:
trace_builder.op_div(std::get<uint32_t>(inst.operands.at(1)),
std::get<uint32_t>(inst.operands.at(2)),
trace_builder.op_div(std::get<uint32_t>(inst.operands.at(2)),
std::get<uint32_t>(inst.operands.at(3)),
std::get<AvmMemoryTag>(inst.operands.at(0)));
std::get<uint32_t>(inst.operands.at(4)),
std::get<AvmMemoryTag>(inst.operands.at(1)));
break;
// Compute - Bitwise
case OpCode::NOT:
trace_builder.op_not(std::get<uint32_t>(inst.operands.at(1)),
std::get<uint32_t>(inst.operands.at(3)),
std::get<AvmMemoryTag>(inst.operands.at(0)));
trace_builder.op_not(std::get<uint32_t>(inst.operands.at(2)),
std::get<uint32_t>(inst.operands.at(4)),
std::get<AvmMemoryTag>(inst.operands.at(1)));
break;
// Execution Environment - Calldata
case OpCode::CALLDATACOPY:
trace_builder.calldata_copy(std::get<uint32_t>(inst.operands.at(0)),
std::get<uint32_t>(inst.operands.at(1)),
trace_builder.calldata_copy(std::get<uint32_t>(inst.operands.at(1)),
std::get<uint32_t>(inst.operands.at(2)),
std::get<uint32_t>(inst.operands.at(3)),
calldata);
break;
// Machine State - Internal Control Flow
Expand All @@ -112,24 +114,25 @@ std::vector<Row> Execution::gen_trace(std::vector<Instruction> const& instructio
case OpCode::SET: {
uint32_t dst_offset = 0;
uint128_t val = 0;
AvmMemoryTag in_tag = std::get<AvmMemoryTag>(inst.operands.at(0));
dst_offset = std::get<uint32_t>(inst.operands.at(2));
// Skip the indirect flag at index 0;
AvmMemoryTag in_tag = std::get<AvmMemoryTag>(inst.operands.at(1));
dst_offset = std::get<uint32_t>(inst.operands.at(3));

switch (in_tag) {
case AvmMemoryTag::U8:
val = std::get<uint8_t>(inst.operands.at(1));
val = std::get<uint8_t>(inst.operands.at(2));
break;
case AvmMemoryTag::U16:
val = std::get<uint16_t>(inst.operands.at(1));
val = std::get<uint16_t>(inst.operands.at(2));
break;
case AvmMemoryTag::U32:
val = std::get<uint32_t>(inst.operands.at(1));
val = std::get<uint32_t>(inst.operands.at(2));
break;
case AvmMemoryTag::U64:
val = std::get<uint64_t>(inst.operands.at(1));
val = std::get<uint64_t>(inst.operands.at(2));
break;
case AvmMemoryTag::U128:
val = std::get<uint128_t>(inst.operands.at(1));
val = std::get<uint128_t>(inst.operands.at(2));
break;
default:
break;
Expand All @@ -140,7 +143,8 @@ std::vector<Row> Execution::gen_trace(std::vector<Instruction> const& instructio
}
// Control Flow - Contract Calls
case OpCode::RETURN:
trace_builder.return_op(std::get<uint32_t>(inst.operands.at(0)), std::get<uint32_t>(inst.operands.at(1)));
// Skip indirect at index 0
trace_builder.return_op(std::get<uint32_t>(inst.operands.at(1)), std::get<uint32_t>(inst.operands.at(2)));
break;
default:
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ class Instruction {
, operands(std::move(operands)){};
};

} // namespace bb::avm_trace
} // namespace bb::avm_trace
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,4 @@ class Bytecode {

std::string to_hex(OpCode opcode);

} // namespace bb::avm_trace
} // namespace bb::avm_trace
Loading

0 comments on commit 4c6820f

Please sign in to comment.