summaryrefslogtreecommitdiffstats
path: root/gnu/llvm/unittests/IR/PassManagerTest.cpp
diff options
context:
space:
mode:
authorpatrick <patrick@openbsd.org>2017-01-24 08:32:59 +0000
committerpatrick <patrick@openbsd.org>2017-01-24 08:32:59 +0000
commit53d771aafdbe5b919f264f53cba3788e2c4cffd2 (patch)
tree7eca39498be0ff1e3a6daf583cd9ca5886bb2636 /gnu/llvm/unittests/IR/PassManagerTest.cpp
parentIn preparation of compiling our kernels with -ffreestanding, explicitly map (diff)
downloadwireguard-openbsd-53d771aafdbe5b919f264f53cba3788e2c4cffd2.tar.xz
wireguard-openbsd-53d771aafdbe5b919f264f53cba3788e2c4cffd2.zip
Import LLVM 4.0.0 rc1 including clang and lld to help the current
development effort on OpenBSD/arm64.
Diffstat (limited to 'gnu/llvm/unittests/IR/PassManagerTest.cpp')
-rw-r--r--gnu/llvm/unittests/IR/PassManagerTest.cpp544
1 files changed, 489 insertions, 55 deletions
diff --git a/gnu/llvm/unittests/IR/PassManagerTest.cpp b/gnu/llvm/unittests/IR/PassManagerTest.cpp
index c2ac863260a..b3a039a364f 100644
--- a/gnu/llvm/unittests/IR/PassManagerTest.cpp
+++ b/gnu/llvm/unittests/IR/PassManagerTest.cpp
@@ -41,12 +41,12 @@ public:
private:
friend AnalysisInfoMixin<TestFunctionAnalysis>;
- static char PassID;
+ static AnalysisKey Key;
int &Runs;
};
-char TestFunctionAnalysis::PassID;
+AnalysisKey TestFunctionAnalysis::Key;
class TestModuleAnalysis : public AnalysisInfoMixin<TestModuleAnalysis> {
public:
@@ -67,12 +67,12 @@ public:
private:
friend AnalysisInfoMixin<TestModuleAnalysis>;
- static char PassID;
+ static AnalysisKey Key;
int &Runs;
};
-char TestModuleAnalysis::PassID;
+AnalysisKey TestModuleAnalysis::Key;
struct TestModulePass : PassInfoMixin<TestModulePass> {
TestModulePass(int &RunCount) : RunCount(RunCount) {}
@@ -91,19 +91,6 @@ struct TestPreservingModulePass : PassInfoMixin<TestPreservingModulePass> {
}
};
-struct TestMinPreservingModulePass
- : PassInfoMixin<TestMinPreservingModulePass> {
- PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) {
- PreservedAnalyses PA;
-
- // Force running an analysis.
- (void)AM.getResult<TestModuleAnalysis>(M);
-
- PA.preserve<FunctionAnalysisManagerModuleProxy>();
- return PA;
- }
-};
-
struct TestFunctionPass : PassInfoMixin<TestFunctionPass> {
TestFunctionPass(int &RunCount, int &AnalyzedInstrCount,
int &AnalyzedFunctionCount,
@@ -181,45 +168,232 @@ public:
"}\n")) {}
};
-TEST_F(PassManagerTest, BasicPreservedAnalyses) {
+TEST(PreservedAnalysesTest, Basic) {
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>());
+ {
+ auto PAC = PA1.getChecker<TestFunctionAnalysis>();
+ EXPECT_FALSE(PAC.preserved());
+ EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>());
+ }
+ {
+ auto PAC = PA1.getChecker<TestModuleAnalysis>();
+ EXPECT_FALSE(PAC.preserved());
+ EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Module>>());
+ }
+ auto PA2 = PreservedAnalyses::none();
+ {
+ auto PAC = PA2.getChecker<TestFunctionAnalysis>();
+ EXPECT_FALSE(PAC.preserved());
+ EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>());
+ }
+ auto PA3 = PreservedAnalyses::all();
+ {
+ auto PAC = PA3.getChecker<TestFunctionAnalysis>();
+ EXPECT_TRUE(PAC.preserved());
+ EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>());
+ }
PreservedAnalyses PA4 = PA1;
- EXPECT_FALSE(PA4.preserved<TestFunctionAnalysis>());
- EXPECT_FALSE(PA4.preserved<TestModuleAnalysis>());
+ {
+ auto PAC = PA4.getChecker<TestFunctionAnalysis>();
+ EXPECT_FALSE(PAC.preserved());
+ EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>());
+ }
PA4 = PA3;
- EXPECT_TRUE(PA4.preserved<TestFunctionAnalysis>());
- EXPECT_TRUE(PA4.preserved<TestModuleAnalysis>());
+ {
+ auto PAC = PA4.getChecker<TestFunctionAnalysis>();
+ EXPECT_TRUE(PAC.preserved());
+ EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>());
+ }
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>());
+ {
+ auto PAC = PA4.getChecker<TestFunctionAnalysis>();
+ EXPECT_FALSE(PAC.preserved());
+ EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>());
+ }
+}
+
+TEST(PreservedAnalysesTest, Preserve) {
+ auto PA = PreservedAnalyses::none();
+ PA.preserve<TestFunctionAnalysis>();
+ EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved());
+ EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>().preserved());
+ PA.preserve<TestModuleAnalysis>();
+ EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved());
+ EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>().preserved());
+
+ // Redundant calls are fine.
+ PA.preserve<TestFunctionAnalysis>();
+ EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>().preserved());
+ EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>().preserved());
+}
+
+TEST(PreservedAnalysesTest, PreserveSets) {
+ auto PA = PreservedAnalyses::none();
+ PA.preserveSet<AllAnalysesOn<Function>>();
+ EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>()
+ .preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>()
+ .preservedSet<AllAnalysesOn<Module>>());
+ PA.preserveSet<AllAnalysesOn<Module>>();
+ EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>()
+ .preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>()
+ .preservedSet<AllAnalysesOn<Module>>());
+
+ // Mixing is fine.
+ PA.preserve<TestFunctionAnalysis>();
+ EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>()
+ .preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>()
+ .preservedSet<AllAnalysesOn<Module>>());
+
+ // Redundant calls are fine.
+ PA.preserveSet<AllAnalysesOn<Module>>();
+ EXPECT_TRUE(PA.getChecker<TestFunctionAnalysis>()
+ .preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_TRUE(PA.getChecker<TestModuleAnalysis>()
+ .preservedSet<AllAnalysesOn<Module>>());
+}
+
+TEST(PreservedAnalysisTest, Intersect) {
+ // Setup the initial sets.
+ auto PA1 = PreservedAnalyses::none();
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>());
+ PA1.preserveSet<AllAnalysesOn<Module>>();
+ auto PA2 = PreservedAnalyses::none();
+ PA2.preserve<TestFunctionAnalysis>();
+ PA2.preserveSet<AllAnalysesOn<Function>>();
+ PA2.preserve<TestModuleAnalysis>();
+ PA2.preserveSet<AllAnalysesOn<Module>>();
+ auto PA3 = PreservedAnalyses::none();
+ PA3.preserve<TestModuleAnalysis>();
+ PA3.preserveSet<AllAnalysesOn<Function>>();
+
+ // Self intersection is a no-op.
+ auto Intersected = PA1;
+ Intersected.intersect(PA1);
+ EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved());
+ EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>()
+ .preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());
+ EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>()
+ .preservedSet<AllAnalysesOn<Module>>());
+
+ // Intersecting with all is a no-op.
+ Intersected.intersect(PreservedAnalyses::all());
+ EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved());
+ EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>()
+ .preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());
+ EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>()
+ .preservedSet<AllAnalysesOn<Module>>());
+
+ // Intersecting a narrow set with a more broad set is the narrow set.
+ Intersected.intersect(PA2);
+ EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved());
+ EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>()
+ .preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());
+ EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>()
+ .preservedSet<AllAnalysesOn<Module>>());
+
+ // Intersecting a broad set with a more narrow set is the narrow set.
+ Intersected = PA2;
+ Intersected.intersect(PA1);
+ EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved());
+ EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>()
+ .preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());
+ EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>()
+ .preservedSet<AllAnalysesOn<Module>>());
+
+ // Intersecting with empty clears.
+ Intersected.intersect(PreservedAnalyses::none());
+ EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preserved());
+ EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>()
+ .preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());
+ EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>()
+ .preservedSet<AllAnalysesOn<Module>>());
+
+ // Intersecting non-overlapping clears.
+ Intersected = PA1;
+ Intersected.intersect(PA3);
+ EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>().preserved());
+ EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>()
+ .preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());
+ EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>()
+ .preservedSet<AllAnalysesOn<Module>>());
+
+ // Intersecting with moves works in when there is storage on both sides.
+ Intersected = PA1;
+ auto Tmp = PA2;
+ Intersected.intersect(std::move(Tmp));
+ EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved());
+ EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>()
+ .preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());
+ EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>()
+ .preservedSet<AllAnalysesOn<Module>>());
+
+ // Intersecting with move works for incoming all and existing all.
+ auto Tmp2 = PreservedAnalyses::all();
+ Intersected.intersect(std::move(Tmp2));
+ EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved());
+ EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>()
+ .preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());
+ EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>()
+ .preservedSet<AllAnalysesOn<Module>>());
+ Intersected = PreservedAnalyses::all();
+ auto Tmp3 = PA1;
+ Intersected.intersect(std::move(Tmp3));
+ EXPECT_TRUE(Intersected.getChecker<TestFunctionAnalysis>().preserved());
+ EXPECT_FALSE(Intersected.getChecker<TestFunctionAnalysis>()
+ .preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_FALSE(Intersected.getChecker<TestModuleAnalysis>().preserved());
+ EXPECT_TRUE(Intersected.getChecker<TestModuleAnalysis>()
+ .preservedSet<AllAnalysesOn<Module>>());
+}
+
+TEST(PreservedAnalysisTest, Abandon) {
+ auto PA = PreservedAnalyses::none();
+
+ // We can abandon things after they are preserved.
+ PA.preserve<TestFunctionAnalysis>();
+ PA.abandon<TestFunctionAnalysis>();
+ EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>().preserved());
+
+ // Repeated is fine, and abandoning if they were never preserved is fine.
+ PA.abandon<TestFunctionAnalysis>();
+ EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>().preserved());
+ PA.abandon<TestModuleAnalysis>();
+ EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>().preserved());
+
+ // Even if the sets are preserved, the abandoned analyses' checker won't
+ // return true for those sets.
+ PA.preserveSet<AllAnalysesOn<Function>>();
+ PA.preserveSet<AllAnalysesOn<Module>>();
+ EXPECT_FALSE(PA.getChecker<TestFunctionAnalysis>()
+ .preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_FALSE(PA.getChecker<TestModuleAnalysis>()
+ .preservedSet<AllAnalysesOn<Module>>());
+
+ // But an arbitrary (opaque) analysis will still observe the sets as
+ // preserved. This also checks that we can use an explicit ID rather than
+ // a type.
+ AnalysisKey FakeKey, *FakeID = &FakeKey;
+ EXPECT_TRUE(PA.getChecker(FakeID).preservedSet<AllAnalysesOn<Function>>());
+ EXPECT_TRUE(PA.getChecker(FakeID).preservedSet<AllAnalysesOn<Module>>());
}
TEST_F(PassManagerTest, Basic) {
- FunctionAnalysisManager FAM;
+ FunctionAnalysisManager FAM(/*DebugLogging*/ true);
int FunctionAnalysisRuns = 0;
FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });
- ModuleAnalysisManager MAM;
+ ModuleAnalysisManager MAM(/*DebugLogging*/ true);
int ModuleAnalysisRuns = 0;
MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); });
MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
@@ -233,11 +407,11 @@ TEST_F(PassManagerTest, Basic) {
int AnalyzedFunctionCount1 = 0;
{
// Pointless scoped copy to test move assignment.
- ModulePassManager NestedMPM;
+ ModulePassManager NestedMPM(/*DebugLogging*/ true);
FunctionPassManager FPM;
{
// Pointless scope to test move assignment.
- FunctionPassManager NestedFPM;
+ FunctionPassManager NestedFPM(/*DebugLogging*/ true);
NestedFPM.addPass(TestFunctionPass(
FunctionPassRunCount1, AnalyzedInstrCount1, AnalyzedFunctionCount1));
FPM = std::move(NestedFPM);
@@ -255,7 +429,7 @@ TEST_F(PassManagerTest, Basic) {
int AnalyzedInstrCount2 = 0;
int AnalyzedFunctionCount2 = 0;
{
- FunctionPassManager FPM;
+ FunctionPassManager FPM(/*DebugLogging*/ true);
FPM.addPass(TestFunctionPass(FunctionPassRunCount2, AnalyzedInstrCount2,
AnalyzedFunctionCount2));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
@@ -268,15 +442,16 @@ TEST_F(PassManagerTest, Basic) {
int AnalyzedInstrCount3 = 0;
int AnalyzedFunctionCount3 = 0;
{
- FunctionPassManager FPM;
+ 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 a minimal intervening passes.
- MPM.addPass(TestMinPreservingModulePass());
+ // 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;
@@ -287,12 +462,13 @@ TEST_F(PassManagerTest, Basic) {
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
}
- // A fifth function pass manager but which uses only cached results.
+ // 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;
+ FunctionPassManager FPM(/*DebugLogging*/ true);
FPM.addPass(TestInvalidationFunctionPass("f"));
FPM.addPass(TestFunctionPass(FunctionPassRunCount5, AnalyzedInstrCount5,
AnalyzedFunctionCount5,
@@ -317,18 +493,276 @@ TEST_F(PassManagerTest, Basic) {
EXPECT_EQ(0, AnalyzedFunctionCount3);
EXPECT_EQ(3, FunctionPassRunCount4);
EXPECT_EQ(5, AnalyzedInstrCount4);
- EXPECT_EQ(0, AnalyzedFunctionCount4);
+ EXPECT_EQ(9, AnalyzedFunctionCount4);
EXPECT_EQ(3, FunctionPassRunCount5);
EXPECT_EQ(2, AnalyzedInstrCount5); // Only 'g' and 'h' were cached.
- EXPECT_EQ(0, AnalyzedFunctionCount5);
+ 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 &FDep, TestModuleAnalysis::Result &MDep)
+ : FDep(FDep), MDep(MDep) {}
+ TestFunctionAnalysis::Result &FDep;
+ TestModuleAnalysis::Result &MDep;
+
+ bool invalidate(Function &F, const PreservedAnalyses &PA,
+ FunctionAnalysisManager::Invalidator &Inv) {
+ auto PAC = PA.getChecker<TestIndirectFunctionAnalysis>();
+ return !(PAC.preserved() ||
+ PAC.preservedSet<AllAnalysesOn<Function>>()) ||
+ 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;
+ auto &FDep = AM.getResult<TestFunctionAnalysis>(F);
+ auto &Proxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
+ const ModuleAnalysisManager &MAM = Proxy.getManager();
+ // For the test, we insist that the module analysis starts off in the
+ // cache.
+ auto &MDep = *MAM.getCachedResult<TestModuleAnalysis>(*F.getParent());
+ // And register the dependency as module analysis dependencies have to be
+ // pre-registered on the proxy.
+ Proxy.registerOuterAnalysisInvalidation<TestModuleAnalysis,
+ TestIndirectFunctionAnalysis>();
+ return Result(FDep, MDep);
+ }
+
+private:
+ friend AnalysisInfoMixin<TestIndirectFunctionAnalysis>;
+ static AnalysisKey Key;
+
+ int &Runs;
+};
+
+AnalysisKey TestIndirectFunctionAnalysis::Key;
+
+/// A test analysis pass which chaches in its result the result from the above
+/// indirect analysis pass.
+///
+/// This allows us to ensure that whenever an analysis pass is invalidated due
+/// to dependencies (especially dependencies across IR units that trigger
+/// asynchronous invalidation) we correctly detect that this may in turn cause
+/// other analysis to be invalidated.
+struct TestDoublyIndirectFunctionAnalysis
+ : public AnalysisInfoMixin<TestDoublyIndirectFunctionAnalysis> {
+ struct Result {
+ Result(TestIndirectFunctionAnalysis::Result &IDep) : IDep(IDep) {}
+ TestIndirectFunctionAnalysis::Result &IDep;
+
+ bool invalidate(Function &F, const PreservedAnalyses &PA,
+ FunctionAnalysisManager::Invalidator &Inv) {
+ auto PAC = PA.getChecker<TestDoublyIndirectFunctionAnalysis>();
+ return !(PAC.preserved() ||
+ PAC.preservedSet<AllAnalysesOn<Function>>()) ||
+ Inv.invalidate<TestIndirectFunctionAnalysis>(F, PA);
+ }
+ };
+
+ TestDoublyIndirectFunctionAnalysis(int &Runs) : Runs(Runs) {}
+
+ /// Run the analysis pass over the function and return a result.
+ Result run(Function &F, FunctionAnalysisManager &AM) {
+ ++Runs;
+ auto &IDep = AM.getResult<TestIndirectFunctionAnalysis>(F);
+ return Result(IDep);
+ }
+
+private:
+ friend AnalysisInfoMixin<TestDoublyIndirectFunctionAnalysis>;
+ static AnalysisKey Key;
+
+ int &Runs;
+};
+
+AnalysisKey TestDoublyIndirectFunctionAnalysis::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 FunctionAnalysisRuns = 0, ModuleAnalysisRuns = 0,
+ IndirectAnalysisRuns = 0, DoublyIndirectAnalysisRuns = 0;
+ FAM.registerPass([&] { return TestFunctionAnalysis(FunctionAnalysisRuns); });
+ FAM.registerPass(
+ [&] { return TestIndirectFunctionAnalysis(IndirectAnalysisRuns); });
+ FAM.registerPass([&] {
+ return TestDoublyIndirectFunctionAnalysis(DoublyIndirectAnalysisRuns);
+ });
+
+ ModuleAnalysisManager MAM(/*DebugLogging*/ true);
+ MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns); });
+ MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
+ FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });
+
+ int InstrCount = 0, FunctionCount = 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) {
+ auto &DoublyIndirectResult =
+ AM.getResult<TestDoublyIndirectFunctionAnalysis>(F);
+ auto &IndirectResult = DoublyIndirectResult.IDep;
+ InstrCount += IndirectResult.FDep.InstructionCount;
+ FunctionCount += IndirectResult.MDep.FunctionCount;
+ 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) {
+ auto &DoublyIndirectResult =
+ AM.getResult<TestDoublyIndirectFunctionAnalysis>(F);
+ auto &IndirectResult = DoublyIndirectResult.IDep;
+ InstrCount += IndirectResult.FDep.InstructionCount;
+ FunctionCount += IndirectResult.MDep.FunctionCount;
+ 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) {
+ auto &DoublyIndirectResult =
+ AM.getResult<TestDoublyIndirectFunctionAnalysis>(F);
+ auto &IndirectResult = DoublyIndirectResult.IDep;
+ InstrCount += IndirectResult.FDep.InstructionCount;
+ FunctionCount += IndirectResult.MDep.FunctionCount;
+ return PreservedAnalyses::all();
+ }));
+
+ // Create a second function pass manager. This will cause the module-level
+ // invalidation to occur, which will force yet another invalidation of the
+ // indirect function-level analysis as the module analysis it depends on gets
+ // invalidated.
+ FunctionPassManager FPM2(/*DebugLogging*/ true);
+ FPM2.addPass(LambdaPass([&](Function &F, FunctionAnalysisManager &AM) {
+ auto &DoublyIndirectResult =
+ AM.getResult<TestDoublyIndirectFunctionAnalysis>(F);
+ auto &IndirectResult = DoublyIndirectResult.IDep;
+ InstrCount += IndirectResult.FDep.InstructionCount;
+ FunctionCount += IndirectResult.MDep.FunctionCount;
+ return PreservedAnalyses::all();
+ }));
+
+ // Add a requires pass to populate the module analysis and then our function
+ // pass pipeline.
+ MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());
+ MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
+ // Now require the module analysis again (it will have been invalidated once)
+ // and then use it again from a function pass manager.
+ MPM.addPass(RequireAnalysisPass<TestModuleAnalysis, Module>());
+ MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM2)));
+ 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, FunctionAnalysisRuns);
+ // The module analysis pass should be run twice here.
+ EXPECT_EQ(2, ModuleAnalysisRuns);
+ // The indirect analysis is invalidated for each function (either directly or
+ // indirectly) and run twice for each.
+ EXPECT_EQ(9, IndirectAnalysisRuns);
+ EXPECT_EQ(9, DoublyIndirectAnalysisRuns);
+
+ // There are five instructions in the module and we add the count four
+ // times.
+ EXPECT_EQ(5 * 4, InstrCount);
+
+ // There are three functions and we count them four times for each of the
+ // three functions.
+ EXPECT_EQ(3 * 4 * 3, FunctionCount);
+}
}