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 <array>
4#include <cstddef>
5#include <cstdint>
6
11
12namespace bb::avm2::tracegen {
13namespace {
14
15using C = Column;
16
17// Per-limb column handles, indexed by byte position 0..15.
18constexpr std::array<C, 16> IA_BYTE = { C::bitwise_ia_byte_0_, C::bitwise_ia_byte_1_, C::bitwise_ia_byte_2_,
19 C::bitwise_ia_byte_3_, C::bitwise_ia_byte_4_, C::bitwise_ia_byte_5_,
20 C::bitwise_ia_byte_6_, C::bitwise_ia_byte_7_, C::bitwise_ia_byte_8_,
21 C::bitwise_ia_byte_9_, C::bitwise_ia_byte_10_, C::bitwise_ia_byte_11_,
22 C::bitwise_ia_byte_12_, C::bitwise_ia_byte_13_, C::bitwise_ia_byte_14_,
23 C::bitwise_ia_byte_15_ };
24constexpr std::array<C, 16> IB_BYTE = { C::bitwise_ib_byte_0_, C::bitwise_ib_byte_1_, C::bitwise_ib_byte_2_,
25 C::bitwise_ib_byte_3_, C::bitwise_ib_byte_4_, C::bitwise_ib_byte_5_,
26 C::bitwise_ib_byte_6_, C::bitwise_ib_byte_7_, C::bitwise_ib_byte_8_,
27 C::bitwise_ib_byte_9_, C::bitwise_ib_byte_10_, C::bitwise_ib_byte_11_,
28 C::bitwise_ib_byte_12_, C::bitwise_ib_byte_13_, C::bitwise_ib_byte_14_,
29 C::bitwise_ib_byte_15_ };
30constexpr std::array<C, 16> OUTPUT_AND = {
31 C::bitwise_output_and_0_, C::bitwise_output_and_1_, C::bitwise_output_and_2_, C::bitwise_output_and_3_,
32 C::bitwise_output_and_4_, C::bitwise_output_and_5_, C::bitwise_output_and_6_, C::bitwise_output_and_7_,
33 C::bitwise_output_and_8_, C::bitwise_output_and_9_, C::bitwise_output_and_10_, C::bitwise_output_and_11_,
34 C::bitwise_output_and_12_, C::bitwise_output_and_13_, C::bitwise_output_and_14_, C::bitwise_output_and_15_
35};
36constexpr std::array<C, 16> OUTPUT_OR = { C::bitwise_output_or_0_, C::bitwise_output_or_1_, C::bitwise_output_or_2_,
37 C::bitwise_output_or_3_, C::bitwise_output_or_4_, C::bitwise_output_or_5_,
38 C::bitwise_output_or_6_, C::bitwise_output_or_7_, C::bitwise_output_or_8_,
39 C::bitwise_output_or_9_, C::bitwise_output_or_10_, C::bitwise_output_or_11_,
40 C::bitwise_output_or_12_, C::bitwise_output_or_13_, C::bitwise_output_or_14_,
41 C::bitwise_output_or_15_ };
42constexpr std::array<C, 16> OUTPUT_XOR = {
43 C::bitwise_output_xor_0_, C::bitwise_output_xor_1_, C::bitwise_output_xor_2_, C::bitwise_output_xor_3_,
44 C::bitwise_output_xor_4_, C::bitwise_output_xor_5_, C::bitwise_output_xor_6_, C::bitwise_output_xor_7_,
45 C::bitwise_output_xor_8_, C::bitwise_output_xor_9_, C::bitwise_output_xor_10_, C::bitwise_output_xor_11_,
46 C::bitwise_output_xor_12_, C::bitwise_output_xor_13_, C::bitwise_output_xor_14_, C::bitwise_output_xor_15_
47};
48
49} // namespace
50
52 TraceContainer& trace)
53{
54 // Inverses of small integers, used for the tag error-check helpers. Tag enum values and their
55 // pairwise differences are in [0, 6], so [0, 6] suffices (0 and 1 are their own trivial cases).
56 static constexpr std::array<FF, 7> precomputed_inverses = [] {
57 std::array<FF, 7> inverses{ 0, 1 };
58 for (size_t i = 2; i < 7; i++) {
59 inverses[i] = FF(i).invert();
60 }
61 return inverses;
62 }();
63
64 // Lambda to map an operation to its op_id selector column.
65 const auto get_op_id_column_selector = [](BitwiseOperation op) {
66 switch (op) {
68 return C::bitwise_sel_and;
70 return C::bitwise_sel_or;
72 return C::bitwise_sel_xor;
73 default:
74 __builtin_unreachable();
75 }
76 };
77
78 // We do not use any shifted columns so we start at row 0.
79 uint32_t row = 0;
80
81 for (const auto& event : events) {
82 const auto tag = event.a.get_tag();
83
84 const uint128_t input_a = static_cast<uint128_t>(event.a.as_ff());
85 const uint128_t input_b = static_cast<uint128_t>(event.b.as_ff());
86 const uint128_t output_c = event.res;
87
88 // Error Handling: tag a is FF or tag a != tag b.
89 const bool is_tag_ff = event.a.get_tag() == MemoryTag::FF;
90 const bool is_tag_mismatch = event.a.get_tag() != event.b.get_tag();
91 // Rely below on MemoryTag::FF being 0.
92 static_assert(static_cast<uint8_t>(MemoryTag::FF) == 0);
93 const uint8_t tag_a_u8 = static_cast<uint8_t>(event.a.get_tag());
94 const uint8_t tag_b_u8 = static_cast<uint8_t>(event.b.get_tag());
95
96 const FF tag_a_inv = precomputed_inverses[tag_a_u8];
97 // For tag_a != tag_b: (-x)^(-1) = -x^(-1) for a field element x.
98 const FF tag_ab_diff_inv = tag_a_u8 > tag_b_u8 ? precomputed_inverses[tag_a_u8 - tag_b_u8]
99 : -precomputed_inverses[tag_b_u8 - tag_a_u8];
100
101 if (is_tag_ff || is_tag_mismatch) {
102 // Error row (single row): sel=1, err=1, sel_compute=0.
103 trace.set(row,
104 { {
105 { C::bitwise_sel, 1 },
106 { C::bitwise_op_id, static_cast<uint8_t>(event.operation) },
107 { C::bitwise_ia, event.a.as_ff() },
108 { C::bitwise_ib, event.b.as_ff() },
109 { C::bitwise_ic, output_c },
110 { C::bitwise_tag_a, tag_a_u8 },
111 { C::bitwise_tag_b, tag_b_u8 },
112 // tag_c stays 0 (FF) on error
113 { C::bitwise_sel_tag_ff_err, is_tag_ff ? 1 : 0 },
114 { C::bitwise_sel_tag_mismatch_err, is_tag_mismatch ? 1 : 0 },
115 { C::bitwise_err, 1 },
116 { C::bitwise_tag_a_inv, tag_a_inv },
117 { C::bitwise_tag_ab_diff_inv, tag_ab_diff_inv },
118 } });
119 row++;
120 continue;
121 }
122
123 // Compute row: one row processes the whole operation across its byte limbs.
124 // (For tag U1 we take only one bit; the byte mask correctly extracts it.)
125 const uint8_t len = get_tag_bytes(tag);
126
127 // For SIMD-64 rows the U128 packs two U64 lanes: ia/ib/ic hold lane 0 (low 64 bits) and
128 // ia_simd/ib_simd/ic_simd hold lane 1 (high 64 bits). For ordinary rows ia/ib/ic hold the
129 // whole value and ia_simd/ib_simd/ic_simd are 0.
130 constexpr uint128_t mask_low_64 = (static_cast<uint128_t>(1) << 64) - 1;
131 const bool simd = event.simd_64;
132 const BitwiseOperation op = event.operation;
133 const auto lane0 = [&](uint128_t v) { return simd ? (v & mask_low_64) : v; };
134 const auto lane1 = [&](uint128_t v) -> uint128_t { return simd ? (v >> 64) : 0; };
135
136 trace.set(row,
137 { {
138 { C::bitwise_sel, 1 },
139 { C::bitwise_sel_compute, 1 },
140 { C::bitwise_sel_simd_64, simd ? 1 : 0 },
141 { C::bitwise_sel_u16, len >= 2 ? 1 : 0 },
142 { C::bitwise_sel_u32, len >= 4 ? 1 : 0 },
143 { C::bitwise_sel_u64, len >= 8 ? 1 : 0 },
144 { C::bitwise_sel_u128, len >= 16 ? 1 : 0 },
145 { C::bitwise_tag_byte_len, len },
146 { get_op_id_column_selector(op), 1 },
147 { C::bitwise_op_id, static_cast<uint8_t>(op) },
148 { C::bitwise_ia, lane0(input_a) },
149 { C::bitwise_ib, lane0(input_b) },
150 { C::bitwise_ic, lane0(output_c) },
151 { C::bitwise_ia_simd, lane1(input_a) },
152 { C::bitwise_ib_simd, lane1(input_b) },
153 { C::bitwise_ic_simd, lane1(output_c) },
154 { C::bitwise_tag_a, tag_a_u8 },
155 { C::bitwise_tag_b, tag_b_u8 },
156 { C::bitwise_tag_c, tag_a_u8 }, // same as tag_a
157 { C::bitwise_tag_a_inv, tag_a_inv },
158 { C::bitwise_tag_ab_diff_inv, tag_ab_diff_inv },
159 } });
160
161 // Fill the active byte limbs; inactive (high-order) limbs are left at zero.
162 for (uint8_t i = 0; i < len; i++) {
163 const uint8_t ia_byte = static_cast<uint8_t>(input_a >> (8 * i));
164 const uint8_t ib_byte = static_cast<uint8_t>(input_b >> (8 * i));
165 trace.set(row,
166 { {
167 { IA_BYTE[i], ia_byte },
168 { IB_BYTE[i], ib_byte },
169 { OUTPUT_AND[i], ia_byte & ib_byte },
170 { OUTPUT_OR[i], ia_byte | ib_byte },
171 { OUTPUT_XOR[i], ia_byte ^ ib_byte },
172 } });
173 }
174 row++;
175 }
176}
177
181 .add<InteractionType::LookupIntoBitwise, lookup_bitwise_byte_operations_1_settings>(C::precomputed_sel_range_16)
183 .add<InteractionType::LookupIntoBitwise, lookup_bitwise_byte_operations_3_settings>(C::precomputed_sel_range_16)
185 .add<InteractionType::LookupIntoBitwise, lookup_bitwise_byte_operations_5_settings>(C::precomputed_sel_range_16)
187 .add<InteractionType::LookupIntoBitwise, lookup_bitwise_byte_operations_7_settings>(C::precomputed_sel_range_16)
189 .add<InteractionType::LookupIntoBitwise, lookup_bitwise_byte_operations_9_settings>(C::precomputed_sel_range_16)
191 C::precomputed_sel_range_16)
192 .add<InteractionType::LookupIntoBitwise, lookup_bitwise_byte_operations_11_settings>(
193 C::precomputed_sel_range_16)
195 C::precomputed_sel_range_16)
196 .add<InteractionType::LookupIntoBitwise, lookup_bitwise_byte_operations_13_settings>(
197 C::precomputed_sel_range_16)
199 C::precomputed_sel_range_16)
200 .add<InteractionType::LookupIntoBitwise, lookup_bitwise_byte_operations_15_settings>(
201 C::precomputed_sel_range_16)
203
204} // 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
uint8_t get_tag_bytes(ValueTag tag)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
uint8_t len
simulation::PublicDataTreeReadWriteEvent event
unsigned __int128 uint128_t
Definition serialize.hpp:45
Settings to be passed ot GenericLookupRelationImpl.