15#include <llvm/ADT/SmallVector.h>
35 const auto &half = field.
half();
37 if (lhs < half && rhs < half) {
39 }
else if (lhs < half) {
45 if (lhs >= half && rhs < half) {
54 const auto &lhs = *
this;
59 const auto &lhs = *
this;
67 DynamicAPInt bound = rhs.b - 1;
82 DynamicAPInt bound = rhs.a + 1;
101 DynamicAPInt low = lhs.a + rhs.a, high = lhs.b + rhs.b;
110 DynamicAPInt v1 = lhs.a * rhs.a;
111 DynamicAPInt v2 = lhs.a * rhs.b;
112 DynamicAPInt v3 = lhs.b * rhs.a;
113 DynamicAPInt v4 = lhs.b * rhs.b;
115 auto minVal = std::min({v1, v2, v3, v4});
116 auto maxVal = std::max({v1, v2, v3, v4});
126 if ((lhs.a < rhs.a) || ((lhs.a == rhs.a) && (lhs.b < rhs.b))) {
127 return std::strong_ordering::less;
129 if ((lhs.a > rhs.a) || ((lhs.a == rhs.a) && (lhs.b > rhs.b))) {
130 return std::strong_ordering::greater;
132 return std::strong_ordering::equal;
144 ensure(w >= 0,
"cannot have negative width");
152 lhs.
getField() == rhs.
getField(),
"interval operations across differing fields is unsupported"
159llvm::SmallVector<UnreducedInterval, 2> getUnsignedCanonicalParts(
const Interval &iv) {
161 llvm::SmallVector<UnreducedInterval, 2> parts;
166 parts.emplace_back(f.zero(), f.maxVal());
170 parts.emplace_back(iv.lhs(), f.maxVal());
171 parts.emplace_back(f.zero(), iv.rhs());
175 parts.emplace_back(iv.lhs(), iv.rhs());
179llvm::SmallVector<UnreducedInterval, 2> getSignedCanonicalParts(
const Interval &iv) {
181 llvm::SmallVector<UnreducedInterval, 2> parts;
186 parts.emplace_back(f.half() - f.prime(), f.half() - f.one());
189 if (iv.isDegenerate()) {
190 DynamicAPInt v = iv.lhs();
192 parts.emplace_back(v, v);
194 parts.emplace_back(v - f.prime(), v - f.prime());
199 parts.emplace_back(iv.lhs(), iv.rhs());
203 parts.emplace_back(iv.lhs() - f.prime(), iv.rhs() - f.prime());
207 parts.emplace_back(f.half() - f.prime(), iv.rhs() - f.prime());
208 parts.emplace_back(iv.lhs(), f.half() - f.one());
212 ensure(iv.isTypeF(),
"expected TypeF interval");
213 parts.emplace_back(iv.lhs() - f.prime(), iv.rhs());
217bool containsZero(
const UnreducedInterval &iv) {
return iv.getLHS() <= 0 && iv.getRHS() >= 0; }
220 const Field &f,
Interval acc,
const llvm::DynamicAPInt &q0,
const llvm::DynamicAPInt &q1,
221 const llvm::DynamicAPInt &q2,
const llvm::DynamicAPInt &q3
223 DynamicAPInt minQ = std::min({q0, q1, q2, q3});
224 DynamicAPInt maxQ = std::max({q0, q1, q2, q3});
226 return acc.
join(piece);
256 const auto &
lhs = *
this;
260 if (
lhs.isEntire() ||
rhs.isEntire()) {
269 if (
lhs.isDegenerate() ||
rhs.isDegenerate()) {
270 return lhs.toUnreduced().doUnion(
rhs.toUnreduced()).reduce(f);
277 auto newLhs = std::min(
lhs.a,
rhs.a);
278 auto newRhs = std::max(
lhs.b,
rhs.b);
279 if (newLhs == newRhs) {
285 auto lhsUnred =
lhs.firstUnreduced();
286 auto opt1 =
rhs.firstUnreduced().doUnion(lhsUnred);
287 auto opt2 =
rhs.secondUnreduced().doUnion(lhsUnred);
288 if (opt1.width() <= opt2.width()) {
289 return opt1.reduce(f);
291 return opt2.reduce(f);
294 return lhs.firstUnreduced().doUnion(
rhs.firstUnreduced()).reduce(f);
297 return lhs.secondUnreduced().doUnion(
rhs.firstUnreduced()).reduce(f);
309 llvm::report_fatal_error(
"unhandled join case");
314 const auto &
lhs = *
this;
320 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
323 if (
lhs.isEntire()) {
326 if (
rhs.isEntire()) {
329 if (
lhs.isDegenerate() &&
rhs.isDegenerate()) {
333 if (
lhs.isDegenerate()) {
336 if (
rhs.isDegenerate()) {
344 auto maxA = std::max(
lhs.a,
rhs.a);
345 auto minB = std::min(
lhs.b,
rhs.b);
348 }
else if (maxA == minB) {
358 return lhs.firstUnreduced().intersect(
rhs.firstUnreduced()).reduce(f);
361 return lhs.secondUnreduced().intersect(
rhs.firstUnreduced()).reduce(f);
364 auto rhsUnred =
rhs.firstUnreduced();
365 auto opt1 =
lhs.firstUnreduced().intersect(rhsUnred).reduce(f);
366 auto opt2 =
lhs.secondUnreduced().intersect(rhsUnred).reduce(f);
367 ensure(!opt1.isEntire() && !opt2.isEntire(),
"impossible intersection");
368 if (opt1.isEmpty()) {
371 if (opt2.isEmpty()) {
374 return opt1.join(opt2);
381 return rhs.intersect(
lhs);
402 if (other.a == f.
zero()) {
405 if (other.b == f.
maxVal()) {
456 if (
lhs.isEmpty() ||
rhs.isEntire()) {
459 if (
rhs.isEmpty() ||
lhs.isEntire()) {
462 return (
lhs.firstUnreduced() +
rhs.firstUnreduced()).reduce(f);
470 if (
lhs == zeroInterval ||
rhs == zeroInterval) {
473 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
476 if (
lhs.isEntire() ||
rhs.isEntire()) {
481 return (
lhs.secondUnreduced() *
rhs.secondUnreduced()).reduce(f);
483 return (
lhs.firstUnreduced() *
rhs.firstUnreduced()).reduce(f);
507 llvm::SmallVector<UnreducedInterval, 2> lhsParts = getUnsignedCanonicalParts(lhs);
508 llvm::SmallVector<UnreducedInterval, 2> rhsParts = getUnsignedCanonicalParts(rhs);
510 if (rhsPart.getLHS() == f.
zero()) {
518 result = joinDivisionPiece(
519 f, result, lhsPart.getLHS() / rhsPart.getRHS(), lhsPart.getLHS() / rhsPart.getLHS(),
520 lhsPart.getRHS() / rhsPart.getRHS(), lhsPart.getRHS() / rhsPart.getLHS()
524 return success(result);
533 llvm::SmallVector<UnreducedInterval, 2> lhsParts = getSignedCanonicalParts(lhs);
534 llvm::SmallVector<UnreducedInterval, 2> rhsParts = getSignedCanonicalParts(rhs);
536 if (containsZero(rhsPart)) {
544 result = joinDivisionPiece(
545 f, result, lhsPart.getLHS() / rhsPart.getLHS(), lhsPart.getLHS() / rhsPart.getRHS(),
546 lhsPart.getRHS() / rhsPart.getLHS(), lhsPart.getRHS() / rhsPart.getRHS()
550 return success(result);
555 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
559 if (
lhs.isDegenerate() &&
rhs.isDegenerate() &&
rhs.a != f.
zero()) {
563 if (
rhs.isDegenerate()) {
574 if (
rhs.isTypeF() ||
rhs.isEntire()) {
579 if (
rhs.intersect(zeroInt) == zeroInt) {
588 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
591 if (
lhs.isDegenerate() &&
rhs.isDegenerate()) {
593 }
else if (
lhs.isDegenerate()) {
595 }
else if (
rhs.isDegenerate()) {
603 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
607 if (
lhs == zeroInterval) {
610 if (
rhs == zeroInterval) {
613 if (
lhs.isDegenerate() &&
rhs.isDegenerate()) {
621 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
625 if (
lhs == zeroInterval) {
628 if (
rhs == zeroInterval) {
631 if (
lhs.isDegenerate() &&
rhs.isDegenerate()) {
639 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
642 if (
lhs.isDegenerate() &&
rhs.isDegenerate()) {
647 DynamicAPInt v =
lhs.a <<
rhs.a;
655 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
658 if (
lhs.isDegenerate() &&
rhs.isDegenerate()) {
671 return field.get().zero();
673 return field.get().one();
675 return field.get().prime();
683 lhs.getField() ==
rhs.getField(),
"interval operations across differing fields is unsupported"
685 ensure(
lhs.isBoolean() &&
rhs.isBoolean(),
"operation only supported for boolean-type intervals");
686 const auto &field =
rhs.getField();
688 if (
lhs.isBoolFalse() ||
rhs.isBoolFalse()) {
691 if (
lhs.isBoolTrue() &&
rhs.isBoolTrue()) {
700 lhs.getField() ==
rhs.getField(),
"interval operations across differing fields is unsupported"
702 ensure(
lhs.isBoolean() &&
rhs.isBoolean(),
"operation only supported for boolean-type intervals");
703 const auto &field =
rhs.getField();
705 if (
lhs.isBoolFalse() &&
rhs.isBoolFalse()) {
708 if (
lhs.isBoolTrue() ||
rhs.isBoolTrue()) {
717 lhs.getField() ==
rhs.getField(),
"interval operations across differing fields is unsupported"
719 ensure(
lhs.isBoolean() &&
rhs.isBoolean(),
"operation only supported for boolean-type intervals");
720 const auto &field =
rhs.getField();
724 if (
lhs.isBoolEither() ||
rhs.isBoolEither()) {
728 if (
lhs.isBoolTrue() &&
rhs.isBoolTrue()) {
731 if (
lhs.isBoolTrue() ||
rhs.isBoolTrue()) {
734 if (
lhs.isBoolFalse() &&
rhs.isBoolFalse()) {
742 ensure(iv.
isBoolean(),
"operation only supported for boolean-type intervals");
758 os <<
'(' << a <<
')';
760 os <<
":[ " << a <<
", " << b <<
" ]";
This file implements helper methods for constructing DynamicAPInts.
Information about the prime finite field used for the interval analysis.
llvm::DynamicAPInt half() const
Returns p / 2.
llvm::DynamicAPInt zero() const
Returns 0 at the bitwidth of the field.
llvm::DynamicAPInt prime() const
For the prime field p, returns p.
llvm::DynamicAPInt reduce(const llvm::DynamicAPInt &i) const
Returns i mod p and reduces the result into the appropriate bitwidth.
llvm::DynamicAPInt one() const
Returns 1 at the bitwidth of the field.
llvm::DynamicAPInt inv(const llvm::DynamicAPInt &i) const
Returns the multiplicative inverse of i in prime field p.
unsigned bitWidth() const
static const Field & getField(llvm::StringRef fieldName, EmitErrorFn errFn)
Get a Field from a given field name string.
llvm::DynamicAPInt maxVal() const
Returns p - 1, which is the max value possible in a prime field described by p.
Intervals over a finite field.
static Interval True(const Field &f)
llvm::DynamicAPInt rhs() const
Interval intersect(const Interval &rhs) const
Intersect.
static std::string_view TypeName(Type t)
void print(llvm::raw_ostream &os) const
UnreducedInterval toUnreduced() const
Convert to an UnreducedInterval.
static Interval Boolean(const Field &f)
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
const Field & getField() const
UnreducedInterval secondUnreduced() const
Get the second side of the interval for TypeA, TypeB, and TypeC intervals.
static Interval False(const Field &f)
static Interval Empty(const Field &f)
Interval operator~() const
llvm::DynamicAPInt lhs() const
static bool areOneOf(const Interval &a, const Interval &b)
Interval()
To satisfy the dataflow::ScalarLatticeValue requirements, this class must be default initializable.
static Interval Degenerate(const Field &f, const llvm::DynamicAPInt &val)
llvm::DynamicAPInt width() const
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...
UnreducedInterval operator-() const
UnreducedInterval intersect(const UnreducedInterval &rhs) const
Compute and return the intersection of this interval and the given RHS.
UnreducedInterval(const llvm::DynamicAPInt &x, const llvm::DynamicAPInt &y)
bool isEmpty() const
Returns true iff width() is zero.
UnreducedInterval computeLTPart(const UnreducedInterval &rhs) const
Return the part of the interval that is guaranteed to be less than the rhs's max value.
UnreducedInterval computeGEPart(const UnreducedInterval &rhs) const
Return the part of the interval that is greater than or equal to the rhs's lower bound.
bool overlaps(const UnreducedInterval &rhs) const
llvm::DynamicAPInt width() const
Compute the width of this interval within a given field f.
UnreducedInterval doUnion(const UnreducedInterval &rhs) const
Compute and return the union of this interval and the given RHS.
UnreducedInterval computeGTPart(const UnreducedInterval &rhs) const
Return the part of the interval that is greater than the rhs's lower bound.
Interval reduce(const Field &field) const
Reduce the interval to an interval in the given field.
UnreducedInterval computeLEPart(const UnreducedInterval &rhs) const
Return the part of the interval that is less than or equal to the rhs's upper bound.
ExpressionValue boolNot(const llvm::SMTSolverRef &solver, const ExpressionValue &val)
ExpressionValue intersection(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
FailureOr< Interval > signedIntDiv(const Interval &lhs, const Interval &rhs)
Computes signed integer division with possibly non-Degenerate divisors.
Interval operator|(const Interval &lhs, const Interval &rhs)
Interval operator^(const Interval &lhs, const Interval &rhs)
void ensure(bool condition, const llvm::Twine &errMsg)
ExpressionValue boolXor(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
Interval operator%(const Interval &lhs, const Interval &rhs)
Interval operator<<(const Interval &lhs, const Interval &rhs)
ExpressionValue boolAnd(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
FailureOr< Interval > unsignedIntDiv(const Interval &lhs, const Interval &rhs)
Computes unsigned integer division with possibly non-Degenerate divisors.
std::strong_ordering operator<=>(const UnreducedInterval &lhs, const UnreducedInterval &rhs)
UnreducedInterval operator-(const UnreducedInterval &lhs, const UnreducedInterval &rhs)
Interval operator>>(const Interval &lhs, const Interval &rhs)
Interval operator&(const Interval &lhs, const Interval &rhs)
UnreducedInterval operator*(const UnreducedInterval &lhs, const UnreducedInterval &rhs)
const Field & checkFields(const Interval &lhs, const Interval &rhs)
UnreducedInterval operator+(const UnreducedInterval &lhs, const UnreducedInterval &rhs)
FailureOr< Interval > feltDiv(const Interval &lhs, const Interval &rhs)
Computes finite-field division by multiplying the dividend by the multiplicative inverse of the divis...
ExpressionValue boolOr(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)