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/STLExtras.h"
21
22namespace llvm {
23
24namespace sandboxir {
25
26#define DEBUG_PREFIX_LOCAL DEBUG_PREFIX "LoadStoreVec: "
27
28/// \returns true if \p Ty is a pointer, or a vector of pointers, in a
29/// non-integral address space, which has no stable integer representation.
30static bool isNonIntegralPtr(Type *Ty, const DataLayout &DL) {
31 Type *ScalarTy = Ty->getScalarType();
32 return ScalarTy->isPointerTy() &&
33 DL.isNonIntegralAddressSpace(ScalarTy->getPointerAddressSpace());
34}
35
36std::optional<Type *> LoadStoreVec::canVectorize(BndlRef<Instruction *> Bndl) {
37 LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "canVectorize() Bundle:\n";
38 VecUtils::dump(Bndl));
39 const auto &LegalityRes = Legality->canVectorizeLSV(Bndl);
40 LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Legality: " << LegalityRes << "\n");
41
42 if (LegalityRes.getSubclassID() != LegalityResultID::Widen)
43 return std::nullopt;
45}
46
47void LoadStoreVec::saveIR(Region &R) {
48 Rgn = &R;
49 const auto &SB = cast<RegionWithScore>(Rgn)->getScoreboard();
50 CostBefore = SB.getAfterCost() - SB.getBeforeCost();
51 Rgn->getContext().save();
52}
53
54bool LoadStoreVec::acceptOrRevert() {
55 const auto &SB = cast<RegionWithScore>(*Rgn).getScoreboard();
56 InstructionCost CostAfter = SB.getAfterCost() - SB.getBeforeCost();
57 InstructionCost CostGain = CostAfter - CostBefore;
58 LLVM_DEBUG(dbgs() << DEBUG_PREFIX_LOCAL << "CostGain=" << CostGain
59 << " (After=" << CostAfter << " Before=" << CostBefore
60 << ")\n");
61 const VectorizeOptions &Opts = getVectorizeOptions(*Ctx);
62 if (CostGain > Opts.sbvec_cost_threshold) {
63 LLVM_DEBUG(dbgs() << DEBUG_PREFIX_LOCAL << "Not profitable, reverting.\n");
64 Ctx->revert();
65 return false;
66 }
67 LLVM_DEBUG(dbgs() << DEBUG_PREFIX_LOCAL << "Profitable accepting.\n");
68 Ctx->accept();
69 return true;
70}
71
72LoadInst *LoadStoreVec::createVectorLoad(BndlRef<Instruction *> Loads) {
73 if (!canVectorize(Loads))
74 return nullptr;
75
77 Value *LdPtr = cast<LoadInst>(Loads[0])->getPointerOperand();
78 Align LdAlign = VecUtils::getVecAlignment(Loads);
79 auto LdWhereIt = std::next(VecUtils::getLowest(Loads)->getIterator());
80 auto *VecL =
81 LoadInst::create(Ty, LdPtr, LdAlign, LdWhereIt, *Ctx, "VecIinitL");
82 Utils::propagateMetadata(VecL, Loads);
83 return VecL;
84}
85
86Constant *LoadStoreVec::getEquivalentConstantWithType(Constant *C,
87 Type *DestTy) {
88 Type *SrcTy = C->getType();
89 // A non-integral pointer has no fixed ptrtoint/inttoptr representation.
90 if (isNonIntegralPtr(SrcTy, *DL) || isNonIntegralPtr(DestTy, *DL))
91 return nullptr;
92 if (SrcTy == DestTy)
93 return C;
94
95 Constant *AsInt = C;
96 if (!SrcTy->isIntegerTy()) {
97 Type *IntTy = IntegerType::get(*Ctx, Utils::getNumBits(SrcTy, *DL));
98 AsInt = SrcTy->isPointerTy() ? ConstantExpr::getPtrToInt(C, IntTy)
99 : ConstantExpr::getBitCast(C, IntTy);
100 }
101 if (DestTy->isIntegerTy())
102 return AsInt;
103 return DestTy->isPointerTy() ? ConstantExpr::getIntToPtr(AsInt, DestTy)
104 : ConstantExpr::getBitCast(AsInt, DestTy);
105}
106
107Constant *LoadStoreVec::createConstantVector(BndlRef<Constant *> Constants) {
108 auto *VecTy =
110 auto *LaneTy = VecTy->getScalarType();
111 unsigned LaneBits = Utils::getNumBits(LaneTy, *DL);
112 assert(all_of(Constants,
113 [&](Value *V) {
114 return Utils::getNumBits(V, *DL) % LaneBits == 0;
115 }) &&
116 "Chain contains a constant which cannot be converted or split");
117
118 // Some of the contents of Constants may be vectors, like <i8 1, i8 2>.
119 // Collect each scalar element in ConstantElements.
120 SmallVector<Constant *, 4> ConstantElements;
121 for (Constant *COp : Constants) {
122 if (auto *AggrCOp = dyn_cast<ConstantAggregate>(COp)) {
123 // If the operand is a constant aggregate, then append all its elements.
124 // TODO: Flatten nested aggregates such as {i32, {i32, <2 x i32>}} in a
125 // follow-up.
126 for (Value *Elm : AggrCOp->operands())
127 ConstantElements.push_back(cast<Constant>(Elm));
128 } else if (auto *SeqCOp = dyn_cast<ConstantDataSequential>(COp)) {
129 for (auto ElmIdx : seq<unsigned>(SeqCOp->getNumElements()))
130 ConstantElements.push_back(SeqCOp->getElementAsConstant(ElmIdx));
131 } else if (auto *Zero = dyn_cast<ConstantAggregateZero>(COp)) {
132 auto *ZeroElm = Zero->getSequentialElement();
133 for ([[maybe_unused]] auto Cnt :
134 seq<unsigned>(Zero->getElementCount().getFixedValue()))
135 ConstantElements.push_back(ZeroElm);
136 } else if (isa<ConstantInt>(COp) && isa<VectorType>(COp->getType())) {
137 auto *Elm = ConstantInt::get(*Ctx, cast<ConstantInt>(COp)->getValue());
138 for ([[maybe_unused]] auto Cnt :
139 seq<unsigned>(cast<VectorType>(COp->getType())
140 ->getElementCount()
141 .getFixedValue()))
142 ConstantElements.push_back(Elm);
143 } else if (isa<ConstantFP>(COp) && isa<VectorType>(COp->getType())) {
144 auto *Elm = ConstantFP::get(cast<ConstantFP>(COp)->getValue(), *Ctx);
145 for ([[maybe_unused]] auto Cnt :
146 seq<unsigned>(cast<VectorType>(COp->getType())
147 ->getElementCount()
148 .getFixedValue()))
149 ConstantElements.push_back(Elm);
150 } else if (isa<PoisonValue>(COp) && isa<VectorType>(COp->getType())) {
151 auto *Elm =
152 PoisonValue::get(cast<VectorType>(COp->getType())->getElementType());
153 for ([[maybe_unused]] auto Cnt :
154 seq<unsigned>(cast<VectorType>(COp->getType())
155 ->getElementCount()
156 .getFixedValue()))
157 ConstantElements.push_back(Elm);
158 } else if (isa<VectorType>(COp->getType())) {
159 // TODO: Flatten the remaining vector constants.
160 return nullptr;
161 } else {
162 ConstantElements.push_back(COp);
163 }
164 }
165
166 // The elements in ConstantElements may not be of the same type, so we need to
167 // convert them to LaneTy such that we end up getting a vector
168 // <ConstantElements.size() x LaneTy>. This may involve converting integers to
169 // floats (e.g., float 1.0 to i32 0x3f800000), widening or slicing values if
170 // not at LaneTy bit-width (e.g., i16 0x0101 to i8 0x01, i8 0x01 on
171 // little-endian), or a combination of the two.
172 SmallVector<Constant *, 8> LaneTyConstants;
173 LaneTyConstants.reserve(ConstantElements.size());
174 for (Constant *C : ConstantElements) {
175 unsigned Bits = Utils::getNumBits(C, *DL);
176 if (Bits == LaneBits) {
177 Constant *Lane = getEquivalentConstantWithType(C, LaneTy);
178 if (Lane == nullptr)
179 return nullptr;
180 LaneTyConstants.push_back(Lane);
181 } else if (Bits > LaneBits) {
182 // C is wider than LaneTy, so slice it into LaneTy-sized pieces. That
183 // needs a known bit pattern. A ConstantExpr (for example, ptrtoint of a
184 // global) or a global's address is only known at link time and cannot
185 // be sliced.
187 return nullptr;
188 APInt Val;
189 if (auto *CI = dyn_cast<ConstantInt>(C))
190 Val = CI->getValue();
191 else if (auto *CFP = dyn_cast<ConstantFP>(C))
192 Val = CFP->getValue().bitcastToAPInt();
193 else
194 Val = APInt::getZero(Bits);
195 unsigned NumSlices = Bits / LaneBits;
196 for (unsigned SliceIdx : seq<unsigned>(NumSlices)) {
197 unsigned EndianAwareSlice =
198 DL->isLittleEndian() ? SliceIdx : NumSlices - 1 - SliceIdx;
199 // extractBits returns an integer. Convert that slice back to LaneTy
200 // without changing its bits, so a float lane stays a float.
201 Constant *SliceCInt = ConstantInt::get(
202 *Ctx, Val.extractBits(LaneBits, EndianAwareSlice * LaneBits));
203 Constant *SliceC = getEquivalentConstantWithType(SliceCInt, LaneTy);
204 if (SliceC == nullptr)
205 return nullptr;
206 LaneTyConstants.push_back(SliceC);
207 }
208 } else {
209 llvm_unreachable("Vector element type size was calculated incorrectly");
210 }
211 }
212 return ConstantVector::get(LaneTyConstants);
213}
214
215bool LoadStoreVec::vectorizeStores(BndlRef<Instruction *> Stores, Region &Rgn) {
216 if (!canVectorize(Stores))
217 return false;
218 SmallVector<Value *, 4> Operands;
219 Operands.reserve(Stores.size());
220 for (auto *I : Stores) {
221 auto *Op = cast<StoreInst>(I)->getValueOperand();
222 Operands.push_back(Op);
223 }
224 BasicBlock *BB = Stores[0]->getParent();
225 // TODO: For now we only support load operands.
226 // TODO: For now we don't cross BBs.
227 // TODO: For now don't vectorize if the loads have external uses.
228 bool AllLoads = all_of(Operands, [BB](Value *V) {
229 auto *LI = dyn_cast<LoadInst>(V);
230 if (LI == nullptr)
231 return false;
232 // TODO: For now we don't cross BBs.
233 if (LI->getParent() != BB)
234 return false;
235 if (LI->hasNUsesOrMore(2))
236 return false;
237 return true;
238 });
239 bool AllConstants =
240 all_of(Operands, [](Value *V) { return isa<Constant>(V); });
241 if (!AllLoads && !AllConstants)
242 return false;
243
244 // Vectorizing mixed floats and integers with external uses may not be
245 // profitable on some targets, so save state here.
246 saveIR(Rgn);
247 Value *VecOp = nullptr;
248 SmallVector<Instruction *, 8> Loads;
249 if (AllLoads) {
250 Loads.reserve(Operands.size());
251 for (Value *Op : Operands)
252 Loads.push_back(cast<Instruction>(Op));
253 VecOp = createVectorLoad(Loads);
254 if (VecOp == nullptr) {
255 Ctx->accept();
256 return false;
257 }
258 } else if (AllConstants) {
260 Constants.reserve(Operands.size());
261 for (Value *Op : Operands)
262 Constants.push_back(cast<Constant>(Op));
263 VecOp = createConstantVector(Constants);
264 if (VecOp == nullptr) {
265 Ctx->accept();
266 return false;
267 }
268 }
269
270 // Generate vector store.
271 Value *StPtr = cast<StoreInst>(Stores[0])->getPointerOperand();
272 Align StAlign = VecUtils::getVecAlignment(Stores);
273 auto StWhereIt = std::next(VecUtils::getLowest(Stores)->getIterator());
274 auto *VecS = StoreInst::create(VecOp, StPtr, StAlign, StWhereIt, *Ctx);
275 Utils::propagateMetadata(VecS, Stores);
276
277 DeadInstrMorgue.collectPotentiallyDeadInstrs(Stores);
278 if (AllLoads)
279 DeadInstrMorgue.collectPotentiallyDeadInstrs<Instruction>(Loads);
280 DeadInstrMorgue.tryEraseDeadInstrs();
281
282 return acceptOrRevert();
283}
284
285LoadInst *LoadStoreVec::vectorizeLoads(BndlRef<Instruction *> Loads,
286 Region &Rgn) {
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 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
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
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
void save()
Convenience function for getTracker().save()
Definition Context.h:245
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
Context & getContext() const
Definition Region.h:169
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:48
unsigned getPointerAddressSpace() const
Get the address space of this pointer or pointer vector type.
Definition Type.h:301
LLVM_ABI Type * getScalarType() const
If this is a vector type, return the element type, otherwise return 'this'.
bool isPointerTy() const
True if this is an instance of PointerType.
Definition Type.h:206
static void propagateMetadata(Instruction *I, const ValueRangeT &VL)
Definition Utils.h:148
static unsigned getNumBits(Type *Ty, const DataLayout &DL)
\Returns the number of bits of Ty.
Definition Utils.h:68
LLVM_ABI Type * getType() const
Definition Value.cpp:46
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 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
static bool isNonIntegralPtr(Type *Ty, const DataLayout &DL)
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