[llvm] r261624 - [PM] Add a unittest for the CGSCC pass manager in the new pass manager
David Blaikie via llvm-commits
llvm-commits at lists.llvm.org
Tue Feb 23 08:33:45 PST 2016
On Tue, Feb 23, 2016 at 2:02 AM, Chandler Carruth via llvm-commits <
llvm-commits at lists.llvm.org> wrote:
> Author: chandlerc
> Date: Tue Feb 23 04:02:02 2016
> New Revision: 261624
>
> URL: http://llvm.org/viewvc/llvm-project?rev=261624&view=rev
> Log:
> [PM] Add a unittest for the CGSCC pass manager in the new pass manager
> system.
>
> Previously, this was only being tested with larger integration tests.
> That makes it hard to isolated specific issues with it, and makes the
> APIs themselves less well tested. Add a unittest based around the same
> patterns used for testing the general pass manager.
>
*thumbs up* for unit testing this sort of stuff - thanks for adding it!
>
> Added:
> llvm/trunk/unittests/Analysis/CGSCCPassManagerTest.cpp
> Modified:
> llvm/trunk/unittests/Analysis/CMakeLists.txt
>
> Added: llvm/trunk/unittests/Analysis/CGSCCPassManagerTest.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Analysis/CGSCCPassManagerTest.cpp?rev=261624&view=auto
>
> ==============================================================================
> --- llvm/trunk/unittests/Analysis/CGSCCPassManagerTest.cpp (added)
> +++ llvm/trunk/unittests/Analysis/CGSCCPassManagerTest.cpp Tue Feb 23
> 04:02:02 2016
> @@ -0,0 +1,287 @@
> +//===- CGSCCPassManagerTest.cpp
> -------------------------------------------===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
>
> +//===----------------------------------------------------------------------===//
> +
> +#include "llvm/Analysis/CGSCCPassManager.h"
> +#include "llvm/Analysis/LazyCallGraph.h"
> +#include "llvm/AsmParser/Parser.h"
> +#include "llvm/IR/Function.h"
> +#include "llvm/IR/InstIterator.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 TestModuleAnalysis {
> +public:
> + struct Result {
> + Result(int Count) : FunctionCount(Count) {}
> + int FunctionCount;
> + };
> +
> + static void *ID() { return (void *)&PassID; }
>
Just a thought - could we remove the burden for passes to provide their own
ID by using a template helper? (template<typename Pass> struct
PassIdentifier { static const char ID; }; sort of thing) or is it helpful
to have implementations be able to differ here? (or just to have simpler
API/interaction?)
> + static StringRef name() { return "TestModuleAnalysis"; }
> +
> + TestModuleAnalysis(int &Runs) : Runs(Runs) {}
> +
> + Result run(Module &M, ModuleAnalysisManager *AM) {
> + ++Runs;
> + return Result(M.size());
> + }
> +
> +private:
> + static char PassID;
> +
> + int &Runs;
> +};
> +
> +char TestModuleAnalysis::PassID;
> +
> +class TestSCCAnalysis {
> +public:
> + struct Result {
> + Result(int Count) : FunctionCount(Count) {}
> + int FunctionCount;
> + };
> +
> + static void *ID() { return (void *)&PassID; }
> + static StringRef name() { return "TestSCCAnalysis"; }
> +
> + TestSCCAnalysis(int &Runs) : Runs(Runs) {}
> +
> + Result run(LazyCallGraph::SCC &C, CGSCCAnalysisManager *AM) {
> + ++Runs;
> + return Result(C.size());
> + }
> +
> +private:
> + static char PassID;
> +
> + int &Runs;
> +};
> +
> +char TestSCCAnalysis::PassID;
> +
> +class TestFunctionAnalysis {
> +public:
> + struct Result {
> + Result(int Count) : InstructionCount(Count) {}
>
I think most, if not all, the ctors in here could be omitted & {} init used
instead. At least for these Result structs that seems OK? Maybe even for
the passes - which just init their members in turn anyway (though I'm not
sure off the top of my head how {} init would interact with reference
members)
> + int InstructionCount;
> + };
> +
> + static void *ID() { return (void *)&PassID; }
> + static StringRef name() { return "TestFunctionAnalysis"; }
> +
> + TestFunctionAnalysis(int &Runs) : Runs(Runs) {}
> +
> + Result run(Function &F, FunctionAnalysisManager *AM) {
> + ++Runs;
> + int Count = 0;
> + for (Instruction &I : instructions(F)) {
> + (void)I;
> + ++Count;
> + }
> + return Result(Count);
> + }
> +
> +private:
> + static char PassID;
> +
> + int &Runs;
> +};
> +
> +char TestFunctionAnalysis::PassID;
> +
> +struct TestModulePass {
> + TestModulePass(int &RunCount) : RunCount(RunCount) {}
> +
> + PreservedAnalyses run(Module &M, ModuleAnalysisManager *AM) {
> + ++RunCount;
> + (void)AM->getResult<TestModuleAnalysis>(M);
> + return PreservedAnalyses::all();
> + }
> +
> + static StringRef name() { return "TestModulePass"; }
> +
> + int &RunCount;
> +};
> +
> +struct TestSCCPass {
> + TestSCCPass(int &RunCount, int &AnalyzedInstrCount,
> + int &AnalyzedSCCFunctionCount,
> + int &AnalyzedModuleFunctionCount,
> + bool OnlyUseCachedResults = false)
> + : RunCount(RunCount), AnalyzedInstrCount(AnalyzedInstrCount),
> + AnalyzedSCCFunctionCount(AnalyzedSCCFunctionCount),
> + AnalyzedModuleFunctionCount(AnalyzedModuleFunctionCount),
> + OnlyUseCachedResults(OnlyUseCachedResults) {}
> +
> + PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager *AM) {
> + ++RunCount;
> +
> + const ModuleAnalysisManager &MAM =
> + AM->getResult<ModuleAnalysisManagerCGSCCProxy>(C).getManager();
> + FunctionAnalysisManager &FAM =
> + AM->getResult<FunctionAnalysisManagerCGSCCProxy>(C).getManager();
> + if (TestModuleAnalysis::Result *TMA =
> + MAM.getCachedResult<TestModuleAnalysis>(
> + *C.begin()->getFunction().getParent()))
> + AnalyzedModuleFunctionCount += TMA->FunctionCount;
> +
> + if (OnlyUseCachedResults) {
> + // Hack to force the use of the cached interface.
> + if (TestSCCAnalysis::Result *AR =
> + AM->getCachedResult<TestSCCAnalysis>(C))
> + AnalyzedSCCFunctionCount += AR->FunctionCount;
> + for (LazyCallGraph::Node &N : C)
> + if (TestFunctionAnalysis::Result *FAR =
> +
> FAM.getCachedResult<TestFunctionAnalysis>(N.getFunction()))
> + AnalyzedInstrCount += FAR->InstructionCount;
> + } else {
> + // Typical path just runs the analysis as needed.
> + TestSCCAnalysis::Result &AR = AM->getResult<TestSCCAnalysis>(C);
> + AnalyzedSCCFunctionCount += AR.FunctionCount;
> + for (LazyCallGraph::Node &N : C) {
> + TestFunctionAnalysis::Result &FAR =
> + FAM.getResult<TestFunctionAnalysis>(N.getFunction());
> + AnalyzedInstrCount += FAR.InstructionCount;
> + }
> + }
> +
> + return PreservedAnalyses::all();
> + }
> +
> + static StringRef name() { return "TestSCCPass"; }
> +
> + int &RunCount;
> + int &AnalyzedInstrCount;
> + int &AnalyzedSCCFunctionCount;
> + int &AnalyzedModuleFunctionCount;
> + bool OnlyUseCachedResults;
> +};
> +
> +struct TestFunctionPass {
> + TestFunctionPass(int &RunCount) : RunCount(RunCount) {}
> +
> + PreservedAnalyses run(Function &M) {
> + ++RunCount;
> + return PreservedAnalyses::none();
> + }
> +
> + static StringRef name() { return "TestFunctionPass"; }
> +
> + int &RunCount;
> +};
> +
> +std::unique_ptr<Module> parseIR(const char *IR) {
> + LLVMContext &C = getGlobalContext();
> + SMDiagnostic Err;
> + return parseAssemblyString(IR, Err, C);
> +}
> +
> +class CGSCCPassManagerTest : public ::testing::Test {
> +protected:
> + std::unique_ptr<Module> M;
> +
> +public:
> + CGSCCPassManagerTest()
> + : M(parseIR("define void @f() {\n"
>
Can/should we use the fancy C++11 string literals here?
> + "entry:\n"
> + " call void @g()\n"
> + " call void @h1()\n"
> + " ret void\n"
> + "}\n"
> + "define void @g() {\n"
> + "entry:\n"
> + " call void @g()\n"
> + " call void @x()\n"
> + " ret void\n"
> + "}\n"
> + "define void @h1() {\n"
> + "entry:\n"
> + " call void @h2()\n"
> + " ret void\n"
> + "}\n"
> + "define void @h2() {\n"
> + "entry:\n"
> + " call void @h3()\n"
> + " call void @x()\n"
> + " ret void\n"
> + "}\n"
> + "define void @h3() {\n"
> + "entry:\n"
> + " call void @h1()\n"
> + " ret void\n"
> + "}\n"
> + "define void @x() {\n"
> + "entry:\n"
> + " ret void\n"
> + "}\n"
> + )) {}
> +};
> +
> +TEST_F(CGSCCPassManagerTest, Basic) {
> + FunctionAnalysisManager FAM(/*DebugLogging*/ true);
> + int FunctionAnalysisRuns = 0;
> + FAM.registerPass([&] { return
> TestFunctionAnalysis(FunctionAnalysisRuns); });
> +
> + CGSCCAnalysisManager CGAM(/*DebugLogging*/ true);
> + int SCCAnalysisRuns = 0;
> + CGAM.registerPass([&] { return TestSCCAnalysis(SCCAnalysisRuns); });
> +
> + ModuleAnalysisManager MAM(/*DebugLogging*/ true);
> + int ModuleAnalysisRuns = 0;
> + MAM.registerPass([&] { return LazyCallGraphAnalysis(); });
> + MAM.registerPass([&] { return TestModuleAnalysis(ModuleAnalysisRuns);
> });
> +
> + MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM);
> });
> + MAM.registerPass([&] { return CGSCCAnalysisManagerModuleProxy(CGAM); });
> + CGAM.registerPass([&] { return FunctionAnalysisManagerCGSCCProxy(FAM);
> });
> + CGAM.registerPass([&] { return ModuleAnalysisManagerCGSCCProxy(MAM); });
> + FAM.registerPass([&] { return CGSCCAnalysisManagerFunctionProxy(CGAM);
> });
> + FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM);
> });
> +
> + ModulePassManager MPM(/*DebugLogging*/ true);
> + int ModulePassRunCount1 = 0;
> + MPM.addPass(TestModulePass(ModulePassRunCount1));
> +
> + CGSCCPassManager CGPM1(/*DebugLogging*/ true);
> + int SCCPassRunCount1 = 0;
> + int AnalyzedInstrCount1 = 0;
> + int AnalyzedSCCFunctionCount1 = 0;
> + int AnalyzedModuleFunctionCount1 = 0;
> + CGPM1.addPass(TestSCCPass(SCCPassRunCount1, AnalyzedInstrCount1,
> + AnalyzedSCCFunctionCount1,
> + AnalyzedModuleFunctionCount1));
> +
> + FunctionPassManager FPM1(/*DebugLogging*/ true);
> + int FunctionPassRunCount1 = 0;
> + FPM1.addPass(TestFunctionPass(FunctionPassRunCount1));
> + CGPM1.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM1)));
> + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM1)));
> +
> + MPM.run(*M, &MAM);
> +
> + EXPECT_EQ(1, ModulePassRunCount1);
>
Perhaps it's obvious to readers in this domain, but it's not clear to me
why these numbers are the right ones - perhaps a comment explaining the
test scenario would be helpful (maybe at the top by the module IR, or down
here by the EXPECTs, not sure)? Or isolating the tests a bit more
(refactoring so as not to have to repeat too much boilerplate setup) so
each one is more obvious?
> +
> + EXPECT_EQ(1, ModuleAnalysisRuns);
> + EXPECT_EQ(4, SCCAnalysisRuns);
> + EXPECT_EQ(6, FunctionAnalysisRuns);
> +
> + EXPECT_EQ(4, SCCPassRunCount1);
> + EXPECT_EQ(14, AnalyzedInstrCount1);
> + EXPECT_EQ(6, AnalyzedSCCFunctionCount1);
> + EXPECT_EQ(4 * 6, AnalyzedModuleFunctionCount1);
> +}
> +
> +}
>
> Modified: llvm/trunk/unittests/Analysis/CMakeLists.txt
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Analysis/CMakeLists.txt?rev=261624&r1=261623&r2=261624&view=diff
>
> ==============================================================================
> --- llvm/trunk/unittests/Analysis/CMakeLists.txt (original)
> +++ llvm/trunk/unittests/Analysis/CMakeLists.txt Tue Feb 23 04:02:02 2016
> @@ -9,6 +9,7 @@ add_llvm_unittest(AnalysisTests
> AliasAnalysisTest.cpp
> CallGraphTest.cpp
> CFGTest.cpp
> + CGSCCPassManagerTest.cpp
> LazyCallGraphTest.cpp
> ScalarEvolutionTest.cpp
> MixedTBAATest.cpp
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20160223/6a212441/attachment-0001.html>
More information about the llvm-commits
mailing list