1 //===- Interval.h -----------------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // The Interval class is a generic interval of ordered objects that implement: 10 // - T * T::getPrevNode() 11 // - T * T::getNextNode() 12 // - bool T::comesBefore(const T *) const 13 // 14 // This is currently used for Instruction intervals. 15 // It provides an API for some basic operations on the interval, including some 16 // simple set operations, like union, intersection and others. 17 // 18 //===----------------------------------------------------------------------===// 19 20 #ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_INSTRINTERVAL_H 21 #define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_INSTRINTERVAL_H 22 23 #include "llvm/ADT/ArrayRef.h" 24 #include "llvm/SandboxIR/Instruction.h" 25 #include "llvm/Support/Compiler.h" 26 #include "llvm/Support/raw_ostream.h" 27 #include <iterator> 28 #include <type_traits> 29 30 namespace llvm::sandboxir { 31 32 /// A simple iterator for iterating the interval. 33 template <typename T, typename IntervalType> class IntervalIterator { 34 T *I; 35 IntervalType &R; 36 37 public: 38 using difference_type = std::ptrdiff_t; 39 using value_type = T; 40 using pointer = value_type *; 41 using reference = T &; 42 using iterator_category = std::bidirectional_iterator_tag; 43 IntervalIterator(T * I,IntervalType & R)44 IntervalIterator(T *I, IntervalType &R) : I(I), R(R) {} 45 bool operator==(const IntervalIterator &Other) const { 46 assert(&R == &Other.R && "Iterators belong to different regions!"); 47 return Other.I == I; 48 } 49 bool operator!=(const IntervalIterator &Other) const { 50 return !(*this == Other); 51 } 52 IntervalIterator &operator++() { 53 assert(I != nullptr && "already at end()!"); 54 I = I->getNextNode(); 55 return *this; 56 } 57 IntervalIterator operator++(int) { 58 auto ItCopy = *this; 59 ++*this; 60 return ItCopy; 61 } 62 IntervalIterator &operator--() { 63 // `I` is nullptr for end() when To is the BB terminator. 64 I = I != nullptr ? I->getPrevNode() : R.bottom(); 65 return *this; 66 } 67 IntervalIterator operator--(int) { 68 auto ItCopy = *this; 69 --*this; 70 return ItCopy; 71 } 72 template <typename HT = std::enable_if<std::is_same<T, T *&>::value>> 73 T &operator*() { 74 return *I; 75 } 76 T &operator*() const { return *I; } 77 }; 78 79 template <typename T> class Interval { 80 T *Top; 81 T *Bottom; 82 83 public: Interval()84 Interval() : Top(nullptr), Bottom(nullptr) {} Interval(T * Top,T * Bottom)85 Interval(T *Top, T *Bottom) : Top(Top), Bottom(Bottom) { 86 assert((Top == Bottom || Top->comesBefore(Bottom)) && 87 "Top should come before Bottom!"); 88 } Interval(ArrayRef<T * > Elems)89 Interval(ArrayRef<T *> Elems) { 90 assert(!Elems.empty() && "Expected non-empty Elems!"); 91 Top = Elems[0]; 92 Bottom = Elems[0]; 93 for (auto *I : drop_begin(Elems)) { 94 if (I->comesBefore(Top)) 95 Top = I; 96 else if (Bottom->comesBefore(I)) 97 Bottom = I; 98 } 99 } empty()100 bool empty() const { 101 assert(((Top == nullptr && Bottom == nullptr) || 102 (Top != nullptr && Bottom != nullptr)) && 103 "Either none or both should be null"); 104 return Top == nullptr; 105 } contains(T * I)106 bool contains(T *I) const { 107 if (empty()) 108 return false; 109 return (Top == I || Top->comesBefore(I)) && 110 (I == Bottom || I->comesBefore(Bottom)); 111 } 112 /// \Returns true if \p Elm is right before the top or right after the bottom. touches(T * Elm)113 bool touches(T *Elm) const { 114 return Top == Elm->getNextNode() || Bottom == Elm->getPrevNode(); 115 } top()116 T *top() const { return Top; } bottom()117 T *bottom() const { return Bottom; } 118 119 using iterator = IntervalIterator<T, Interval>; begin()120 iterator begin() { return iterator(Top, *this); } end()121 iterator end() { 122 return iterator(Bottom != nullptr ? Bottom->getNextNode() : nullptr, *this); 123 } begin()124 iterator begin() const { 125 return iterator(Top, const_cast<Interval &>(*this)); 126 } end()127 iterator end() const { 128 return iterator(Bottom != nullptr ? Bottom->getNextNode() : nullptr, 129 const_cast<Interval &>(*this)); 130 } 131 /// Equality. 132 bool operator==(const Interval &Other) const { 133 return Top == Other.Top && Bottom == Other.Bottom; 134 } 135 /// Inequality. 136 bool operator!=(const Interval &Other) const { return !(*this == Other); } 137 /// \Returns true if this interval comes before \p Other in program order. 138 /// This expects disjoint intervals. comesBefore(const Interval & Other)139 bool comesBefore(const Interval &Other) const { 140 assert(disjoint(Other) && "Expect disjoint intervals!"); 141 return bottom()->comesBefore(Other.top()); 142 } 143 /// \Returns true if this and \p Other have nothing in common. 144 bool disjoint(const Interval &Other) const; 145 /// \Returns the intersection between this and \p Other. 146 // Example: 147 // |----| this 148 // |---| Other 149 // |-| this->getIntersection(Other) intersection(const Interval & Other)150 Interval intersection(const Interval &Other) const { 151 if (empty()) 152 return *this; 153 if (Other.empty()) 154 return Interval(); 155 // 1. No overlap 156 // A---B this 157 // C--D Other 158 if (Bottom->comesBefore(Other.Top) || Other.Bottom->comesBefore(Top)) 159 return Interval(); 160 // 2. Overlap. 161 // A---B this 162 // C--D Other 163 auto NewTopI = Top->comesBefore(Other.Top) ? Other.Top : Top; 164 auto NewBottomI = Bottom->comesBefore(Other.Bottom) ? Bottom : Other.Bottom; 165 return Interval(NewTopI, NewBottomI); 166 } 167 /// Difference operation. This returns up to two intervals. 168 // Example: 169 // |--------| this 170 // |-| Other 171 // |-| |--| this - Other 172 SmallVector<Interval, 2> operator-(const Interval &Other) { 173 if (disjoint(Other)) 174 return {*this}; 175 if (Other.empty()) 176 return {*this}; 177 if (*this == Other) 178 return {Interval()}; 179 Interval Intersection = intersection(Other); 180 SmallVector<Interval, 2> Result; 181 // Part 1, skip if empty. 182 if (Top != Intersection.Top) 183 Result.emplace_back(Top, Intersection.Top->getPrevNode()); 184 // Part 2, skip if empty. 185 if (Intersection.Bottom != Bottom) 186 Result.emplace_back(Intersection.Bottom->getNextNode(), Bottom); 187 return Result; 188 } 189 /// \Returns the interval difference `this - Other`. This will crash in Debug 190 /// if the result is not a single interval. getSingleDiff(const Interval & Other)191 Interval getSingleDiff(const Interval &Other) { 192 auto Diff = *this - Other; 193 assert(Diff.size() == 1 && "Expected a single interval!"); 194 return Diff[0]; 195 } 196 /// \Returns a single interval that spans across both this and \p Other. 197 // For example: 198 // |---| this 199 // |---| Other 200 // |----------| this->getUnionInterval(Other) getUnionInterval(const Interval & Other)201 Interval getUnionInterval(const Interval &Other) { 202 if (empty()) 203 return Other; 204 if (Other.empty()) 205 return *this; 206 auto *NewTop = Top->comesBefore(Other.Top) ? Top : Other.Top; 207 auto *NewBottom = Bottom->comesBefore(Other.Bottom) ? Other.Bottom : Bottom; 208 return {NewTop, NewBottom}; 209 } 210 211 /// Update the interval when \p I is about to be moved before \p Before. 212 // SFINAE disables this for non-Instructions. 213 template <typename HelperT = T> 214 std::enable_if_t<std::is_same<HelperT, Instruction>::value, void> 215 notifyMoveInstr(HelperT *I, decltype(I->getIterator()) BeforeIt) { 216 assert(contains(I) && "Expect `I` in interval!"); 217 assert(I->getIterator() != BeforeIt && "Can't move `I` before itself!"); 218 219 // Nothing to do if the instruction won't move. 220 if (std::next(I->getIterator()) == BeforeIt) 221 return; 222 223 T *NewTop = Top->getIterator() == BeforeIt ? I 224 : I == Top ? Top->getNextNode() 225 : Top; 226 T *NewBottom = std::next(Bottom->getIterator()) == BeforeIt ? I 227 : I == Bottom ? Bottom->getPrevNode() 228 : Bottom; 229 Top = NewTop; 230 Bottom = NewBottom; 231 } 232 233 #ifndef NDEBUG 234 void print(raw_ostream &OS) const; 235 LLVM_DUMP_METHOD void dump() const; 236 #endif 237 }; 238 239 // Defined in Transforms/Vectorize/SandboxVectorizer/Interval.cpp 240 extern template class LLVM_TEMPLATE_ABI Interval<Instruction>; 241 242 } // namespace llvm::sandboxir 243 244 #endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_INSTRINTERVAL_H 245