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
16
17namespace bb::avm2::simulation {
18
20{
21 // We'll be filling in the event as we progress.
24 // We initialize all the phases with the original operands.
25 // This is expected for non-address (i.e., immediate) operands.
26 // For address operands, we'll update them as we go.
27 for (const auto& operand : instruction.operands) {
28 event.resolution_info.push_back({
29 .after_relative = operand,
30 .resolved_operand = operand,
31 });
32 }
33
34 // Note: it's fine to query instruction info in here since it does not trigger events.
35 // Also, if addressing is being resolved, we can assume that instruction fetching succeeded.
36 ExecutionOpCode exec_opcode = instruction_info_db.get(instruction.opcode).exec_opcode;
37 const ExecInstructionSpec& spec = instruction_info_db.get(exec_opcode);
38
39 // This represents either: (1) wrong info in the spec, or (2) a wrong witgen deserialization.
40 // Therefore, it is not an error the circuit should be able to prove.
41 assert(spec.num_addresses <= instruction.operands.size());
42
43 // We will read the base address only if we have any relative operands.
44 std::optional<MemoryValue> base_address;
45
46 // We process each address separately.
47 // Even if one fails, we continue processing the other ones.
48 // This is to simplify error handling in the circuit.
49 for (size_t i = 0; i < spec.num_addresses; ++i) {
50 auto& resolution_info = event.resolution_info[i];
51 try {
52 // Simulation and the circuit assume that the operands are valid addresses.
53 // This should be guaranteed by instruction fetching and the wire format.
54 assert(FF(static_cast<uint32_t>(instruction.operands[i].as_ff())) == instruction.operands[i].as_ff());
55
56 // Guarantees by this point:
57 // - original operand is a valid address IF interpreted as a MemoryAddress.
58
59 // Then, we process relative addressing for all the addresses.
60 // That is, if relative addressing is used, after_relative[i] = base_address + operands[i].
61 // We first store the operands as is, and then we'll update them if they are relative.
62 resolution_info.after_relative = instruction.operands[i]; // default value if not relative.
63 if (is_operand_relative(instruction.indirect, i)) {
64 // Load the base address if we haven't already.
65 if (!base_address) {
66 base_address = memory.get(0);
67 event.base_address = *base_address;
68 }
69 // This does not produce events. We are expected to check the tag to be UINT32.
70 if (!memory.is_valid_address(*base_address)) {
72 }
73
74 // We extend the address to FF to avoid overflows.
75 FF offset = resolution_info.after_relative;
76 offset += *base_address;
77 // We store the offset as an FF operand. If the circuit needs to prove overflow, it will
78 // need the full value.
79 resolution_info.after_relative = Operand::from<FF>(offset);
81 // If this happens, it means that the relative computation overflowed. However both the base and
82 // operand addresses by themselves were valid.
84 }
85 }
86 // Now that we are sure that the offset is valid, we can update the value to be of the right type.
87 resolution_info.after_relative =
88 Operand::from(static_cast<MemoryAddress>(resolution_info.after_relative.as_ff()));
89
90 // Guarantees by this point:
91 // - original operand is a valid address IF interpreted as MemoryAddress.
92 // - after_relative is a valid address.
93
94 // Then indirection.
95 // That is, if indirection is used, resolved_operands[i] = memory[after_relative[i]].
96 // We first store the after_relative values as is, and then we'll update them if they are indirect.
97 resolution_info.resolved_operand = resolution_info.after_relative;
98 if (is_operand_indirect(instruction.indirect, i)) {
99 resolution_info.resolved_operand = memory.get(resolution_info.after_relative.as<MemoryAddress>());
100 if (!memory.is_valid_address(resolution_info.resolved_operand)) {
102 }
103 }
104
105 // Guarantees by this point:
106 // - original operand is a valid address IF interpreted as MemoryAddress.
107 // - after_relative is a valid address.
108 // - resolved_operand is a valid address.
109 } catch (const AddressingEventError& e) {
110 resolution_info.error = e;
111 }
112 }
113
115 // If any entry in resolution_info has an error set, throw.
116 if (std::any_of(event.resolution_info.begin(), event.resolution_info.end(), [](const auto& info) {
117 return info.error.has_value();
118 })) {
119 // Signal the error to the caller.
120 // On purpose we don't give any more information than "Error resolving operands."
121 throw AddressingException();
122 }
123
124 // Collect resolved operands and return them.
125 std::vector<Operand> resolved_operands;
126 resolved_operands.reserve(event.resolution_info.size());
127 for (const auto& info : event.resolution_info) {
128 resolved_operands.push_back(info.resolved_operand);
129 }
130 return resolved_operands;
131}
132
134{
135 // Precondition: address should fit in 33 bits.
136 uint128_t address_u128 = uint128_t(address);
137 assert(address_u128 <= 0x1FFFFFFFF);
138 // This leaks circuit information.
139 // See https://hackmd.io/moq6viBpRJeLpWrHAogCZw?view#Comparison-between-range-constrained-numbers.
140 bool address_lt_32_bits = (static_cast<uint32_t>(address_u128) == address_u128);
141 uint128_t two_to_32 = uint128_t(1) << 32;
142 uint128_t result = address_lt_32_bits ? two_to_32 - address_u128 - 1 : address_u128 - two_to_32;
143 range_check.assert_range(result, 32);
144 return address_lt_32_bits;
145}
146
147} // namespace bb::avm2::simulation
static TaggedValue from(T value)
bool is_valid_address(const FF &address)
EventEmitterInterface< AddressingEvent > & events
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:70
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
unsigned __int128 uint128_t
Definition serialize.hpp:44