LLZK 0.1.0
Veridise's ZK Language IR
Loading...
Searching...
No Matches
Intervals.cpp
Go to the documentation of this file.
1//===-- Intervals.cpp ---------------------------------------------*- C++ -*-===//
2//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2025 Veridise Inc.
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
9
13
14using namespace mlir;
15
16namespace llzk {
17
18/* UnreducedInterval */
19
21 if (safeGt(a, b)) {
22 return Interval::Empty(field);
23 }
24 if (safeGe(width(), field.prime())) {
25 return Interval::Entire(field);
26 }
27 auto lhs = field.reduce(a), rhs = field.reduce(b);
28 // lhs and rhs are now guaranteed to have the same bitwidth, so we can use
29 // built-in functions.
30 if ((rhs - lhs).isZero()) {
31 return Interval::Degenerate(field, lhs);
32 }
33
34 const auto &half = field.half();
35 if (lhs.ule(rhs)) {
36 if (lhs.ult(half) && rhs.ult(half)) {
37 return Interval::TypeA(field, lhs, rhs);
38 } else if (lhs.ult(half)) {
39 return Interval::TypeC(field, lhs, rhs);
40 } else {
41 return Interval::TypeB(field, lhs, rhs);
42 }
43 } else {
44 if (lhs.uge(half) && rhs.ult(half)) {
45 return Interval::TypeF(field, lhs, rhs);
46 } else {
47 return Interval::Entire(field);
48 }
49 }
50}
51
53 auto &lhs = *this;
54 return UnreducedInterval(safeMax(lhs.a, rhs.a), safeMin(lhs.b, rhs.b));
55}
56
58 auto &lhs = *this;
59 return UnreducedInterval(safeMin(lhs.a, rhs.a), safeMax(lhs.b, rhs.b));
60}
61
63 if (isEmpty() || rhs.isEmpty()) {
64 return *this;
65 }
66 auto one = llvm::APSInt(llvm::APInt(a.getBitWidth(), 1));
67 auto bound = expandingSub(rhs.b, one);
68 return UnreducedInterval(a, safeMin(b, bound));
69}
70
72 if (isEmpty() || rhs.isEmpty()) {
73 return *this;
74 }
75 return UnreducedInterval(a, safeMin(b, rhs.b));
76}
77
79 if (isEmpty() || rhs.isEmpty()) {
80 return *this;
81 }
82 auto one = llvm::APSInt(llvm::APInt(a.getBitWidth(), 1));
83 auto bound = expandingAdd(rhs.a, one);
84 return UnreducedInterval(safeMax(a, bound), b);
85}
86
88 if (isEmpty() || rhs.isEmpty()) {
89 return *this;
90 }
91 return UnreducedInterval(safeMax(a, rhs.a), b);
92}
93
95 if (isEmpty()) {
96 return *this;
97 }
98 return UnreducedInterval(-b, -a);
99}
100
102 llvm::APSInt low = expandingAdd(lhs.a, rhs.a), high = expandingAdd(lhs.b, rhs.b);
103 return UnreducedInterval(low, high);
104}
105
107 return lhs + (-rhs);
108}
109
111 auto v1 = expandingMul(lhs.a, rhs.a);
112 auto v2 = expandingMul(lhs.a, rhs.b);
113 auto v3 = expandingMul(lhs.b, rhs.a);
114 auto v4 = expandingMul(lhs.b, rhs.b);
115
116 auto minVal = safeMin({v1, v2, v3, v4});
117 auto maxVal = safeMax({v1, v2, v3, v4});
118
119 return UnreducedInterval(minVal, maxVal);
120}
121
123 return isNotEmpty() && rhs.isNotEmpty() && safeGe(b, rhs.a) && safeLe(a, rhs.b);
124}
125
126std::strong_ordering operator<=>(const UnreducedInterval &lhs, const UnreducedInterval &rhs) {
127 if (safeLt(lhs.a, rhs.a) || (safeEq(lhs.a, rhs.a) && safeLt(lhs.b, rhs.b))) {
128 return std::strong_ordering::less;
129 }
130 if (safeGt(lhs.a, rhs.a) || (safeEq(lhs.a, rhs.a) && safeGt(lhs.b, rhs.b))) {
131 return std::strong_ordering::greater;
132 }
133 return std::strong_ordering::equal;
134}
135
136llvm::APSInt UnreducedInterval::width() const {
137 llvm::APSInt w;
138 if (safeGt(a, b)) {
139 // This would be reduced to an empty Interval, so the width is just zero.
140 w = llvm::APSInt::getUnsigned(0);
141 } else {
142 // Since the range is inclusive, we add one to the difference to get the true width.
143 w = expandingSub(b, a)++;
144 }
145 ensure(safeGe(w, llvm::APSInt::getUnsigned(0)), "cannot have negative width");
146 return w;
147}
148
149bool UnreducedInterval::isEmpty() const { return safeEq(width(), llvm::APSInt::getUnsigned(0)); }
150
151/* Interval */
152
153const Field &checkFields(const Interval &lhs, const Interval &rhs) {
154 ensure(
155 lhs.getField() == rhs.getField(), "interval operations across differing fields is unsupported"
156 );
157 return lhs.getField();
158}
159
161 if (isEmpty()) {
162 // Since ranges are inclusive, empty is encoded as `[a, b]` where `a` > `b`.
163 // This matches the definition provided by UnreducedInterval::width().
164 return UnreducedInterval(field.get().one(), field.get().zero());
165 }
166 if (isEntire()) {
167 return UnreducedInterval(field.get().zero(), field.get().maxVal());
168 }
169 return UnreducedInterval(a, b);
170}
171
173 if (is<Type::TypeF>()) {
174 return UnreducedInterval(a - field.get().prime(), b);
175 }
176 return toUnreduced();
177}
178
180 ensure(is<Type::TypeA, Type::TypeB, Type::TypeC>(), "unsupported range type");
181 return UnreducedInterval(a - field.get().prime(), b - field.get().prime());
182}
183
185 auto &lhs = *this;
186 const Field &f = checkFields(lhs, rhs);
187
188 // Trivial cases
189 if (lhs.isEntire() || rhs.isEntire()) {
190 return Interval::Entire(f);
191 }
192 if (lhs.isEmpty()) {
193 return rhs;
194 }
195 if (rhs.isEmpty()) {
196 return lhs;
197 }
198 if (lhs.isDegenerate() || rhs.isDegenerate()) {
199 return lhs.toUnreduced().doUnion(rhs.toUnreduced()).reduce(f);
200 }
201
202 // More complex cases
203 if (areOneOf<
206 return Interval(rhs.ty, f, std::min(lhs.a, rhs.a), std::max(lhs.b, rhs.b));
207 }
209 auto lhsUnred = lhs.firstUnreduced();
210 auto opt1 = rhs.firstUnreduced().doUnion(lhsUnred);
211 auto opt2 = rhs.secondUnreduced().doUnion(lhsUnred);
212 if (opt1.width() <= opt2.width()) {
213 return opt1.reduce(f);
214 }
215 return opt2.reduce(f);
216 }
218 return lhs.firstUnreduced().doUnion(rhs.firstUnreduced()).reduce(f);
219 }
221 return lhs.secondUnreduced().doUnion(rhs.firstUnreduced()).reduce(f);
222 }
224 return Interval::Entire(f);
225 }
226 if (areOneOf<
229 lhs, rhs
230 )) {
231 return rhs.join(lhs);
232 }
233 llvm::report_fatal_error("unhandled join case");
234 return Interval::Entire(f);
235}
236
238 auto &lhs = *this;
239 const Field &f = checkFields(lhs, rhs);
240 // Trivial cases
241 if (lhs.isEmpty() || rhs.isEmpty()) {
242 return Interval::Empty(f);
243 }
244 if (lhs.isEntire()) {
245 return rhs;
246 }
247 if (rhs.isEntire()) {
248 return lhs;
249 }
250 if (lhs.isDegenerate() || rhs.isDegenerate()) {
251 return lhs.toUnreduced().intersect(rhs.toUnreduced()).reduce(f);
252 }
253
254 // More complex cases
255 if (areOneOf<
258 auto maxA = std::max(lhs.a, rhs.a);
259 auto minB = std::min(lhs.b, rhs.b);
260 if (maxA <= minB) {
261 return Interval(lhs.ty, f, maxA, minB);
262 } else {
263 return Interval::Empty(f);
264 }
265 }
267 return Interval::Empty(f);
268 }
270 return lhs.firstUnreduced().intersect(rhs.firstUnreduced()).reduce(f);
271 }
273 return lhs.secondUnreduced().intersect(rhs.firstUnreduced()).reduce(f);
274 }
276 auto rhsUnred = rhs.firstUnreduced();
277 auto opt1 = lhs.firstUnreduced().intersect(rhsUnred).reduce(f);
278 auto opt2 = lhs.secondUnreduced().intersect(rhsUnred).reduce(f);
279 ensure(!opt1.isEntire() && !opt2.isEntire(), "impossible intersection");
280 if (opt1.isEmpty()) {
281 return opt2;
282 }
283 if (opt2.isEmpty()) {
284 return opt1;
285 }
286 return opt1.join(opt2);
287 }
288 if (areOneOf<
291 lhs, rhs
292 )) {
293 return rhs.intersect(lhs);
294 }
295 return Interval::Empty(f);
296}
297
299 const Field &f = checkFields(*this, other);
300 // intersect checks that we're in the same field
302 if (intersection.isEmpty()) {
303 // There's nothing to remove, so just return this
304 return *this;
305 }
306
307 // Trivial cases with a non-empty intersection
308 if (isDegenerate() || other.isEntire()) {
309 return Interval::Empty(f);
310 }
311 if (isEntire()) {
312 // Since we don't support punching arbitrary holes in ranges, we only reduce
313 // entire ranges if other is [0, b] or [a, prime - 1]
314 if (other.a == f.zero()) {
315 return UnreducedInterval(other.b + f.one(), f.maxVal()).reduce(f);
316 }
317 if (other.b == f.maxVal()) {
318 return UnreducedInterval(f.zero(), other.a - f.one()).reduce(f);
319 }
320
321 return *this;
322 }
323
324 // Non-trivial cases
325 // - Internal+internal or external+external cases
328 areOneOf<{Type::TypeF, Type::TypeF}>(*this, intersection)) {
329 // The intersection needs to be at the end of the interval, otherwise we would
330 // split the interval in two, and we aren't set up to support multiple intervals
331 // per value.
332 if (a != intersection.a && b != intersection.b) {
333 return *this;
334 }
335 // Otherwise, remove the intersection and reduce
336 if (a == intersection.a) {
337 return UnreducedInterval(intersection.b + f.one(), b).reduce(f);
338 }
339 // else b == intersection.b
340 return UnreducedInterval(a, intersection.a - f.one()).reduce(f);
341 }
342 // - Mixed internal/external cases. We flip the comparison
343 if (isTypeF()) {
344 if (a != intersection.b && b != intersection.a) {
345 return *this;
346 }
347 // Otherwise, remove the intersection and reduce
348 if (a == intersection.b) {
349 return UnreducedInterval(intersection.a + f.one(), b).reduce(f);
350 }
351 // else b == intersection.a
352 return UnreducedInterval(a, intersection.b - f.one()).reduce(f);
353 }
354
355 // In cases we don't know how to handle, we over-approximate and return
356 // the original interval.
357 return *this;
358}
359
360Interval Interval::operator-() const { return (-firstUnreduced()).reduce(field.get()); }
361
363 return Interval::Degenerate(field.get(), field.get().one()) - *this;
364}
365
367 const Field &f = checkFields(lhs, rhs);
368 if (lhs.isEmpty()) {
369 return rhs;
370 }
371 if (rhs.isEmpty()) {
372 return lhs;
373 }
374 return (lhs.firstUnreduced() + rhs.firstUnreduced()).reduce(f);
375}
376
377Interval operator-(const Interval &lhs, const Interval &rhs) { return lhs + (-rhs); }
378
380 const Field &f = checkFields(lhs, rhs);
381 auto zeroInterval = Interval::Degenerate(f, f.zero());
382 if (lhs == zeroInterval || rhs == zeroInterval) {
383 return zeroInterval;
384 }
385 if (lhs.isEmpty() || rhs.isEmpty()) {
386 return Interval::Empty(f);
387 }
388 if (lhs.isEntire() || rhs.isEntire()) {
389 return Interval::Entire(f);
390 }
391
393 return (lhs.secondUnreduced() * rhs.secondUnreduced()).reduce(f);
394 }
395 return (lhs.firstUnreduced() * rhs.firstUnreduced()).reduce(f);
396}
397
398FailureOr<Interval> operator/(const Interval &lhs, const Interval &rhs) {
399 const Field &f = checkFields(lhs, rhs);
400 if (rhs.width() > f.one()) {
401 return Interval::Entire(f);
402 }
403 if (rhs.a.isZero()) {
404 return failure();
405 }
406 return success(UnreducedInterval(lhs.a / rhs.a, lhs.b / rhs.a).reduce(f));
407}
408
410 const Field &f = checkFields(lhs, rhs);
411 return UnreducedInterval(f.zero(), rhs.b).reduce(f);
412}
413
415 const Field &f = checkFields(lhs, rhs);
416 if (lhs.isEmpty() || rhs.isEmpty()) {
417 return Interval::Empty(f);
418 }
419 if (lhs.isDegenerate() && rhs.isDegenerate()) {
420 return Interval::Degenerate(f, lhs.a & rhs.a);
421 } else if (lhs.isDegenerate()) {
422 return UnreducedInterval(f.zero(), lhs.a).reduce(f);
423 } else if (rhs.isDegenerate()) {
424 return UnreducedInterval(f.zero(), rhs.a).reduce(f);
425 }
426 return Interval::Entire(f);
427}
428
430 const Field &f = checkFields(lhs, rhs);
431 if (lhs.isEmpty() || rhs.isEmpty()) {
432 return Interval::Empty(f);
433 }
434 if (lhs.isDegenerate() && rhs.isDegenerate()) {
435 if (safeGt(rhs.a, APSInt::getUnsigned(f.bitWidth()))) {
436 return Interval::Entire(f);
437 }
438
439 unsigned shiftAmt = rhs.a.getZExtValue();
440 auto v = lhs.a.relativeShl(shiftAmt);
441 return UnreducedInterval(v, v).reduce(f);
442 }
443 return Interval::Entire(f);
444}
445
447 const Field &f = checkFields(lhs, rhs);
448 if (lhs.isEmpty() || rhs.isEmpty()) {
449 return Interval::Empty(f);
450 }
451 if (lhs.isDegenerate() && rhs.isDegenerate()) {
452 if (safeGt(rhs.a, APSInt::getUnsigned(f.bitWidth()))) {
453 return Interval::Degenerate(f, f.zero());
454 }
455
456 unsigned shiftAmt = rhs.a.getZExtValue();
457 return Interval::Degenerate(f, lhs.a.relativeShr(shiftAmt));
458 }
459 return Interval::Entire(f);
460}
461
462llvm::APSInt Interval::width() const {
463 switch (ty) {
464 case Type::Empty:
465 return field.get().zero();
466 case Type::Degenerate:
467 return field.get().one();
468 case Type::Entire:
469 return field.get().prime();
470 default:
471 return field.get().reduce(toUnreduced().width());
472 }
473}
474
476 ensure(
477 lhs.getField() == rhs.getField(), "interval operations across differing fields is unsupported"
478 );
479 ensure(lhs.isBoolean() && rhs.isBoolean(), "operation only supported for boolean-type intervals");
480 const auto &field = rhs.getField();
481
482 if (lhs.isBoolFalse() || rhs.isBoolFalse()) {
483 return Interval::False(field);
484 }
485 if (lhs.isBoolTrue() && rhs.isBoolTrue()) {
486 return Interval::True(field);
487 }
488
489 return Interval::Boolean(field);
490}
491
493 ensure(
494 lhs.getField() == rhs.getField(), "interval operations across differing fields is unsupported"
495 );
496 ensure(lhs.isBoolean() && rhs.isBoolean(), "operation only supported for boolean-type intervals");
497 const auto &field = rhs.getField();
498
499 if (lhs.isBoolFalse() && rhs.isBoolFalse()) {
500 return Interval::False(field);
501 }
502 if (lhs.isBoolTrue() || rhs.isBoolTrue()) {
503 return Interval::True(field);
504 }
505
506 return Interval::Boolean(field);
507}
508
510 ensure(
511 lhs.getField() == rhs.getField(), "interval operations across differing fields is unsupported"
512 );
513 ensure(lhs.isBoolean() && rhs.isBoolean(), "operation only supported for boolean-type intervals");
514 const auto &field = rhs.getField();
515
516 // Xor-ing anything with [0, 1] could still result in either case, so just return
517 // the full boolean range.
518 if (lhs.isBoolEither() || rhs.isBoolEither()) {
519 return Interval::Boolean(lhs.getField());
520 }
521
522 if (lhs.isBoolTrue() && rhs.isBoolTrue()) {
523 return Interval::False(field);
524 }
525 if (lhs.isBoolTrue() || rhs.isBoolTrue()) {
526 return Interval::True(field);
527 }
528 if (lhs.isBoolFalse() && rhs.isBoolFalse()) {
529 return Interval::False(field);
530 }
531
532 return Interval::Boolean(field);
533}
534
536 ensure(iv.isBoolean(), "operation only supported for boolean-type intervals");
537 const auto &field = iv.getField();
538
539 if (iv.isBoolTrue()) {
540 return Interval::False(field);
541 }
542 if (iv.isBoolFalse()) {
543 return Interval::True(field);
544 }
545
546 return iv;
547}
548
549void Interval::print(mlir::raw_ostream &os) const {
550 os << TypeName(ty);
551 if (is<Type::Degenerate>()) {
552 os << '(' << a << ')';
553 } else if (!is<Type::Entire, Type::Empty>()) {
554 os << ":[ " << a << ", " << b << " ]";
555 }
556}
557
558} // namespace llzk
This file defines helpers for manipulating APInts/APSInts for large numbers and operations over those...
Information about the prime finite field used for the interval analysis.
Definition Field.h:22
llvm::APSInt one() const
Returns 1 at the bitwidth of the field.
Definition Field.h:46
llvm::APSInt zero() const
Returns 0 at the bitwidth of the field.
Definition Field.h:43
llvm::APSInt half() const
Returns p / 2.
Definition Field.h:37
unsigned bitWidth() const
Definition Field.h:57
llvm::APSInt maxVal() const
Returns p - 1, which is the max value possible in a prime field described by p.
Definition Field.h:49
llvm::APSInt prime() const
For the prime field p, returns p.
Definition Field.h:34
llvm::APSInt reduce(llvm::APSInt i) const
Returns i mod p and reduces the result into the appropriate bitwidth.
Definition Field.cpp:52
Intervals over a finite field.
Definition Intervals.h:214
bool isEmpty() const
Definition Intervals.h:318
static Interval True(const Field &f)
Definition Intervals.h:233
Interval intersect(const Interval &rhs) const
Intersect.
bool isBoolean() const
Definition Intervals.h:330
static std::string_view TypeName(Type t)
Definition Intervals.h:221
llvm::APSInt rhs() const
Definition Intervals.h:343
void print(llvm::raw_ostream &os) const
UnreducedInterval toUnreduced() const
Convert to an UnreducedInterval.
static Interval Boolean(const Field &f)
Definition Intervals.h:235
bool isBoolFalse() const
Definition Intervals.h:327
UnreducedInterval firstUnreduced() const
Get the first side of the interval for TypeF intervals, otherwise just get the full interval as an Un...
bool isDegenerate() const
Definition Intervals.h:320
const Field & getField() const
Definition Intervals.h:338
bool isBoolTrue() const
Definition Intervals.h:328
bool is() const
Definition Intervals.h:332
UnreducedInterval secondUnreduced() const
Get the second side of the interval for TypeA, TypeB, and TypeC intervals.
bool isTypeF() const
Definition Intervals.h:325
llvm::APSInt width() const
static Interval False(const Field &f)
Definition Intervals.h:231
llvm::APSInt lhs() const
Definition Intervals.h:342
Interval operator~() const
static bool areOneOf(const Interval &a, const Interval &b)
Definition Intervals.h:271
Interval()
To satisfy the dataflow::ScalarLatticeValue requirements, this class must be default initializable.
Definition Intervals.h:257
bool isEntire() const
Definition Intervals.h:321
Interval difference(const Interval &other) const
Computes and returns this - (this & other) if the operation produces a single interval.
Interval operator-() const
Interval join(const Interval &rhs) const
Union.
An inclusive interval [a, b] where a and b are arbitrary integers not necessarily bound to a given fi...
Definition Intervals.h:26
UnreducedInterval operator-() const
Definition Intervals.cpp:94
UnreducedInterval intersect(const UnreducedInterval &rhs) const
Compute and return the intersection of this interval and the given RHS.
Definition Intervals.cpp:52
bool isEmpty() const
Returns true iff width() is zero.
UnreducedInterval(llvm::APSInt x, llvm::APSInt y)
Definition Intervals.h:41
llvm::APSInt width() const
Compute the width of this interval within a given field f.
UnreducedInterval computeLTPart(const UnreducedInterval &rhs) const
Return the part of the interval that is guaranteed to be less than the rhs's max value.
Definition Intervals.cpp:62
UnreducedInterval computeGEPart(const UnreducedInterval &rhs) const
Return the part of the interval that is greater than or equal to the rhs's lower bound.
Definition Intervals.cpp:87
bool isNotEmpty() const
Definition Intervals.h:130
bool overlaps(const UnreducedInterval &rhs) const
UnreducedInterval doUnion(const UnreducedInterval &rhs) const
Compute and return the union of this interval and the given RHS.
Definition Intervals.cpp:57
UnreducedInterval computeGTPart(const UnreducedInterval &rhs) const
Return the part of the interval that is greater than the rhs's lower bound.
Definition Intervals.cpp:78
Interval reduce(const Field &field) const
Reduce the interval to an interval in the given field.
Definition Intervals.cpp:20
UnreducedInterval computeLEPart(const UnreducedInterval &rhs) const
Return the part of the interval that is less than or equal to the rhs's upper bound.
Definition Intervals.cpp:71
llvm::APSInt safeMax(const llvm::APSInt &lhs, const llvm::APSInt &rhs)
Definition APIntHelper.h:98
FailureOr< Interval > operator/(const Interval &lhs, const Interval &rhs)
Interval operator%(const Interval &lhs, const Interval &rhs)
llvm::APSInt expandingAdd(llvm::APSInt lhs, llvm::APSInt rhs)
Safely add lhs and rhs, expanding the width of the result as necessary.
void ensure(bool condition, llvm::Twine errMsg)
Definition ErrorHelper.h:35
std::strong_ordering operator<=>(const UnreducedInterval &lhs, const UnreducedInterval &rhs)
llvm::APSInt expandingSub(llvm::APSInt lhs, llvm::APSInt rhs)
Safely subtract lhs and rhs, expanding the width of the result as necessary.
UnreducedInterval operator-(const UnreducedInterval &lhs, const UnreducedInterval &rhs)
bool safeGt(const llvm::APSInt &lhs, const llvm::APSInt &rhs)
Definition APIntHelper.h:82
bool safeEq(const llvm::APSInt &lhs, const llvm::APSInt &rhs)
Definition APIntHelper.h:74
Interval operator>>(const Interval &lhs, const Interval &rhs)
bool safeLt(const llvm::APSInt &lhs, const llvm::APSInt &rhs)
Definition APIntHelper.h:66
Interval operator&(const Interval &lhs, const Interval &rhs)
ExpressionValue boolXor(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
llvm::APSInt expandingMul(llvm::APSInt lhs, llvm::APSInt rhs)
Safely multiple lhs and rhs, expanding the width of the result as necessary.
raw_ostream & operator<<(raw_ostream &os, const ConstrainRef &rhs)
bool safeLe(const llvm::APSInt &lhs, const llvm::APSInt &rhs)
Definition APIntHelper.h:70
UnreducedInterval operator*(const UnreducedInterval &lhs, const UnreducedInterval &rhs)
bool safeGe(const llvm::APSInt &lhs, const llvm::APSInt &rhs)
Definition APIntHelper.h:86
ExpressionValue intersection(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
const Field & checkFields(const Interval &lhs, const Interval &rhs)
UnreducedInterval operator+(const UnreducedInterval &lhs, const UnreducedInterval &rhs)
ExpressionValue boolNot(llvm::SMTSolverRef solver, const ExpressionValue &val)
llvm::APSInt safeMin(const llvm::APSInt &lhs, const llvm::APSInt &rhs)
Definition APIntHelper.h:90
ExpressionValue boolOr(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
ExpressionValue boolAnd(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)