//===- 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/IR/PassManager.h"#include"llvm/AsmParser/Parser.h"#include"llvm/IR/Function.h"#include"llvm/IR/LLVMContext.h"#include"llvm/IR/Module.h"#include"llvm/Support/SourceMgr.h"#include"gtest/gtest.h"usingnamespacellvm;namespace{classTestFunctionAnalysis:publicAnalysisInfoMixin<TestFunctionAnalysis>{public:structResult{Result(intCount):InstructionCount(Count){}intInstructionCount;};TestFunctionAnalysis(int&Runs):Runs(Runs){}/// \brief Run the analysis pass over the function and return a result.Resultrun(Function&F,FunctionAnalysisManager&AM){++Runs;intCount=0;for(Function::iteratorBBI=F.begin(),BBE=F.end();BBI!=BBE;++BBI)for(BasicBlock::iteratorII=BBI->begin(),IE=BBI->end();II!=IE;++II)++Count;returnResult(Count);}private:friendAnalysisInfoMixin<TestFunctionAnalysis>;staticAnalysisKeyKey;int&Runs;};AnalysisKeyTestFunctionAnalysis::Key;classTestModuleAnalysis:publicAnalysisInfoMixin<TestModuleAnalysis>{public:structResult{Result(intCount):FunctionCount(Count){}intFunctionCount;};TestModuleAnalysis(int&Runs):Runs(Runs){}Resultrun(Module&M,ModuleAnalysisManager&AM){++Runs;intCount=0;for(Module::iteratorI=M.begin(),E=M.end();I!=E;++I)++Count;returnResult(Count);}private:friendAnalysisInfoMixin<TestModuleAnalysis>;staticAnalysisKeyKey;int&Runs;};AnalysisKeyTestModuleAnalysis::Key;structTestModulePass:PassInfoMixin<TestModulePass>{TestModulePass(int&RunCount):RunCount(RunCount){}PreservedAnalysesrun(Module&M,ModuleAnalysisManager&){++RunCount;returnPreservedAnalyses::none();}int&RunCount;};structTestPreservingModulePass:PassInfoMixin<TestPreservingModulePass>{PreservedAnalysesrun(Module&M,ModuleAnalysisManager&){returnPreservedAnalyses::all();}};structTestFunctionPass:PassInfoMixin<TestFunctionPass>{TestFunctionPass(int&RunCount,int&AnalyzedInstrCount,int&AnalyzedFunctionCount,boolOnlyUseCachedResults=false):RunCount(RunCount),AnalyzedInstrCount(AnalyzedInstrCount),AnalyzedFunctionCount(AnalyzedFunctionCount),OnlyUseCachedResults(OnlyUseCachedResults){}PreservedAnalysesrun(Function&F,FunctionAnalysisManager&AM){++RunCount;constModuleAnalysisManager&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;}returnPreservedAnalyses::all();}int&RunCount;int&AnalyzedInstrCount;int&AnalyzedFunctionCount;boolOnlyUseCachedResults;};// A test function pass that invalidates all function analyses for a function// with a specific name.structTestInvalidationFunctionPass:PassInfoMixin<TestInvalidationFunctionPass>{TestInvalidationFunctionPass(StringRefFunctionName):Name(FunctionName){}PreservedAnalysesrun(Function&F,FunctionAnalysisManager&AM){returnF.getName()==Name?PreservedAnalyses::none():PreservedAnalyses::all();}StringRefName;};std::unique_ptr<Module>parseIR(LLVMContext&Context,constchar*IR){SMDiagnosticErr;returnparseAssemblyString(IR,Err,Context);}classPassManagerTest:public::testing::Test{protected:LLVMContextContext;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(PreservedAnalysesTest,Basic){PreservedAnalysesPA1=PreservedAnalyses();{autoPAC=PA1.getChecker<TestFunctionAnalysis>();EXPECT_FALSE(PAC.preserved());EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>());}{autoPAC=PA1.getChecker<TestModuleAnalysis>();EXPECT_FALSE(PAC.preserved());EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Module>>());}autoPA2=PreservedAnalyses::none();{autoPAC=PA2.getChecker<TestFunctionAnalysis>();EXPECT_FALSE(PAC.preserved());EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>());}autoPA3=PreservedAnalyses::all();{autoPAC=PA3.getChecker<TestFunctionAnalysis>();EXPECT_TRUE(PAC.preserved());EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>());}PreservedAnalysesPA4=PA1;{autoPAC=PA4.getChecker<TestFunctionAnalysis>();EXPECT_FALSE(PAC.preserved());EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>());}PA4=PA3;{autoPAC=PA4.getChecker<TestFunctionAnalysis>();EXPECT_TRUE(PAC.preserved());EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>());}PA4=std::move(PA2);{autoPAC=PA4.getChecker<TestFunctionAnalysis>();EXPECT_FALSE(PAC.preserved());EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Function>>());}autoPA5=PreservedAnalyses::allInSet<AllAnalysesOn<Function>>();{autoPAC=PA5.getChecker<TestFunctionAnalysis>();EXPECT_FALSE(PAC.preserved());EXPECT_TRUE(PAC.preservedSet<AllAnalysesOn<Function>>());EXPECT_FALSE(PAC.preservedSet<AllAnalysesOn<Module>>());}}TEST(PreservedAnalysesTest,Preserve){autoPA=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){autoPA=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.autoPA1=PreservedAnalyses::none();PA1.preserve<TestFunctionAnalysis>();PA1.preserveSet<AllAnalysesOn<Module>>();autoPA2=PreservedAnalyses::none();PA2.preserve<TestFunctionAnalysis>();PA2.preserveSet<AllAnalysesOn<Function>>();PA2.preserve<TestModuleAnalysis>();PA2.preserveSet<AllAnalysesOn<Module>>();autoPA3=PreservedAnalyses::none();PA3.preserve<TestModuleAnalysis>();PA3.preserveSet<AllAnalysesOn<Function>>();// Self intersection is a no-op.autoIntersected=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;autoTmp=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.autoTmp2=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();autoTmp3=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){autoPA=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.AnalysisKeyFakeKey,*FakeID=&FakeKey;EXPECT_TRUE(PA.getChecker(FakeID).preservedSet<AllAnalysesOn<Function>>());EXPECT_TRUE(PA.getChecker(FakeID).preservedSet<AllAnalysesOn<Module>>());}TEST_F(PassManagerTest,Basic){FunctionAnalysisManagerFAM(/*DebugLogging*/true);intFunctionAnalysisRuns=0;FAM.registerPass([&]{returnTestFunctionAnalysis(FunctionAnalysisRuns);});ModuleAnalysisManagerMAM(/*DebugLogging*/true);intModuleAnalysisRuns=0;MAM.registerPass([&]{returnTestModuleAnalysis(ModuleAnalysisRuns);});MAM.registerPass([&]{returnFunctionAnalysisManagerModuleProxy(FAM);});FAM.registerPass([&]{returnModuleAnalysisManagerFunctionProxy(MAM);});ModulePassManagerMPM;// Count the runs over a Function.intFunctionPassRunCount1=0;intAnalyzedInstrCount1=0;intAnalyzedFunctionCount1=0;{// Pointless scoped copy to test move assignment.ModulePassManagerNestedMPM(/*DebugLogging*/true);FunctionPassManagerFPM;{// Pointless scope to test move assignment.FunctionPassManagerNestedFPM(/*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.intModulePassRunCount=0;MPM.addPass(TestModulePass(ModulePassRunCount));// Count the runs over a Function in a separate manager.intFunctionPassRunCount2=0;intAnalyzedInstrCount2=0;intAnalyzedFunctionCount2=0;{FunctionPassManagerFPM(/*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());intFunctionPassRunCount3=0;intAnalyzedInstrCount3=0;intAnalyzedFunctionCount3=0;{FunctionPassManagerFPM(/*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>());intFunctionPassRunCount4=0;intAnalyzedInstrCount4=0;intAnalyzedFunctionCount4=0;{FunctionPassManagerFPM;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.intFunctionPassRunCount5=0;intAnalyzedInstrCount5=0;intAnalyzedFunctionCount5=0;{FunctionPassManagerFPM(/*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 functionsEXPECT_EQ(7,FunctionAnalysisRuns);EXPECT_EQ(1,ModuleAnalysisRuns);}// A customized pass manager that passes extra arguments through the// infrastructure.typedefAnalysisManager<Function,int>CustomizedAnalysisManager;typedefPassManager<Function,CustomizedAnalysisManager,int,int&>CustomizedPassManager;classCustomizedAnalysis:publicAnalysisInfoMixin<CustomizedAnalysis>{public:structResult{Result(intI):I(I){}intI;};Resultrun(Function&F,CustomizedAnalysisManager&AM,intI){returnResult(I);}private:friendAnalysisInfoMixin<CustomizedAnalysis>;staticAnalysisKeyKey;};AnalysisKeyCustomizedAnalysis::Key;structCustomizedPass:PassInfoMixin<CustomizedPass>{std::function<void(CustomizedAnalysis::Result&,int&)>Callback;template<typenameCallbackT>CustomizedPass(CallbackTCallback):Callback(Callback){}PreservedAnalysesrun(Function&F,CustomizedAnalysisManager&AM,intI,int&O){Callback(AM.getResult<CustomizedAnalysis>(F,I),O);returnPreservedAnalyses::none();}};TEST_F(PassManagerTest,CustomizedPassManagerArgs){CustomizedAnalysisManagerAM;AM.registerPass([&]{returnCustomizedAnalysis();});CustomizedPassManagerPM;// Add an instance of the customized pass that just accumulates the input// after it is round-tripped through the analysis.intResult=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.structTestIndirectFunctionAnalysis:publicAnalysisInfoMixin<TestIndirectFunctionAnalysis>{structResult{Result(TestFunctionAnalysis::Result&FDep,TestModuleAnalysis::Result&MDep):FDep(FDep),MDep(MDep){}TestFunctionAnalysis::Result&FDep;TestModuleAnalysis::Result&MDep;boolinvalidate(Function&F,constPreservedAnalyses&PA,FunctionAnalysisManager::Invalidator&Inv){autoPAC=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.Resultrun(Function&F,FunctionAnalysisManager&AM){++Runs;auto&FDep=AM.getResult<TestFunctionAnalysis>(F);auto&Proxy=AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);constModuleAnalysisManager&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>();returnResult(FDep,MDep);}private:friendAnalysisInfoMixin<TestIndirectFunctionAnalysis>;staticAnalysisKeyKey;int&Runs;};AnalysisKeyTestIndirectFunctionAnalysis::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.structTestDoublyIndirectFunctionAnalysis:publicAnalysisInfoMixin<TestDoublyIndirectFunctionAnalysis>{structResult{Result(TestIndirectFunctionAnalysis::Result&IDep):IDep(IDep){}TestIndirectFunctionAnalysis::Result&IDep;boolinvalidate(Function&F,constPreservedAnalyses&PA,FunctionAnalysisManager::Invalidator&Inv){autoPAC=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.Resultrun(Function&F,FunctionAnalysisManager&AM){++Runs;auto&IDep=AM.getResult<TestIndirectFunctionAnalysis>(F);returnResult(IDep);}private:friendAnalysisInfoMixin<TestDoublyIndirectFunctionAnalysis>;staticAnalysisKeyKey;int&Runs;};AnalysisKeyTestDoublyIndirectFunctionAnalysis::Key;structLambdaPass:publicPassInfoMixin<LambdaPass>{usingFuncT=std::function<PreservedAnalyses(Function&,FunctionAnalysisManager&)>;LambdaPass(FuncTFunc):Func(std::move(Func)){}PreservedAnalysesrun(Function&F,FunctionAnalysisManager&AM){returnFunc(F,AM);}FuncTFunc;};TEST_F(PassManagerTest,IndirectAnalysisInvalidation){FunctionAnalysisManagerFAM(/*DebugLogging*/true);intFunctionAnalysisRuns=0,ModuleAnalysisRuns=0,IndirectAnalysisRuns=0,DoublyIndirectAnalysisRuns=0;FAM.registerPass([&]{returnTestFunctionAnalysis(FunctionAnalysisRuns);});FAM.registerPass([&]{returnTestIndirectFunctionAnalysis(IndirectAnalysisRuns);});FAM.registerPass([&]{returnTestDoublyIndirectFunctionAnalysis(DoublyIndirectAnalysisRuns);});ModuleAnalysisManagerMAM(/*DebugLogging*/true);MAM.registerPass([&]{returnTestModuleAnalysis(ModuleAnalysisRuns);});MAM.registerPass([&]{returnFunctionAnalysisManagerModuleProxy(FAM);});FAM.registerPass([&]{returnModuleAnalysisManagerFunctionProxy(MAM);});intInstrCount=0,FunctionCount=0;ModulePassManagerMPM(/*DebugLogging*/true);FunctionPassManagerFPM(/*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;returnPreservedAnalyses::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;autoPA=PreservedAnalyses::none();if(F.getName()=="g")PA.preserve<TestFunctionAnalysis>();elseif(F.getName()=="h")PA.preserve<TestIndirectFunctionAnalysis>();returnPA;}));// 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;returnPreservedAnalyses::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.FunctionPassManagerFPM2(/*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;returnPreservedAnalyses::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);}}