xref: /src/contrib/llvm-project/llvm/lib/Analysis/CodeMetrics.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
163faed5bSDimitry Andric //===- CodeMetrics.cpp - Code cost measurements ---------------------------===//
263faed5bSDimitry Andric //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
663faed5bSDimitry Andric //
763faed5bSDimitry Andric //===----------------------------------------------------------------------===//
863faed5bSDimitry Andric //
963faed5bSDimitry Andric // This file implements code cost measurement utilities.
1063faed5bSDimitry Andric //
1163faed5bSDimitry Andric //===----------------------------------------------------------------------===//
1263faed5bSDimitry Andric 
1363faed5bSDimitry Andric #include "llvm/Analysis/CodeMetrics.h"
14cfca06d7SDimitry Andric #include "llvm/ADT/SmallPtrSet.h"
157ab83427SDimitry Andric #include "llvm/Analysis/AssumptionCache.h"
1667c32a98SDimitry Andric #include "llvm/Analysis/LoopInfo.h"
174a16efa3SDimitry Andric #include "llvm/Analysis/TargetTransformInfo.h"
184a16efa3SDimitry Andric #include "llvm/IR/Function.h"
19ac9a064cSDimitry Andric #include "llvm/IR/IntrinsicInst.h"
2067c32a98SDimitry Andric #include "llvm/Support/Debug.h"
21344a3780SDimitry Andric #include "llvm/Support/InstructionCost.h"
2267c32a98SDimitry Andric 
2367c32a98SDimitry Andric #define DEBUG_TYPE "code-metrics"
2463faed5bSDimitry Andric 
2563faed5bSDimitry Andric using namespace llvm;
2663faed5bSDimitry Andric 
27b915e9e0SDimitry Andric static void
appendSpeculatableOperands(const Value * V,SmallPtrSetImpl<const Value * > & Visited,SmallVectorImpl<const Value * > & Worklist)28b915e9e0SDimitry Andric appendSpeculatableOperands(const Value *V,
29b915e9e0SDimitry Andric                            SmallPtrSetImpl<const Value *> &Visited,
30b915e9e0SDimitry Andric                            SmallVectorImpl<const Value *> &Worklist) {
31b915e9e0SDimitry Andric   const User *U = dyn_cast<User>(V);
32b915e9e0SDimitry Andric   if (!U)
33b915e9e0SDimitry Andric     return;
34b915e9e0SDimitry Andric 
35b915e9e0SDimitry Andric   for (const Value *Operand : U->operands())
36b915e9e0SDimitry Andric     if (Visited.insert(Operand).second)
37c0981da4SDimitry Andric       if (const auto *I = dyn_cast<Instruction>(Operand))
38c0981da4SDimitry Andric         if (!I->mayHaveSideEffects() && !I->isTerminator())
39c0981da4SDimitry Andric           Worklist.push_back(I);
40b915e9e0SDimitry Andric }
41b915e9e0SDimitry Andric 
completeEphemeralValues(SmallPtrSetImpl<const Value * > & Visited,SmallVectorImpl<const Value * > & Worklist,SmallPtrSetImpl<const Value * > & EphValues)42b915e9e0SDimitry Andric static void completeEphemeralValues(SmallPtrSetImpl<const Value *> &Visited,
43b915e9e0SDimitry Andric                                     SmallVectorImpl<const Value *> &Worklist,
4467c32a98SDimitry Andric                                     SmallPtrSetImpl<const Value *> &EphValues) {
4567c32a98SDimitry Andric   // Note: We don't speculate PHIs here, so we'll miss instruction chains kept
4667c32a98SDimitry Andric   // alive only by ephemeral values.
4767c32a98SDimitry Andric 
48b915e9e0SDimitry Andric   // Walk the worklist using an index but without caching the size so we can
49b915e9e0SDimitry Andric   // append more entries as we process the worklist. This forms a queue without
50b915e9e0SDimitry Andric   // quadratic behavior by just leaving processed nodes at the head of the
51b915e9e0SDimitry Andric   // worklist forever.
52b915e9e0SDimitry Andric   for (int i = 0; i < (int)Worklist.size(); ++i) {
53b915e9e0SDimitry Andric     const Value *V = Worklist[i];
5467c32a98SDimitry Andric 
55b915e9e0SDimitry Andric     assert(Visited.count(V) &&
56b915e9e0SDimitry Andric            "Failed to add a worklist entry to our visited set!");
5767c32a98SDimitry Andric 
5867c32a98SDimitry Andric     // If all uses of this value are ephemeral, then so is this value.
59b915e9e0SDimitry Andric     if (!all_of(V->users(), [&](const User *U) { return EphValues.count(U); }))
6067c32a98SDimitry Andric       continue;
6167c32a98SDimitry Andric 
6267c32a98SDimitry Andric     EphValues.insert(V);
63eb11fae6SDimitry Andric     LLVM_DEBUG(dbgs() << "Ephemeral Value: " << *V << "\n");
6467c32a98SDimitry Andric 
65b915e9e0SDimitry Andric     // Append any more operands to consider.
66b915e9e0SDimitry Andric     appendSpeculatableOperands(V, Visited, Worklist);
6767c32a98SDimitry Andric   }
6867c32a98SDimitry Andric }
6967c32a98SDimitry Andric 
7067c32a98SDimitry Andric // Find all ephemeral values.
collectEphemeralValues(const Loop * L,AssumptionCache * AC,SmallPtrSetImpl<const Value * > & EphValues)7167c32a98SDimitry Andric void CodeMetrics::collectEphemeralValues(
7267c32a98SDimitry Andric     const Loop *L, AssumptionCache *AC,
7367c32a98SDimitry Andric     SmallPtrSetImpl<const Value *> &EphValues) {
74b915e9e0SDimitry Andric   SmallPtrSet<const Value *, 32> Visited;
75b915e9e0SDimitry Andric   SmallVector<const Value *, 16> Worklist;
7667c32a98SDimitry Andric 
7767c32a98SDimitry Andric   for (auto &AssumeVH : AC->assumptions()) {
7867c32a98SDimitry Andric     if (!AssumeVH)
7967c32a98SDimitry Andric       continue;
8067c32a98SDimitry Andric     Instruction *I = cast<Instruction>(AssumeVH);
8167c32a98SDimitry Andric 
82b915e9e0SDimitry Andric     // Filter out call sites outside of the loop so we don't do a function's
8367c32a98SDimitry Andric     // worth of work for each of its loops (and, in the common case, ephemeral
8467c32a98SDimitry Andric     // values in the loop are likely due to @llvm.assume calls in the loop).
8567c32a98SDimitry Andric     if (!L->contains(I->getParent()))
8667c32a98SDimitry Andric       continue;
8767c32a98SDimitry Andric 
88b915e9e0SDimitry Andric     if (EphValues.insert(I).second)
89b915e9e0SDimitry Andric       appendSpeculatableOperands(I, Visited, Worklist);
9067c32a98SDimitry Andric   }
9167c32a98SDimitry Andric 
92b915e9e0SDimitry Andric   completeEphemeralValues(Visited, Worklist, EphValues);
9367c32a98SDimitry Andric }
9467c32a98SDimitry Andric 
collectEphemeralValues(const Function * F,AssumptionCache * AC,SmallPtrSetImpl<const Value * > & EphValues)9567c32a98SDimitry Andric void CodeMetrics::collectEphemeralValues(
9667c32a98SDimitry Andric     const Function *F, AssumptionCache *AC,
9767c32a98SDimitry Andric     SmallPtrSetImpl<const Value *> &EphValues) {
98b915e9e0SDimitry Andric   SmallPtrSet<const Value *, 32> Visited;
99b915e9e0SDimitry Andric   SmallVector<const Value *, 16> Worklist;
10067c32a98SDimitry Andric 
10167c32a98SDimitry Andric   for (auto &AssumeVH : AC->assumptions()) {
10267c32a98SDimitry Andric     if (!AssumeVH)
10367c32a98SDimitry Andric       continue;
10467c32a98SDimitry Andric     Instruction *I = cast<Instruction>(AssumeVH);
10567c32a98SDimitry Andric     assert(I->getParent()->getParent() == F &&
10667c32a98SDimitry Andric            "Found assumption for the wrong function!");
107b915e9e0SDimitry Andric 
108b915e9e0SDimitry Andric     if (EphValues.insert(I).second)
109b915e9e0SDimitry Andric       appendSpeculatableOperands(I, Visited, Worklist);
11067c32a98SDimitry Andric   }
11167c32a98SDimitry Andric 
112b915e9e0SDimitry Andric   completeEphemeralValues(Visited, Worklist, EphValues);
11367c32a98SDimitry Andric }
11467c32a98SDimitry Andric 
extendsConvergenceOutsideLoop(const Instruction & I,const Loop * L)115ac9a064cSDimitry Andric static bool extendsConvergenceOutsideLoop(const Instruction &I, const Loop *L) {
116ac9a064cSDimitry Andric   if (!L)
117ac9a064cSDimitry Andric     return false;
118ac9a064cSDimitry Andric   if (!isa<ConvergenceControlInst>(I))
119ac9a064cSDimitry Andric     return false;
120ac9a064cSDimitry Andric   for (const auto *U : I.users()) {
121ac9a064cSDimitry Andric     if (!L->contains(cast<Instruction>(U)))
122ac9a064cSDimitry Andric       return true;
123ac9a064cSDimitry Andric   }
124ac9a064cSDimitry Andric   return false;
125ac9a064cSDimitry Andric }
126ac9a064cSDimitry Andric 
12701095a5dSDimitry Andric /// Fill in the current structure with information gleaned from the specified
12801095a5dSDimitry Andric /// block.
analyzeBasicBlock(const BasicBlock * BB,const TargetTransformInfo & TTI,const SmallPtrSetImpl<const Value * > & EphValues,bool PrepareForLTO,const Loop * L)129b60736ecSDimitry Andric void CodeMetrics::analyzeBasicBlock(
130b60736ecSDimitry Andric     const BasicBlock *BB, const TargetTransformInfo &TTI,
131ac9a064cSDimitry Andric     const SmallPtrSetImpl<const Value *> &EphValues, bool PrepareForLTO,
132ac9a064cSDimitry Andric     const Loop *L) {
13363faed5bSDimitry Andric   ++NumBlocks;
134344a3780SDimitry Andric   InstructionCost NumInstsBeforeThisBB = NumInsts;
13501095a5dSDimitry Andric   for (const Instruction &I : *BB) {
13667c32a98SDimitry Andric     // Skip ephemeral values.
13701095a5dSDimitry Andric     if (EphValues.count(&I))
13867c32a98SDimitry Andric       continue;
13967c32a98SDimitry Andric 
14063faed5bSDimitry Andric     // Special handling for calls.
141e6d15924SDimitry Andric     if (const auto *Call = dyn_cast<CallBase>(&I)) {
142e6d15924SDimitry Andric       if (const Function *F = Call->getCalledFunction()) {
143b60736ecSDimitry Andric         bool IsLoweredToCall = TTI.isLoweredToCall(F);
14463faed5bSDimitry Andric         // If a function is both internal and has a single use, then it is
14563faed5bSDimitry Andric         // extremely likely to get inlined in the future (it was probably
14663faed5bSDimitry Andric         // exposed by an interleaved devirtualization pass).
147b60736ecSDimitry Andric         // When preparing for LTO, liberally consider calls as inline
148b60736ecSDimitry Andric         // candidates.
149b60736ecSDimitry Andric         if (!Call->isNoInline() && IsLoweredToCall &&
15008e8dd7bSDimitry Andric             ((F->hasInternalLinkage() && F->hasOneLiveUse()) ||
15108e8dd7bSDimitry Andric              PrepareForLTO)) {
15263faed5bSDimitry Andric           ++NumInlineCandidates;
153b60736ecSDimitry Andric         }
15463faed5bSDimitry Andric 
15563faed5bSDimitry Andric         // If this call is to function itself, then the function is recursive.
15663faed5bSDimitry Andric         // Inlining it into other functions is a bad idea, because this is
15763faed5bSDimitry Andric         // basically just a form of loop peeling, and our metrics aren't useful
15863faed5bSDimitry Andric         // for that case.
15963faed5bSDimitry Andric         if (F == BB->getParent())
16063faed5bSDimitry Andric           isRecursive = true;
16163faed5bSDimitry Andric 
162b60736ecSDimitry Andric         if (IsLoweredToCall)
1634a16efa3SDimitry Andric           ++NumCalls;
1644a16efa3SDimitry Andric       } else {
16563faed5bSDimitry Andric         // We don't want inline asm to count as a call - that would prevent loop
16663faed5bSDimitry Andric         // unrolling. The argument setup cost is still real, though.
167e6d15924SDimitry Andric         if (!Call->isInlineAsm())
16863faed5bSDimitry Andric           ++NumCalls;
16963faed5bSDimitry Andric       }
17063faed5bSDimitry Andric     }
17163faed5bSDimitry Andric 
17201095a5dSDimitry Andric     if (const AllocaInst *AI = dyn_cast<AllocaInst>(&I)) {
17363faed5bSDimitry Andric       if (!AI->isStaticAlloca())
17463faed5bSDimitry Andric         this->usesDynamicAlloca = true;
17563faed5bSDimitry Andric     }
17663faed5bSDimitry Andric 
17701095a5dSDimitry Andric     if (isa<ExtractElementInst>(I) || I.getType()->isVectorTy())
17863faed5bSDimitry Andric       ++NumVectorInsts;
17963faed5bSDimitry Andric 
180ac9a064cSDimitry Andric     if (I.getType()->isTokenTy() && !isa<ConvergenceControlInst>(I) &&
181ac9a064cSDimitry Andric         I.isUsedOutsideOfBlock(BB)) {
182ac9a064cSDimitry Andric       LLVM_DEBUG(dbgs() << I
183ac9a064cSDimitry Andric                         << "\n  Cannot duplicate a token value used outside "
184ac9a064cSDimitry Andric                            "the current block (except convergence control).\n");
185dd58ef01SDimitry Andric       notDuplicatable = true;
18601095a5dSDimitry Andric     }
1874a16efa3SDimitry Andric 
188ac9a064cSDimitry Andric     if (const CallBase *CB = dyn_cast<CallBase>(&I)) {
189ac9a064cSDimitry Andric       if (CB->cannotDuplicate())
1904a16efa3SDimitry Andric         notDuplicatable = true;
191ac9a064cSDimitry Andric       // Compute a meet over the visited blocks for the following partial order:
192ac9a064cSDimitry Andric       //
193ac9a064cSDimitry Andric       // None -> { Controlled, ExtendedLoop, Uncontrolled}
194ac9a064cSDimitry Andric       // Controlled -> ExtendedLoop
195ac9a064cSDimitry Andric       if (Convergence <= ConvergenceKind::Controlled && CB->isConvergent()) {
196ac9a064cSDimitry Andric         if (isa<ConvergenceControlInst>(CB) ||
197ac9a064cSDimitry Andric             CB->getConvergenceControlToken()) {
198ac9a064cSDimitry Andric           assert(Convergence != ConvergenceKind::Uncontrolled);
199ac9a064cSDimitry Andric           LLVM_DEBUG(dbgs() << "Found controlled convergence:\n" << I << "\n");
200ac9a064cSDimitry Andric           if (extendsConvergenceOutsideLoop(I, L))
201ac9a064cSDimitry Andric             Convergence = ConvergenceKind::ExtendedLoop;
202ac9a064cSDimitry Andric           else {
203ac9a064cSDimitry Andric             assert(Convergence != ConvergenceKind::ExtendedLoop);
204ac9a064cSDimitry Andric             Convergence = ConvergenceKind::Controlled;
205ac9a064cSDimitry Andric           }
206ac9a064cSDimitry Andric         } else {
207ac9a064cSDimitry Andric           assert(Convergence == ConvergenceKind::None);
208ac9a064cSDimitry Andric           Convergence = ConvergenceKind::Uncontrolled;
209ac9a064cSDimitry Andric         }
210ac9a064cSDimitry Andric       }
211ac9a064cSDimitry Andric     }
2124a16efa3SDimitry Andric 
213e3b55780SDimitry Andric     NumInsts += TTI.getInstructionCost(&I, TargetTransformInfo::TCK_CodeSize);
21463faed5bSDimitry Andric   }
21563faed5bSDimitry Andric 
21663faed5bSDimitry Andric   if (isa<ReturnInst>(BB->getTerminator()))
21763faed5bSDimitry Andric     ++NumRets;
21863faed5bSDimitry Andric 
21963faed5bSDimitry Andric   // We never want to inline functions that contain an indirectbr.  This is
22063faed5bSDimitry Andric   // incorrect because all the blockaddress's (in static global initializers
22163faed5bSDimitry Andric   // for example) would be referring to the original function, and this indirect
22263faed5bSDimitry Andric   // jump would jump from the inlined copy of the function into the original
22363faed5bSDimitry Andric   // function which is extremely undefined behavior.
22463faed5bSDimitry Andric   // FIXME: This logic isn't really right; we can safely inline functions
22563faed5bSDimitry Andric   // with indirectbr's as long as no other function or global references the
22663faed5bSDimitry Andric   // blockaddress of a block within the current function.  And as a QOI issue,
22763faed5bSDimitry Andric   // if someone is using a blockaddress without an indirectbr, and that
22863faed5bSDimitry Andric   // reference somehow ends up in another function or global, we probably
22963faed5bSDimitry Andric   // don't want to inline this function.
2304a16efa3SDimitry Andric   notDuplicatable |= isa<IndirectBrInst>(BB->getTerminator());
23163faed5bSDimitry Andric 
23263faed5bSDimitry Andric   // Remember NumInsts for this BB.
233145449b1SDimitry Andric   InstructionCost NumInstsThisBB = NumInsts - NumInstsBeforeThisBB;
234145449b1SDimitry Andric   NumBBInsts[BB] = NumInstsThisBB;
23563faed5bSDimitry Andric }
236