mirror of
https://github.com/RPCSX/llvm.git
synced 2025-01-05 03:10:29 +00:00
8bf2780092
Summary: This never really got implemented, and was very hard to test before a lot of the refactoring changes to make things more robust. But now we can test it thoroughly and cleanly, especially at the CGSCC level. The core idea is that when an inner analysis manager proxy receives the invalidation event for the outer IR unit, it needs to walk the inner IR units and propagate it to the inner analysis manager for each of those units. For example, each function in the SCC needs to get an invalidation event when the SCC gets one. The function / module interaction is somewhat boring here. This really becomes interesting in the face of analysis-backed IR units. This patch effectively handles all of the CGSCC layer's needs -- both invalidating SCC analysis and invalidating function analysis when an SCC gets invalidated. However, this second aspect doesn't really handle the LoopAnalysisManager well at this point. That one will need some change of design in order to fully integrate, because unlike the call graph, the entire function behind a LoopAnalysis's results can vanish out from under us, and we won't even have a cached API to access. I'd like to try to separate solving the loop problems into a subsequent patch though in order to keep this more focused so I've adapted them to the API and updated the tests that immediately fail, but I've not added the level of testing and validation at that layer that I have at the CGSCC layer. An important aspect of this change is that the proxy for the FunctionAnalysisManager at the SCC pass layer doesn't work like the other proxies for an inner IR unit as it doesn't directly manage the FunctionAnalysisManager and invalidation or clearing of it. This would create an ever worsening problem of dual ownership of this responsibility, split between the module-level FAM proxy and this SCC-level FAM proxy. Instead, this patch changes the SCC-level FAM proxy to work in terms of the module-level proxy and defer to it to handle much of the updates. It only does SCC-specific invalidation. This will become more important in subsequent patches that support more complex invalidaiton scenarios. Reviewers: jlebar Subscribers: mehdi_amini, mcrosier, mzolotukhin, llvm-commits Differential Revision: https://reviews.llvm.org/D27197 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@289317 91177308-0d34-0410-b5e6-96231b3b80d8
485 lines
16 KiB
C++
485 lines
16 KiB
C++
//===- llvm/unittest/IR/PassManager.cpp - PassManager tests ---------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/AsmParser/Parser.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/IR/PassManager.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
|
|
class TestFunctionAnalysis : public AnalysisInfoMixin<TestFunctionAnalysis> {
|
|
public:
|
|
struct Result {
|
|
Result(int Count) : InstructionCount(Count) {}
|
|
int InstructionCount;
|
|
};
|
|
|
|
TestFunctionAnalysis(int &Runs) : Runs(Runs) {}
|
|
|
|
/// \brief Run the analysis pass over the function and return a result.
|
|
Result run(Function &F, FunctionAnalysisManager &AM) {
|
|
++Runs;
|
|
int Count = 0;
|
|
for (Function::iterator BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI)
|
|
for (BasicBlock::iterator II = BBI->begin(), IE = BBI->end(); II != IE;
|
|
++II)
|
|
++Count;
|
|
return Result(Count);
|
|
}
|
|
|
|
private:
|
|
friend AnalysisInfoMixin<TestFunctionAnalysis>;
|
|
static AnalysisKey Key;
|
|
|
|
int &Runs;
|
|
};
|
|
|
|
AnalysisKey TestFunctionAnalysis::Key;
|
|
|
|
class TestModuleAnalysis : public AnalysisInfoMixin<TestModuleAnalysis> {
|
|
public:
|
|
struct Result {
|
|
Result(int Count) : FunctionCount(Count) {}
|
|
int FunctionCount;
|
|
};
|
|
|
|
TestModuleAnalysis(int &Runs) : Runs(Runs) {}
|
|
|
|
Result run(Module &M, ModuleAnalysisManager &AM) {
|
|
++Runs;
|
|
int Count = 0;
|
|
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
|
|
++Count;
|
|
return Result(Count);
|
|
}
|
|
|
|
private:
|
|
friend AnalysisInfoMixin<TestModuleAnalysis>;
|
|
static AnalysisKey Key;
|
|
|
|
int &Runs;
|
|
};
|
|
|
|
AnalysisKey TestModuleAnalysis::Key;
|
|
|
|
struct TestModulePass : PassInfoMixin<TestModulePass> {
|
|
TestModulePass(int &RunCount) : RunCount(RunCount) {}
|
|
|
|
PreservedAnalyses run(Module &M, ModuleAnalysisManager &) {
|
|
++RunCount;
|
|
return PreservedAnalyses::none();
|
|
}
|
|
|
|
int &RunCount;
|
|
};
|
|
|
|
struct TestPreservingModulePass : PassInfoMixin<TestPreservingModulePass> {
|
|
PreservedAnalyses run(Module &M, ModuleAnalysisManager &) {
|
|
return PreservedAnalyses::all();
|
|
}
|
|
};
|
|
|
|
struct TestFunctionPass : PassInfoMixin<TestFunctionPass> {
|
|
TestFunctionPass(int &RunCount, int &AnalyzedInstrCount,
|
|
int &AnalyzedFunctionCount,
|
|
bool OnlyUseCachedResults = false)
|
|
: RunCount(RunCount), AnalyzedInstrCount(AnalyzedInstrCount),
|
|
AnalyzedFunctionCount(AnalyzedFunctionCount),
|
|
OnlyUseCachedResults(OnlyUseCachedResults) {}
|
|
|
|
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
|
|
++RunCount;
|
|
|
|
const ModuleAnalysisManager &MAM =
|
|
AM.getResult<ModuleAnalysisManagerFunctionProxy>(F).getManager();
|
|
if (TestModuleAnalysis::Result *TMA =
|
|
MAM.getCachedResult<TestModuleAnalysis>(*F.getParent()))
|
|
AnalyzedFunctionCount += TMA->FunctionCount;
|
|
|
|
if (OnlyUseCachedResults) {
|
|
// Hack to force the use of the cached interface.
|
|
if (TestFunctionAnalysis::Result *AR =
|
|
AM.getCachedResult<TestFunctionAnalysis>(F))
|
|
AnalyzedInstrCount += AR->InstructionCount;
|
|
} else {
|
|
// Typical path just runs the analysis as needed.
|
|
TestFunctionAnalysis::Result &AR = AM.getResult<TestFunctionAnalysis>(F);
|
|
AnalyzedInstrCount += AR.InstructionCount;
|
|
}
|
|
|
|
return PreservedAnalyses::all();
|
|
}
|
|
|
|
int &RunCount;
|
|
int &AnalyzedInstrCount;
|
|
int &AnalyzedFunctionCount;
|
|
bool OnlyUseCachedResults;
|
|
};
|
|
|
|
// A test function pass that invalidates all function analyses for a function
|
|
// with a specific name.
|
|
struct TestInvalidationFunctionPass
|
|
: PassInfoMixin<TestInvalidationFunctionPass> {
|
|
TestInvalidationFunctionPass(StringRef FunctionName) : Name(FunctionName) {}
|
|
|
|
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
|
|
return F.getName() == Name ? PreservedAnalyses::none()
|
|
: PreservedAnalyses::all();
|
|
}
|
|
|
|
StringRef Name;
|
|
};
|
|
|
|
std::unique_ptr<Module> parseIR(LLVMContext &Context, const char *IR) {
|
|
SMDiagnostic Err;
|
|
return parseAssemblyString(IR, Err, Context);
|
|
}
|
|
|
|
class PassManagerTest : public ::testing::Test {
|
|
protected:
|
|
LLVMContext Context;
|
|
std::unique_ptr<Module> M;
|
|
|
|
public:
|
|
PassManagerTest()
|
|
: M(parseIR(Context, "define void @f() {\n"
|
|
"entry:\n"
|
|
" call void @g()\n"
|
|
" call void @h()\n"
|
|
" ret void\n"
|
|
"}\n"
|
|
"define void @g() {\n"
|
|
" ret void\n"
|
|
"}\n"
|
|
"define void @h() {\n"
|
|
" ret void\n"
|
|
"}\n")) {}
|
|
};
|
|
|
|
TEST_F(PassManagerTest, BasicPreservedAnalyses) {
|
|
PreservedAnalyses PA1 = PreservedAnalyses();
|
|
EXPECT_FALSE(PA1.preserved<TestFunctionAnalysis>());
|
|
EXPECT_FALSE(PA1.preserved<TestModuleAnalysis>());
|
|
PreservedAnalyses PA2 = PreservedAnalyses::none();
|
|
EXPECT_FALSE(PA2.preserved<TestFunctionAnalysis>());
|
|
EXPECT_FALSE(PA2.preserved<TestModuleAnalysis>());
|
|
PreservedAnalyses PA3 = PreservedAnalyses::all();
|
|
EXPECT_TRUE(PA3.preserved<TestFunctionAnalysis>());
|
|
EXPECT_TRUE(PA3.preserved<TestModuleAnalysis>());
|
|
PreservedAnalyses PA4 = PA1;
|
|
EXPECT_FALSE(PA4.preserved<TestFunctionAnalysis>());
|
|
EXPECT_FALSE(PA4.preserved<TestModuleAnalysis>());
|
|
PA4 = PA3;
|
|
EXPECT_TRUE(PA4.preserved<TestFunctionAnalysis>());
|
|
EXPECT_TRUE(PA4.preserved<TestModuleAnalysis>());
|
|
PA4 = std::move(PA2);
|
|
EXPECT_FALSE(PA4.preserved<TestFunctionAnalysis>());
|
|
EXPECT_FALSE(PA4.preserved<TestModuleAnalysis>());
|
|
PA4.preserve<TestFunctionAnalysis>();
|
|
EXPECT_TRUE(PA4.preserved<TestFunctionAnalysis>());
|
|
EXPECT_FALSE(PA4.preserved<TestModuleAnalysis>());
|
|
PA1.preserve<TestModuleAnalysis>();
|
|
EXPECT_FALSE(PA1.preserved<TestFunctionAnalysis>());
|
|
EXPECT_TRUE(PA1.preserved<TestModuleAnalysis>());
|
|
PA1.preserve<TestFunctionAnalysis>();
|
|
EXPECT_TRUE(PA1.preserved<TestFunctionAnalysis>());
|
|
EXPECT_TRUE(PA1.preserved<TestModuleAnalysis>());
|
|
PA1.intersect(PA4);
|
|
EXPECT_TRUE(PA1.preserved<TestFunctionAnalysis>());
|
|
EXPECT_FALSE(PA1.preserved<TestModuleAnalysis>());
|
|
}
|
|
|
|
TEST_F(PassManagerTest, Basic) {
|
|
FunctionAnalysisManager FAM(/*DebugLogging*/ true);
|
|
int FunctionAnalysisRuns = 0;
|
|
FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });
|
|
|
|
ModuleAnalysisManager MAM(/*DebugLogging*/ true);
|
|
int ModuleAnalysisRuns = 0;
|
|
MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); });
|
|
MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
|
|
FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });
|
|
|
|
ModulePassManager MPM;
|
|
|
|
// Count the runs over a Function.
|
|
int FunctionPassRunCount1 = 0;
|
|
int AnalyzedInstrCount1 = 0;
|
|
int AnalyzedFunctionCount1 = 0;
|
|
{
|
|
// Pointless scoped copy to test move assignment.
|
|
ModulePassManager NestedMPM(/*DebugLogging*/ true);
|
|
FunctionPassManager FPM;
|
|
{
|
|
// Pointless scope to test move assignment.
|
|
FunctionPassManager NestedFPM(/*DebugLogging*/ true);
|
|
NestedFPM.addPass(TestFunctionPass(
|
|
FunctionPassRunCount1, AnalyzedInstrCount1, AnalyzedFunctionCount1));
|
|
FPM = std::move(NestedFPM);
|
|
}
|
|
NestedMPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
|
|
MPM = std::move(NestedMPM);
|
|
}
|
|
|
|
// Count the runs over a module.
|
|
int ModulePassRunCount = 0;
|
|
MPM.addPass(TestModulePass(ModulePassRunCount));
|
|
|
|
// Count the runs over a Function in a separate manager.
|
|
int FunctionPassRunCount2 = 0;
|
|
int AnalyzedInstrCount2 = 0;
|
|
int AnalyzedFunctionCount2 = 0;
|
|
{
|
|
FunctionPassManager FPM(/*DebugLogging*/ true);
|
|
FPM.addPass(TestFunctionPass(FunctionPassRunCount2, AnalyzedInstrCount2,
|
|
AnalyzedFunctionCount2));
|
|
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
|
|
}
|
|
|
|
// A third function pass manager but with only preserving intervening passes
|
|
// and with a function pass that invalidates exactly one analysis.
|
|
MPM.addPass(TestPreservingModulePass());
|
|
int FunctionPassRunCount3 = 0;
|
|
int AnalyzedInstrCount3 = 0;
|
|
int AnalyzedFunctionCount3 = 0;
|
|
{
|
|
FunctionPassManager FPM(/*DebugLogging*/ true);
|
|
FPM.addPass(TestFunctionPass(FunctionPassRunCount3, AnalyzedInstrCount3,
|
|
AnalyzedFunctionCount3));
|
|
FPM.addPass(TestInvalidationFunctionPass("f"));
|
|
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
|
|
}
|
|
|
|
// A fourth function pass manager but with only preserving intervening
|
|
// passes but triggering the module analysis.
|
|
MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());
|
|
int FunctionPassRunCount4 = 0;
|
|
int AnalyzedInstrCount4 = 0;
|
|
int AnalyzedFunctionCount4 = 0;
|
|
{
|
|
FunctionPassManager FPM;
|
|
FPM.addPass(TestFunctionPass(FunctionPassRunCount4, AnalyzedInstrCount4,
|
|
AnalyzedFunctionCount4));
|
|
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
|
|
}
|
|
|
|
// A fifth function pass manager which invalidates one function first but
|
|
// uses only cached results.
|
|
int FunctionPassRunCount5 = 0;
|
|
int AnalyzedInstrCount5 = 0;
|
|
int AnalyzedFunctionCount5 = 0;
|
|
{
|
|
FunctionPassManager FPM(/*DebugLogging*/ true);
|
|
FPM.addPass(TestInvalidationFunctionPass("f"));
|
|
FPM.addPass(TestFunctionPass(FunctionPassRunCount5, AnalyzedInstrCount5,
|
|
AnalyzedFunctionCount5,
|
|
/*OnlyUseCachedResults=*/true));
|
|
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
|
|
}
|
|
|
|
MPM.run(*M, MAM);
|
|
|
|
// Validate module pass counters.
|
|
EXPECT_EQ(1, ModulePassRunCount);
|
|
|
|
// Validate all function pass counter sets are the same.
|
|
EXPECT_EQ(3, FunctionPassRunCount1);
|
|
EXPECT_EQ(5, AnalyzedInstrCount1);
|
|
EXPECT_EQ(0, AnalyzedFunctionCount1);
|
|
EXPECT_EQ(3, FunctionPassRunCount2);
|
|
EXPECT_EQ(5, AnalyzedInstrCount2);
|
|
EXPECT_EQ(0, AnalyzedFunctionCount2);
|
|
EXPECT_EQ(3, FunctionPassRunCount3);
|
|
EXPECT_EQ(5, AnalyzedInstrCount3);
|
|
EXPECT_EQ(0, AnalyzedFunctionCount3);
|
|
EXPECT_EQ(3, FunctionPassRunCount4);
|
|
EXPECT_EQ(5, AnalyzedInstrCount4);
|
|
EXPECT_EQ(9, AnalyzedFunctionCount4);
|
|
EXPECT_EQ(3, FunctionPassRunCount5);
|
|
EXPECT_EQ(2, AnalyzedInstrCount5); // Only 'g' and 'h' were cached.
|
|
EXPECT_EQ(9, AnalyzedFunctionCount5);
|
|
|
|
// Validate the analysis counters:
|
|
// first run over 3 functions, then module pass invalidates
|
|
// second run over 3 functions, nothing invalidates
|
|
// third run over 0 functions, but 1 function invalidated
|
|
// fourth run over 1 function
|
|
// fifth run invalidates 1 function first, but runs over 0 functions
|
|
EXPECT_EQ(7, FunctionAnalysisRuns);
|
|
|
|
EXPECT_EQ(1, ModuleAnalysisRuns);
|
|
}
|
|
|
|
// A customized pass manager that passes extra arguments through the
|
|
// infrastructure.
|
|
typedef AnalysisManager<Function, int> CustomizedAnalysisManager;
|
|
typedef PassManager<Function, CustomizedAnalysisManager, int, int &>
|
|
CustomizedPassManager;
|
|
|
|
class CustomizedAnalysis : public AnalysisInfoMixin<CustomizedAnalysis> {
|
|
public:
|
|
struct Result {
|
|
Result(int I) : I(I) {}
|
|
int I;
|
|
};
|
|
|
|
Result run(Function &F, CustomizedAnalysisManager &AM, int I) {
|
|
return Result(I);
|
|
}
|
|
|
|
private:
|
|
friend AnalysisInfoMixin<CustomizedAnalysis>;
|
|
static AnalysisKey Key;
|
|
};
|
|
|
|
AnalysisKey CustomizedAnalysis::Key;
|
|
|
|
struct CustomizedPass : PassInfoMixin<CustomizedPass> {
|
|
std::function<void(CustomizedAnalysis::Result &, int &)> Callback;
|
|
|
|
template <typename CallbackT>
|
|
CustomizedPass(CallbackT Callback) : Callback(Callback) {}
|
|
|
|
PreservedAnalyses run(Function &F, CustomizedAnalysisManager &AM, int I,
|
|
int &O) {
|
|
Callback(AM.getResult<CustomizedAnalysis>(F, I), O);
|
|
return PreservedAnalyses::none();
|
|
}
|
|
};
|
|
|
|
TEST_F(PassManagerTest, CustomizedPassManagerArgs) {
|
|
CustomizedAnalysisManager AM;
|
|
AM.registerPass([&] { return CustomizedAnalysis(); });
|
|
|
|
CustomizedPassManager PM;
|
|
|
|
// Add an instance of the customized pass that just accumulates the input
|
|
// after it is round-tripped through the analysis.
|
|
int Result = 0;
|
|
PM.addPass(
|
|
CustomizedPass([](CustomizedAnalysis::Result &R, int &O) { O += R.I; }));
|
|
|
|
// Run this over every function with the input of 42.
|
|
for (Function &F : *M)
|
|
PM.run(F, AM, 42, Result);
|
|
|
|
// And ensure that we accumulated the correct result.
|
|
EXPECT_EQ(42 * (int)M->size(), Result);
|
|
}
|
|
|
|
/// A test analysis pass which caches in its result another analysis pass and
|
|
/// uses it to serve queries. This requires the result to invalidate itself
|
|
/// when its dependency is invalidated.
|
|
struct TestIndirectFunctionAnalysis
|
|
: public AnalysisInfoMixin<TestIndirectFunctionAnalysis> {
|
|
struct Result {
|
|
Result(TestFunctionAnalysis::Result &Dep) : Dep(Dep) {}
|
|
TestFunctionAnalysis::Result &Dep;
|
|
|
|
bool invalidate(Function &F, const PreservedAnalyses &PA,
|
|
FunctionAnalysisManager::Invalidator &Inv) {
|
|
return !PA.preserved<TestIndirectFunctionAnalysis>() ||
|
|
Inv.invalidate<TestFunctionAnalysis>(F, PA);
|
|
}
|
|
};
|
|
|
|
TestIndirectFunctionAnalysis(int &Runs) : Runs(Runs) {}
|
|
|
|
/// Run the analysis pass over the function and return a result.
|
|
Result run(Function &F, FunctionAnalysisManager &AM) {
|
|
++Runs;
|
|
return Result(AM.getResult<TestFunctionAnalysis>(F));
|
|
}
|
|
|
|
private:
|
|
friend AnalysisInfoMixin<TestIndirectFunctionAnalysis>;
|
|
static AnalysisKey Key;
|
|
|
|
int &Runs;
|
|
};
|
|
|
|
AnalysisKey TestIndirectFunctionAnalysis::Key;
|
|
|
|
struct LambdaPass : public PassInfoMixin<LambdaPass> {
|
|
using FuncT = std::function<PreservedAnalyses(Function &, FunctionAnalysisManager &)>;
|
|
|
|
LambdaPass(FuncT Func) : Func(std::move(Func)) {}
|
|
|
|
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
|
|
return Func(F, AM);
|
|
}
|
|
|
|
FuncT Func;
|
|
};
|
|
|
|
TEST_F(PassManagerTest, IndirectAnalysisInvalidation) {
|
|
FunctionAnalysisManager FAM(/*DebugLogging*/ true);
|
|
int AnalysisRuns = 0, IndirectAnalysisRuns = 0;
|
|
FAM.registerPass([&] { return TestFunctionAnalysis(AnalysisRuns); });
|
|
FAM.registerPass(
|
|
[&] { return TestIndirectFunctionAnalysis(IndirectAnalysisRuns); });
|
|
|
|
ModuleAnalysisManager MAM(/*DebugLogging*/ true);
|
|
MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
|
|
FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });
|
|
|
|
int InstrCount = 0;
|
|
ModulePassManager MPM(/*DebugLogging*/ true);
|
|
FunctionPassManager FPM(/*DebugLogging*/ true);
|
|
// First just use the analysis to get the instruction count, and preserve
|
|
// everything.
|
|
FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) {
|
|
InstrCount +=
|
|
AM.getResult<TestIndirectFunctionAnalysis>(F).Dep.InstructionCount;
|
|
return PreservedAnalyses::all();
|
|
}));
|
|
// Next, invalidate
|
|
// - both analyses for "f",
|
|
// - just the underlying (indirect) analysis for "g", and
|
|
// - just the direct analysis for "h".
|
|
FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) {
|
|
InstrCount +=
|
|
AM.getResult<TestIndirectFunctionAnalysis>(F).Dep.InstructionCount;
|
|
auto PA = PreservedAnalyses::none();
|
|
if (F.getName() == "g")
|
|
PA.preserve<TestFunctionAnalysis>();
|
|
else if (F.getName() == "h")
|
|
PA.preserve<TestIndirectFunctionAnalysis>();
|
|
return PA;
|
|
}));
|
|
// Finally, use the analysis again on each function, forcing re-computation
|
|
// for all of them.
|
|
FPM.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) {
|
|
InstrCount +=
|
|
AM.getResult<TestIndirectFunctionAnalysis>(F).Dep.InstructionCount;
|
|
return PreservedAnalyses::all();
|
|
}));
|
|
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
|
|
MPM.run(*M, MAM);
|
|
|
|
// There are generally two possible runs for each of the three functions. But
|
|
// for one function, we only invalidate the indirect analysis so the base one
|
|
// only gets run five times.
|
|
EXPECT_EQ(5, AnalysisRuns);
|
|
// The indirect analysis is invalidated for each function (either directly or
|
|
// indirectly) and run twice for each.
|
|
EXPECT_EQ(6, IndirectAnalysisRuns);
|
|
|
|
// There are five instructions in the module and we add the count three
|
|
// times.
|
|
EXPECT_EQ(5 * 3, InstrCount);
|
|
}
|
|
}
|