Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
alu.cpp
Go to the documentation of this file.
2
3#include <cstdint>
4#include <memory>
5
9
10namespace bb::avm2::simulation {
11
13{
14 try {
15 MemoryValue c = a + b; // This will throw if the tags do not match.
16 events.emit({ .operation = AluOperation::ADD, .a = a, .b = b, .c = c });
17 return c;
18 } catch (const TagMismatchException& e) {
19 events.emit({ .operation = AluOperation::ADD, .a = a, .b = b, .error = AluError::TAG_ERROR });
20 throw AluException("ADD, " + std::string(e.what()));
21 }
22}
23
25{
26 try {
27 MemoryValue c = a - b; // This will throw if the tags do not match.
28 events.emit({ .operation = AluOperation::SUB, .a = a, .b = b, .c = c });
29 return c;
30 } catch (const TagMismatchException& e) {
31 events.emit({ .operation = AluOperation::SUB, .a = a, .b = b, .error = AluError::TAG_ERROR });
32 throw AluException("SUB, " + std::string(e.what()));
33 }
34}
35
37{
38 try {
39 MemoryValue c = a * b; // This will throw if the tags do not match.
40 uint256_t a_int = static_cast<uint256_t>(a.as_ff());
41 uint256_t b_int = static_cast<uint256_t>(b.as_ff());
42 MemoryTag tag = a.get_tag();
43 if (tag == MemoryTag::U128) {
44 // For u128, we decompose a and b into 64 bit chunks and discard the highest bits given by the product:
45 auto a_decomp = decompose(static_cast<uint128_t>(a.as_ff()));
46 auto b_decomp = decompose(static_cast<uint128_t>(b.as_ff()));
47 range_check.assert_range(a_decomp.lo, 64);
48 range_check.assert_range(a_decomp.hi, 64);
49 range_check.assert_range(b_decomp.lo, 64);
50 range_check.assert_range(b_decomp.hi, 64);
51 auto hi_operand = static_cast<uint256_t>(a_decomp.hi) * static_cast<uint256_t>(b_decomp.hi);
52 // c_hi = old_c_hi - a_hi * b_hi % 2^64
53 uint256_t c_hi = (((a_int * b_int) >> 128) - hi_operand) % (uint256_t(1) << 64);
54 range_check.assert_range(static_cast<uint128_t>(c_hi), 64);
55 }
56 events.emit({ .operation = AluOperation::MUL, .a = a, .b = b, .c = c });
57 return c;
58 } catch (const TagMismatchException& e) {
59 events.emit({ .operation = AluOperation::MUL, .a = a, .b = b, .error = AluError::TAG_ERROR });
60 throw AluException("MUL, " + std::string(e.what()));
61 }
62}
63
65{
66 try {
67 MemoryValue c = a / b; // This will throw if the tags do not match or if we divide by 0.
68 MemoryValue remainder = a - c * b;
69 MemoryTag tag = a.get_tag();
70
71 if (tag == MemoryTag::FF) {
72 // DIV on a field is not a valid operation, but should be recoverable.
73 // TODO(MW): cleanup - It comes under the umbrella of tag errors (like NOT) but MemoryValue c = a / b does
74 // not throw, so I sin here and throw a not relevant error we know will create a TAG_ERROR:
75 throw TagMismatchException("Cannot perform integer division on a field element");
76 }
77
78 // Check remainder < b:
79 greater_than.gt(b, remainder);
80 if (tag == MemoryTag::U128) {
81 // For u128, we decompose c and b into 64 bit chunks and discard the highest bits given by the product:
82 auto c_decomp = decompose(static_cast<uint128_t>(c.as_ff()));
83 auto b_decomp = decompose(static_cast<uint128_t>(b.as_ff()));
84 range_check.assert_range(c_decomp.lo, 64);
85 range_check.assert_range(c_decomp.hi, 64);
86 range_check.assert_range(b_decomp.lo, 64);
87 range_check.assert_range(b_decomp.hi, 64);
88 }
89 events.emit({ .operation = AluOperation::DIV, .a = a, .b = b, .c = c });
90 return c;
91 } catch (const TagMismatchException& e) {
92 events.emit({ .operation = AluOperation::DIV, .a = a, .b = b, .error = AluError::TAG_ERROR });
93 throw AluException("DIV, " + std::string(e.what()));
94 } catch (const DivisionByZero& e) {
95 events.emit({ .operation = AluOperation::DIV, .a = a, .b = b, .error = AluError::DIV_0_ERROR });
96 throw AluException("DIV, " + std::string(e.what()));
97 }
98}
99
101{
102 try {
103 MemoryValue c = a / b; // This will throw if the tags do not match or if we divide by 0.
104
105 if (a.get_tag() != MemoryTag::FF) {
106 // We cannot reach this case from execution because the tags are forced to be FF (see below*).
107 // TODO(MW): cleanup - It comes under the umbrella of tag errors (like NOT) but MemoryValue c = a / b does
108 // not throw, so I sin here and throw a not relevant error we know will create a TAG_ERROR:
109 throw TagMismatchException("Cannot perform field division on an integer");
110 }
111
112 events.emit({ .operation = AluOperation::FDIV, .a = a, .b = b, .c = c });
113 return c;
114 } catch (const TagMismatchException& e) {
115 // *This is unreachable from execution and exists to manage and test tag errors:
116 events.emit({ .operation = AluOperation::FDIV, .a = a, .b = b, .error = AluError::TAG_ERROR });
117 throw AluException("FDIV, " + std::string(e.what()));
118 } catch (const DivisionByZero& e) {
119 events.emit({ .operation = AluOperation::FDIV, .a = a, .b = b, .error = AluError::DIV_0_ERROR });
120 throw AluException("FDIV, " + std::string(e.what()));
121 }
122}
123
125{
126 // Brillig semantic enforces that tags match for EQ.
127 if (a.get_tag() != b.get_tag()) {
128 events.emit({ .operation = AluOperation::EQ, .a = a, .b = b, .error = AluError::TAG_ERROR });
129 throw AluException("EQ, Tag mismatch between operands.");
130 }
131
132 MemoryValue c = MemoryValue::from<uint1_t>(a == b ? 1 : 0);
133 events.emit({ .operation = AluOperation::EQ, .a = a, .b = b, .c = c });
134 return c;
135}
136
138{
139 // Brillig semantic enforces that tags match for LT.
140 // This is special cased because comparison operators do not throw on tag mismatch.
141 if (a.get_tag() != b.get_tag()) {
142 events.emit({ .operation = AluOperation::LT, .a = a, .b = b, .error = AluError::TAG_ERROR });
143 throw AluException("LT, Tag mismatch between operands.");
144 }
145 // Use the greater_than interface to check if b > a, which is the same as a < b.
146 bool res = greater_than.gt(b, a);
147 MemoryValue c = MemoryValue::from<uint1_t>(res);
148 events.emit({ .operation = AluOperation::LT, .a = a, .b = b, .c = c });
149 return c;
150}
151
153{
154 // Brillig semantic enforces that tags match for LTE.
155 if (a.get_tag() != b.get_tag()) {
156 events.emit({ .operation = AluOperation::LTE, .a = a, .b = b, .error = AluError::TAG_ERROR });
157 throw AluException("LT, Tag mismatch between operands.");
158 }
159 // Note: the result of LTE is the opposite of GT
160 // Use the greater_than interface to check if a > b
161 bool res = greater_than.gt(a, b);
162 // The result of LTE is the opposite of the result of GT
163 MemoryValue c = MemoryValue::from<uint1_t>(!res);
164 events.emit({ .operation = AluOperation::LTE, .a = a, .b = b, .c = c });
165 return c;
166}
167
169{
170 try {
171 MemoryValue b = ~a; // Throws if the tag is not compatible with NOT operation (i.e. it is an FF type).
172 events.emit({ .operation = AluOperation::NOT, .a = a, .b = b });
173 return b;
174 } catch (const InvalidOperationTag& e) {
175 events.emit({ .operation = AluOperation::NOT, .a = a, .error = AluError::TAG_ERROR });
176 throw AluException("NOT, " + std::string(e.what()));
177 }
178}
179
181{
182 try {
183 MemoryValue c = a << b; // This will throw if the tags do not match or are FF.
184 auto tag_bits = get_tag_bits(a.get_tag());
185 auto b_num = static_cast<uint128_t>(b.as_ff());
186
187 bool overflow = b_num > tag_bits;
188 uint8_t a_lo_bits = overflow ? tag_bits : tag_bits - static_cast<uint8_t>(b_num);
189 auto a_lo =
190 overflow ? b_num - tag_bits : static_cast<uint128_t>(a.as_ff()) % (static_cast<uint128_t>(1) << a_lo_bits);
191 range_check.assert_range(a_lo, a_lo_bits);
192 range_check.assert_range(static_cast<uint128_t>(a.as_ff()) >> a_lo_bits,
193 overflow ? tag_bits : static_cast<uint8_t>(b_num));
194 events.emit({ .operation = AluOperation::SHL, .a = a, .b = b, .c = c });
195 return c;
196 } catch (const TagMismatchException& e) {
197 events.emit({ .operation = AluOperation::SHL, .a = a, .b = b, .error = AluError::TAG_ERROR });
198 throw AluException("SHL, " + std::string(e.what()));
199 } catch (const InvalidOperationTag& e) {
200 events.emit({ .operation = AluOperation::SHL, .a = a, .b = b, .error = AluError::TAG_ERROR });
201 throw AluException("SHL, " + std::string(e.what()));
202 } catch (const std::exception& e) {
203 // We have some err not handled by TAG_ERROR, so we rethrow back to execution:
204 throw e;
205 }
206}
207
209{
210 try {
211 MemoryValue c = a >> b; // This will throw if the tags do not match or are FF.
212 auto tag_bits = get_tag_bits(a.get_tag());
213 auto b_num = static_cast<uint128_t>(b.as_ff());
214
215 bool overflow = b_num > tag_bits;
216 uint8_t a_lo_bits = overflow ? tag_bits : static_cast<uint8_t>(b_num);
217 auto a_lo =
218 overflow ? b_num - tag_bits : static_cast<uint128_t>(a.as_ff()) % (static_cast<uint128_t>(1) << a_lo_bits);
219 range_check.assert_range(a_lo, a_lo_bits);
220 range_check.assert_range(static_cast<uint128_t>(a.as_ff()) >> a_lo_bits,
221 overflow ? tag_bits : tag_bits - static_cast<uint8_t>(b_num));
222 events.emit({ .operation = AluOperation::SHR, .a = a, .b = b, .c = c });
223 return c;
224 } catch (const TagMismatchException& e) {
225 events.emit({ .operation = AluOperation::SHR, .a = a, .b = b, .error = AluError::TAG_ERROR });
226 throw AluException("SHR, " + std::string(e.what()));
227 } catch (const InvalidOperationTag& e) {
228 events.emit({ .operation = AluOperation::SHR, .a = a, .b = b, .error = AluError::TAG_ERROR });
229 throw AluException("SHR, " + std::string(e.what()));
230 } catch (const std::exception& e) {
231 // We have some err not handled by TAG_ERROR, so we rethrow back to execution:
232 throw e;
233 }
234}
235
237{
239
240 // Circuit leakage: range check for `mid` value defined by a = ic + mid * 2^dst_tag_bits + hi_128 * 2^128 and
241 // mid is (128 - dst_tag_bits) bits.
242 bool is_trivial = dst_tag == MemoryTag::FF || static_cast<uint256_t>(a) <= get_tag_max_value(dst_tag);
243 if (!is_trivial) {
244
245 uint128_t a_lo = 0;
246 if (static_cast<uint256_t>(a) >= (static_cast<uint256_t>(1) << 128)) {
247 // Canonical decomposition
248 U256Decomposition decomposition = field_gt.canon_dec(a);
249 a_lo = decomposition.lo;
250 } else {
251 a_lo = static_cast<uint128_t>(a);
252 }
253
254 // cpp standard on bitwise shifts:
255 // "If the value of rhs is negative or is not less than the number of bits in lhs, the behavior is undefined."
256 // For this reason, we handle the case where dst_tag is U128 separately as shifting of 128 bits is undefined.
257 const uint128_t mid = dst_tag == MemoryTag::U128 ? 0 : a_lo >> get_tag_bits(dst_tag);
258 range_check.assert_range(mid, 128 - get_tag_bits(dst_tag));
259 }
260
261 // We put dst_tag in b to have correct deduplication and also to encode it in the event.
262 // Note however that in alu subtrace, dst_tag will be set in ia_tag.
263 events.emit({ .operation = AluOperation::TRUNCATE,
265 .b = MemoryValue::from_tag(MemoryTag::FF, static_cast<uint8_t>(dst_tag)),
266 .c = c });
267 return c;
268}
269
270} // namespace bb::avm2::simulation
MemoryTag dst_tag
static TaggedValue from_tag_truncating(ValueTag tag, FF value)
static TaggedValue from_tag(ValueTag tag, FF value)
MemoryValue lte(const MemoryValue &a, const MemoryValue &b) override
Definition alu.cpp:152
MemoryValue fdiv(const MemoryValue &a, const MemoryValue &b) override
Definition alu.cpp:100
MemoryValue truncate(const FF &a, MemoryTag dst_tag) override
Definition alu.cpp:236
MemoryValue eq(const MemoryValue &a, const MemoryValue &b) override
Definition alu.cpp:124
MemoryValue lt(const MemoryValue &a, const MemoryValue &b) override
Definition alu.cpp:137
MemoryValue shl(const MemoryValue &a, const MemoryValue &b) override
Definition alu.cpp:180
MemoryValue add(const MemoryValue &a, const MemoryValue &b) override
Definition alu.cpp:12
MemoryValue sub(const MemoryValue &a, const MemoryValue &b) override
Definition alu.cpp:24
FieldGreaterThanInterface & field_gt
Definition alu.hpp:54
MemoryValue mul(const MemoryValue &a, const MemoryValue &b) override
Definition alu.cpp:36
MemoryValue shr(const MemoryValue &a, const MemoryValue &b) override
Definition alu.cpp:208
EventEmitterInterface< AluEvent > & events
Definition alu.hpp:56
MemoryValue op_not(const MemoryValue &a) override
Definition alu.cpp:168
GreaterThanInterface & greater_than
Definition alu.hpp:53
MemoryValue div(const MemoryValue &a, const MemoryValue &b) override
Definition alu.cpp:64
virtual U256Decomposition canon_dec(const FF &a)=0
virtual bool gt(const FF &a, const FF &b)=0
FF a
FF b
U256Decomposition decompose(const uint256_t &x)
uint8_t get_tag_bits(ValueTag tag)
uint256_t get_tag_max_value(ValueTag tag)
AvmFlavorSettings::FF FF
Definition field.hpp:10
unsigned __int128 uint128_t
Definition serialize.hpp:44