LLVM 24.0.0git
LoadStoreVec.cpp
Go to the documentation of this file.
1//===- LoadStoreVec.cpp - Vectorizer pass short load-store chains ---------===//
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
11#include "llvm/ADT/DenseSet.h"
12#include "llvm/ADT/STLExtras.h"
23
24namespace llvm {
25
26namespace sandboxir {
27
28#define DEBUG_PREFIX_LOCAL DEBUG_PREFIX "LoadStoreVec: "
29
30std::optional<Type *> LoadStoreVec::canVectorize(BndlRef<Instruction *> Bndl) {
31 LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "canVectorize() Bundle:\n";
32 VecUtils::dump(Bndl));
33 const auto &LegalityRes = Legality->canVectorizeLSV(Bndl);
34 LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Legality: " << LegalityRes << "\n");
35
36 if (LegalityRes.getSubclassID() != LegalityResultID::Widen)
37 return std::nullopt;
39}
40
41void LoadStoreVec::saveIR(Region &R) {
42 Rgn = &R;
43 const auto &SB = cast<RegionWithScore>(Rgn)->getScoreboard();
44 CostBefore = SB.getAfterCost() - SB.getBeforeCost();
45 Rgn->getContext().save();
46}
47
48bool LoadStoreVec::acceptOrRevert() {
49 const auto &SB = cast<RegionWithScore>(*Rgn).getScoreboard();
50 InstructionCost CostAfter = SB.getAfterCost() - SB.getBeforeCost();
51 InstructionCost CostGain = CostAfter - CostBefore;
52 LLVM_DEBUG(dbgs() << DEBUG_PREFIX_LOCAL << "CostGain=" << CostGain
53 << " (After=" << CostAfter << " Before=" << CostBefore
54 << ")\n");
55 const VectorizeOptions &Opts = getVectorizeOptions(*Ctx);
56 if (CostGain > Opts.sbvec_cost_threshold) {
57 LLVM_DEBUG(dbgs() << DEBUG_PREFIX_LOCAL << "Not profitable, reverting.\n");
58 Ctx->revert();
59 return false;
60 }
61 LLVM_DEBUG(dbgs() << DEBUG_PREFIX_LOCAL << "Profitable accepting.\n");
62 Ctx->accept();
63 return true;
64}
65
66LoadInst *LoadStoreVec::createVectorLoad(BndlRef<Instruction *> Loads) {
68 Loads, A->getScalarEvolution(), *DL))
69 return nullptr;
70 if (!canVectorize(Loads))
71 return nullptr;
72
74 Value *LdPtr = cast<LoadInst>(Loads[0])->getPointerOperand();
75 Align LdAlign = VecUtils::getVecAlignment(Loads);
76 auto LdWhereIt = std::next(VecUtils::getLowest(Loads)->getIterator());
77 return LoadInst::create(Ty, LdPtr, LdAlign, LdWhereIt, *Ctx, "VecIinitL");
78}
79
80Constant *LoadStoreVec::getEquivalentConstantWithType(Constant *C,
81 Type *DestTy) {
82 // A non-integral pointer has no fixed ptrtoint/inttoptr width.
83 if (DestTy->isPointerTy())
84 assert(!DL->isNonIntegralAddressSpace(DestTy->getPointerAddressSpace()) &&
85 "DestTy has no fixed size");
86 Type *SrcTy = C->getType();
87 if (SrcTy == DestTy)
88 return C;
89
90 Constant *AsInt = C;
91 if (!SrcTy->isIntegerTy()) {
92 Type *IntTy = IntegerType::get(*Ctx, Utils::getNumBits(SrcTy, *DL));
93 AsInt = SrcTy->isPointerTy() ? ConstantExpr::getPtrToInt(C, IntTy)
94 : ConstantExpr::getBitCast(C, IntTy);
95 }
96 if (DestTy->isIntegerTy())
97 return AsInt;
98 return DestTy->isPointerTy() ? ConstantExpr::getIntToPtr(AsInt, DestTy)
99 : ConstantExpr::getBitCast(AsInt, DestTy);
100}
101
102Constant *LoadStoreVec::createConstantVector(BndlRef<Constant *> Constants) {
103 auto *VecTy =
105 auto *LaneTy = VecTy->getScalarType();
106 unsigned LaneBits = Utils::getNumBits(LaneTy, *DL);
107 assert(all_of(Constants,
108 [&](Value *V) {
109 return Utils::getNumBits(V, *DL) % LaneBits == 0;
110 }) &&
111 "Chain contains a constant which cannot be converted or split");
112
113 // Some of the contents of Constants may be vectors, like <i8 1, i8 2>.
114 // Collect each scalar element in ConstantElements.
115 SmallVector<Constant *, 4> ConstantElements;
116 for (Constant *COp : Constants) {
117 if (auto *AggrCOp = dyn_cast<ConstantAggregate>(COp)) {
118 // If the operand is a constant aggregate, then append all its elements.
119 // TODO: Flatten nested aggregates such as {i32, {i32, <2 x i32>}} in a
120 // follow-up.
121 for (Value *Elm : AggrCOp->operands())
122 ConstantElements.push_back(cast<Constant>(Elm));
123 } else if (auto *SeqCOp = dyn_cast<ConstantDataSequential>(COp)) {
124 for (auto ElmIdx : seq<unsigned>(SeqCOp->getNumElements()))
125 ConstantElements.push_back(SeqCOp->getElementAsConstant(ElmIdx));
126 } else if (auto *Zero = dyn_cast<ConstantAggregateZero>(COp)) {
127 auto *ZeroElm = Zero->getSequentialElement();
128 for ([[maybe_unused]] auto Cnt :
129 seq<unsigned>(Zero->getElementCount().getFixedValue()))
130 ConstantElements.push_back(ZeroElm);
131 } else if (isa<ConstantInt>(COp) && isa<VectorType>(COp->getType())) {
132 auto *Elm = ConstantInt::get(*Ctx, cast<ConstantInt>(COp)->getValue());
133 for ([[maybe_unused]] auto Cnt :
134 seq<unsigned>(cast<VectorType>(COp->getType())
135 ->getElementCount()
136 .getFixedValue()))
137 ConstantElements.push_back(Elm);
138 } else if (isa<ConstantFP>(COp) && isa<VectorType>(COp->getType())) {
139 auto *Elm = ConstantFP::get(cast<ConstantFP>(COp)->getValue(), *Ctx);
140 for ([[maybe_unused]] auto Cnt :
141 seq<unsigned>(cast<VectorType>(COp->getType())
142 ->getElementCount()
143 .getFixedValue()))
144 ConstantElements.push_back(Elm);
145 } else if (isa<PoisonValue>(COp) && isa<VectorType>(COp->getType())) {
146 auto *Elm =
147 PoisonValue::get(cast<VectorType>(COp->getType())->getElementType());
148 for ([[maybe_unused]] auto Cnt :
149 seq<unsigned>(cast<VectorType>(COp->getType())
150 ->getElementCount()
151 .getFixedValue()))
152 ConstantElements.push_back(Elm);
153 } else if (isa<VectorType>(COp->getType())) {
154 // TODO: Flatten the remaining vector constants.
155 return nullptr;
156 } else {
157 ConstantElements.push_back(COp);
158 }
159 }
160
161 // The elements in ConstantElements may not be of the same type, so we need to
162 // convert them to LaneTy such that we end up getting a vector
163 // <ConstantElements.size() x LaneTy>. This may involve converting integers to
164 // floats (e.g., float 1.0 to i32 0x3f800000), widening or slicing values if
165 // not at LaneTy bit-width (e.g., i16 0x0101 to i8 0x01, i8 0x01 on
166 // little-endian), or a combination of the two.
167 SmallVector<Constant *, 8> LaneTyConstants;
168 LaneTyConstants.reserve(ConstantElements.size());
169 for (Constant *C : ConstantElements) {
170 unsigned Bits = Utils::getNumBits(C, *DL);
171 if (Bits == LaneBits) {
172 Constant *Lane = getEquivalentConstantWithType(C, LaneTy);
173 if (Lane == nullptr)
174 return nullptr;
175 LaneTyConstants.push_back(Lane);
176 } else if (Bits > LaneBits) {
177 // C is wider than LaneTy, so slice it into LaneTy-sized pieces. That
178 // needs a known bit pattern. A ConstantExpr (for example, ptrtoint of a
179 // global) or a global's address is only known at link time and cannot
180 // be sliced.
182 return nullptr;
183 APInt Val;
184 if (auto *CI = dyn_cast<ConstantInt>(C))
185 Val = CI->getValue();
186 else if (auto *CFP = dyn_cast<ConstantFP>(C))
187 Val = CFP->getValue().bitcastToAPInt();
188 else
189 Val = APInt::getZero(Bits);
190 unsigned NumSlices = Bits / LaneBits;
191 for (unsigned SliceIdx : seq<unsigned>(NumSlices)) {
192 unsigned EndianAwareSlice =
193 DL->isLittleEndian() ? SliceIdx : NumSlices - 1 - SliceIdx;
194 // extractBits returns an integer. Convert that slice back to LaneTy
195 // without changing its bits, so a float lane stays a float.
196 Constant *SliceCInt = ConstantInt::get(
197 *Ctx, Val.extractBits(LaneBits, EndianAwareSlice * LaneBits));
198 Constant *SliceC = getEquivalentConstantWithType(SliceCInt, LaneTy);
199 if (SliceC == nullptr)
200 return nullptr;
201 LaneTyConstants.push_back(SliceC);
202 }
203 } else {
204 llvm_unreachable("Vector element type size was calculated incorrectly");
205 }
206 }
207 return ConstantVector::get(LaneTyConstants);
208}
209
210bool LoadStoreVec::vectorizeStores(BndlRef<Instruction *> Stores, Region &Rgn) {
212 Stores, A->getScalarEvolution(), *DL))
213 return false;
214 if (!canVectorize(Stores))
215 return false;
216 SmallVector<Value *, 4> Operands;
217 Operands.reserve(Stores.size());
218 for (auto *I : Stores) {
219 auto *Op = cast<StoreInst>(I)->getValueOperand();
220 Operands.push_back(Op);
221 }
222 BasicBlock *BB = Stores[0]->getParent();
223 // TODO: For now we only support load operands.
224 // TODO: For now we don't cross BBs.
225 // TODO: For now don't vectorize if the loads have external uses.
226 bool AllLoads = all_of(Operands, [BB](Value *V) {
227 auto *LI = dyn_cast<LoadInst>(V);
228 if (LI == nullptr)
229 return false;
230 // TODO: For now we don't cross BBs.
231 if (LI->getParent() != BB)
232 return false;
233 if (LI->hasNUsesOrMore(2))
234 return false;
235 return true;
236 });
237 bool AllConstants =
238 all_of(Operands, [](Value *V) { return isa<Constant>(V); });
239 if (!AllLoads && !AllConstants)
240 return false;
241
242 // Vectorizing mixed floats and integers with external uses may not be
243 // profitable on some targets, so save state here.
244 saveIR(Rgn);
245 Value *VecOp = nullptr;
246 SmallVector<Instruction *, 8> Loads;
247 if (AllLoads) {
248 Loads.reserve(Operands.size());
249 for (Value *Op : Operands)
250 Loads.push_back(cast<Instruction>(Op));
251 VecOp = createVectorLoad(Loads);
252 if (VecOp == nullptr) {
253 Ctx->accept();
254 return false;
255 }
256 } else if (AllConstants) {
258 Constants.reserve(Operands.size());
259 for (Value *Op : Operands)
260 Constants.push_back(cast<Constant>(Op));
261 VecOp = createConstantVector(Constants);
262 if (VecOp == nullptr) {
263 Ctx->accept();
264 return false;
265 }
266 }
267
268 // Generate vector store.
269 Value *StPtr = cast<StoreInst>(Stores[0])->getPointerOperand();
270 Align StAlign = VecUtils::getVecAlignment(Stores);
271 auto StWhereIt = std::next(VecUtils::getLowest(Stores)->getIterator());
272 StoreInst::create(VecOp, StPtr, StAlign, StWhereIt, *Ctx);
273
274 DeadInstrMorgue.collectPotentiallyDeadInstrs(Stores);
275 if (AllLoads)
276 DeadInstrMorgue.collectPotentiallyDeadInstrs<Instruction>(Loads);
277 DeadInstrMorgue.tryEraseDeadInstrs();
278
279 return acceptOrRevert();
280}
281
282LoadInst *LoadStoreVec::vectorizeLoads(BndlRef<Instruction *> Loads,
283 Region &Rgn) {
285 Loads, A->getScalarEvolution(), *DL))
286 return nullptr;
287 auto VecTy = canVectorize(Loads);
288 if (!VecTy)
289 return nullptr;
290
291 // TODO: Support mixed-type top-level load chains.
292 Type *VecElemTy = cast<FixedVectorType>(*VecTy)->getElementType();
293 if (!all_of(Loads, [VecElemTy](Instruction *I) {
294 return VecUtils::getElementType(I->getType()) == VecElemTy;
295 }))
296 return nullptr;
297
298 saveIR(Rgn);
299
300 auto *VecLoad = createVectorLoad(Loads);
301 if (VecLoad == nullptr) {
302 Ctx->accept();
303 return nullptr;
304 }
305
306 BasicBlock::iterator WhereIt = std::next(VecLoad->getIterator());
307 for (auto [Lane, OrigV] : VecUtils::enumerateLanes(Loads)) {
308 auto *OrigLoad = cast<LoadInst>(OrigV);
309 if (OrigLoad->hasNUses(0))
310 continue;
311 Value *Unpacked =
312 VecUtils::unpack(VecLoad, OrigLoad->getType(), Lane, WhereIt);
313 OrigLoad->replaceAllUsesWith(Unpacked);
314 }
315
316 DeadInstrMorgue.collectPotentiallyDeadInstrs(Loads);
317 DeadInstrMorgue.tryEraseDeadInstrs();
318
319 if (!acceptOrRevert())
320 return nullptr;
321 return VecLoad;
322}
323
324bool LoadStoreVec::runOnRegion(Region &Rgn, const Analyses &RegionAnalyses) {
325 SmallVector<Instruction *, 8> Bndl(Rgn.getAux().begin(), Rgn.getAux().end());
326 if (Bndl.size() < 2)
327 return false;
328 Function &F = *Bndl[0]->getParent()->getParent();
329 DL = &F.getParent()->getDataLayout();
330 Ctx = &F.getContext();
331 A = &RegionAnalyses;
332 InstrMaps IMaps;
333 Legality = std::make_unique<LegalityAnalysis>(
334 A->getAA(), A->getScalarEvolution(), F.getParent()->getDataLayout(),
335 A->getTTI(), F.getContext(), IMaps, SchedDirection::BottomUp);
336
337 auto Opc = Bndl[0]->getOpcode();
338 assert(
339 all_of(Bndl, [Opc](Instruction *I) { return I->getOpcode() == Opc; }) &&
340 "Expected a homogeneous seed slice!");
341
342 bool Changed = false;
343 switch (Opc) {
344 case Instruction::Opcode::Load:
345 Changed = vectorizeLoads(Bndl, Rgn) != nullptr;
346 break;
347 case Instruction::Opcode::Store:
348 Changed = vectorizeStores(Bndl, Rgn);
349 break;
350 default:
351 llvm_unreachable("Expected Load or Store");
352 }
353 return Changed;
354}
355
356} // namespace sandboxir
357
358} // namespace llvm
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
This file defines the DenseSet and SmallDenseSet classes.
This file defines an InstructionCost class that is used when calculating the cost of an instruction,...
static constexpr Value * getValue(Ty &ValueOrUse)
#define DEBUG_PREFIX_LOCAL
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
SI Fold Operands
This file contains some templates that are useful if you are working with the STL at all.
#define LLVM_DEBUG(...)
Definition Debug.h:119
#define DEBUG_PREFIX
Definition Debug.h:19
static APInt getZero(unsigned numBits)
Get the '0' value for the specified bit-width.
Definition APInt.h:196
InstListType::iterator iterator
Instruction iterators...
Definition BasicBlock.h:170
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An ArrayRef of Values or Instructions that we can print/dump for debugging.
Definition VecUtils.h:39
static LLVM_ABI Constant * getPtrToInt(Constant *C, Type *Ty)
Definition Constant.cpp:470
static LLVM_ABI Constant * getIntToPtr(Constant *C, Type *Ty)
Definition Constant.cpp:476
static LLVM_ABI Constant * get(Type *Ty, double V)
This returns a ConstantFP, or a vector containing a splat of a ConstantFP, for the specified value in...
Definition Constant.cpp:90
static LLVM_ABI Constant * get(Type *Ty, uint64_t V, bool IsSigned=false)
If Ty is a vector type, return a Constant with a splat of the given value.
Definition Constant.cpp:48
static LLVM_ABI Constant * get(ArrayRef< Constant * > V)
Definition Constant.cpp:176
Maps the original instructions to the vectorized instrs and the reverse.
Definition InstrMaps.h:50
A sandboxir::User with operands, opcode and linked with previous/next instructions in an instruction ...
Definition Instruction.h:43
static LLVM_ABI IntegerType * get(Context &C, unsigned NumBits)
static LLVM_ABI LoadInst * create(Type *Ty, Value *Ptr, MaybeAlign Align, InsertPosition Pos, bool IsVolatile, Context &Ctx, const Twine &Name="")
bool runOnRegion(Region &Rgn, const Analyses &A) final
\Returns true if it modifies R.
static LLVM_ABI PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
Definition Constant.cpp:263
static LLVM_ABI StoreInst * create(Value *V, Value *Ptr, MaybeAlign Align, InsertPosition Pos, bool IsVolatile, Context &Ctx)
Just like llvm::Type these are immutable, unique, never get freed and can only be created via static ...
Definition Type.h:49
static unsigned getNumBits(Type *Ty, const DataLayout &DL)
\Returns the number of bits of Ty.
Definition Utils.h:67
static Instruction * getLowest(ArrayRef< Instruction * > Instrs)
\Returns the instruction in Instrs that is lowest in the BB.
Definition VecUtils.h:240
static Value * unpack(Value *FromVec, Type *ExtrTy, unsigned Lane, BasicBlock::iterator WhereIt)
Emits the necessary instruction sequence to extract element of type ExtrTy at Lane from FromVec.
Definition VecUtils.h:418
static LLVM_DUMP_METHOD void dump(ArrayRef< Value * > Bndl)
Helper dump function for debugging.
Definition VecUtils.cpp:166
static auto enumerateLanes(const ValueContainerT &Range)
Helper for creating LaneValueEnumerator ranges.
Definition VecUtils.h:536
static bool areConsecutive(LoadOrStoreT *I1, LoadOrStoreT *I2, ScalarEvolution &SE, const DataLayout &DL)
\Returns true if I1 and I2 are load/stores accessing consecutive memory addresses.
Definition VecUtils.h:101
static Type * getElementType(Type *Ty)
Returns Ty if scalar or its element type if vector.
Definition VecUtils.h:94
static Align getVecAlignment(AlignRangeT AlignRange, TypeRangeT TypeRange)
Given the alignments of the instructions before vectorization, this returns the alignment of the vect...
Definition VecUtils.h:547
static Type * getCombinedVectorTypeFor(BndlRef< T * > Bndl, const DataLayout &DL)
\Returns the combined vector type for Bndl, even when the element types differ.
Definition VecUtils.h:205
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
BndlRef(const T &OneElt) -> BndlRef< T >
BasicBlock(llvm::BasicBlock *BB, Context &SBCtx)
Definition BasicBlock.h:75
friend class Instruction
Iterator for Instructions in a `BasicBlock.
Definition BasicBlock.h:73
This is an optimization pass for GlobalISel generic memory operations.
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
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
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
DWARFExpression::Operation Op
const VectorizeOptions & getVectorizeOptions(const LLVMContext &C)
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