Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
execution_trace_block.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Luke, Raju], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
14#include <cstddef>
15
16#ifdef CHECK_CIRCUIT_STACKTRACES
17#include <backward.hpp>
18#endif
19
20namespace bb {
21
22#ifdef CHECK_CIRCUIT_STACKTRACES
23struct BbStackTrace : backward::StackTrace {
24 BbStackTrace() { load_here(32); }
25};
26struct StackTraces {
27 std::vector<BbStackTrace> stack_traces;
28 void populate() { stack_traces.emplace_back(); }
29 void print(size_t gate_idx) const { backward::Printer{}.print(stack_traces.at(gate_idx)); }
30 // Don't interfere with equality semantics of structs that include this in debug builds
31 bool operator==(const StackTraces& other) const
32 {
33 static_cast<void>(other);
34 return true;
35 }
36};
37#endif
38
46template <typename FF> class Selector {
47 public:
48 Selector() = default;
49 virtual ~Selector() = default;
50
51 // Delete copy/move to avoid object slicing and unintended behavior
52 Selector(const Selector&) = default;
53 Selector& operator=(const Selector&) = default;
54 Selector(Selector&&) = delete;
56
61 void emplace_back(const FF& value) { push_back(value); }
62
67 virtual void emplace_back(int value) = 0;
68
73 virtual void push_back(const FF& value) = 0;
74
79 virtual void resize(size_t new_size) = 0;
80
86 virtual const FF& operator[](size_t index) const = 0;
87
92 virtual const FF& back() const = 0;
93
97 virtual size_t size() const = 0;
98
102 virtual bool empty() const = 0;
103
109 virtual void set(size_t idx, int value) = 0;
110
116 virtual void set(size_t idx, const FF& value) = 0;
117
121 virtual void free_memory() {}
122};
123
130template <typename FF> class SlabVectorSelector : public Selector<FF> {
131 public:
133
134 void emplace_back(int i) override { data.emplace_back(i); }
135 void push_back(const FF& value) override { data.push_back(value); }
136 void set(size_t idx, int i) override { data[idx] = i; }
137 void set(size_t idx, const FF& value) override { data[idx] = value; }
138 void resize(size_t new_size) override { data.resize(new_size); }
139
140 bool operator==(const SlabVectorSelector& other) const { return data == other.data; }
141
142 const FF& operator[](size_t i) const override { return data[i]; }
143 const FF& back() const override { return data.back(); }
144
145 size_t size() const override { return data.size(); }
146 bool empty() const override { return data.empty(); }
147
148 void free_memory() override
149 {
150 data.clear();
151 data.shrink_to_fit();
152 }
153
154 private:
155 std::vector<FF> data;
156};
157
162enum class GateKind : uint8_t {
163 None = 0,
164 BusRead,
165 Lookup,
166 Arith,
168 Elliptic,
169 Memory,
170 Nnf,
172 Poseidon2Int, // Ultra-only
177};
178
184template <typename FF, size_t NUM_WIRES_> class ExecutionTraceBlock {
185 public:
186 static constexpr size_t NUM_WIRES = NUM_WIRES_;
187
189 using WireType = std::vector<uint32_t>;
190 using Wires = std::array<WireType, NUM_WIRES>;
191
193
198 {
199 gate_selectors.reserve(kinds.size());
200 for (GateKind k : kinds) {
201 gate_selectors.emplace_back(std::piecewise_construct, std::forward_as_tuple(k), std::forward_as_tuple());
202 }
203 }
204
208 ExecutionTraceBlock& operator=(ExecutionTraceBlock&&) noexcept = default;
210
211#ifdef CHECK_CIRCUIT_STACKTRACES
212 // If enabled, we keep slow stack traces to be able to correlate gates with code locations where they were added
213 StackTraces stack_traces;
214#endif
215#ifdef TRACY_HACK_GATES_AS_MEMORY
216 std::vector<size_t> allocated_gates;
217#endif
219 {
220#ifdef TRACY_HACK_GATES_AS_MEMORY
221 std::unique_lock<std::mutex> lock(GLOBAL_GATE_MUTEX);
222 GLOBAL_GATE++;
223 TRACY_GATE_ALLOC(GLOBAL_GATE);
224 allocated_gates.push_back(GLOBAL_GATE);
225#endif
226 }
227
228 Wires wires; // vectors of indices into a witness variables array
229 size_t cached_size_ = 0; // set by free_data() so size() works after freeing
230 bool data_freed_ = false; // true after free_data() has been called
231 uint32_t trace_offset_ = std::numeric_limits<uint32_t>::max(); // where this block starts in the trace
232
233 uint32_t trace_offset() const
234 {
235 BB_ASSERT(trace_offset_ != std::numeric_limits<uint32_t>::max());
236 return trace_offset_;
237 }
238
239 // The first trace row past this block's data (trace_offset + size).
240 size_t trace_end() const { return trace_offset() + size(); }
241
242 bool operator==(const ExecutionTraceBlock& other) const = default;
243
244 size_t size() const { return data_freed_ ? cached_size_ : std::get<0>(this->wires).size(); }
245
251 {
252 for (auto& [k, s] : gate_selectors) {
253 if (k == kind) {
254 return s;
255 }
256 }
257 throw_or_abort("ExecutionTraceBlock: block does not own this gate kind");
258 return gate_selectors[0].second; // unreachable
259 }
260
266 {
267 for (auto& [k, s] : gate_selectors) {
268 s.emplace_back(value);
269 }
270 }
271
277 {
278 for (auto& [k, s] : gate_selectors) {
279 s.emplace_back(k == kind ? value : FF{ 0 });
280 }
281 }
282
287 {
289 ptrs.reserve(non_gate_selectors.size() + gate_selectors.size());
290 for (auto& s : non_gate_selectors) {
291 ptrs.push_back(&s);
292 }
293 for (auto& [k, s] : gate_selectors) {
294 ptrs.push_back(&s);
295 }
296 return RefVector<Selector<FF>>(ptrs);
297 }
298
299#ifdef TRACY_HACK_GATES_AS_MEMORY
301 {
302 std::unique_lock<std::mutex> lock(GLOBAL_GATE_MUTEX);
303 for ([[maybe_unused]] size_t gate : allocated_gates) {
304 if (!FREED_GATES.contains(gate)) {
305 TRACY_GATE_FREE(gate);
306 FREED_GATES.insert(gate);
307 }
308 }
309 }
310#endif
311
317 {
319 data_freed_ = true;
320 for (auto& wire : wires) {
321 wire.clear();
322 wire.shrink_to_fit();
323 }
324 for (auto& sel : non_gate_selectors) {
325 sel.free_memory();
326 }
327 for (auto& [k, sel] : gate_selectors) {
328 sel.free_memory();
329 }
330 }
331
332 void populate_wires(const uint32_t& idx_1, const uint32_t& idx_2, const uint32_t& idx_3, const uint32_t& idx_4)
333 {
334#ifdef CHECK_CIRCUIT_STACKTRACES
335 this->stack_traces.populate();
336#endif
337 this->tracy_gate();
338 this->wires[0].emplace_back(idx_1);
339 this->wires[1].emplace_back(idx_2);
340 this->wires[2].emplace_back(idx_3);
341 this->wires[3].emplace_back(idx_4);
342 }
343
344 auto& w_l() { return std::get<0>(this->wires); };
345 auto& w_r() { return std::get<1>(this->wires); };
346 auto& w_o() { return std::get<2>(this->wires); };
347 auto& w_4() { return std::get<3>(this->wires); };
348
356
358
360};
361
366template <typename FF, size_t NUM_WIRES>
368{
369 for (const auto& [k, s] : block.gate_selectors) {
370 if (k == kind) {
371 return s[idx];
372 }
373 }
374 return FF{ 0 };
375}
376
377} // namespace bb
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
Basic structure for storing gate data in a builder. Holds wires, the 7 non-gate selectors present on ...
SlabVectorSelector< FF > & gate_selector_for(GateKind kind)
Reference to this block's selector for kind; aborts if the block does not own it. For cross-block rea...
std::vector< uint32_t > WireType
std::array< WireType, NUM_WIRES > Wires
ExecutionTraceBlock(ExecutionTraceBlock &&) noexcept=default
static constexpr size_t NUM_WIRES
std::vector< std::pair< GateKind, SlabVectorSelector< FF > > > gate_selectors
bool operator==(const ExecutionTraceBlock &other) const =default
void set_gate_selector(GateKind kind, const FF &value)
Append a row activating one gate kind. Writes value to the selector for kind and 0 to all other owned...
ExecutionTraceBlock & operator=(const ExecutionTraceBlock &)=default
RefVector< Selector< FF > > get_selectors()
All selectors of this block: 7 non-gate followed by the owned gate selectors.
void populate_wires(const uint32_t &idx_1, const uint32_t &idx_2, const uint32_t &idx_3, const uint32_t &idx_4)
void free_data()
Release wire and selector memory. Caches block size so size() still works.
void set_gate_selector(const FF &value)
Append a row writing value to every gate-selector this block owns. To activate one specific kind on a...
~ExecutionTraceBlock()=default
ExecutionTraceBlock(std::initializer_list< GateKind > kinds)
Construct a block that owns the listed gate kinds.
ExecutionTraceBlock(const ExecutionTraceBlock &)=default
std::array< SlabVectorSelector< FF >, 7 > non_gate_selectors
A template class for a reference vector. Behaves as if std::vector<T&> was possible.
Abstract interface for a generic selector.
virtual void resize(size_t new_size)=0
Resize the selector.
virtual size_t size() const =0
Get the number of elements.
virtual ~Selector()=default
virtual void emplace_back(int value)=0
Append an integer value to the selector.
virtual const FF & back() const =0
Get the last value in the selector.
Selector(Selector &&)=delete
virtual void set(size_t idx, int value)=0
Set the value at index using integer.
virtual bool empty() const =0
Check if the selector is empty.
virtual void push_back(const FF &value)=0
Push a field element to the selector.
Selector & operator=(const Selector &)=default
Selector & operator=(Selector &&)=delete
virtual void set(size_t idx, const FF &value)=0
Set the value at index using a field element.
virtual const FF & operator[](size_t index) const =0
Get value at specified index.
virtual void free_memory()
Release all memory held by this selector.
Selector()=default
void emplace_back(const FF &value)
Append a field element to the selector.
Selector(const Selector &)=default
Selector backed by a slab allocator vector.
void push_back(const FF &value) override
Push a field element to the selector.
void free_memory() override
Release all memory held by this selector.
void set(size_t idx, const FF &value) override
Set the value at index using a field element.
void resize(size_t new_size) override
Resize the selector.
size_t size() const override
Get the number of elements.
bool operator==(const SlabVectorSelector &other) const
const FF & operator[](size_t i) const override
Get value at specified index.
void emplace_back(int i) override
Append an integer value to the selector.
const FF & back() const override
Get the last value in the selector.
void set(size_t idx, int i) override
Set the value at index using integer.
bool empty() const override
Check if the selector is empty.
#define TRACY_GATE_ALLOC(t)
Definition mem.hpp:16
#define TRACY_GATE_FREE(t)
Definition mem.hpp:17
bool operator==(schnorr_signature const &lhs, schnorr_signature const &rhs)
Definition schnorr.hpp:38
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
FF read_gate_selector(const ExecutionTraceBlock< FF, NUM_WIRES > &block, GateKind kind, size_t idx)
Gate-selector value at (block, idx) for kind, returning zero if block does not own this kind....
GateKind
Tag identifying which gate selector a block owns. Used by cross-block readers to decide whether (bloc...
@ Poseidon2QuadIntTerminal
@ Poseidon2TransitionEntry
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
void throw_or_abort(std::string const &err)