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) {
74 Value *LdPtr = cast<LoadInst>(Loads[0])->getPointerOperand();
75 Align LdAlign = VecUtils::getVecAlignment(Loads);
76 auto LdWhereIt = std::next(VecUtils::getLowest(Loads)->getIterator());
77 auto *VecL =
78 LoadInst::create(Ty, LdPtr, LdAlign, LdWhereIt, *Ctx, "VecIinitL");
79 Utils::propagateMetadata(VecL, Loads);
80 return VecL;
81}
82
83Constant *LoadStoreVec::getEquivalentConstantWithType(Constant *C,
84 Type *DestTy) {
85 Type *SrcTy = C->getType();
86 // A non-integral pointer has no fixed ptrtoint/inttoptr representation.
87 if (isNonIntegralPtr(SrcTy, *DL) || isNonIntegralPtr(DestTy, *DL))
88 return nullptr;
89 if (SrcTy == DestTy)
90 return C;
91
92 Constant *AsInt = C;
93 if (!SrcTy->isIntegerTy()) {
94 Type *IntTy = IntegerType::get(*Ctx, Utils::getNumBits(SrcTy, *DL));
95 AsInt = SrcTy->isPointerTy() ? ConstantExpr::getPtrToInt(C, IntTy)
96 : ConstantExpr::getBitCast(C, IntTy);
97 }
98 if (DestTy->isIntegerTy())
99 return AsInt;
100 return DestTy->isPointerTy() ? ConstantExpr::getIntToPtr(AsInt, DestTy)
101 : ConstantExpr::getBitCast(AsInt, DestTy);
102}
103
104Constant *LoadStoreVec::createConstantVector(BndlRef<Constant *> Constants) {
105 auto *VecTy =
107 auto *LaneTy = VecTy->getScalarType();
108 unsigned LaneBits = Utils::getNumBits(LaneTy, *DL);
109 assert(all_of(Constants,
110 [&](Value *V) {
111 return Utils::getNumBits(V, *DL) % LaneBits == 0;
112 }) &&
113 "Chain contains a constant which cannot be converted or split");
114
115 // Some of the contents of Constants may be vectors, like <i8 1, i8 2>.
116 // Collect each scalar element in ConstantElements.
117 SmallVector<Constant *, 4> ConstantElements;
118 for (Constant *COp : Constants) {
119 if (auto *AggrCOp = dyn_cast<ConstantAggregate>(COp)) {
120 // If the operand is a constant aggregate, then append all its elements.
121 // TODO: Flatten nested aggregates such as {i32, {i32, <2 x i32>}} in a
122 // follow-up.
123 for (Value *Elm : AggrCOp->operands())
124 ConstantElements.push_back(cast<Constant>(Elm));
125 } else if (auto *SeqCOp = dyn_cast<ConstantDataSequential>(COp)) {
126 for (auto ElmIdx : seq<unsigned>(SeqCOp->getNumElements()))
127 ConstantElements.push_back(SeqCOp->getElementAsConstant(ElmIdx));
128 } else if (auto *Zero = dyn_cast<ConstantAggregateZero>(COp)) {
129 auto *ZeroElm = Zero->getSequentialElement();
130 for ([[maybe_unused]] auto Cnt :
131 seq<unsigned>(Zero->getElementCount().getFixedValue()))
132 ConstantElements.push_back(ZeroElm);
133 } else if (isa<ConstantInt>(COp) && isa<VectorType>(COp->getType())) {
134 auto *Elm = ConstantInt::get(*Ctx, cast<ConstantInt>(COp)->getValue());
135 for ([[maybe_unused]] auto Cnt :
136 seq<unsigned>(cast<VectorType>(COp->getType())
137 ->getElementCount()
138 .getFixedValue()))
139 ConstantElements.push_back(Elm);
140 } else if (isa<ConstantFP>(COp) && isa<VectorType>(COp->getType())) {
141 auto *Elm = ConstantFP::get(cast<ConstantFP>(COp)->getValue(), *Ctx);
142 for ([[maybe_unused]] auto Cnt :
143 seq<unsigned>(cast<VectorType>(COp->getType())
144 ->getElementCount()
145 .getFixedValue()))
146 ConstantElements.push_back(Elm);
147 } else if (isa<PoisonValue>(COp) && isa<VectorType>(COp->getType())) {
148 auto *Elm =
149 PoisonValue::get(cast<VectorType>(COp->getType())->getElementType());
150 for ([[maybe_unused]] auto Cnt :
151 seq<unsigned>(cast<VectorType>(COp->getType())
152 ->getElementCount()
153 .getFixedValue()))
154 ConstantElements.push_back(Elm);
155 } else if (isa<VectorType>(COp->getType())) {
156 // TODO: Flatten the remaining vector constants.
157 return nullptr;
158 } else {
159 ConstantElements.push_back(COp);
160 }
161 }
162
163 // The elements in ConstantElements may not be of the same type, so we need to
164 // convert them to LaneTy such that we end up getting a vector
165 // <ConstantElements.size() x LaneTy>. This may involve converting integers to
166 // floats (e.g., float 1.0 to i32 0x3f800000), widening or slicing values if
167 // not at LaneTy bit-width (e.g., i16 0x0101 to i8 0x01, i8 0x01 on
168 // little-endian), or a combination of the two.
169 SmallVector<Constant *, 8> LaneTyConstants;
170 LaneTyConstants.reserve(ConstantElements.size());
171 for (Constant *C : ConstantElements) {
172 unsigned Bits = Utils::getNumBits(C, *DL);
173 if (Bits == LaneBits) {
174 Constant *Lane = getEquivalentConstantWithType(C, LaneTy);
175 if (Lane == nullptr)
176 return nullptr;
177 LaneTyConstants.push_back(Lane);
178 } else if (Bits > LaneBits) {
179 // C is wider than LaneTy, so slice it into LaneTy-sized pieces. That
180 // needs a known bit pattern. A ConstantExpr (for example, ptrtoint of a
181 // global) or a global's address is only known at link time and cannot
182 // be sliced.
184 return nullptr;
185 APInt Val;
186 if (auto *CI = dyn_cast<ConstantInt>(C))
187 Val = CI->getValue();
188 else if (auto *CFP = dyn_cast<ConstantFP>(C))
189 Val = CFP->getValue().bitcastToAPInt();
190 else
191 Val = APInt::getZero(Bits);
192 unsigned NumSlices = Bits / LaneBits;
193 for (unsigned SliceIdx : seq<unsigned>(NumSlices)) {
194 unsigned EndianAwareSlice =
195 DL->isLittleEndian() ? SliceIdx : NumSlices - 1 - SliceIdx;
196 // extractBits returns an integer. Convert that slice back to LaneTy
197 // without changing its bits, so a float lane stays a float.
198 Constant *SliceCInt = ConstantInt::get(
199 *Ctx, Val.extractBits(LaneBits, EndianAwareSlice * LaneBits));
200 Constant *SliceC = getEquivalentConstantWithType(SliceCInt, LaneTy);
201 if (SliceC == nullptr)
202 return nullptr;
203 LaneTyConstants.push_back(SliceC);
204 }
205 } else {
206 llvm_unreachable("Vector element type size was calculated incorrectly");
207 }
208 }
209 return ConstantVector::get(LaneTyConstants);
210}
211
212bool LoadStoreVec::vectorizeStores(BndlRef<Instruction *> Stores, Region &Rgn) {
213 if (!canVectorize(Stores))
214 return false;
215 SmallVector<Value *, 4> Operands;
216 Operands.reserve(Stores.size());
217 for (auto *I : Stores) {
218 auto *Op = cast<StoreInst>(I)->getValueOperand();
219 Operands.push_back(Op);
220 }
221 BasicBlock *BB = Stores[0]->getParent();
222 // TODO: For now we only support load operands.
223 // TODO: For now we don't cross BBs.
224 // TODO: For now don't vectorize if the loads have external uses.
225 bool AllLoads = all_of(Operands, [BB](Value *V) {
226 auto *LI = dyn_cast<LoadInst>(V);
227 if (LI == nullptr)
228 return false;
229 // TODO: For now we don't cross BBs.
230 if (LI->getParent() != BB)
231 return false;
232 if (LI->hasNUsesOrMore(2))
233 return false;
234 return true;
235 });
236 bool AllConstants =
237 all_of(Operands, [](Value *V) { return isa<Constant>(V); });
238 if (!AllLoads && !AllConstants)
239 return false;
240
241 // Vectorizing mixed floats and integers with external uses may not be
242 // profitable on some targets, so save state here.
243 saveIR(Rgn);
244 Value *VecOp = nullptr;
245 SmallVector<Instruction *, 8> Loads;
246 if (AllLoads) {
247 Loads.reserve(Operands.size());
248 for (Value *Op : Operands)
249 Loads.push_back(cast<Instruction>(Op));
250 if (!canVectorize(Loads)) {
251 Ctx->accept();
252 return false;
253 }
254 VecOp = createVectorLoad(Loads);
255 assert(VecOp != nullptr && "Expected vector value!");
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 auto *VecS = StoreInst::create(VecOp, StPtr, StAlign, StWhereIt, *Ctx);
273 Utils::propagateMetadata(VecS, Stores);
274
275 DeadInstrMorgue.collectPotentiallyDeadInstrs(Stores);
276 if (AllLoads)
277 DeadInstrMorgue.collectPotentiallyDeadInstrs<Instruction>(Loads);
278 DeadInstrMorgue.tryEraseDeadInstrs();
279
280 return acceptOrRevert();
281}
282
283LoadInst *LoadStoreVec::vectorizeLoads(BndlRef<Instruction *> Loads,
284 Region &Rgn) {
285 auto VecTy = canVectorize(Loads);
286 if (!VecTy)
287 return nullptr;
288
289 // TODO: Support mixed-type top-level load chains.
290 Type *VecElemTy = cast<FixedVectorType>(*VecTy)->getElementType();
291 if (!all_of(Loads, [VecElemTy](Instruction *I) {
292 return VecUtils::getElementType(I->getType()) == VecElemTy;
293 }))
294 return nullptr;
295
296 saveIR(Rgn);
297
298 auto *VecLoad = createVectorLoad(Loads);
299 assert(VecLoad != nullptr && "Expected vector load");
300
301 BasicBlock::iterator WhereIt = std::next(VecLoad->getIterator());
302 for (auto [Lane, OrigV] : VecUtils::enumerateLanes(Loads)) {
303 auto *OrigLoad = cast<LoadInst>(OrigV);
304 if (OrigLoad->hasNUses(0))
305 continue;
306 Value *Unpacked =
307 VecUtils::unpack(VecLoad, OrigLoad->getType(), Lane, WhereIt);
308 OrigLoad->replaceAllUsesWith(Unpacked);
309 }
310
311 DeadInstrMorgue.collectPotentiallyDeadInstrs(Loads);
312 DeadInstrMorgue.tryEraseDeadInstrs();
313
314 if (!acceptOrRevert())
315 return nullptr;
316 return VecLoad;
317}
318
319bool LoadStoreVec::runOnRegion(Region &Rgn, const Analyses &RegionAnalyses) {
320 SmallVector<Instruction *, 8> Bndl(Rgn.getAux().begin(), Rgn.getAux().end());
321 if (Bndl.size() < 2)
322 return false;
323 Function &F = *Bndl[0]->getParent()->getParent();
324 DL = &F.getParent()->getDataLayout();
325 Ctx = &F.getContext();
326 A = &RegionAnalyses;
327 InstrMaps IMaps;
328 Legality = std::make_unique<LegalityAnalysis>(
329 A->getAA(), A->getScalarEvolution(), F.getParent()->getDataLayout(),
330 A->getTTI(), F.getContext(), IMaps, SchedDirection::BottomUp);
331
332 auto Opc = Bndl[0]->getOpcode();
333 assert(
334 all_of(Bndl, [Opc](Instruction *I) { return I->getOpcode() == Opc; }) &&
335 "Expected a homogeneous seed slice!");
336
337 bool Changed = false;
338 switch (Opc) {
339 case Instruction::Opcode::Load:
340 Changed = vectorizeLoads(Bndl, Rgn) != nullptr;
341 break;
342 case Instruction::Opcode::Store:
343 Changed = vectorizeStores(Bndl, Rgn);
344 break;
345 default:
346 llvm_unreachable("Expected Load or Store");
347 }
348 return Changed;
349}
350
351} // namespace sandboxir
352
353} // 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