43#define DEBUG_TYPE "loop-cache-cost"
50 assert(!
Loops.empty() &&
"Expecting a non-empy loop vector");
55 if (ParentLoop ==
nullptr) {
56 assert(
Loops.size() == 1 &&
"Expecting a single loop");
90 return StepRec == &ElemSize;
106 <<
" could not be computed, using DefaultTripCount\n");
119 OS << R.StoreOrLoadInst;
120 OS <<
", IsValid=false.";
124 OS << *R.BasePointer;
125 for (
const SCEV *Subscript : R.Subscripts)
126 OS <<
"[" << *Subscript <<
"]";
130 OS <<
"[" << *
Size <<
"]";
137 : StoreOrLoadInst(StoreOrLoadInst), SE(SE) {
139 "Expecting a load or store instruction");
141 IsValid = delinearize(LI);
150 assert(IsValid &&
"Expecting a valid reference");
152 if (BasePointer !=
Other.getBasePointer() && !isAliased(
Other,
AA)) {
154 <<
"No spacial reuse: different base pointers\n");
159 if (NumSubscripts !=
Other.getNumSubscripts()) {
161 <<
"No spacial reuse: different number of subscripts\n");
170 << *
Other.getSubscript(SubNum) <<
"\n");
178 const SCEV *OtherLastSubscript =
Other.getLastSubscript();
180 SE.getMinusSCEV(LastSubscript, OtherLastSubscript));
182 if (Diff ==
nullptr) {
184 <<
"No spacial reuse, difference between subscript:\n\t"
185 << *LastSubscript <<
"\n\t" << OtherLastSubscript
186 <<
"\nis not constant.\n");
190 bool InSameCacheLine = (Diff->
getValue()->getSExtValue() < CLS);
199 return InSameCacheLine;
204 unsigned MaxDistance,
const Loop &L,
206 assert(IsValid &&
"Expecting a valid reference");
208 if (BasePointer !=
Other.getBasePointer() && !isAliased(
Other,
AA)) {
210 <<
"No temporal reuse: different base pointer\n");
214 std::unique_ptr<Dependence>
D =
222 if (
D->isLoopIndependent()) {
230 int LoopDepth = L.getLoopDepth();
231 int Levels =
D->getLevels();
232 for (
int Level = 1; Level <= Levels; ++Level) {
233 const SCEV *Distance =
D->getDistance(Level);
236 if (SCEVConst ==
nullptr) {
242 if (Level != LoopDepth && !CI.
isZero()) {
244 <<
"No temporal reuse: distance is not zero at depth=" << Level
247 }
else if (Level == LoopDepth && CI.
getSExtValue() > MaxDistance) {
250 <<
"No temporal reuse: distance is greater than MaxDistance at depth="
261 unsigned CLS)
const {
262 assert(IsValid &&
"Expecting a valid reference");
264 dbgs().
indent(2) <<
"Computing cache cost for:\n";
269 if (isLoopInvariant(L)) {
275 assert(TripCount &&
"Expecting valid TripCount");
278 const SCEV *RefCost =
nullptr;
279 const SCEV *Stride =
nullptr;
280 if (isConsecutive(L, Stride, CLS)) {
283 assert(Stride !=
nullptr &&
284 "Stride should not be null for consecutive access!");
287 Stride = SE.getNoopOrAnyExtend(Stride, WiderType);
288 TripCount = SE.getNoopOrZeroExtend(TripCount, WiderType);
289 const SCEV *Numerator = SE.getMulExpr(Stride, TripCount);
298 <<
"Access is consecutive: RefCost=(TripCount*Stride)/CLS="
299 << *RefCost <<
"\n");
310 int Index = getSubscriptIndex(L);
311 assert(Index >= 0 &&
"Could not locate a valid Index");
316 const SCEV *TripCount =
325 RefCost = SE.getMulExpr(SE.getNoopOrZeroExtend(RefCost, WiderType),
326 SE.getNoopOrZeroExtend(TripCount, WiderType));
330 <<
"Access is not consecutive: RefCost=" << *RefCost <<
"\n");
332 assert(RefCost &&
"Expecting a valid RefCost");
339 return ConstantCost->getValue()->getLimitedValue(
340 std::numeric_limits<int64_t>::max());
343 <<
"RefCost is not a constant! Setting to RefCost=InvalidCost "
344 "(invalid value).\n");
349bool IndexedReference::delinearize(
const LoopInfo &LI) {
350 assert(Subscripts.empty() &&
"Subscripts should be empty");
351 assert(Sizes.empty() &&
"Sizes should be empty");
352 assert(!IsValid &&
"Should be called once from the constructor");
353 LLVM_DEBUG(
dbgs() <<
"Delinearizing: " << StoreOrLoadInst <<
"\n");
359 const SCEV *AccessFn =
363 if (BasePointer ==
nullptr) {
366 <<
"ERROR: failed to delinearize, can't identify base pointer\n");
370 bool IsFixedSize =
false;
377 <<
"', AccessFn: " << *AccessFn <<
"\n");
383 <<
"', AccessFn: " << *AccessFn <<
"\n");
386 SE.getElementSize(&StoreOrLoadInst));
389 if (Subscripts.empty() || Sizes.empty() ||
390 Subscripts.size() != Sizes.size()) {
395 <<
"ERROR: failed to delinearize reference\n");
409 if (StepRec && SE.isKnownNegative(StepRec))
410 AccessFn = SE.getAddRecExpr(AccessFnAR->
getStart(),
411 SE.getNegativeSCEV(StepRec),
413 const SCEV *Div = SE.getUDivExactExpr(AccessFn, ElemSize);
414 Subscripts.push_back(Div);
415 Sizes.push_back(ElemSize);
418 return all_of(Subscripts, [&](
const SCEV *Subscript) {
419 return isSimpleAddRecurrence(*Subscript, *L);
426bool IndexedReference::isLoopInvariant(
const Loop &L)
const {
428 assert(Addr !=
nullptr &&
"Expecting either a load or a store instruction");
429 assert(SE.isSCEVable(Addr->
getType()) &&
"Addr should be SCEVable");
431 if (SE.isLoopInvariant(SE.getSCEV(Addr), &L))
436 bool allCoeffForLoopAreZero =
all_of(Subscripts, [&](
const SCEV *Subscript) {
437 return isCoeffForLoopZeroOrInvariant(*Subscript, L);
440 return allCoeffForLoopAreZero;
443bool IndexedReference::isConsecutive(
const Loop &L,
const SCEV *&Stride,
444 unsigned CLS)
const {
447 const SCEV *LastSubscript = Subscripts.back();
448 for (
const SCEV *Subscript : Subscripts) {
449 if (Subscript == LastSubscript)
451 if (!isCoeffForLoopZeroOrInvariant(*Subscript, L))
456 const SCEV *Coeff = getLastCoefficient();
457 const SCEV *ElemSize = Sizes.back();
469 Stride = SE.getMulExpr(SE.getNoopOrSignExtend(Coeff, WiderType),
470 SE.getNoopOrSignExtend(ElemSize, WiderType));
473 Stride = SE.isKnownNegative(Stride) ? SE.getNegativeSCEV(Stride) : Stride;
477int IndexedReference::getSubscriptIndex(
const Loop &L)
const {
480 if (AR && AR->
getLoop() == &L) {
487const SCEV *IndexedReference::getLastCoefficient()
const {
493bool IndexedReference::isCoeffForLoopZeroOrInvariant(
const SCEV &Subscript,
494 const Loop &L)
const {
496 return (AR !=
nullptr) ? AR->
getLoop() != &
L
497 : SE.isLoopInvariant(&Subscript, &L);
500bool IndexedReference::isSimpleAddRecurrence(
const SCEV &Subscript,
501 const Loop &L)
const {
514 if (!SE.isLoopInvariant(Start, &L) || !SE.isLoopInvariant(Step, &L))
531 for (
const auto &LC :
CC.LoopCosts) {
532 const Loop *L = LC.first;
533 OS <<
"Loop '" << L->getName() <<
"' has cost = " << LC.second <<
"\n";
541 std::optional<unsigned> TRT)
542 : Loops(Loops), LI(LI), SE(SE), TTI(TTI), AA(AA), DI(DI) {
543 assert(!Loops.empty() &&
"Expecting a non-empty loop vector.");
545 this->TRT = TRT.value_or(Opts.temporal_reuse_threshold);
547 for (
const Loop *L : Loops) {
548 unsigned TripCount = SE.getSmallConstantTripCount(L);
549 TripCount = (TripCount == 0) ? Opts.default_trip_count : TripCount;
550 TripCounts.push_back({L, TripCount});
553 calculateCacheFootprint();
556static std::unique_ptr<CacheCost>
559 std::optional<unsigned> TRT) {
561 LLVM_DEBUG(
dbgs() <<
"Expecting the outermost loop in a loop nest\n");
569 LLVM_DEBUG(
dbgs() <<
"Cannot compute cache cost of loop nest with more "
570 "than one innermost loop\n");
574 return std::make_unique<CacheCost>(
Loops, LI, SE,
TTI,
AA, DI, TRT);
577std::unique_ptr<CacheCost>
583void CacheCost::calculateCacheFootprint() {
586 if (!populateReferenceGroups(RefGroups))
593 [L](
const LoopCacheCostTy &LCC) {
return LCC.first == L; }) &&
594 "Should not add duplicate element");
595 CacheCostTy LoopCost = computeLoopCacheCost(*L, RefGroups);
596 LoopCosts.
push_back(std::make_pair(L, LoopCost));
604 assert(RefGroups.
empty() &&
"Reference groups should be empty");
606 unsigned CLS = TTI.getCacheLineSize();
608 assert(InnerMostLoop !=
nullptr &&
"Expecting a valid innermost loop");
610 for (BasicBlock *BB : InnerMostLoop->
getBlocks()) {
611 for (Instruction &
I : *BB) {
615 std::unique_ptr<IndexedReference>
R(
new IndexedReference(
I, LI, SE));
621 const IndexedReference &Representative = *RefGroup.front();
623 dbgs() <<
"References:\n";
640 std::optional<bool> HasTemporalReuse =
641 R->hasTemporalReuse(Representative, *TRT, *InnerMostLoop, DI, AA);
642 std::optional<bool> HasSpacialReuse =
643 R->hasSpacialReuse(Representative, CLS, AA);
645 if ((HasTemporalReuse && *HasTemporalReuse) ||
646 (HasSpacialReuse && *HasSpacialReuse)) {
647 RefGroup.push_back(std::move(R));
656 RefGroups.push_back(std::move(RG));
661 if (RefGroups.empty())
665 dbgs() <<
"\nIDENTIFIED REFERENCE GROUPS:\n";
668 dbgs().
indent(2) <<
"RefGroup " << n <<
":\n";
669 for (
const auto &
IR : RG)
680CacheCost::computeLoopCacheCost(
const Loop &L,
683 <<
"' as innermost loop.\n");
687 for (
const auto &TC : TripCounts) {
690 TripCountsProduct *= TC.second;
695 CacheCostTy RefGroupCost = computeRefGroupCacheCost(RG, L);
696 LoopCost += RefGroupCost * TripCountsProduct;
700 <<
"' has cost=" << LoopCost <<
"\n");
706 const Loop &L)
const {
707 assert(!RG.
empty() &&
"Reference group should have at least one member.");
709 const IndexedReference *Representative = RG.
front().get();
718 OS <<
"Printing analysis 'Loop Cache Analysis' for function '" <<
F.getName()
727 if (std::unique_ptr<CacheCost>
CC =
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file builds on the ADT/GraphTraits.h file to build a generic breadth first graph iterator.
static GCRegistry::Add< StatepointGC > D("statepoint-example", "an example strategy for statepoint")
Legalize the Machine IR a function s Machine IR
static bool isOneDimensionalArray(const SCEV &AccessFn, const SCEV &ElemSize, const Loop &L, ScalarEvolution &SE)
static std::unique_ptr< CacheCost > getCacheCostImpl(Loop &Root, LoopInfo &LI, ScalarEvolution &SE, TargetTransformInfo &TTI, AAResults &AA, DependenceInfo &DI, std::optional< unsigned > TRT)
static const SCEV * computeTripCount(const Loop &L, const SCEV &ElemSize, ScalarEvolution &SE)
Compute the trip count for the given loop L or assume a default value if it is not a compile time con...
static Loop * getInnerMostLoop(const LoopVectorTy &Loops)
Retrieve the innermost loop in the given loop nest Loops.
This file defines the interface for the loop cache analysis.
FunctionAnalysisManager FAM
Provides some synthesis utilities to produce sequences of values.
This file defines the SmallVector class.
A manager for alias analyses.
bool isMustAlias(const MemoryLocation &LocA, const MemoryLocation &LocB)
A trivial helper function to check to see if the specified pointers are must-alias.
LLVM Basic Block Representation.
CacheCost represents the estimated cost of a inner loop as the number of cache lines used by the memo...
LLVM_ABI CacheCost(const LoopVectorTy &Loops, const LoopInfo &LI, ScalarEvolution &SE, TargetTransformInfo &TTI, AAResults &AA, DependenceInfo &DI, std::optional< unsigned > TRT=std::nullopt)
Construct a CacheCost object for the loop nest described by Loops.
static LLVM_ABI std::unique_ptr< CacheCost > getCacheCost(Loop &Root, LoopStandardAnalysisResults &AR, DependenceInfo &DI, std::optional< unsigned > TRT=std::nullopt)
Create a CacheCost for the loop nest rooted by Root.
@ ICMP_ULT
unsigned less than
This is the shared class of boolean and integer constants.
bool isZero() const
This is just a convenience method to make client code smaller for a common code.
int64_t getSExtValue() const
Return the constant as a 64-bit integer value after it has been sign extended as appropriate for the ...
AnalysisPass to compute dependence information in a function.
DependenceInfo - This class is the main dependence-analysis driver.
LLVM_ABI std::unique_ptr< Dependence > depends(Instruction *Src, Instruction *Dst, bool UnderRuntimeAssumptions=false)
depends - Tests for a dependence between the Src and Dst instructions.
Represents a memory reference as a base pointer and a set of indexing operations.
LLVM_ABI CacheCostTy computeRefCost(const Loop &L, unsigned CLS) const
Compute the cost of the reference w.r.t.
const SCEV * getSubscript(unsigned SubNum) const
LLVM_ABI std::optional< bool > hasSpacialReuse(const IndexedReference &Other, unsigned CLS, AAResults &AA) const
Return true/false if the current object and the indexed reference Other are/aren't in the same cache ...
LLVM_ABI std::optional< bool > hasTemporalReuse(const IndexedReference &Other, unsigned MaxDistance, const Loop &L, DependenceInfo &DI, AAResults &AA) const
Return true if the current object and the indexed reference Other have distance smaller than MaxDista...
LLVM_ABI IndexedReference(Instruction &StoreOrLoadInst, const LoopInfo &LI, ScalarEvolution &SE)
Construct an indexed reference given a StoreOrLoadInst instruction.
const SCEV * getLastSubscript() const
size_t getNumSubscripts() const
static InstructionCost getInvalid(CostType Val=0)
@ MAX_INT_BITS
Maximum number of bits that can be specified.
Analysis pass that exposes the LoopInfo for a function.
bool isOutermost() const
Return true if the loop does not have a parent (natural) loop.
unsigned getLoopDepth() const
Return the nesting level of this loop.
ArrayRef< BlockT * > getBlocks() const
Get a list of the basic blocks which make up this loop.
LoopT * getParentLoop() const
Return the parent loop if it exists or nullptr for top level loops.
LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM)
const std::vector< LoopT * > & getTopLevelLoops() const
Return the top-level loops.
LoopT * getLoopFor(const BlockT *BB) const
Return the inner most loop that BB lives in.
Represents a single loop in the control flow graph.
static LLVM_ABI MemoryLocation get(const LoadInst *LI)
Return a location with information about the memory reference by the given instruction.
A set of analyses that are preserved following a run of a transformation pass.
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
This node represents a polynomial recurrence on the trip count of the specified loop.
bool isAffine() const
Return true if this represents an expression A + B*x where A and B are loop invariant values.
const Loop * getLoop() const
SCEVUse getStepRecurrence(ScalarEvolution &SE) const
Constructs and returns the recurrence indicating how much this expression steps by.
This class represents a constant integer value.
ConstantInt * getValue() const
This class represents an analyzed expression in the program.
Type * getType() const
Return the LLVM type of this SCEV expression.
Analysis pass that exposes the ScalarEvolution for a function.
The main scalar evolution driver.
LLVM_ABI bool isKnownNegative(const SCEV *S)
Test if the given expression is known to be negative.
LLVM_ABI const SCEV * getBackedgeTakenCount(const Loop *L, ExitCountKind Kind=Exact)
If the specified loop has a predictable backedge-taken count, return it, otherwise return a SCEVCould...
LLVM_ABI const SCEV * getMinusSCEV(SCEVUse LHS, SCEVUse RHS, SCEVFlags Flags=SCEV::FlagNone, unsigned Depth=0)
Return LHS-RHS.
LLVM_ABI SCEVUse getSCEVAtScope(const SCEV *S, const Loop *L)
Return a SCEV expression for the specified value at the specified scope in the program.
LLVM_ABI const SCEV * getConstant(ConstantInt *V)
LLVM_ABI const SCEV * getTripCountFromExitCount(const SCEV *ExitCount)
A version of getTripCountFromExitCount below which always picks an evaluation type which can not resu...
LLVM_ABI bool isLoopInvariant(const SCEV *S, const Loop *L)
Return true if the value of the given SCEV is unchanging in the specified loop.
LLVM_ABI const SCEV * getPointerBase(const SCEV *V)
Transitively follow the chain of pointer-type operands until reaching a SCEV that does not have a sin...
LLVM_ABI const SCEV * getElementSize(Instruction *Inst)
Return the size of an element read or written by Inst.
LLVM_ABI const SCEV * getNegativeSCEV(const SCEV *V, SCEVFlags Flags=SCEV::FlagNone)
Return the SCEV object corresponding to -V.
void push_back(const T &Elt)
Analysis pass providing the TargetTransformInfo.
The instances of the Type class are immutable: once they are created, they are never changed.
LLVM_ABI Type * getExtendedType() const
Given scalar/vector integer type, returns a type with elements twice as wide as in the original type.
Type * getType() const
All values are typed, get the type of this value.
const ParentTy * getParent() const
This class implements an extremely fast bulk output stream that can only output to a stream.
raw_ostream & indent(unsigned NumSpaces)
indent - Insert 'NumSpaces' spaces.
Abstract Attribute helper functions.
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.
InstructionCost CacheCostTy
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
SmallVector< std::unique_ptr< IndexedReference >, 8 > ReferenceGroupTy
A reference group represents a set of memory references that exhibit temporal or spacial reuse.
const Value * getPointerOperand(const Value *V)
A helper function that returns the pointer operand of a load, store or GEP instruction.
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
const AnalysisOptions & getAnalysisOptions(const LLVMContext &C)
auto dyn_cast_or_null(const Y &Val)
LLVM_ABI bool delinearizeFixedSizeArray(ScalarEvolution &SE, const SCEV *Expr, SmallVectorImpl< const SCEV * > &Subscripts, SmallVectorImpl< const SCEV * > &Sizes, const SCEV *ElementSize)
Split this SCEVAddRecExpr into two vectors of SCEVs representing the subscripts and sizes of an acces...
SmallVector< Loop *, 8 > LoopVectorTy
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
bool none_of(R &&Range, UnaryPredicate P)
Provide wrappers to std::none_of which take ranges instead of having to pass begin/end explicitly.
bool is_sorted(R &&Range, Compare C)
Wrapper function around std::is_sorted to check if elements in a range R are sorted with respect to a...
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...
iterator_range< bf_iterator< T > > breadth_first(const T &G)
raw_ostream & operator<<(raw_ostream &OS, const APFixedPoint &FX)
LLVM_ABI void delinearize(ScalarEvolution &SE, const SCEV *Expr, SmallVectorImpl< const SCEV * > &Subscripts, SmallVectorImpl< const SCEV * > &Sizes, const SCEV *ElementSize)
Split this SCEVAddRecExpr into two vectors of SCEVs representing the subscripts and sizes of an array...
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
constexpr auto seq(T Begin, T End)
Iterate over an integral type from Begin up to - but not including - End.
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
SmallVector< ReferenceGroupTy, 8 > ReferenceGroupsTy
The adaptor from a function pass to a loop pass computes these analyses and makes them available to t...
TargetTransformInfo & TTI