LLVM 24.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() = default;
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 bool tryInsert(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!");
83 UsedLanes.set(Idx);
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) {
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 // Early return if we can't determine the mem access ordering.
160 auto DiffOpt = Utils::getPointerDiffInBytes(
162 if (!DiffOpt)
163 return false;
164
165 auto Cmp = [&SE](Instruction *I0, Instruction *I1) {
167 cast<LoadOrStoreT>(I1), SE);
168 };
169 // Find the first element after I in mem. Then insert I before it.
170 insertAt(llvm::upper_bound(*this, I, Cmp), I);
171 return true;
172 }
173};
174
177
178/// Class to conveniently track Seeds within SeedBundles. Saves newly collected
179/// seeds in the proper bundle. Supports constant-time removal, as seeds and
180/// entire bundles are vectorized and marked used to signify removal. Iterators
181/// skip bundles that are completely used.
183 // Use the same key for different seeds if they are the same type and
184 // reference the same pointer, even if at different offsets. This directs
185 // potentially vectorizable seeds into the same bundle.
186 using KeyT = std::tuple<Value *, Type *, Instruction::Opcode>;
187 // Trying to vectorize too many seeds at once is expensive in
188 // compilation-time. Use a vector of bundles (all with the same key) to
189 // partition the candidate set into more manageable units. Each bundle is
190 // size-limited by sbvec-seed-bundle-size-limit. TODO: There might be a
191 // better way to divide these than by simple insertion order.
193 using BundleMapT = MapVector<KeyT, ValT>;
194 // Map from {pointer, Type, Opcode} to a vector of bundles.
195 BundleMapT Bundles;
196 // Allows finding a particular Instruction's bundle.
198
199 ScalarEvolution &SE;
200 unsigned BundleSizeLimit;
201
202 template <typename LoadOrStoreT>
203 KeyT getKey(LoadOrStoreT *LSI, bool AllowDiffTypes) const;
204
205public:
207
208 class iterator {
209 BundleMapT *Map = nullptr;
211 ValT *Vec = nullptr;
212 size_t VecIdx;
213
214 public:
215 using difference_type = std::ptrdiff_t;
219 using iterator_category = std::input_iterator_tag;
220
221 /// Iterates over the \p Map of SeedBundle Vectors, starting at \p MapIt,
222 /// and \p Vec at \p VecIdx, skipping vectors that are completely
223 /// used. Iteration order over the keys {Pointer, Type, Opcode} follows
224 /// DenseMap iteration order. For a given key, the vectors of
225 /// SeedBundles will be returned in insertion order. As in the
226 /// pseudo code below:
227 ///
228 /// for Key,Value in Bundles
229 /// for SeedBundleVector in Value
230 /// for SeedBundle in SeedBundleVector
231 /// if !SeedBundle.allUsed() ...
232 ///
233 /// Note that the bundles themselves may have additional ordering, created
234 /// by the subclasses by insertAt. The bundles themselves may also have used
235 /// instructions.
236
237 // TODO: Range_size counts fully used-bundles. Further, iterating over
238 // anything other than the Bundles in a SeedContainer includes used
239 // seeds. Rework the iterator logic to clean this up.
240 iterator(BundleMapT &Map, BundleMapT::iterator MapIt, ValT *Vec, int VecIdx)
241 : Map(&Map), MapIt(MapIt), Vec(Vec), VecIdx(VecIdx) {}
243 assert(Vec != nullptr && "Already at end!");
244 return *(*Vec)[VecIdx];
245 }
246 // Skip completely used bundles by repeatedly calling operator++().
247 void skipUsed() {
248 while (Vec && VecIdx < Vec->size() && this->operator*().allUsed())
249 ++(*this);
250 }
251 // Iterators iterate over the bundles
253 ++VecIdx;
254 if (VecIdx >= Vec->size()) {
255 assert(MapIt != Map->end() && "Already at end!");
256 VecIdx = 0;
257 ++MapIt;
258 if (MapIt != Map->end())
259 Vec = &MapIt->second;
260 else {
261 Vec = nullptr;
262 }
263 }
264 skipUsed();
265 return *this;
266 }
268 auto Copy = *this;
269 ++(*this);
270 return Copy;
271 }
272 bool operator==(const iterator &Other) const {
273 assert(Map == Other.Map && "Iterator of different objects!");
274 return MapIt == Other.MapIt && VecIdx == Other.VecIdx;
275 }
276 bool operator!=(const iterator &Other) const { return !(*this == Other); }
277 };
279 template <typename LoadOrStoreT>
280 void insert(LoadOrStoreT *LSI, bool AllowDiffTypes);
281 // To support constant-time erase, these just mark the element used, rather
282 // than actually removing them from the bundle.
284 bool erase(const KeyT &Key) { return Bundles.erase(Key); }
286 if (Bundles.empty())
287 return end();
288 auto BeginIt =
289 iterator(Bundles, Bundles.begin(), &Bundles.begin()->second, 0);
290 BeginIt.skipUsed();
291 return BeginIt;
292 }
293 iterator end() { return iterator(Bundles, Bundles.end(), nullptr, 0); }
294 unsigned size() const { return Bundles.size(); }
295
296#ifndef NDEBUG
297 void print(raw_ostream &OS) const;
298 LLVM_DUMP_METHOD void dump() const;
299#endif // NDEBUG
300};
301
302// Explicit instantiations
303extern template LLVM_TEMPLATE_ABI void
305extern template LLVM_TEMPLATE_ABI void
307
309 SeedContainer StoreSeeds;
310 SeedContainer LoadSeeds;
311 Context &Ctx;
312 Context::CallbackID EraseCallbackID;
313 /// \Returns the number of SeedBundle groups for all seed types.
314 /// This is to be used for limiting compilation time.
315 unsigned totalNumSeedGroups() const {
316 return StoreSeeds.size() + LoadSeeds.size();
317 }
318
319public:
321 bool CollectStores, bool CollectLoads,
322 bool AllowDiffTypes = false);
324
327#ifndef NDEBUG
328 void print(raw_ostream &OS) const;
329 LLVM_DUMP_METHOD void dump() const;
330#endif
331};
332
333} // namespace llvm::sandboxir
334
335#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:215
#define LLVM_TEMPLATE_ABI
Definition Compiler.h:216
#define LLVM_DUMP_METHOD
Mark debug helper function definitions like dump() that should not be stripped from debug builds.
Definition Compiler.h:686
#define I(x, y, z)
Definition MD5.cpp:57
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
An instruction for reading from memory.
This class implements a map that also provides access to all stored values in a deterministic order.
Definition MapVector.h:38
typename SmallVector< std::pair< KeyT, ValT >, 0 >::const_iterator const_iterator
Definition MapVector.h:45
typename SmallVector< std::pair< KeyT, ValT >, 0 >::iterator iterator
Definition MapVector.h:44
The main scalar evolution driver.
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
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
raw_ostream & indent(unsigned NumSpaces)
indent - Insert 'NumSpaces' spaces.
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.
bool tryInsert(sandboxir::Instruction *I, ScalarEvolution &SE) override
MemSeedBundle(SmallVector< Instruction * > &&SV, ScalarEvolution &SE)
MemSeedBundle(LoadOrStoreT *MemI)
A set of candidate Instructions for vectorizing together.
SeedBundle & operator=(const SeedBundle &)=delete
SmallVector< Instruction * >::iterator iterator
virtual bool tryInsert(Instruction *I, ScalarEvolution &SE)=0
bool isUsed(unsigned Element) const
\Returns whether or not Element has been used.
virtual ~SeedBundle()=default
SmallVector< Instruction * > Seeds
void setUsed(unsigned ElementIdx, unsigned Sz=1, bool VerifyUnused=true)
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.
unsigned getNumUnusedBits() const
const_iterator begin() const
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.
SeedBundle(SmallVector< Instruction * > &&L)
SeedBundle(Instruction *I)
Initialize a bundle with I.
unsigned getFirstUnusedElementIdx() const
std::size_t size() const
\Returns the number of seed elements in the bundle.
SmallVector< Instruction * >::const_iterator const_iterator
Instruction * operator[](unsigned Idx) const
BitVector UsedLanes
The lanes that we have already vectorized.
const_iterator end() const
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()
LLVM_ABI SeedCollector(BasicBlock *BB, ScalarEvolution &SE, bool CollectStores, bool CollectLoads, bool AllowDiffTypes=false)
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)
LLVM_ABI SeedContainer(ScalarEvolution &SE)
BundleMapT::const_iterator const_iterator
LLVM_DUMP_METHOD void dump() const
LLVM_ABI bool erase(Instruction *I)
static std::optional< int > getPointerDiffInBytes(LoadOrStoreT *I0, LoadOrStoreT *I1, ScalarEvolution &SE)
\Returns the gap between the memory locations accessed by I0 and I1 in bytes.
Definition Utils.h:93
static unsigned getNumBits(Type *Ty, const DataLayout &DL)
\Returns the number of bits of Ty.
Definition Utils.h:67
static std::optional< bool > atLowerAddress(LoadOrStoreT *I0, LoadOrStoreT *I1, ScalarEvolution &SE)
\Returns true if I0 accesses a memory location lower than I1.
Definition Utils.h:115
This provides a very simple, boring adaptor for a begin and end iterator into a range type.
BasicBlock(llvm::BasicBlock *BB, Context &SBCtx)
Definition BasicBlock.h:75
BBIterator iterator
Definition BasicBlock.h:87
MemSeedBundle< sandboxir::LoadInst > LoadSeedBundle
MemSeedBundle< sandboxir::StoreInst > StoreSeedBundle
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:1781
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:1755
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:2570
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:2081
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
@ Other
Any other memory.
Definition ModRef.h:68
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:1933
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
constexpr auto seq(T Begin, T End)
Iterate over an integral type from Begin up to - but not including - End.
Definition Sequence.h:341
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:878