diff --git a/llvm/include/llvm/CodeGen/MachinePassManager.h b/llvm/include/llvm/CodeGen/MachinePassManager.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/CodeGen/MachinePassManager.h @@ -0,0 +1,220 @@ +//===- PassManager.h --- Pass management for CodeGen ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header defines the pass manager interface for codegen. The codegen +// pipeline consists of only machine function passes. There is no container +// relationship between IR module/function and machine function in terms of pass +// manager organization. So there is no need for adaptor classes (for example +// ModuleToMachineFunctionAdaptor). Since invalidation could only happen among +// machine function passes, there is no proxy classes to handle cross-IR-unit +// invalidation. IR analysis results are provided for machine function passes by +// their respective analysis managers such as ModuleAnalysisManager and +// FunctionAnalysisManager. +// +// `doInitilization`/`doFinalization` are available like they do in legacy pass +// manager. This is for machine function passes to work on module level +// constructs. One such pass is AsmPrinter. +// +// Machine pass could also run over the module (call it machine module pass +// here). Passes using this API includes MachineOutliner, MachineDebugify etc.. +// +// TODO: Add MachineFunctionProperties support. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_PASS_MANAGER_H +#define LLVM_CODEGEN_PASS_MANAGER_H + +#include "llvm/ADT/FunctionExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/type_traits.h" + +namespace llvm { +class Module; + +extern template class AnalysisManager; + +/// A AnalysisManager that also exposes IR analysis results. +class MachineFunctionAnalysisManager : public AnalysisManager { +public: + using Base = AnalysisManager; + + MachineFunctionAnalysisManager() : Base(false), FAM(nullptr), MAM(nullptr) {} + MachineFunctionAnalysisManager(FunctionAnalysisManager &FAM, + ModuleAnalysisManager &MAM, + bool DebugLogging = false) + : Base(DebugLogging), FAM(&FAM), MAM(&MAM) {} + MachineFunctionAnalysisManager(MachineFunctionAnalysisManager &&) = default; + MachineFunctionAnalysisManager & + operator=(MachineFunctionAnalysisManager &&) = default; + + // Register additional IR function analysis + template + bool registerFunctionAnalysisPass(PassBuilderT &&PassBuilder) { + return FAM->registerPass(PassBuilder); + } + + // Register additional IR module analysis + template + bool registerModuleAnalysisPass(PassBuilderT &&PassBuilder) { + return MAM->registerPass(PassBuilder); + } + + // Query IR function analysis + template + typename PassT::Result &getResult(const Function &F) { + return FAM->getResult(const_cast(F)); + } + template + typename PassT::Result *getCachedResult(const Function &F) { + return FAM->getCachedResult(const_cast(F)); + } + + // Query IR module analysis + template typename PassT::Result &getResult(const Module &M) { + return MAM->getResult(const_cast(M)); + } + template + typename PassT::Result *getCachedResult(const Module &M) { + return MAM->getCachedResult(const_cast(M)); + } + + // Query machine function analysis + using Base::getCachedResult; + using Base::getResult; + +private: + // FIXME: Add LoopAnalysisManager or CGSCCAnalysisManager if needed. + FunctionAnalysisManager *FAM; + ModuleAnalysisManager *MAM; +}; + +extern template class PassManager; + +class MachineFunctionPassManager + : public PassManager { + using Base = PassManager; + +public: + MachineFunctionPassManager(bool DebugLogging = false, + bool RequireCodeGenSCCOrder = false) + : Base(DebugLogging), RequireCodeGenSCCOrder(RequireCodeGenSCCOrder) {} + MachineFunctionPassManager(MachineFunctionPassManager &&) = default; + MachineFunctionPassManager & + operator=(MachineFunctionPassManager &&) = default; + + /// Entry point for codegen. + Error run(Module &M, MachineFunctionAnalysisManager &MFAM); + + template void addPass(PassT &&Pass) { + Base::addPass(std::forward(Pass)); + PassConceptT *P = Passes.back().get(); + addDoInitialization(P); + addDoFinalization(P); + + // Add machine module pass. + // Machine module pass need to define a method: + // `Error run(Module &, MachineFunctionAnalysisManager &)`. + // FIXME: machine module passes still need the usual machine function pass + // interface, namely, + // `PreservedAnalyses run(MachineFunction &, + // MachineFunctionAnalysisManager &)` + // But this interface wouldn't be executed. It is just a placeholder + // to satisfy the pass manager type-erased inteface. This + // special-casing of machine module pass is due to its limited use + // cases and the unnecessary complexity it may bring to the machine + // pass manager. + addRunOnModule(P); + } + +private: + template + using has_init_t = decltype(std::declval().doInitialization( + std::declval(), + std::declval())); + + template + std::enable_if_t::value> + addDoInitialization(PassConceptT *Pass) {} + + template + std::enable_if_t::value> + addDoInitialization(PassConceptT *Pass) { + using PassModelT = + detail::PassModel; + auto *P = static_cast(Pass); + InitializationFuncs.emplace_back( + [=](Module &M, MachineFunctionAnalysisManager &MFAM) { + return P->Pass.doInitialization(M, MFAM); + }); + } + + template + using has_fini_t = decltype(std::declval().doFinalization( + std::declval(), + std::declval())); + + template + std::enable_if_t::value> + addDoFinalization(PassConceptT *Pass) {} + + template + std::enable_if_t::value> + addDoFinalization(PassConceptT *Pass) { + using PassModelT = + detail::PassModel; + auto *P = static_cast(Pass); + FinalizationFuncs.emplace_back( + [=](Module &M, MachineFunctionAnalysisManager &MFAM) { + return P->Pass.doFinalization(M, MFAM); + }); + } + + template + using is_machine_module_pass_t = decltype(std::declval().run( + std::declval(), + std::declval())); + + template + std::enable_if_t::value> + addRunOnModule(PassConceptT *Pass) {} + + template + std::enable_if_t::value> + addRunOnModule(PassConceptT *Pass) { + using PassModelT = + detail::PassModel; + auto *P = static_cast(Pass); + HasRunOnModulePasses.emplace( + Passes.size() - 1, + [=](Module &M, MachineFunctionAnalysisManager &MFAM) { + return P->Pass.run(M, MFAM); + }); + } + + using FuncTy = Error(Module &, MachineFunctionAnalysisManager &); + SmallVector, 4> InitializationFuncs; + SmallVector, 4> FinalizationFuncs; + + // A set of `Passes` indexes where matching pass has defined `run(Module&,..)` + using PassIndex = decltype(Passes)::size_type; + std::map> HasRunOnModulePasses; + + // Force codegen to run according to the callgraph. + bool RequireCodeGenSCCOrder; +}; + +} // end namespace llvm + +#endif // LLVM_CODEGEN_PASS_MANAGER_H diff --git a/llvm/include/llvm/IR/PassManager.h b/llvm/include/llvm/IR/PassManager.h --- a/llvm/include/llvm/IR/PassManager.h +++ b/llvm/include/llvm/IR/PassManager.h @@ -560,6 +560,8 @@ } static bool isSkippable() { return false; } + +protected: using PassConceptT = detail::PassConcept; diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -92,6 +92,7 @@ MachineOperand.cpp MachineOptimizationRemarkEmitter.cpp MachineOutliner.cpp + MachinePassManager.cpp MachinePipeliner.cpp MachinePostDominators.cpp MachineRegionInfo.cpp diff --git a/llvm/lib/CodeGen/LLVMBuild.txt b/llvm/lib/CodeGen/LLVMBuild.txt --- a/llvm/lib/CodeGen/LLVMBuild.txt +++ b/llvm/lib/CodeGen/LLVMBuild.txt @@ -21,4 +21,4 @@ type = Library name = CodeGen parent = Libraries -required_libraries = Analysis BitReader BitWriter Core MC ProfileData Scalar Support Target TransformUtils +required_libraries = Analysis BitReader BitWriter Core MC Passes ProfileData Scalar Support Target TransformUtils diff --git a/llvm/lib/CodeGen/MachinePassManager.cpp b/llvm/lib/CodeGen/MachinePassManager.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/CodeGen/MachinePassManager.cpp @@ -0,0 +1,93 @@ +//===---------- MachinePassManager.cpp ------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the pass management machinery for machine functions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/MachinePassManager.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/IR/PassManagerImpl.h" + +using namespace llvm; + +namespace llvm { +template class AllAnalysesOn; +template class AnalysisManager; +template class PassManager; + +Error MachineFunctionPassManager::run(Module &M, + MachineFunctionAnalysisManager &MFAM) { + // MachineModuleAnalysis is a module analysis pass that is never invalidated + // because we don't run any module pass in codegen pipeline. This is very + // important because the codegen state is stored in MMI which is the analysis + // result of MachineModuleAnalysis. MMI should not be recomputed. + auto &MMI = MFAM.getResult(M); + + assert(!RequireCodeGenSCCOrder && "not implemented"); + + if (DebugLogging) { + dbgs() << "Starting " << getTypeName() + << " pass manager run.\n"; + } + + for (auto &F : InitializationFuncs) { + if (auto Err = F(M, MFAM)) + return Err; + } + + for (unsigned Idx = 0, Size = Passes.size(); Idx != Size; ++Idx) { + auto *P = Passes[Idx].get(); + + // Run machine moulde passes + if (HasRunOnModulePasses.count(Idx)) { + if (DebugLogging) + dbgs() << "Running pass: " << P->name() << " on " << M.getName() + << '\n'; + if (auto Err = HasRunOnModulePasses.at(Idx)(M, MFAM)) + return Err; + continue; + } + + for (Function &F : M) { + // Do not codegen any 'available_externally' functions at all, they have + // definitions outside the translation unit. + if (F.hasAvailableExternallyLinkage()) + continue; + + MachineFunction &MF = MMI.getOrCreateMachineFunction(F); + PassInstrumentation PI = MFAM.getResult(MF); + if (!PI.runBeforePass(*P, MF)) + continue; + + if (DebugLogging) { + dbgs() << "Running pass: " << P->name() << " on " << MF.getName() + << '\n'; + } + + // TODO: EmitSizeRemarks + PreservedAnalyses PassPA = P->run(MF, MFAM); + PI.runAfterPass(*P, MF); + MFAM.invalidate(MF, PassPA); + } + } + + for (auto &F : FinalizationFuncs) { + if (auto Err = F(M, MFAM)) + return Err; + } + + if (DebugLogging) { + dbgs() << "Finished " << getTypeName() + << " pass manager run.\n"; + } + + return Error::success(); +} + +} // namespace llvm diff --git a/llvm/unittests/CodeGen/CMakeLists.txt b/llvm/unittests/CodeGen/CMakeLists.txt --- a/llvm/unittests/CodeGen/CMakeLists.txt +++ b/llvm/unittests/CodeGen/CMakeLists.txt @@ -7,6 +7,7 @@ Core MC MIRParser + Passes SelectionDAG Support Target @@ -20,6 +21,7 @@ MachineInstrBundleIteratorTest.cpp MachineInstrTest.cpp MachineOperandTest.cpp + PassManagerTest.cpp ScalableVectorMVTsTest.cpp TypeTraitsTest.cpp TargetOptionsTest.cpp diff --git a/llvm/unittests/CodeGen/PassManagerTest.cpp b/llvm/unittests/CodeGen/PassManagerTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/CodeGen/PassManagerTest.cpp @@ -0,0 +1,249 @@ +//===- llvm/unittest/CodeGen/PassManager.cpp - PassManager tests ----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/CGSCCPassManager.h" +#include "llvm/Analysis/LoopAnalysisManager.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachinePassManager.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Target/TargetMachine.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +class TestFunctionAnalysis : public AnalysisInfoMixin { +public: + struct Result { + Result(int Count) : InstructionCount(Count) {} + int InstructionCount; + }; + + /// Run the analysis pass over the function and return a result. + Result run(Function &F, FunctionAnalysisManager &AM) { + 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; + static AnalysisKey Key; +}; + +AnalysisKey TestFunctionAnalysis::Key; + +class TestMachineFunctionAnalysis + : public AnalysisInfoMixin { +public: + struct Result { + Result(int Count) : InstructionCount(Count) {} + int InstructionCount; + }; + + /// Run the analysis pass over the machine function and return a result. + Result run(MachineFunction &MF, MachineFunctionAnalysisManager::Base &AM) { + auto &MFAM = static_cast(AM); + // Query function analysis result. + TestFunctionAnalysis::Result &FAR = + MFAM.getResult(MF.getFunction()); + // + 5 + return FAR.InstructionCount; + } + +private: + friend AnalysisInfoMixin; + static AnalysisKey Key; +}; + +AnalysisKey TestMachineFunctionAnalysis::Key; + +struct TestMachineFunctionPass : public PassInfoMixin { + TestMachineFunctionPass(int &Count, int &BeforeInitialization, + int &BeforeFinalization, + int &MachineFunctionPassCount) + : Count(Count), BeforeInitialization(BeforeInitialization), + BeforeFinalization(BeforeFinalization), + MachineFunctionPassCount(MachineFunctionPassCount) {} + + Error doInitialization(Module &M, MachineFunctionAnalysisManager &MFAM) { + // + 1 + ++Count; + BeforeInitialization = Count; + return Error::success(); + } + Error doFinalization(Module &M, MachineFunctionAnalysisManager &MFAM) { + // + 1 + ++Count; + BeforeFinalization = Count; + return Error::success(); + } + + PreservedAnalyses run(MachineFunction &MF, + MachineFunctionAnalysisManager &MFAM) { + // Query function analysis result. + TestFunctionAnalysis::Result &FAR = + MFAM.getResult(MF.getFunction()); + // + 5 + Count += FAR.InstructionCount; + + // Query module analysis result. + MachineModuleInfo &MMI = + MFAM.getResult(*MF.getFunction().getParent()); + // + 3 + Count += (MMI.getModule() == MF.getFunction().getParent()); + + // Query machine function analysis result. + TestMachineFunctionAnalysis::Result &MFAR = + MFAM.getResult(MF); + // + 5 + Count += MFAR.InstructionCount; + + MachineFunctionPassCount = Count; + + return PreservedAnalyses::none(); + } + + int &Count; + int &BeforeInitialization; + int &BeforeFinalization; + int &MachineFunctionPassCount; +}; + +struct TestMachineModulePass : public PassInfoMixin { + TestMachineModulePass(int &Count, int &MachineModulePassCount) + : Count(Count), MachineModulePassCount(MachineModulePassCount) {} + + Error run(Module &M, MachineFunctionAnalysisManager &MFAM) { + MachineModuleInfo &MMI = MFAM.getResult(M); + // + 1 + Count += (MMI.getModule() == &M); + MachineModulePassCount = Count; + return Error::success(); + } + + PreservedAnalyses run(MachineFunction &MF, + MachineFunctionAnalysisManager &AM) { + llvm_unreachable("This should never be reached. This function is a " + "placeholder to make machine pass manager " + "less intrusive with this feature."); + } + + int &Count; + int &MachineModulePassCount; +}; + +std::unique_ptr parseIR(LLVMContext &Context, const char *IR) { + SMDiagnostic Err; + return parseAssemblyString(IR, Err, Context); +} + +class PassManagerTest : public ::testing::Test { +protected: + LLVMContext Context; + std::unique_ptr M; + std::unique_ptr TM; + +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")) { + // MachineModuleAnalysis needs a TargetMachine instance. + llvm::InitializeAllTargets(); + + std::string Error; + const Target *TheTarget = + TargetRegistry::lookupTarget("x86_64-unknown-linux", Error); + // If we didn't build x86, do not run the test. + if (!TheTarget) + return; + + TargetOptions Options; + TM.reset(TheTarget->createTargetMachine("x86_64-unknown-linux", "", "", + Options, None)); + } +}; + +TEST_F(PassManagerTest, Basic) { + LLVMTargetMachine *LLVMTM = static_cast(TM.get()); + M->setDataLayout(TM->createDataLayout()); + + LoopAnalysisManager LAM(/*DebugLogging*/ true); + FunctionAnalysisManager FAM(/*DebugLogging*/ true); + CGSCCAnalysisManager CGAM(/*DebugLogging*/ true); + ModuleAnalysisManager MAM(/*DebugLogging*/ true); + PassBuilder PB(TM.get()); + PB.registerModuleAnalyses(MAM); + PB.registerFunctionAnalyses(FAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + + FAM.registerPass([&] { return TestFunctionAnalysis(); }); + FAM.registerPass([&] { return PassInstrumentationAnalysis(); }); + MAM.registerPass([&] { return MachineModuleAnalysis(LLVMTM); }); + MAM.registerPass([&] { return PassInstrumentationAnalysis(); }); + + MachineFunctionAnalysisManager MFAM; + { + // Test move assignment. + MachineFunctionAnalysisManager NestedMFAM(FAM, MAM, + /*DebugLogging*/ true); + NestedMFAM.registerPass([&] { return PassInstrumentationAnalysis(); }); + NestedMFAM.registerPass([&] { return TestMachineFunctionAnalysis(); }); + MFAM = std::move(NestedMFAM); + } + + int Count = 0; + int BeforeInitialization; + int BeforeFinalization; + int TestMachineFunctionCount; + int TestMachineModuleCount1; + int TestMachineModuleCount2; + + MachineFunctionPassManager MFPM; + { + // Test move assignment. + MachineFunctionPassManager NestedMFPM(/*DebugLogging*/ true); + NestedMFPM.addPass(TestMachineModulePass(Count, TestMachineModuleCount1)); + NestedMFPM.addPass(TestMachineFunctionPass(Count, BeforeInitialization, + BeforeFinalization, + TestMachineFunctionCount)); + NestedMFPM.addPass(TestMachineModulePass(Count, TestMachineModuleCount2)); + MFPM = std::move(NestedMFPM); + } + + cantFail(MFPM.run(*M, MFAM)); + + EXPECT_EQ(1, BeforeInitialization); + EXPECT_EQ(2, TestMachineModuleCount1); + EXPECT_EQ(15, TestMachineFunctionCount); + EXPECT_EQ(16, TestMachineModuleCount2); + EXPECT_EQ(17, BeforeFinalization); + EXPECT_EQ(17, Count); +} + +} // namespace