Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bitwise_trace.cpp
Go to the documentation of this file.
2
3#include <cstdint>
4
7
8namespace bb::avm2::tracegen {
9
11 TraceContainer& trace)
12{
13 using C = Column;
14
15 // Precomputed inverses ranges from 0 to 15. (for columns bitwise_ctr_min_one_inv, tag inverses)
16 // The max ctr value is 16 since the largest tag is U128 which has 16 bytes, we need inverses up to 15 for the ctr
17 // since we use ctr-1 in the bitwise_ctr_min_one_inv column.
18 static constexpr std::array<FF, 16> precomputed_inverses = [] {
19 std::array<FF, 16> inverses{ 0, 1 };
20 // skip 0 since it's not invertible, inverse(1) = 1 so we can skip it as well
21 for (size_t i = 2; i <= 15; i++) {
22 inverses[i] = FF(i).invert();
23 }
24 return inverses;
25 }();
26
27 // Lambda to map the column selector of the op_id.
28 const auto get_op_id_column_selector = [](BitwiseOperation op_id) {
29 switch (op_id) {
31 return C::bitwise_sel_and;
33 return C::bitwise_sel_or;
35 return C::bitwise_sel_xor;
36 default:
37 __builtin_unreachable();
38 }
39 };
40
41 uint32_t row = 1;
42 for (const auto& event : events) {
43 auto tag = event.a.get_tag();
44
45 // We start with full inputs and output and we shift
46 // them byte-per-byte to the right.
47 // In the event of an error, the truncation caused by this casting does not matter since we will not use these
48 // in the error case (see below for details).
49 uint128_t input_a = static_cast<uint128_t>(event.a.as_ff());
50 uint128_t input_b = static_cast<uint128_t>(event.b.as_ff());
51 uint128_t output_c = event.res;
52
53 // Error Handling, check tag a is FF or tag a != tag b
54 bool is_tag_ff = event.a.get_tag() == MemoryTag::FF;
55 bool is_tag_mismatch = event.a.get_tag() != event.b.get_tag();
56 // For tag_a != FF
57 // Rely below on MemoryTag::FF being 0
58 static_assert(static_cast<uint8_t>(MemoryTag::FF) == 0);
59 const uint8_t tag_a_u8 = static_cast<uint8_t>(event.a.get_tag());
60 const uint8_t tag_b_u8 = static_cast<uint8_t>(event.b.get_tag());
61
62 FF tag_a_inv = precomputed_inverses[tag_a_u8];
63 // For tag_a != tag_b
64 FF tag_ab_diff_inv = 0;
65 if (tag_a_u8 > tag_b_u8) {
66 tag_ab_diff_inv = precomputed_inverses[tag_a_u8 - tag_b_u8];
67 } else {
68 // (-x)^(-1) = -x^(-1) for a field element x.
69 tag_ab_diff_inv = -precomputed_inverses[tag_b_u8 - tag_a_u8];
70 }
71
72 if (is_tag_ff || is_tag_mismatch) {
73 // There is an error, fill in values that are still needed to satisfy constraints despite the error.
74 // Error rows are single-row blocks: start=1, end=1, sel=1, err=1, sel_compute=0.
75 trace.set(row,
76 { {
77 { C::bitwise_sel, 1 },
78 { C::bitwise_op_id, static_cast<uint8_t>(event.operation) },
79 { C::bitwise_start, 1 },
80 { C::bitwise_sel_get_ctr, 0 },
81 { C::bitwise_end, 1 }, // Error triggers end
82 { C::bitwise_acc_ia, event.a.as_ff() },
83 { C::bitwise_acc_ib, event.b.as_ff() },
84 { C::bitwise_acc_ic, output_c },
85 { C::bitwise_ia_byte, event.a.as_ff() },
86 { C::bitwise_ib_byte, event.b.as_ff() },
87 { C::bitwise_ic_byte, output_c },
88 { C::bitwise_tag_a, tag_a_u8 },
89 { C::bitwise_tag_b, tag_b_u8 },
90 { C::bitwise_tag_c, static_cast<uint8_t>(MemoryTag::FF) }, // Since error
91 // Err Flags
92 { C::bitwise_sel_tag_ff_err, is_tag_ff ? 1 : 0 },
93 { C::bitwise_sel_tag_mismatch_err, is_tag_mismatch ? 1 : 0 },
94 { C::bitwise_err, 1 },
95 // Err Helpers
96 { C::bitwise_tag_a_inv, tag_a_inv },
97 { C::bitwise_tag_ab_diff_inv, tag_ab_diff_inv },
98
99 } });
100 row++;
101 continue; // Skip the rest of the processing for this event
102 }
103
104 // At this point we know that we will not error, so we can proceed with the bitwise operation.
105
106 // Note that for tag U1, we take only one bit. This is correctly
107 // captured below since input_a/b and output_c are each a single bit
108 // and the byte mask correctly extracts it.
109 constexpr uint128_t mask_low_byte = (1 << 8) - 1;
110 const auto start_ctr = get_tag_bytes(tag);
111
112 for (int ctr = start_ctr; ctr > 0; ctr--) {
113 bool is_start = (ctr == start_ctr);
114 uint8_t ia_byte = input_a & mask_low_byte;
115 uint8_t ib_byte = input_b & mask_low_byte;
116 trace.set(row,
117 { { { C::bitwise_sel, 1 },
118 { C::bitwise_sel_compute, 1 },
119 { get_op_id_column_selector(event.operation), 1 },
120 { C::bitwise_op_id, static_cast<uint8_t>(event.operation) },
121 // It is fine to use the truncated input_a/b here instead of event.a/b because if event.a/b
122 // were FF values we would have taken the error branch above.
123 { C::bitwise_acc_ia, input_a },
124 { C::bitwise_acc_ib, input_b },
125 { C::bitwise_acc_ic, output_c },
126 { C::bitwise_ia_byte, ia_byte },
127 { C::bitwise_ib_byte, ib_byte },
128 { C::bitwise_ic_byte, output_c & mask_low_byte },
129 { C::bitwise_output_and, ia_byte & ib_byte },
130 { C::bitwise_output_or, ia_byte | ib_byte },
131 { C::bitwise_output_xor, ia_byte ^ ib_byte },
132 { C::bitwise_tag_a, is_start ? tag_a_u8 : 0 },
133 { C::bitwise_tag_b, is_start ? tag_b_u8 : 0 },
134 { C::bitwise_tag_c, is_start ? tag_a_u8 : 0 }, // same as tag_a
135 { C::bitwise_ctr, ctr },
136 { C::bitwise_ctr_min_one_inv, precomputed_inverses[static_cast<uint8_t>(ctr - 1)] },
137 { C::bitwise_end, ctr == 1 ? 1 : 0 },
138 { C::bitwise_start, is_start ? 1 : 0 },
139 { C::bitwise_sel_get_ctr, is_start ? 1 : 0 }, // Same as bitwise_start but in non-error case
140 // Err Helpers, in the happy path we still need to prove we would not have errored
141 { C::bitwise_tag_a_inv, is_start ? tag_a_inv : 0 },
142 { C::bitwise_tag_ab_diff_inv, is_start ? tag_ab_diff_inv : 0 } } });
143
144 input_a >>= 8;
145 input_b >>= 8;
146 output_c >>= 8;
147 row++;
148 }
149 }
150}
151
155 .add<lookup_bitwise_integral_tag_length_settings, InteractionType::LookupIntoIndexedByRow>();
156
157} // namespace bb::avm2::tracegen
void process(const simulation::EventEmitterInterface< simulation::BitwiseEvent >::Container &events, TraceContainer &trace)
Populate the bitwise trace columns from simulation events.
static const InteractionDefinition interactions
Interaction definitions for outbound lookups (BYTE_OPERATIONS, INTEGRAL_TAG_LENGTH).
InteractionDefinition & add(auto &&... args)
TestTraceContainer trace
AvmFlavorSettings::FF FF
Definition field.hpp:10
lookup_settings< lookup_bitwise_byte_operations_settings_ > lookup_bitwise_byte_operations_settings
uint8_t get_tag_bytes(ValueTag tag)
simulation::PublicDataTreeReadWriteEvent event
unsigned __int128 uint128_t
Definition serialize.hpp:45