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