LLVM 22.0.0git
SeedCollector.h
Go to the documentation of this file.
1//===- SeedCollector.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// This file contains the mechanism for collecting the seed instructions that
9// are used as starting points for forming the vectorization graph.
10//===----------------------------------------------------------------------===//
11
12#ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SEEDCOLLECTOR_H
13#define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SEEDCOLLECTOR_H
14
15#include "llvm/ADT/BitVector.h"
22#include <iterator>
23#include <memory>
24
25namespace llvm::sandboxir {
26
27/// A set of candidate Instructions for vectorizing together.
29public:
30 /// Initialize a bundle with \p I.
31 explicit SeedBundle(Instruction *I) { insertAt(begin(), I); }
33 for (auto &S : Seeds)
35 }
36 /// No need to allow copies.
37 SeedBundle(const SeedBundle &) = delete;
38 SeedBundle &operator=(const SeedBundle &) = delete;
39 virtual ~SeedBundle() {}
40
43 iterator begin() { return Seeds.begin(); }
44 iterator end() { return Seeds.end(); }
45 const_iterator begin() const { return Seeds.begin(); }
46 const_iterator end() const { return Seeds.end(); }
47
48 Instruction *operator[](unsigned Idx) const { return Seeds[Idx]; }
49
50 /// Insert \p I into position \p P. Clients should choose Pos
51 /// by symbol, symbol-offset, and program order (which depends if scheduling
52 /// bottom-up or top-down).
54 Seeds.insert(Pos, I);
56 }
57
58 virtual void insert(Instruction *I, ScalarEvolution &SE) = 0;
59
60 unsigned getFirstUnusedElementIdx() const {
61 for (unsigned ElmIdx : seq<unsigned>(0, Seeds.size()))
62 if (!isUsed(ElmIdx))
63 return ElmIdx;
64 return Seeds.size();
65 }
66 /// Marks instruction \p I "used" within the bundle. Clients
67 /// use this property when assembling a vectorized instruction from
68 /// the seeds in a bundle. This allows constant time evaluation
69 /// and "removal" from the list.
71 auto It = llvm::find(*this, I);
72 assert(It != end() && "Instruction not in the bundle!");
73 auto Idx = It - begin();
74 setUsed(Idx, 1, /*VerifyUnused=*/false);
75 }
76
77 void setUsed(unsigned ElementIdx, unsigned Sz = 1, bool VerifyUnused = true) {
78 if (ElementIdx + Sz >= UsedLanes.size())
79 UsedLanes.resize(ElementIdx + Sz);
80 for (unsigned Idx : seq<unsigned>(ElementIdx, ElementIdx + Sz)) {
81 assert((!VerifyUnused || !UsedLanes.test(Idx)) &&
82 "Already marked as used!");
85 }
87 }
88 /// \Returns whether or not \p Element has been used.
89 bool isUsed(unsigned Element) const {
90 return Element < UsedLanes.size() && UsedLanes.test(Element);
91 }
92 bool allUsed() const { return UsedLaneCount == Seeds.size(); }
93 unsigned getNumUnusedBits() const { return NumUnusedBits; }
94
95 /// \Returns a slice of seed elements, starting at the element \p StartIdx,
96 /// with a total size <= \p MaxVecRegBits, or an empty slice if the
97 /// requirements cannot be met . If \p ForcePowOf2 is true, then the returned
98 /// slice will have a total number of bits that is a power of 2.
100 getSlice(unsigned StartIdx, unsigned MaxVecRegBits, bool ForcePowOf2);
101
102 /// \Returns the number of seed elements in the bundle.
103 std::size_t size() const { return Seeds.size(); }
104
105protected:
107 /// The lanes that we have already vectorized.
109 /// Tracks used lanes for constant-time accessor.
110 unsigned UsedLaneCount = 0;
111 /// Tracks the remaining bits available to vectorize
112 unsigned NumUnusedBits = 0;
113
114public:
115#ifndef NDEBUG
116 void dump(raw_ostream &OS) const {
117 for (auto [ElmIdx, I] : enumerate(*this)) {
118 OS.indent(2) << ElmIdx << ". ";
119 if (isUsed(ElmIdx))
120 OS << "[USED]";
121 else
122 OS << *I;
123 OS << "\n";
124 }
125 }
126 LLVM_DUMP_METHOD void dump() const {
127 dump(dbgs());
128 dbgs() << "\n";
129 }
130#endif // NDEBUG
131};
132
133/// Specialization of SeedBundle for memory access instructions. Keeps
134/// seeds sorted in ascending memory order, which is convenient for slicing
135/// these bundles into vectorizable groups.
136template <typename LoadOrStoreT> class MemSeedBundle : public SeedBundle {
137public:
139 : SeedBundle(std::move(SV)) {
140 static_assert(std::is_same<LoadOrStoreT, LoadInst>::value ||
141 std::is_same<LoadOrStoreT, StoreInst>::value,
142 "Expected LoadInst or StoreInst!");
143 assert(all_of(Seeds, [](auto *S) { return isa<LoadOrStoreT>(S); }) &&
144 "Expected Load or Store instructions!");
145 auto Cmp = [&SE](Instruction *I0, Instruction *I1) {
146 return Utils::atLowerAddress(cast<LoadOrStoreT>(I0),
147 cast<LoadOrStoreT>(I1), SE);
148 };
149 std::sort(Seeds.begin(), Seeds.end(), Cmp);
150 }
151 explicit MemSeedBundle(LoadOrStoreT *MemI) : SeedBundle(MemI) {
152 static_assert(std::is_same<LoadOrStoreT, LoadInst>::value ||
153 std::is_same<LoadOrStoreT, StoreInst>::value,
154 "Expected LoadInst or StoreInst!");
155 assert(isa<LoadOrStoreT>(MemI) && "Expected Load or Store!");
156 }
158 assert(isa<LoadOrStoreT>(I) && "Expected a Store or a Load!");
159 auto Cmp = [&SE](Instruction *I0, Instruction *I1) {
160 return Utils::atLowerAddress(cast<LoadOrStoreT>(I0),
161 cast<LoadOrStoreT>(I1), SE);
162 };
163 // Find the first element after I in mem. Then insert I before it.
164 insertAt(llvm::upper_bound(*this, I, Cmp), I);
165 }
166};
167
170
171/// Class to conveniently track Seeds within SeedBundles. Saves newly collected
172/// seeds in the proper bundle. Supports constant-time removal, as seeds and
173/// entire bundles are vectorized and marked used to signify removal. Iterators
174/// skip bundles that are completely used.
176 // Use the same key for different seeds if they are the same type and
177 // reference the same pointer, even if at different offsets. This directs
178 // potentially vectorizable seeds into the same bundle.
179 using KeyT = std::tuple<Value *, Type *, Instruction::Opcode>;
180 // Trying to vectorize too many seeds at once is expensive in
181 // compilation-time. Use a vector of bundles (all with the same key) to
182 // partition the candidate set into more manageable units. Each bundle is
183 // size-limited by sbvec-seed-bundle-size-limit. TODO: There might be a
184 // better way to divide these than by simple insertion order.
187 // Map from {pointer, Type, Opcode} to a vector of bundles.
188 BundleMapT Bundles;
189 // Allows finding a particular Instruction's bundle.
191
192 ScalarEvolution &SE;
193
194 template <typename LoadOrStoreT>
195 KeyT getKey(LoadOrStoreT *LSI, bool AllowDiffTypes) const;
196
197public:
199
200 class iterator {
201 BundleMapT *Map = nullptr;
203 ValT *Vec = nullptr;
204 size_t VecIdx;
205
206 public:
207 using difference_type = std::ptrdiff_t;
211 using iterator_category = std::input_iterator_tag;
212
213 /// Iterates over the \p Map of SeedBundle Vectors, starting at \p MapIt,
214 /// and \p Vec at \p VecIdx, skipping vectors that are completely
215 /// used. Iteration order over the keys {Pointer, Type, Opcode} follows
216 /// DenseMap iteration order. For a given key, the vectors of
217 /// SeedBundles will be returned in insertion order. As in the
218 /// pseudo code below:
219 ///
220 /// for Key,Value in Bundles
221 /// for SeedBundleVector in Value
222 /// for SeedBundle in SeedBundleVector
223 /// if !SeedBundle.allUsed() ...
224 ///
225 /// Note that the bundles themselves may have additional ordering, created
226 /// by the subclasses by insertAt. The bundles themselves may also have used
227 /// instructions.
228
229 // TODO: Range_size counts fully used-bundles. Further, iterating over
230 // anything other than the Bundles in a SeedContainer includes used
231 // seeds. Rework the iterator logic to clean this up.
232 iterator(BundleMapT &Map, BundleMapT::iterator MapIt, ValT *Vec, int VecIdx)
233 : Map(&Map), MapIt(MapIt), Vec(Vec), VecIdx(VecIdx) {}
235 assert(Vec != nullptr && "Already at end!");
236 return *(*Vec)[VecIdx];
237 }
238 // Skip completely used bundles by repeatedly calling operator++().
239 void skipUsed() {
240 while (Vec && VecIdx < Vec->size() && this->operator*().allUsed())
241 ++(*this);
242 }
243 // Iterators iterate over the bundles
245 ++VecIdx;
246 if (VecIdx >= Vec->size()) {
247 assert(MapIt != Map->end() && "Already at end!");
248 VecIdx = 0;
249 ++MapIt;
250 if (MapIt != Map->end())
251 Vec = &MapIt->second;
252 else {
253 Vec = nullptr;
254 }
255 }
256 skipUsed();
257 return *this;
258 }
260 auto Copy = *this;
261 ++(*this);
262 return Copy;
263 }
264 bool operator==(const iterator &Other) const {
265 assert(Map == Other.Map && "Iterator of different objects!");
266 return MapIt == Other.MapIt && VecIdx == Other.VecIdx;
267 }
268 bool operator!=(const iterator &Other) const { return !(*this == Other); }
269 };
271 template <typename LoadOrStoreT>
272 void insert(LoadOrStoreT *LSI, bool AllowDiffTypes);
273 // To support constant-time erase, these just mark the element used, rather
274 // than actually removing them from the bundle.
276 bool erase(const KeyT &Key) { return Bundles.erase(Key); }
278 if (Bundles.empty())
279 return end();
280 auto BeginIt =
281 iterator(Bundles, Bundles.begin(), &Bundles.begin()->second, 0);
282 BeginIt.skipUsed();
283 return BeginIt;
284 }
285 iterator end() { return iterator(Bundles, Bundles.end(), nullptr, 0); }
286 unsigned size() const { return Bundles.size(); }
287
288#ifndef NDEBUG
289 void print(raw_ostream &OS) const;
290 LLVM_DUMP_METHOD void dump() const;
291#endif // NDEBUG
292};
293
294// Explicit instantiations
295extern template LLVM_TEMPLATE_ABI void
296SeedContainer::insert<LoadInst>(LoadInst *, bool);
297extern template LLVM_TEMPLATE_ABI void
298SeedContainer::insert<StoreInst>(StoreInst *, bool);
299
301 SeedContainer StoreSeeds;
302 SeedContainer LoadSeeds;
303 Context &Ctx;
304 Context::CallbackID EraseCallbackID;
305 /// \Returns the number of SeedBundle groups for all seed types.
306 /// This is to be used for limiting compilation time.
307 unsigned totalNumSeedGroups() const {
308 return StoreSeeds.size() + LoadSeeds.size();
309 }
310
311public:
313 bool CollectStores, bool CollectLoads,
314 bool AllowDiffTypes = false);
316
318 return {StoreSeeds.begin(), StoreSeeds.end()};
319 }
321 return {LoadSeeds.begin(), LoadSeeds.end()};
322 }
323#ifndef NDEBUG
324 void print(raw_ostream &OS) const;
325 LLVM_DUMP_METHOD void dump() const;
326#endif
327};
328
329} // namespace llvm::sandboxir
330
331#endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SEEDCOLLECTOR_H
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file implements the BitVector class.
#define LLVM_ABI
Definition: Compiler.h:213
#define LLVM_TEMPLATE_ABI
Definition: Compiler.h:214
#define LLVM_DUMP_METHOD
Mark debug helper function definitions like dump() that should not be stripped from debug builds.
Definition: Compiler.h:638
Returns the sub type a function will return at a given Idx Should correspond to the result type of an ExtractValue instruction executed with just that one unsigned Idx
#define I(x, y, z)
Definition: MD5.cpp:58
raw_pwrite_stream & OS
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
bool test(unsigned Idx) const
Definition: BitVector.h:461
void resize(unsigned N, bool t=false)
resize - Grow or shrink the bitvector.
Definition: BitVector.h:341
BitVector & set()
Definition: BitVector.h:351
size_type size() const
size - Returns the number of bits in this bitvector.
Definition: BitVector.h:159
An instruction for reading from memory.
Definition: Instructions.h:180
typename VectorType::iterator iterator
Definition: MapVector.h:42
iterator end()
Definition: MapVector.h:67
VectorType::iterator erase(typename VectorType::iterator Iterator)
Remove the element given by Iterator.
Definition: MapVector.h:167
bool empty() const
Definition: MapVector.h:75
iterator begin()
Definition: MapVector.h:65
size_type size() const
Definition: MapVector.h:56
typename VectorType::const_iterator const_iterator
Definition: MapVector.h:43
The main scalar evolution driver.
size_t size() const
Definition: SmallVector.h:79
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1197
An instruction for storing to memory.
Definition: Instructions.h:296
A range adaptor for a pair of iterators.
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:53
Contains a list of sandboxir::Instruction's.
Definition: BasicBlock.h:68
An ID for a registered callback.
Definition: Context.h:49
A sandboxir::User with operands, opcode and linked with previous/next instructions in an instruction ...
Definition: Instruction.h:43
Specialization of SeedBundle for memory access instructions.
MemSeedBundle(SmallVector< Instruction * > &&SV, ScalarEvolution &SE)
void insert(sandboxir::Instruction *I, ScalarEvolution &SE) override
MemSeedBundle(LoadOrStoreT *MemI)
A set of candidate Instructions for vectorizing together.
Definition: SeedCollector.h:28
SeedBundle & operator=(const SeedBundle &)=delete
bool isUsed(unsigned Element) const
\Returns whether or not Element has been used.
Definition: SeedCollector.h:89
SmallVector< Instruction * > Seeds
void setUsed(unsigned ElementIdx, unsigned Sz=1, bool VerifyUnused=true)
Definition: SeedCollector.h:77
LLVM_ABI ArrayRef< Instruction * > getSlice(unsigned StartIdx, unsigned MaxVecRegBits, bool ForcePowOf2)
\Returns a slice of seed elements, starting at the element StartIdx, with a total size <= MaxVecRegBi...
void insertAt(iterator Pos, Instruction *I)
Insert I into position P.
Definition: SeedCollector.h:53
unsigned getNumUnusedBits() const
Definition: SeedCollector.h:93
const_iterator begin() const
Definition: SeedCollector.h:45
unsigned NumUnusedBits
Tracks the remaining bits available to vectorize.
SeedBundle(const SeedBundle &)=delete
No need to allow copies.
void setUsed(Instruction *I)
Marks instruction I "used" within the bundle.
Definition: SeedCollector.h:70
SeedBundle(SmallVector< Instruction * > &&L)
Definition: SeedCollector.h:32
virtual void insert(Instruction *I, ScalarEvolution &SE)=0
SeedBundle(Instruction *I)
Initialize a bundle with I.
Definition: SeedCollector.h:31
unsigned getFirstUnusedElementIdx() const
Definition: SeedCollector.h:60
std::size_t size() const
\Returns the number of seed elements in the bundle.
Instruction * operator[](unsigned Idx) const
Definition: SeedCollector.h:48
BitVector UsedLanes
The lanes that we have already vectorized.
const_iterator end() const
Definition: SeedCollector.h:46
unsigned UsedLaneCount
Tracks used lanes for constant-time accessor.
void dump(raw_ostream &OS) const
LLVM_DUMP_METHOD void dump() const
iterator_range< SeedContainer::iterator > getLoadSeeds()
iterator_range< SeedContainer::iterator > getStoreSeeds()
bool operator==(const iterator &Other) const
iterator(BundleMapT &Map, BundleMapT::iterator MapIt, ValT *Vec, int VecIdx)
Iterates over the Map of SeedBundle Vectors, starting at MapIt, and Vec at VecIdx,...
std::input_iterator_tag iterator_category
bool operator!=(const iterator &Other) const
Class to conveniently track Seeds within SeedBundles.
void print(raw_ostream &OS) const
bool erase(const KeyT &Key)
void insert(LoadOrStoreT *LSI, bool AllowDiffTypes)
SeedContainer(ScalarEvolution &SE)
BundleMapT::const_iterator const_iterator
LLVM_DUMP_METHOD void dump() const
LLVM_ABI bool erase(Instruction *I)
static bool atLowerAddress(LoadOrStoreT *I0, LoadOrStoreT *I1, ScalarEvolution &SE)
\Returns true if I0 accesses a memory location lower than I1.
Definition: Utils.h:113
static unsigned getNumBits(Type *Ty, const DataLayout &DL)
\Returns the number of bits of Ty.
Definition: Utils.h:66
This provides a very simple, boring adaptor for a begin and end iterator into a range type.
void dump(const SparseBitVector< ElementSize > &LHS, raw_ostream &out)
auto find(R &&Range, const T &Val)
Provide wrappers to std::find which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1770
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1744
Printable print(const GCNRegPressure &RP, const GCNSubtarget *ST=nullptr, unsigned DynamicVGPRBlockSize=0)
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
Definition: STLExtras.h:2491
auto upper_bound(R &&Range, T &&Value)
Provide wrappers to std::upper_bound which take ranges instead of having to pass begin/end explicitly...
Definition: STLExtras.h:2026
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:207
@ Other
Any other memory.
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1886
Implement std::hash so that hash_code can be used in STL containers.
Definition: BitVector.h:856