Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
addressing.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstdint>
5#include <vector>
6
17
18namespace bb::avm2::simulation {
19
21{
22 BB_BENCH_NAME("Addressing::resolve");
23 // We'll be filling in the event as we progress.
26 // We initialize all the phases with the original operands.
27 // This is expected for non-address (i.e., immediate) operands.
28 // For address operands, we'll update them as we go.
29 for (const auto& operand : instruction.operands) {
30 event.resolution_info.push_back({
31 .after_relative = operand,
32 .resolved_operand = operand,
33 });
34 }
35
36 // Note: it's fine to query instruction info in here since it does not trigger events.
37 // Also, if addressing is being resolved, we can assume that instruction fetching succeeded.
38 ExecutionOpCode exec_opcode = instruction_info_db.get(instruction.opcode).exec_opcode;
39 const ExecInstructionSpec& spec = instruction_info_db.get(exec_opcode);
40
41 // This represents either: (1) wrong info in the spec, or (2) a wrong witgen deserialization.
42 // Therefore, it is not an error the circuit should be able to prove.
43 assert(spec.num_addresses <= instruction.operands.size());
44
45 // We will read the base address only if we have any relative operands.
46 std::optional<MemoryValue> base_address;
47
48 // We process each address separately.
49 // Even if one fails, we continue processing the other ones.
50 // This is to simplify error handling in the circuit.
51 for (size_t i = 0; i < spec.num_addresses; ++i) {
52 auto& resolution_info = event.resolution_info[i];
53 try {
54 // Simulation and the circuit assume that the operands are valid addresses.
55 // This should be guaranteed by instruction fetching and the wire format.
56 assert(FF(static_cast<uint32_t>(instruction.operands[i].as_ff())) == instruction.operands[i].as_ff());
57
58 // Guarantees by this point:
59 // - original operand is a valid address IF interpreted as a MemoryAddress.
60
61 // Then, we process relative addressing for all the addresses.
62 // That is, if relative addressing is used, after_relative[i] = base_address + operands[i].
63 // We first store the operands as is, and then we'll update them if they are relative.
64 resolution_info.after_relative = instruction.operands[i]; // default value if not relative.
65 if (is_operand_relative(instruction.indirect, i)) {
66 // Load the base address if we haven't already.
67 if (!base_address) {
68 base_address = memory.get(0);
69 event.base_address = *base_address;
70 }
71 // This does not produce events. We are expected to check the tag to be UINT32.
72 if (!memory.is_valid_address(*base_address)) {
74 }
75
76 // We extend the address to FF to avoid overflows.
77 FF offset = resolution_info.after_relative;
78 // Note: Since we know that the offset and the base address are valid, the addition fits in 33 bits.
79 offset += *base_address;
80 // We store the offset as an FF operand. If the circuit needs to prove overflow, it will
81 // need the full value.
82 resolution_info.after_relative = Operand::from<FF>(offset);
84 // If this happens, it means that the relative computation overflowed. However both the base and
85 // operand addresses by themselves were valid.
87 }
88 }
89 // Now that we are sure that the offset is valid, we can update the value to be of the right type.
90 resolution_info.after_relative =
91 Operand::from(static_cast<MemoryAddress>(resolution_info.after_relative.as_ff()));
92
93 // Guarantees by this point:
94 // - original operand is a valid address IF interpreted as MemoryAddress.
95 // - after_relative is a valid address.
96
97 // Then indirection.
98 // That is, if indirection is used, resolved_operands[i] = memory[after_relative[i]].
99 // We first store the after_relative values as is, and then we'll update them if they are indirect.
100 resolution_info.resolved_operand = resolution_info.after_relative;
101 if (is_operand_indirect(instruction.indirect, i)) {
102 resolution_info.resolved_operand = memory.get(resolution_info.after_relative.as<MemoryAddress>());
103 if (!memory.is_valid_address(resolution_info.resolved_operand)) {
105 }
106 }
107
108 // Guarantees by this point:
109 // - original operand is a valid address IF interpreted as MemoryAddress.
110 // - after_relative is a valid address.
111 // - resolved_operand is a valid address.
112 } catch (const AddressingEventError& e) {
113 resolution_info.error = e;
114 }
115 }
116
118 // If any entry in resolution_info has an error set, throw.
119 if (std::any_of(event.resolution_info.begin(), event.resolution_info.end(), [](const auto& info) {
120 return info.error.has_value();
121 })) {
122 // Signal the error to the caller.
123 // On purpose we don't give any more information than "Error resolving operands."
124 throw AddressingException();
125 }
126
127 // Collect resolved operands and return them.
128 std::vector<Operand> resolved_operands;
129 resolved_operands.reserve(event.resolution_info.size());
130 for (const auto& info : event.resolution_info) {
131 resolved_operands.push_back(info.resolved_operand);
132 }
133 return resolved_operands;
134}
135
136// Precondition: address should fit in 64 bits.
137// The call in `resolve` guarantees this.
139{
140 uint64_t address_u64 = static_cast<uint64_t>(address);
141 assert(address == FF(address_u64));
142
143 return gt.gt(address_u64, AVM_HIGHEST_MEM_ADDRESS);
144}
145
146} // namespace bb::avm2::simulation
#define AVM_HIGHEST_MEM_ADDRESS
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:218
static TaggedValue from(T value)
EventEmitterInterface< AddressingEvent > & events
bool is_address_out_of_range(const FF &address)
std::vector< Operand > resolve(const Instruction &instruction, MemoryInterface &memory) override
const InstructionInfoDBInterface & instruction_info_db
virtual const ExecInstructionSpec & get(ExecutionOpCode opcode) const =0
void info(Args... args)
Definition log.hpp:74
ssize_t offset
Definition engine.cpp:36
Instruction instruction
bool is_operand_relative(uint16_t indirect_flag, size_t operand_index)
Definition addressing.hpp:8
bool is_operand_indirect(uint16_t indirect_flag, size_t operand_index)
uint32_t MemoryAddress
AvmFlavorSettings::FF FF
Definition field.hpp:10
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
simulation::PublicDataTreeReadWriteEvent event