Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: indirect mem flag deserialisation #4877

Merged
merged 2 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading