[llvm] f46dd19 - [mlgo] Incrementally update FunctionPropertiesInfo during inlining
Mircea Trofin via llvm-commits
llvm-commits at lists.llvm.org
Tue May 31 17:27:40 PDT 2022
Author: Mircea Trofin
Date: 2022-05-31T17:27:32-07:00
New Revision: f46dd19b480496d2ba0a57d12935882e530f2b93
URL: https://github.com/llvm/llvm-project/commit/f46dd19b480496d2ba0a57d12935882e530f2b93
DIFF: https://github.com/llvm/llvm-project/commit/f46dd19b480496d2ba0a57d12935882e530f2b93.diff
LOG: [mlgo] Incrementally update FunctionPropertiesInfo during inlining
Re-computing FunctionPropertiesInfo after each inlining may be very time
consuming: in certain cases, e.g. large caller with lots of callsites,
and when the overall IR doesn't increase (thus not tripping a size bloat
threshold).
This patch addresses this by incrementally updating
FunctionPropertiesInfo.
Differential Revision: https://reviews.llvm.org/D125841
Added:
Modified:
llvm/include/llvm/Analysis/FunctionPropertiesAnalysis.h
llvm/include/llvm/Analysis/MLInlineAdvisor.h
llvm/lib/Analysis/FunctionPropertiesAnalysis.cpp
llvm/lib/Analysis/MLInlineAdvisor.cpp
llvm/unittests/Analysis/FunctionPropertiesAnalysisTest.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/Analysis/FunctionPropertiesAnalysis.h b/llvm/include/llvm/Analysis/FunctionPropertiesAnalysis.h
index b1b37ad00e93..1eb02de59b71 100644
--- a/llvm/include/llvm/Analysis/FunctionPropertiesAnalysis.h
+++ b/llvm/include/llvm/Analysis/FunctionPropertiesAnalysis.h
@@ -14,6 +14,9 @@
#ifndef LLVM_ANALYSIS_FUNCTIONPROPERTIESANALYSIS_H
#define LLVM_ANALYSIS_FUNCTIONPROPERTIESANALYSIS_H
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/PassManager.h"
namespace llvm {
@@ -21,10 +24,23 @@ class Function;
class LoopInfo;
class FunctionPropertiesInfo {
+ friend class FunctionPropertiesUpdater;
+ void updateForBB(const BasicBlock &BB, int64_t Direction);
+ void updateAggregateStats(const Function &F, const LoopInfo &LI);
+ void reIncludeBB(const BasicBlock &BB, const LoopInfo &LI);
+
public:
static FunctionPropertiesInfo getFunctionPropertiesInfo(const Function &F,
const LoopInfo &LI);
+ bool operator==(const FunctionPropertiesInfo &FPI) const {
+ return std::memcmp(this, &FPI, sizeof(FunctionPropertiesInfo)) == 0;
+ }
+
+ bool operator!=(const FunctionPropertiesInfo &FPI) const {
+ return !(*this == FPI);
+ }
+
void print(raw_ostream &OS) const;
/// Number of basic blocks
@@ -57,6 +73,9 @@ class FunctionPropertiesInfo {
// Number of Top Level Loops in the Function
int64_t TopLevelLoopCount = 0;
+
+ // All non-debug instructions
+ int64_t TotalInstructionCount = 0;
};
// Analysis pass
@@ -82,5 +101,24 @@ class FunctionPropertiesPrinterPass
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
};
+/// Correctly update FunctionPropertiesInfo post-inlining. A
+/// FunctionPropertiesUpdater keeps the state necessary for tracking the changes
+/// llvm::InlineFunction makes. The idea is that inlining will at most modify
+/// a few BBs of the Caller (maybe the entry BB and definitely the callsite BB)
+/// and potentially affect exception handling BBs in the case of invoke
+/// inlining.
+class FunctionPropertiesUpdater {
+public:
+ FunctionPropertiesUpdater(FunctionPropertiesInfo &FPI, const CallBase &CB);
+
+ void finish(const LoopInfo &LI);
+
+private:
+ FunctionPropertiesInfo &FPI;
+ const BasicBlock &CallSiteBB;
+ const Function &Caller;
+
+ DenseSet<const BasicBlock *> Successors;
+};
} // namespace llvm
#endif // LLVM_ANALYSIS_FUNCTIONPROPERTIESANALYSIS_H
diff --git a/llvm/include/llvm/Analysis/MLInlineAdvisor.h b/llvm/include/llvm/Analysis/MLInlineAdvisor.h
index 1daa919c66e2..0b416ee57a94 100644
--- a/llvm/include/llvm/Analysis/MLInlineAdvisor.h
+++ b/llvm/include/llvm/Analysis/MLInlineAdvisor.h
@@ -9,6 +9,7 @@
#ifndef LLVM_ANALYSIS_MLINLINEADVISOR_H
#define LLVM_ANALYSIS_MLINLINEADVISOR_H
+#include "llvm/Analysis/FunctionPropertiesAnalysis.h"
#include "llvm/Analysis/InlineAdvisor.h"
#include "llvm/Analysis/LazyCallGraph.h"
#include "llvm/Analysis/MLModelRunner.h"
@@ -33,13 +34,17 @@ class MLInlineAdvisor : public InlineAdvisor {
void onPassEntry() override;
void onPassExit(LazyCallGraph::SCC *SCC) override;
- int64_t getIRSize(const Function &F) const { return F.getInstructionCount(); }
+ int64_t getIRSize(Function &F) const {
+ return getCachedFPI(F).TotalInstructionCount;
+ }
void onSuccessfulInlining(const MLInlineAdvice &Advice,
bool CalleeWasDeleted);
bool isForcedToStop() const { return ForceStop; }
int64_t getLocalCalls(Function &F);
const MLModelRunner &getModelRunner() const { return *ModelRunner.get(); }
+ FunctionPropertiesInfo &getCachedFPI(Function &) const;
+ const LoopInfo &getLoopInfo(Function &F) const;
protected:
std::unique_ptr<InlineAdvice> getAdviceImpl(CallBase &CB) override;
@@ -67,6 +72,8 @@ class MLInlineAdvisor : public InlineAdvisor {
<< "\n";
}
+ mutable DenseMap<const Function *, FunctionPropertiesInfo> FPICache;
+
LazyCallGraph &CG;
int64_t NodeCount = 0;
@@ -86,16 +93,7 @@ class MLInlineAdvisor : public InlineAdvisor {
class MLInlineAdvice : public InlineAdvice {
public:
MLInlineAdvice(MLInlineAdvisor *Advisor, CallBase &CB,
- OptimizationRemarkEmitter &ORE, bool Recommendation)
- : InlineAdvice(Advisor, CB, ORE, Recommendation),
- CallerIRSize(Advisor->isForcedToStop() ? 0
- : Advisor->getIRSize(*Caller)),
- CalleeIRSize(Advisor->isForcedToStop() ? 0
- : Advisor->getIRSize(*Callee)),
- CallerAndCalleeEdges(Advisor->isForcedToStop()
- ? 0
- : (Advisor->getLocalCalls(*Caller) +
- Advisor->getLocalCalls(*Callee))) {}
+ OptimizationRemarkEmitter &ORE, bool Recommendation);
virtual ~MLInlineAdvice() = default;
void recordInliningImpl() override;
@@ -112,10 +110,14 @@ class MLInlineAdvice : public InlineAdvice {
private:
void reportContextForRemark(DiagnosticInfoOptimizationBase &OR);
-
+ void updateCachedCallerFPI();
MLInlineAdvisor *getAdvisor() const {
return static_cast<MLInlineAdvisor *>(Advisor);
};
+ // Make a copy of the FPI of the caller right before inlining. If inlining
+ // fails, we can just update the cache with that value.
+ const FunctionPropertiesInfo PreInlineCallerFPI;
+ Optional<FunctionPropertiesUpdater> FPU;
};
} // namespace llvm
diff --git a/llvm/lib/Analysis/FunctionPropertiesAnalysis.cpp b/llvm/lib/Analysis/FunctionPropertiesAnalysis.cpp
index 1dc90422ca7b..66498f4914f0 100644
--- a/llvm/lib/Analysis/FunctionPropertiesAnalysis.cpp
+++ b/llvm/lib/Analysis/FunctionPropertiesAnalysis.cpp
@@ -12,49 +12,75 @@
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/FunctionPropertiesAnalysis.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/IR/CFG.h"
#include "llvm/IR/Instructions.h"
+#include <deque>
using namespace llvm;
-FunctionPropertiesInfo
-FunctionPropertiesInfo::getFunctionPropertiesInfo(const Function &F,
- const LoopInfo &LI) {
-
- FunctionPropertiesInfo FPI;
+namespace {
+int64_t getNrBlocksFromCond(const BasicBlock &BB) {
+ int64_t Ret = 0;
+ if (const auto *BI = dyn_cast<BranchInst>(BB.getTerminator())) {
+ if (BI->isConditional())
+ Ret += BI->getNumSuccessors();
+ } else if (const auto *SI = dyn_cast<SwitchInst>(BB.getTerminator())) {
+ Ret += (SI->getNumCases() + (nullptr != SI->getDefaultDest()));
+ }
+ return Ret;
+}
- FPI.Uses = ((!F.hasLocalLinkage()) ? 1 : 0) + F.getNumUses();
+int64_t getUses(const Function &F) {
+ return ((!F.hasLocalLinkage()) ? 1 : 0) + F.getNumUses();
+}
+} // namespace
- for (const auto &BB : F) {
- ++FPI.BasicBlockCount;
+void FunctionPropertiesInfo::reIncludeBB(const BasicBlock &BB,
+ const LoopInfo &LI) {
+ updateForBB(BB, +1);
+ MaxLoopDepth =
+ std::max(MaxLoopDepth, static_cast<int64_t>(LI.getLoopDepth(&BB)));
+}
- if (const auto *BI = dyn_cast<BranchInst>(BB.getTerminator())) {
- if (BI->isConditional())
- FPI.BlocksReachedFromConditionalInstruction += BI->getNumSuccessors();
- } else if (const auto *SI = dyn_cast<SwitchInst>(BB.getTerminator())) {
- FPI.BlocksReachedFromConditionalInstruction +=
- (SI->getNumCases() + (nullptr != SI->getDefaultDest()));
+void FunctionPropertiesInfo::updateForBB(const BasicBlock &BB,
+ int64_t Direction) {
+ assert(Direction == 1 || Direction == -1);
+ BasicBlockCount += Direction;
+ BlocksReachedFromConditionalInstruction +=
+ (Direction * getNrBlocksFromCond(BB));
+ for (const auto &I : BB) {
+ if (auto *CS = dyn_cast<CallBase>(&I)) {
+ const auto *Callee = CS->getCalledFunction();
+ if (Callee && !Callee->isIntrinsic() && !Callee->isDeclaration())
+ DirectCallsToDefinedFunctions += Direction;
}
-
- for (const auto &I : BB) {
- if (auto *CS = dyn_cast<CallBase>(&I)) {
- const auto *Callee = CS->getCalledFunction();
- if (Callee && !Callee->isIntrinsic() && !Callee->isDeclaration())
- ++FPI.DirectCallsToDefinedFunctions;
- }
- if (I.getOpcode() == Instruction::Load) {
- ++FPI.LoadInstCount;
- } else if (I.getOpcode() == Instruction::Store) {
- ++FPI.StoreInstCount;
- }
+ if (I.getOpcode() == Instruction::Load) {
+ LoadInstCount += Direction;
+ } else if (I.getOpcode() == Instruction::Store) {
+ StoreInstCount += Direction;
}
- // Loop Depth of the Basic Block
- int64_t LoopDepth;
- LoopDepth = LI.getLoopDepth(&BB);
- if (FPI.MaxLoopDepth < LoopDepth)
- FPI.MaxLoopDepth = LoopDepth;
}
- FPI.TopLevelLoopCount += llvm::size(LI);
+ TotalInstructionCount += Direction * BB.sizeWithoutDebug();
+}
+
+void FunctionPropertiesInfo::updateAggregateStats(const Function &F,
+ const LoopInfo &LI) {
+
+ Uses = getUses(F);
+ TopLevelLoopCount = llvm::size(LI);
+}
+
+FunctionPropertiesInfo
+FunctionPropertiesInfo::getFunctionPropertiesInfo(const Function &F,
+ const LoopInfo &LI) {
+
+ FunctionPropertiesInfo FPI;
+ for (const auto &BB : F)
+ if (!pred_empty(&BB) || BB.isEntryBlock())
+ FPI.reIncludeBB(BB, LI);
+ FPI.updateAggregateStats(F, LI);
return FPI;
}
@@ -68,7 +94,8 @@ void FunctionPropertiesInfo::print(raw_ostream &OS) const {
<< "LoadInstCount: " << LoadInstCount << "\n"
<< "StoreInstCount: " << StoreInstCount << "\n"
<< "MaxLoopDepth: " << MaxLoopDepth << "\n"
- << "TopLevelLoopCount: " << TopLevelLoopCount << "\n\n";
+ << "TopLevelLoopCount: " << TopLevelLoopCount << "\n"
+ << "TotalInstructionCount: " << TotalInstructionCount << "\n\n";
}
AnalysisKey FunctionPropertiesAnalysis::Key;
@@ -87,3 +114,60 @@ FunctionPropertiesPrinterPass::run(Function &F, FunctionAnalysisManager &AM) {
AM.getResult<FunctionPropertiesAnalysis>(F).print(OS);
return PreservedAnalyses::all();
}
+
+FunctionPropertiesUpdater::FunctionPropertiesUpdater(
+ FunctionPropertiesInfo &FPI, const CallBase &CB)
+ : FPI(FPI), CallSiteBB(*CB.getParent()), Caller(*CallSiteBB.getParent()) {
+
+ // For BBs that are likely to change, we subtract from feature totals their
+ // contribution. Some features, like max loop counts or depths, are left
+ // invalid, as they will be updated post-inlining.
+ SmallPtrSet<const BasicBlock *, 4> LikelyToChangeBBs;
+ // The CB BB will change - it'll either be split or the callee's body (single
+ // BB) will be pasted in.
+ LikelyToChangeBBs.insert(&CallSiteBB);
+
+ // The caller's entry BB may change due to new alloca instructions.
+ LikelyToChangeBBs.insert(&*Caller.begin());
+
+ // The successors may become unreachable in the case of `invoke` inlining.
+ // We track successors separately, too, because they form a boundary, together
+ // with the CB BB ('Entry') between which the inlined callee will be pasted.
+ Successors.insert(succ_begin(&CallSiteBB), succ_end(&CallSiteBB));
+ for (const auto *BB : Successors)
+ LikelyToChangeBBs.insert(BB);
+
+ // Commit the change. While some of the BBs accounted for above may play dual
+ // role - e.g. caller's entry BB may be the same as the callsite BB - set
+ // insertion semantics make sure we account them once. This needs to be
+ // followed in `finish`, too.
+ for (const auto *BB : LikelyToChangeBBs)
+ FPI.updateForBB(*BB, -1);
+}
+
+void FunctionPropertiesUpdater::finish(const LoopInfo &LI) {
+ DenseSet<const BasicBlock *> ReIncluded;
+ std::deque<const BasicBlock *> Worklist;
+
+ if (&CallSiteBB != &*Caller.begin()) {
+ FPI.reIncludeBB(*Caller.begin(), LI);
+ ReIncluded.insert(&*Caller.begin());
+ }
+
+ // Update feature values from the BBs that were copied from the callee, or
+ // might have been modified because of inlining. The latter have been
+ // subtracted in the FunctionPropertiesUpdater ctor.
+ Worklist.push_back(&CallSiteBB);
+ while (!Worklist.empty()) {
+ const auto *BB = Worklist.front();
+ Worklist.pop_front();
+ if (!ReIncluded.insert(BB).second)
+ continue;
+ FPI.reIncludeBB(*BB, LI);
+ if (!Successors.contains(BB))
+ for (const auto *Succ : successors(BB))
+ Worklist.push_back(Succ);
+ }
+ FPI.updateAggregateStats(Caller, LI);
+ assert(FPI == FunctionPropertiesInfo::getFunctionPropertiesInfo(Caller, LI));
+}
diff --git a/llvm/lib/Analysis/MLInlineAdvisor.cpp b/llvm/lib/Analysis/MLInlineAdvisor.cpp
index cc454dd3687d..b3bfe5efb401 100644
--- a/llvm/lib/Analysis/MLInlineAdvisor.cpp
+++ b/llvm/lib/Analysis/MLInlineAdvisor.cpp
@@ -19,6 +19,7 @@
#include "llvm/Analysis/InlineCost.h"
#include "llvm/Analysis/InlineModelFeatureMaps.h"
#include "llvm/Analysis/LazyCallGraph.h"
+#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/MLModelRunner.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/TargetTransformInfo.h"
@@ -132,6 +133,7 @@ unsigned MLInlineAdvisor::getInitialFunctionLevel(const Function &F) const {
}
void MLInlineAdvisor::onPassEntry() {
+ FPICache.clear();
// Function passes executed between InlinerPass runs may have changed the
// module-wide features.
// The cgscc pass manager rules are such that:
@@ -170,6 +172,8 @@ void MLInlineAdvisor::onPassEntry() {
}
void MLInlineAdvisor::onPassExit(LazyCallGraph::SCC *LastSCC) {
+ // No need to keep this around - function passes will invalidate it.
+ FPICache.clear();
if (!LastSCC)
return;
// Keep track of the nodes and edges we last saw. Then, in onPassEntry,
@@ -187,8 +191,7 @@ void MLInlineAdvisor::onPassExit(LazyCallGraph::SCC *LastSCC) {
}
int64_t MLInlineAdvisor::getLocalCalls(Function &F) {
- return FAM.getResult<FunctionPropertiesAnalysis>(F)
- .DirectCallsToDefinedFunctions;
+ return getCachedFPI(F).DirectCallsToDefinedFunctions;
}
// Update the internal state of the advisor, and force invalidate feature
@@ -220,15 +223,13 @@ void MLInlineAdvisor::onSuccessfulInlining(const MLInlineAdvice &Advice,
// For edges, we 'forget' the edges that the caller and callee used to have
// before inlining, and add back what they currently have together.
int64_t NewCallerAndCalleeEdges =
- FAM.getResult<FunctionPropertiesAnalysis>(*Caller)
- .DirectCallsToDefinedFunctions;
+ getCachedFPI(*Caller).DirectCallsToDefinedFunctions;
if (CalleeWasDeleted)
--NodeCount;
else
NewCallerAndCalleeEdges +=
- FAM.getResult<FunctionPropertiesAnalysis>(*Callee)
- .DirectCallsToDefinedFunctions;
+ getCachedFPI(*Callee).DirectCallsToDefinedFunctions;
EdgeCount += (NewCallerAndCalleeEdges - Advice.CallerAndCalleeEdges);
assert(CurrentIRSize >= 0 && EdgeCount >= 0 && NodeCount >= 0);
}
@@ -241,6 +242,15 @@ int64_t MLInlineAdvisor::getModuleIRSize() const {
return Ret;
}
+FunctionPropertiesInfo &MLInlineAdvisor::getCachedFPI(Function &F) const {
+ auto InsertPair =
+ FPICache.insert(std::make_pair(&F, FunctionPropertiesInfo()));
+ if (!InsertPair.second)
+ return InsertPair.first->second;
+ InsertPair.first->second = FAM.getResult<FunctionPropertiesAnalysis>(F);
+ return InsertPair.first->second;
+}
+
std::unique_ptr<InlineAdvice> MLInlineAdvisor::getAdviceImpl(CallBase &CB) {
auto &Caller = *CB.getCaller();
auto &Callee = *CB.getCalledFunction();
@@ -300,8 +310,8 @@ std::unique_ptr<InlineAdvice> MLInlineAdvisor::getAdviceImpl(CallBase &CB) {
NrCtantParams += (isa<Constant>(*I));
}
- auto &CallerBefore = FAM.getResult<FunctionPropertiesAnalysis>(Caller);
- auto &CalleeBefore = FAM.getResult<FunctionPropertiesAnalysis>(Callee);
+ auto &CallerBefore = getCachedFPI(Caller);
+ auto &CalleeBefore = getCachedFPI(Callee);
*ModelRunner->getTensor<int64_t>(FeatureIndex::CalleeBasicBlockCount) =
CalleeBefore.BasicBlockCount;
@@ -354,11 +364,30 @@ std::unique_ptr<InlineAdvice> MLInlineAdvisor::getMandatoryAdvice(CallBase &CB,
return std::make_unique<InlineAdvice>(this, CB, getCallerORE(CB), Advice);
}
+const LoopInfo &MLInlineAdvisor::getLoopInfo(Function &F) const {
+ return FAM.getResult<LoopAnalysis>(F);
+}
+
std::unique_ptr<MLInlineAdvice>
MLInlineAdvisor::getMandatoryAdviceImpl(CallBase &CB) {
return std::make_unique<MLInlineAdvice>(this, CB, getCallerORE(CB), true);
}
+MLInlineAdvice::MLInlineAdvice(MLInlineAdvisor *Advisor, CallBase &CB,
+ OptimizationRemarkEmitter &ORE,
+ bool Recommendation)
+ : InlineAdvice(Advisor, CB, ORE, Recommendation),
+ CallerIRSize(Advisor->isForcedToStop() ? 0 : Advisor->getIRSize(*Caller)),
+ CalleeIRSize(Advisor->isForcedToStop() ? 0 : Advisor->getIRSize(*Callee)),
+ CallerAndCalleeEdges(Advisor->isForcedToStop()
+ ? 0
+ : (Advisor->getLocalCalls(*Caller) +
+ Advisor->getLocalCalls(*Callee))),
+ PreInlineCallerFPI(Advisor->getCachedFPI(*Caller)) {
+ if (Recommendation)
+ FPU.emplace(Advisor->getCachedFPI(*getCaller()), CB);
+}
+
void MLInlineAdvice::reportContextForRemark(
DiagnosticInfoOptimizationBase &OR) {
using namespace ore;
@@ -369,7 +398,12 @@ void MLInlineAdvice::reportContextForRemark(
OR << NV("ShouldInline", isInliningRecommended());
}
+void MLInlineAdvice::updateCachedCallerFPI() {
+ FPU->finish(getAdvisor()->getLoopInfo(*Caller));
+}
+
void MLInlineAdvice::recordInliningImpl() {
+ updateCachedCallerFPI();
ORE.emit([&]() {
OptimizationRemark R(DEBUG_TYPE, "InliningSuccess", DLoc, Block);
reportContextForRemark(R);
@@ -379,6 +413,7 @@ void MLInlineAdvice::recordInliningImpl() {
}
void MLInlineAdvice::recordInliningWithCalleeDeletedImpl() {
+ updateCachedCallerFPI();
ORE.emit([&]() {
OptimizationRemark R(DEBUG_TYPE, "InliningSuccessWithCalleeDeleted", DLoc,
Block);
@@ -390,6 +425,7 @@ void MLInlineAdvice::recordInliningWithCalleeDeletedImpl() {
void MLInlineAdvice::recordUnsuccessfulInliningImpl(
const InlineResult &Result) {
+ getAdvisor()->getCachedFPI(*Caller) = PreInlineCallerFPI;
ORE.emit([&]() {
OptimizationRemarkMissed R(DEBUG_TYPE, "InliningAttemptedAndUnsuccessful",
DLoc, Block);
@@ -398,6 +434,7 @@ void MLInlineAdvice::recordUnsuccessfulInliningImpl(
});
}
void MLInlineAdvice::recordUnattemptedInliningImpl() {
+ assert(!FPU);
ORE.emit([&]() {
OptimizationRemarkMissed R(DEBUG_TYPE, "IniningNotAttempted", DLoc, Block);
reportContextForRemark(R);
diff --git a/llvm/unittests/Analysis/FunctionPropertiesAnalysisTest.cpp b/llvm/unittests/Analysis/FunctionPropertiesAnalysisTest.cpp
index 6ed7657f08f7..76a63c00a9a1 100644
--- a/llvm/unittests/Analysis/FunctionPropertiesAnalysisTest.cpp
+++ b/llvm/unittests/Analysis/FunctionPropertiesAnalysisTest.cpp
@@ -7,14 +7,20 @@
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/FunctionPropertiesAnalysis.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/SourceMgr.h"
+#include "llvm/Transforms/Utils/Cloning.h"
#include "gtest/gtest.h"
+#include <cstring>
using namespace llvm;
namespace {
@@ -37,6 +43,15 @@ class FunctionPropertiesAnalysisTest : public testing::Test {
Err.print("MLAnalysisTests", errs());
return Mod;
}
+
+ CallBase* findCall(Function& F, const char* Name = nullptr) {
+ for (auto &BB : F)
+ for (auto &I : BB )
+ if (auto *CB = dyn_cast<CallBase>(&I))
+ if (!Name || CB->getName() == Name)
+ return CB;
+ return nullptr;
+ }
};
TEST_F(FunctionPropertiesAnalysisTest, BasicTest) {
@@ -91,4 +106,441 @@ define internal i32 @top() {
EXPECT_EQ(BranchesFeatures.MaxLoopDepth, 0);
EXPECT_EQ(BranchesFeatures.TopLevelLoopCount, 0);
}
+
+TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBSimple) {
+ LLVMContext C;
+ std::unique_ptr<Module> M = makeLLVMModule(C,
+ R"IR(
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-linux-gnu"
+define i32 @f1(i32 %a) {
+ %b = call i32 @f2(i32 %a)
+ %c = add i32 %b, 2
+ ret i32 %c
+}
+
+define i32 @f2(i32 %a) {
+ %b = add i32 %a, 1
+ ret i32 %b
+}
+)IR");
+
+ Function *F1 = M->getFunction("f1");
+ CallBase* CB = findCall(*F1, "b");
+ EXPECT_NE(CB, nullptr);
+
+ FunctionPropertiesInfo ExpectedInitial;
+ ExpectedInitial.BasicBlockCount = 1;
+ ExpectedInitial.TotalInstructionCount = 3;
+ ExpectedInitial.Uses = 1;
+ ExpectedInitial.DirectCallsToDefinedFunctions = 1;
+
+ FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;
+ ExpectedFinal.DirectCallsToDefinedFunctions = 0;
+
+ auto FPI = buildFPI(*F1);
+ EXPECT_EQ(FPI, ExpectedInitial);
+
+ FunctionPropertiesUpdater FPU(FPI, *CB);
+ InlineFunctionInfo IFI;
+ auto IR = llvm::InlineFunction(*CB, IFI);
+ EXPECT_TRUE(IR.isSuccess());
+ FPU.finish(*LI);
+ EXPECT_EQ(FPI, ExpectedFinal);
+}
+
+TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLargerCFG) {
+ LLVMContext C;
+ std::unique_ptr<Module> M = makeLLVMModule(C,
+ R"IR(
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-linux-gnu"
+define i32 @f1(i32 %a) {
+entry:
+ %i = icmp slt i32 %a, 0
+ br i1 %i, label %if.then, label %if.else
+if.then:
+ %b = call i32 @f2(i32 %a)
+ %c1 = add i32 %b, 2
+ br label %end
+if.else:
+ %c2 = add i32 %a, 1
+ br label %end
+end:
+ %ret = phi i32 [%c1, %if.then],[%c2, %if.else]
+ ret i32 %ret
+}
+
+define i32 @f2(i32 %a) {
+ %b = add i32 %a, 1
+ ret i32 %b
+}
+)IR");
+
+ Function *F1 = M->getFunction("f1");
+ CallBase* CB = findCall(*F1, "b");
+ EXPECT_NE(CB, nullptr);
+
+ FunctionPropertiesInfo ExpectedInitial;
+ ExpectedInitial.BasicBlockCount = 4;
+ ExpectedInitial.BlocksReachedFromConditionalInstruction = 2;
+ ExpectedInitial.TotalInstructionCount = 9;
+ ExpectedInitial.Uses = 1;
+ ExpectedInitial.DirectCallsToDefinedFunctions = 1;
+
+ FunctionPropertiesInfo ExpectedFinal = ExpectedInitial;
+ ExpectedFinal.DirectCallsToDefinedFunctions = 0;
+
+ auto FPI = buildFPI(*F1);
+ EXPECT_EQ(FPI, ExpectedInitial);
+
+ FunctionPropertiesUpdater FPU(FPI, *CB);
+ InlineFunctionInfo IFI;
+ auto IR = llvm::InlineFunction(*CB, IFI);
+ EXPECT_TRUE(IR.isSuccess());
+ FPU.finish(*LI);
+ EXPECT_EQ(FPI, ExpectedFinal);
+}
+
+TEST_F(FunctionPropertiesAnalysisTest, InlineSameBBLoops) {
+ LLVMContext C;
+ std::unique_ptr<Module> M = makeLLVMModule(C,
+ R"IR(
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-linux-gnu"
+define i32 @f1(i32 %a) {
+entry:
+ %i = icmp slt i32 %a, 0
+ br i1 %i, label %if.then, label %if.else
+if.then:
+ %b = call i32 @f2(i32 %a)
+ %c1 = add i32 %b, 2
+ br label %end
+if.else:
+ %c2 = add i32 %a, 1
+ br label %end
+end:
+ %ret = phi i32 [%c1, %if.then],[%c2, %if.else]
+ ret i32 %ret
+}
+
+define i32 @f2(i32 %a) {
+entry:
+ br label %loop
+loop:
+ %indvar = phi i32 [%indvar.next, %loop], [0, %entry]
+ %b = add i32 %a, %indvar
+ %indvar.next = add i32 %indvar, 1
+ %cond = icmp slt i32 %indvar.next, %a
+ br i1 %cond, label %loop, label %exit
+exit:
+ ret i32 %b
+}
+)IR");
+
+ Function *F1 = M->getFunction("f1");
+ CallBase* CB = findCall(*F1, "b");
+ EXPECT_NE(CB, nullptr);
+
+ FunctionPropertiesInfo ExpectedInitial;
+ ExpectedInitial.BasicBlockCount = 4;
+ ExpectedInitial.BlocksReachedFromConditionalInstruction = 2;
+ ExpectedInitial.TotalInstructionCount = 9;
+ ExpectedInitial.Uses = 1;
+ ExpectedInitial.DirectCallsToDefinedFunctions = 1;
+
+ FunctionPropertiesInfo ExpectedFinal;
+ ExpectedFinal.BasicBlockCount = 6;
+ ExpectedFinal.BlocksReachedFromConditionalInstruction = 4;
+ ExpectedFinal.Uses = 1;
+ ExpectedFinal.MaxLoopDepth = 1;
+ ExpectedFinal.TopLevelLoopCount = 1;
+ ExpectedFinal.TotalInstructionCount = 14;
+
+ auto FPI = buildFPI(*F1);
+ EXPECT_EQ(FPI, ExpectedInitial);
+ FunctionPropertiesUpdater FPU(FPI, *CB);
+ InlineFunctionInfo IFI;
+
+ auto IR = llvm::InlineFunction(*CB, IFI);
+ EXPECT_TRUE(IR.isSuccess());
+ DominatorTree DTNew(*F1);
+ LoopInfo LINew(DTNew);
+ FPU.finish(LINew);
+ EXPECT_EQ(FPI, ExpectedFinal);
+}
+
+TEST_F(FunctionPropertiesAnalysisTest, InvokeSimple) {
+ LLVMContext C;
+ std::unique_ptr<Module> M = makeLLVMModule(C,
+ R"IR(
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-linux-gnu"
+declare void @might_throw()
+
+define internal void @callee() {
+entry:
+ call void @might_throw()
+ ret void
+}
+
+define i32 @caller() personality i32 (...)* @__gxx_personality_v0 {
+entry:
+ invoke void @callee()
+ to label %cont unwind label %exc
+
+cont:
+ ret i32 0
+
+exc:
+ %exn = landingpad {i8*, i32}
+ cleanup
+ ret i32 1
+}
+
+declare i32 @__gxx_personality_v0(...)
+)IR");
+
+ Function *F1 = M->getFunction("caller");
+ CallBase* CB = findCall(*F1);
+ EXPECT_NE(CB, nullptr);
+
+ auto FPI = buildFPI(*F1);
+ FunctionPropertiesUpdater FPU(FPI, *CB);
+ InlineFunctionInfo IFI;
+ auto IR = llvm::InlineFunction(*CB, IFI);
+ EXPECT_TRUE(IR.isSuccess());
+ DominatorTree DTNew(*F1);
+ LoopInfo LINew(DTNew);
+ FPU.finish(LINew);
+ EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),
+ F1->getBasicBlockList().size());
+ EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),
+ F1->getInstructionCount());
+}
+
+TEST_F(FunctionPropertiesAnalysisTest, InvokeUnreachableHandler) {
+ LLVMContext C;
+ std::unique_ptr<Module> M = makeLLVMModule(C,
+ R"IR(
+declare void @might_throw()
+
+define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 {
+entry:
+ invoke void @might_throw()
+ to label %cont unwind label %exc
+
+cont:
+ ret i32 0
+
+exc:
+ %exn = landingpad {i8*, i32}
+ cleanup
+ resume { i8*, i32 } %exn
+}
+
+define i32 @caller() personality i32 (...)* @__gxx_personality_v0 {
+entry:
+ %X = invoke i32 @callee()
+ to label %cont unwind label %Handler
+
+cont:
+ ret i32 %X
+
+Handler:
+ %exn = landingpad {i8*, i32}
+ cleanup
+ ret i32 1
+}
+
+declare i32 @__gxx_personality_v0(...)
+)IR");
+
+ Function *F1 = M->getFunction("caller");
+ CallBase* CB = findCall(*F1);
+ EXPECT_NE(CB, nullptr);
+
+ auto FPI = buildFPI(*F1);
+ FunctionPropertiesUpdater FPU(FPI, *CB);
+ InlineFunctionInfo IFI;
+ auto IR = llvm::InlineFunction(*CB, IFI);
+ EXPECT_TRUE(IR.isSuccess());
+ DominatorTree DTNew(*F1);
+ LoopInfo LINew(DTNew);
+ FPU.finish(LINew);
+ EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),
+ F1->getBasicBlockList().size() - 1);
+ EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),
+ F1->getInstructionCount() - 2);
+ EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, LINew));
+}
+
+TEST_F(FunctionPropertiesAnalysisTest, Rethrow) {
+ LLVMContext C;
+ std::unique_ptr<Module> M = makeLLVMModule(C,
+ R"IR(
+declare void @might_throw()
+
+define internal i32 @callee() personality i32 (...)* @__gxx_personality_v0 {
+entry:
+ invoke void @might_throw()
+ to label %cont unwind label %exc
+
+cont:
+ ret i32 0
+
+exc:
+ %exn = landingpad {i8*, i32}
+ cleanup
+ resume { i8*, i32 } %exn
+}
+
+define i32 @caller() personality i32 (...)* @__gxx_personality_v0 {
+entry:
+ %X = invoke i32 @callee()
+ to label %cont unwind label %Handler
+
+cont:
+ ret i32 %X
+
+Handler:
+ %exn = landingpad {i8*, i32}
+ cleanup
+ ret i32 1
+}
+
+declare i32 @__gxx_personality_v0(...)
+)IR");
+
+ Function *F1 = M->getFunction("caller");
+ CallBase* CB = findCall(*F1);
+ EXPECT_NE(CB, nullptr);
+
+ auto FPI = buildFPI(*F1);
+ FunctionPropertiesUpdater FPU(FPI, *CB);
+ InlineFunctionInfo IFI;
+ auto IR = llvm::InlineFunction(*CB, IFI);
+ EXPECT_TRUE(IR.isSuccess());
+ DominatorTree DTNew(*F1);
+ LoopInfo LINew(DTNew);
+ FPU.finish(LINew);
+ EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),
+ F1->getBasicBlockList().size() - 1);
+ EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),
+ F1->getInstructionCount() - 2);
+ EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, LINew));
+}
+
+TEST_F(FunctionPropertiesAnalysisTest, LPadChanges) {
+ LLVMContext C;
+ std::unique_ptr<Module> M = makeLLVMModule(C,
+ R"IR(
+declare void @external_func()
+
+ at exception_type1 = external global i8
+ at exception_type2 = external global i8
+
+
+define internal void @inner() personality i8* null {
+ invoke void @external_func()
+ to label %cont unwind label %lpad
+cont:
+ ret void
+lpad:
+ %lp = landingpad i32
+ catch i8* @exception_type1
+ resume i32 %lp
+}
+
+define void @outer() personality i8* null {
+ invoke void @inner()
+ to label %cont unwind label %lpad
+cont:
+ ret void
+lpad:
+ %lp = landingpad i32
+ cleanup
+ catch i8* @exception_type2
+ resume i32 %lp
+}
+
+)IR");
+
+ Function *F1 = M->getFunction("outer");
+ CallBase* CB = findCall(*F1);
+ EXPECT_NE(CB, nullptr);
+
+ auto FPI = buildFPI(*F1);
+ FunctionPropertiesUpdater FPU(FPI, *CB);
+ InlineFunctionInfo IFI;
+ auto IR = llvm::InlineFunction(*CB, IFI);
+ EXPECT_TRUE(IR.isSuccess());
+ DominatorTree DTNew(*F1);
+ LoopInfo LINew(DTNew);
+ FPU.finish(LINew);
+ EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),
+ F1->getBasicBlockList().size() - 1);
+ EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),
+ F1->getInstructionCount() - 2);
+ EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, LINew));
+}
+
+TEST_F(FunctionPropertiesAnalysisTest, LPadChangesConditional) {
+ LLVMContext C;
+ std::unique_ptr<Module> M = makeLLVMModule(C,
+ R"IR(
+declare void @external_func()
+
+ at exception_type1 = external global i8
+ at exception_type2 = external global i8
+
+
+define internal void @inner() personality i8* null {
+ invoke void @external_func()
+ to label %cont unwind label %lpad
+cont:
+ ret void
+lpad:
+ %lp = landingpad i32
+ catch i8* @exception_type1
+ resume i32 %lp
+}
+
+define void @outer(i32 %a) personality i8* null {
+entry:
+ %i = icmp slt i32 %a, 0
+ br i1 %i, label %if.then, label %cont
+if.then:
+ invoke void @inner()
+ to label %cont unwind label %lpad
+cont:
+ ret void
+lpad:
+ %lp = landingpad i32
+ cleanup
+ catch i8* @exception_type2
+ resume i32 %lp
+}
+
+)IR");
+
+ Function *F1 = M->getFunction("outer");
+ CallBase* CB = findCall(*F1);
+ EXPECT_NE(CB, nullptr);
+
+ auto FPI = buildFPI(*F1);
+ FunctionPropertiesUpdater FPU(FPI, *CB);
+ InlineFunctionInfo IFI;
+ auto IR = llvm::InlineFunction(*CB, IFI);
+ EXPECT_TRUE(IR.isSuccess());
+ DominatorTree DTNew(*F1);
+ LoopInfo LINew(DTNew);
+ FPU.finish(LINew);
+ EXPECT_EQ(static_cast<size_t>(FPI.BasicBlockCount),
+ F1->getBasicBlockList().size() - 1);
+ EXPECT_EQ(static_cast<size_t>(FPI.TotalInstructionCount),
+ F1->getInstructionCount() - 2);
+ EXPECT_EQ(FPI, FunctionPropertiesInfo::getFunctionPropertiesInfo(*F1, LINew));
+}
+
} // end anonymous namespace
More information about the llvm-commits
mailing list