//===- 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 using namespace llvm; bool ConstantRangeList::isOrderedRanges(ArrayRef 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::getConstantRangeList(ArrayRef 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())); assert(getBitWidth() == NewRange.getBitWidth()); // Handle common cases. if (empty() || Ranges.back().getUpper().slt(NewRange.getLower())) { Ranges.push_back(NewRange); return; } 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 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 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 { assert(getBitWidth() == CRL.getBitWidth() && "ConstantRangeList bitwidths don't agree!"); // Handle common cases. if (empty()) return CRL; if (CRL.empty()) return *this; 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 { assert(getBitWidth() == CRL.getBitWidth() && "ConstantRangeList bitwidths don't agree!"); // Handle common cases. if (empty()) return *this; if (CRL.empty()) return CRL; 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