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