xref: /freebsd/contrib/llvm-project/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/Interval.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
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