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; }
219llvm::DynamicAPInt absSigned(
const llvm::DynamicAPInt &v) {
return v < 0 ? -v : v; }
222 const Field &f,
const Interval &acc,
const llvm::DynamicAPInt &q0,
const llvm::DynamicAPInt &q1,
223 const llvm::DynamicAPInt &q2,
const llvm::DynamicAPInt &q3
225 DynamicAPInt minQ = std::min({q0, q1, q2, q3});
226 DynamicAPInt maxQ = std::max({q0, q1, q2, q3});
228 return acc.
join(piece);
258 const auto &
lhs = *
this;
262 if (
lhs.isEntire() ||
rhs.isEntire()) {
271 if (
lhs.isDegenerate() ||
rhs.isDegenerate()) {
272 return lhs.toUnreduced().doUnion(
rhs.toUnreduced()).reduce(f);
279 auto newLhs = std::min(
lhs.a,
rhs.a);
280 auto newRhs = std::max(
lhs.b,
rhs.b);
281 if (newLhs == newRhs) {
287 auto lhsUnred =
lhs.firstUnreduced();
288 auto opt1 =
rhs.firstUnreduced().doUnion(lhsUnred);
289 auto opt2 =
rhs.secondUnreduced().doUnion(lhsUnred);
290 if (opt1.width() <= opt2.width()) {
291 return opt1.reduce(f);
293 return opt2.reduce(f);
296 return lhs.firstUnreduced().doUnion(
rhs.firstUnreduced()).reduce(f);
299 return lhs.secondUnreduced().doUnion(
rhs.firstUnreduced()).reduce(f);
311 llvm::report_fatal_error(
"unhandled join case");
316 const auto &
lhs = *
this;
322 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
325 if (
lhs.isEntire()) {
328 if (
rhs.isEntire()) {
331 if (
lhs.isDegenerate() &&
rhs.isDegenerate()) {
335 if (
lhs.isDegenerate()) {
338 if (
rhs.isDegenerate()) {
346 auto maxA = std::max(
lhs.a,
rhs.a);
347 auto minB = std::min(
lhs.b,
rhs.b);
350 }
else if (maxA == minB) {
360 return lhs.firstUnreduced().intersect(
rhs.firstUnreduced()).reduce(f);
363 return lhs.secondUnreduced().intersect(
rhs.firstUnreduced()).reduce(f);
366 auto rhsUnred =
rhs.firstUnreduced();
367 auto opt1 =
lhs.firstUnreduced().intersect(rhsUnred).reduce(f);
368 auto opt2 =
lhs.secondUnreduced().intersect(rhsUnred).reduce(f);
369 ensure(!opt1.isEntire() && !opt2.isEntire(),
"impossible intersection");
370 if (opt1.isEmpty()) {
373 if (opt2.isEmpty()) {
376 return opt1.join(opt2);
383 return rhs.intersect(
lhs);
404 if (other.a == f.
zero()) {
407 if (other.b == f.
maxVal()) {
458 if (
lhs.isEmpty() ||
rhs.isEntire()) {
461 if (
rhs.isEmpty() ||
lhs.isEntire()) {
464 return (
lhs.firstUnreduced() +
rhs.firstUnreduced()).reduce(f);
472 if (
lhs == zeroInterval ||
rhs == zeroInterval) {
475 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
478 if (
lhs.isEntire() ||
rhs.isEntire()) {
483 return (
lhs.secondUnreduced() *
rhs.secondUnreduced()).reduce(f);
485 return (
lhs.firstUnreduced() *
rhs.firstUnreduced()).reduce(f);
509 llvm::SmallVector<UnreducedInterval, 2> lhsParts = getUnsignedCanonicalParts(lhs);
510 llvm::SmallVector<UnreducedInterval, 2> rhsParts = getUnsignedCanonicalParts(rhs);
512 if (rhsPart.getLHS() == f.
zero()) {
520 result = joinDivisionPiece(
521 f, result, lhsPart.getLHS() / rhsPart.getRHS(), lhsPart.getLHS() / rhsPart.getLHS(),
522 lhsPart.getRHS() / rhsPart.getRHS(), lhsPart.getRHS() / rhsPart.getLHS()
526 return success(result);
535 llvm::SmallVector<UnreducedInterval, 2> lhsParts = getSignedCanonicalParts(lhs);
536 llvm::SmallVector<UnreducedInterval, 2> rhsParts = getSignedCanonicalParts(rhs);
538 if (containsZero(rhsPart)) {
546 result = joinDivisionPiece(
547 f, result, lhsPart.getLHS() / rhsPart.getLHS(), lhsPart.getLHS() / rhsPart.getRHS(),
548 lhsPart.getRHS() / rhsPart.getLHS(), lhsPart.getRHS() / rhsPart.getRHS()
552 return success(result);
561 llvm::SmallVector<UnreducedInterval, 2> lhsParts = getSignedCanonicalParts(lhs);
562 llvm::SmallVector<UnreducedInterval, 2> rhsParts = getSignedCanonicalParts(rhs);
567 if (containsZero(rhsPart)) {
571 if (lhsPart.getLHS() == lhsPart.getRHS() && rhsPart.getLHS() == rhsPart.getRHS()) {
572 auto rem = lhsPart.getLHS() % rhsPart.getLHS();
577 llvm::DynamicAPInt maxAbsDivisor =
578 std::max(absSigned(rhsPart.getLHS()), absSigned(rhsPart.getRHS()));
579 if (maxAbsDivisor == 0) {
583 llvm::DynamicAPInt maxAbsRemainder = maxAbsDivisor - 1;
584 llvm::DynamicAPInt low = lhsPart.getLHS() < 0 ? std::max(lhsPart.getLHS(), -maxAbsRemainder)
585 : llvm::DynamicAPInt(0);
586 llvm::DynamicAPInt high = lhsPart.getRHS() > 0 ? std::min(lhsPart.getRHS(), maxAbsRemainder)
587 : llvm::DynamicAPInt(0);
597 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
601 if (
lhs.isDegenerate() &&
rhs.isDegenerate() &&
rhs.a != f.
zero()) {
605 if (
rhs.isDegenerate()) {
616 if (
rhs.isTypeF() ||
rhs.isEntire()) {
621 if (
rhs.intersect(zeroInt) == zeroInt) {
630 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
633 if (
lhs.isDegenerate() &&
rhs.isDegenerate()) {
635 }
else if (
lhs.isDegenerate()) {
637 }
else if (
rhs.isDegenerate()) {
645 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
649 if (
lhs == zeroInterval) {
652 if (
rhs == zeroInterval) {
655 if (
lhs.isDegenerate() &&
rhs.isDegenerate()) {
663 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
667 if (
lhs == zeroInterval) {
670 if (
rhs == zeroInterval) {
673 if (
lhs.isDegenerate() &&
rhs.isDegenerate()) {
681 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
684 if (
lhs.isDegenerate() &&
rhs.isDegenerate()) {
689 DynamicAPInt v =
lhs.a <<
rhs.a;
697 if (
lhs.isEmpty() ||
rhs.isEmpty()) {
700 if (
lhs.isDegenerate() &&
rhs.isDegenerate()) {
713 return field.get().zero();
715 return field.get().one();
717 return field.get().prime();
725 lhs.getField() ==
rhs.getField(),
"interval operations across differing fields is unsupported"
727 ensure(
lhs.isBoolean() &&
rhs.isBoolean(),
"operation only supported for boolean-type intervals");
728 const auto &field =
rhs.getField();
730 if (
lhs.isBoolFalse() ||
rhs.isBoolFalse()) {
733 if (
lhs.isBoolTrue() &&
rhs.isBoolTrue()) {
742 lhs.getField() ==
rhs.getField(),
"interval operations across differing fields is unsupported"
744 ensure(
lhs.isBoolean() &&
rhs.isBoolean(),
"operation only supported for boolean-type intervals");
745 const auto &field =
rhs.getField();
747 if (
lhs.isBoolFalse() &&
rhs.isBoolFalse()) {
750 if (
lhs.isBoolTrue() ||
rhs.isBoolTrue()) {
759 lhs.getField() ==
rhs.getField(),
"interval operations across differing fields is unsupported"
761 ensure(
lhs.isBoolean() &&
rhs.isBoolean(),
"operation only supported for boolean-type intervals");
762 const auto &field =
rhs.getField();
766 if (
lhs.isBoolEither() ||
rhs.isBoolEither()) {
770 if (
lhs.isBoolTrue() &&
rhs.isBoolTrue()) {
773 if (
lhs.isBoolTrue() ||
rhs.isBoolTrue()) {
776 if (
lhs.isBoolFalse() &&
rhs.isBoolFalse()) {
784 ensure(iv.
isBoolean(),
"operation only supported for boolean-type intervals");
800 os <<
'(' << a <<
')';
802 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)
Interval signedMod(const Interval &lhs, const Interval &rhs)
Computes signed integer remainder with possibly non-Degenerate divisors.
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)