mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 15:46:07 +00:00

This should act like range. Previously ConstantRangeList assumed a 64-bit range. Now query from the actual entries. This also means that the empty range has no bitwidth, so move asserts to avoid checking the bitwidth of empty ranges.
247 lines
8.5 KiB
C++
247 lines
8.5 KiB
C++
//===- ConstantRangeList.cpp - ConstantRangeList implementation -----------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/IR/ConstantRangeList.h"
|
|
#include <cstddef>
|
|
|
|
using namespace llvm;
|
|
|
|
bool ConstantRangeList::isOrderedRanges(ArrayRef<ConstantRange> RangesRef) {
|
|
if (RangesRef.empty())
|
|
return true;
|
|
auto Range = RangesRef[0];
|
|
if (Range.getLower().sge(Range.getUpper()))
|
|
return false;
|
|
for (unsigned i = 1; i < RangesRef.size(); i++) {
|
|
auto CurRange = RangesRef[i];
|
|
auto PreRange = RangesRef[i - 1];
|
|
if (CurRange.getLower().sge(CurRange.getUpper()) ||
|
|
CurRange.getLower().sle(PreRange.getUpper()))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::optional<ConstantRangeList>
|
|
ConstantRangeList::getConstantRangeList(ArrayRef<ConstantRange> RangesRef) {
|
|
if (!isOrderedRanges(RangesRef))
|
|
return std::nullopt;
|
|
return ConstantRangeList(RangesRef);
|
|
}
|
|
|
|
void ConstantRangeList::insert(const ConstantRange &NewRange) {
|
|
if (NewRange.isEmptySet())
|
|
return;
|
|
assert(!NewRange.isFullSet() && "Do not support full set");
|
|
assert(NewRange.getLower().slt(NewRange.getUpper()));
|
|
// Handle common cases.
|
|
if (empty() || Ranges.back().getUpper().slt(NewRange.getLower())) {
|
|
Ranges.push_back(NewRange);
|
|
return;
|
|
}
|
|
|
|
assert(getBitWidth() == NewRange.getBitWidth());
|
|
|
|
if (NewRange.getUpper().slt(Ranges.front().getLower())) {
|
|
Ranges.insert(Ranges.begin(), NewRange);
|
|
return;
|
|
}
|
|
|
|
auto LowerBound = lower_bound(
|
|
Ranges, NewRange, [](const ConstantRange &a, const ConstantRange &b) {
|
|
return a.getLower().slt(b.getLower());
|
|
});
|
|
if (LowerBound != Ranges.end() && LowerBound->contains(NewRange))
|
|
return;
|
|
|
|
// Slow insert.
|
|
SmallVector<ConstantRange, 2> ExistingTail(LowerBound, Ranges.end());
|
|
Ranges.erase(LowerBound, Ranges.end());
|
|
// Merge consecutive ranges.
|
|
if (!Ranges.empty() && NewRange.getLower().sle(Ranges.back().getUpper())) {
|
|
APInt NewLower = Ranges.back().getLower();
|
|
APInt NewUpper =
|
|
APIntOps::smax(NewRange.getUpper(), Ranges.back().getUpper());
|
|
Ranges.back() = ConstantRange(NewLower, NewUpper);
|
|
} else {
|
|
Ranges.push_back(NewRange);
|
|
}
|
|
for (auto Iter = ExistingTail.begin(); Iter != ExistingTail.end(); Iter++) {
|
|
if (Ranges.back().getUpper().slt(Iter->getLower())) {
|
|
Ranges.push_back(*Iter);
|
|
} else {
|
|
APInt NewLower = Ranges.back().getLower();
|
|
APInt NewUpper =
|
|
APIntOps::smax(Iter->getUpper(), Ranges.back().getUpper());
|
|
Ranges.back() = ConstantRange(NewLower, NewUpper);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConstantRangeList::subtract(const ConstantRange &SubRange) {
|
|
if (SubRange.isEmptySet() || empty())
|
|
return;
|
|
assert(!SubRange.isFullSet() && "Do not support full set");
|
|
assert(SubRange.getLower().slt(SubRange.getUpper()));
|
|
assert(getBitWidth() == SubRange.getBitWidth());
|
|
// Handle common cases.
|
|
if (Ranges.back().getUpper().sle(SubRange.getLower()))
|
|
return;
|
|
if (SubRange.getUpper().sle(Ranges.front().getLower()))
|
|
return;
|
|
|
|
SmallVector<ConstantRange, 2> Result;
|
|
auto AppendRangeIfNonEmpty = [&Result](APInt Start, APInt End) {
|
|
if (Start.slt(End))
|
|
Result.push_back(ConstantRange(Start, End));
|
|
};
|
|
for (auto &Range : Ranges) {
|
|
if (SubRange.getUpper().sle(Range.getLower()) ||
|
|
Range.getUpper().sle(SubRange.getLower())) {
|
|
// "Range" and "SubRange" do not overlap.
|
|
// L---U : Range
|
|
// L---U : SubRange (Case1)
|
|
// L---U : SubRange (Case2)
|
|
Result.push_back(Range);
|
|
} else if (Range.getLower().sle(SubRange.getLower()) &&
|
|
SubRange.getUpper().sle(Range.getUpper())) {
|
|
// "Range" contains "SubRange".
|
|
// L---U : Range
|
|
// L-U : SubRange
|
|
// Note that ConstantRange::contains(ConstantRange) checks unsigned,
|
|
// but we need signed checking here.
|
|
AppendRangeIfNonEmpty(Range.getLower(), SubRange.getLower());
|
|
AppendRangeIfNonEmpty(SubRange.getUpper(), Range.getUpper());
|
|
} else if (SubRange.getLower().sle(Range.getLower()) &&
|
|
Range.getUpper().sle(SubRange.getUpper())) {
|
|
// "SubRange" contains "Range".
|
|
// L-U : Range
|
|
// L---U : SubRange
|
|
continue;
|
|
} else if (Range.getLower().sge(SubRange.getLower()) &&
|
|
Range.getLower().sle(SubRange.getUpper())) {
|
|
// "Range" and "SubRange" overlap at the left.
|
|
// L---U : Range
|
|
// L---U : SubRange
|
|
AppendRangeIfNonEmpty(SubRange.getUpper(), Range.getUpper());
|
|
} else {
|
|
// "Range" and "SubRange" overlap at the right.
|
|
// L---U : Range
|
|
// L---U : SubRange
|
|
assert(SubRange.getLower().sge(Range.getLower()) &&
|
|
SubRange.getLower().sle(Range.getUpper()));
|
|
AppendRangeIfNonEmpty(Range.getLower(), SubRange.getLower());
|
|
}
|
|
}
|
|
|
|
Ranges = Result;
|
|
}
|
|
|
|
ConstantRangeList
|
|
ConstantRangeList::unionWith(const ConstantRangeList &CRL) const {
|
|
// Handle common cases.
|
|
if (empty())
|
|
return CRL;
|
|
if (CRL.empty())
|
|
return *this;
|
|
|
|
assert(getBitWidth() == CRL.getBitWidth() &&
|
|
"ConstantRangeList bitwidths don't agree!");
|
|
|
|
ConstantRangeList Result;
|
|
size_t i = 0, j = 0;
|
|
// "PreviousRange" tracks the lowest unioned range that is being processed.
|
|
// Its lower is fixed and the upper may be updated over iterations.
|
|
ConstantRange PreviousRange(getBitWidth(), false);
|
|
if (Ranges[i].getLower().slt(CRL.Ranges[j].getLower())) {
|
|
PreviousRange = Ranges[i++];
|
|
} else {
|
|
PreviousRange = CRL.Ranges[j++];
|
|
}
|
|
|
|
// Try to union "PreviousRange" and "CR". If they are disjoint, push
|
|
// "PreviousRange" to the result and assign it to "CR", a new union range.
|
|
// Otherwise, update the upper of "PreviousRange" to cover "CR". Note that,
|
|
// the lower of "PreviousRange" is always less or equal the lower of "CR".
|
|
auto UnionAndUpdateRange = [&PreviousRange,
|
|
&Result](const ConstantRange &CR) {
|
|
if (PreviousRange.getUpper().slt(CR.getLower())) {
|
|
Result.Ranges.push_back(PreviousRange);
|
|
PreviousRange = CR;
|
|
} else {
|
|
PreviousRange = ConstantRange(
|
|
PreviousRange.getLower(),
|
|
APIntOps::smax(PreviousRange.getUpper(), CR.getUpper()));
|
|
}
|
|
};
|
|
while (i < size() || j < CRL.size()) {
|
|
if (j == CRL.size() ||
|
|
(i < size() && Ranges[i].getLower().slt(CRL.Ranges[j].getLower()))) {
|
|
// Merge PreviousRange with this.
|
|
UnionAndUpdateRange(Ranges[i++]);
|
|
} else {
|
|
// Merge PreviousRange with CRL.
|
|
UnionAndUpdateRange(CRL.Ranges[j++]);
|
|
}
|
|
}
|
|
Result.Ranges.push_back(PreviousRange);
|
|
return Result;
|
|
}
|
|
|
|
ConstantRangeList
|
|
ConstantRangeList::intersectWith(const ConstantRangeList &CRL) const {
|
|
// Handle common cases.
|
|
if (empty())
|
|
return *this;
|
|
if (CRL.empty())
|
|
return CRL;
|
|
|
|
assert(getBitWidth() == CRL.getBitWidth() &&
|
|
"ConstantRangeList bitwidths don't agree!");
|
|
|
|
ConstantRangeList Result;
|
|
size_t i = 0, j = 0;
|
|
while (i < size() && j < CRL.size()) {
|
|
auto &Range = this->Ranges[i];
|
|
auto &OtherRange = CRL.Ranges[j];
|
|
|
|
// The intersection of two Ranges is (max(lowers), min(uppers)), and it's
|
|
// possible that max(lowers) > min(uppers) if they don't have intersection.
|
|
// Add the intersection to result only if it's non-empty.
|
|
// To keep simple, we don't call ConstantRange::intersectWith() as it
|
|
// considers the complex upper wrapped case and may result two ranges,
|
|
// like (2, 8) && (6, 4) = {(2, 4), (6, 8)}.
|
|
APInt Start = APIntOps::smax(Range.getLower(), OtherRange.getLower());
|
|
APInt End = APIntOps::smin(Range.getUpper(), OtherRange.getUpper());
|
|
if (Start.slt(End))
|
|
Result.Ranges.push_back(ConstantRange(Start, End));
|
|
|
|
// Move to the next Range in one list determined by the uppers.
|
|
// For example: A = {(0, 2), (4, 8)}; B = {(-2, 5), (6, 10)}
|
|
// We need to intersect three pairs: A0 && B0; A1 && B0; A1 && B1.
|
|
if (Range.getUpper().slt(OtherRange.getUpper()))
|
|
i++;
|
|
else
|
|
j++;
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
void ConstantRangeList::print(raw_ostream &OS) const {
|
|
interleaveComma(Ranges, OS, [&](ConstantRange CR) {
|
|
OS << "(" << CR.getLower() << ", " << CR.getUpper() << ")";
|
|
});
|
|
}
|
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
LLVM_DUMP_METHOD void ConstantRangeList::dump() const {
|
|
print(dbgs());
|
|
dbgs() << '\n';
|
|
}
|
|
#endif
|