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