diff --git a/llvm/include/llvm/CodeGen/CodeGenPassBuilder.h b/llvm/include/llvm/CodeGen/CodeGenPassBuilder.h index f540f3774c414..0ea81347638e9 100644 --- a/llvm/include/llvm/CodeGen/CodeGenPassBuilder.h +++ b/llvm/include/llvm/CodeGen/CodeGenPassBuilder.h @@ -175,45 +175,33 @@ template class CodeGenPassBuilder { // Function object to maintain state while adding codegen IR passes. class AddIRPass { public: - AddIRPass(ModulePassManager &MPM, bool DebugPM, bool Check = true) - : MPM(MPM) { - if (Check) - AddingFunctionPasses = false; - } + AddIRPass(ModulePassManager &MPM) : MPM(MPM) {} ~AddIRPass() { - MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); - } - - // Add Function Pass - template - std::enable_if_t::value> - operator()(PassT &&Pass) { - if (AddingFunctionPasses && !*AddingFunctionPasses) - AddingFunctionPasses = true; - FPM.addPass(std::forward(Pass)); + if (!FPM.isEmpty()) + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); } - // Add Module Pass - template - std::enable_if_t::value && - !is_detected::value> - operator()(PassT &&Pass) { - assert((!AddingFunctionPasses || !*AddingFunctionPasses) && - "could not add module pass after adding function pass"); - MPM.addPass(std::forward(Pass)); + template void operator()(PassT &&Pass) { + static_assert((is_detected::value || + is_detected::value) && + "Only module pass and function pass are supported."); + + // Add Function Pass + if constexpr (is_detected::value) { + FPM.addPass(std::forward(Pass)); + } else { + // Add Module Pass + if (!FPM.isEmpty()) { + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); + FPM = FunctionPassManager(); + } + MPM.addPass(std::forward(Pass)); + } } private: ModulePassManager &MPM; FunctionPassManager FPM; - // The codegen IR pipeline are mostly function passes with the exceptions of - // a few loop and module passes. `AddingFunctionPasses` make sures that - // we could only add module passes at the beginning of the pipeline. Once - // we begin adding function passes, we could no longer add module passes. - // This special-casing introduces less adaptor passes. If we have the need - // of adding module passes after function passes, we could change the - // implementation to accommodate that. - std::optional AddingFunctionPasses; }; // Function object to maintain state while adding codegen machine passes. @@ -488,7 +476,7 @@ Error CodeGenPassBuilder::buildPipeline( ModulePassManager &MPM, MachineFunctionPassManager &MFPM, raw_pwrite_stream &Out, raw_pwrite_stream *DwoOut, CodeGenFileType FileType) const { - AddIRPass addIRPass(MPM, Opt.DebugPM); + AddIRPass addIRPass(MPM); // `ProfileSummaryInfo` is always valid. addIRPass(RequireAnalysisPass()); addIRPass(RequireAnalysisPass()); @@ -627,8 +615,8 @@ void CodeGenPassBuilder::addIRPasses(AddIRPass &addPass) const { // Run loop strength reduction before anything else. if (getOptLevel() != CodeGenOptLevel::None && !Opt.DisableLSR) { - addPass(createFunctionToLoopPassAdaptor( - LoopStrengthReducePass(), /*UseMemorySSA*/ true, Opt.DebugPM)); + addPass(createFunctionToLoopPassAdaptor(LoopStrengthReducePass(), + /*UseMemorySSA=*/true)); // FIXME: use -stop-after so we could remove PrintLSR if (Opt.PrintLSR) addPass(PrintFunctionPass(dbgs(), "\n\n*** Code after LSR ***\n")); @@ -647,9 +635,6 @@ void CodeGenPassBuilder::addIRPasses(AddIRPass &addPass) const { // Run GC lowering passes for builtin collectors // TODO: add a pass insertion point here addPass(GCLoweringPass()); - // FIXME: `ShadowStackGCLoweringPass` now is a - // module pass, so it will trigger assertion. - // See comment of `AddingFunctionPasses` addPass(ShadowStackGCLoweringPass()); addPass(LowerConstantIntrinsicsPass()); diff --git a/llvm/include/llvm/Passes/PassBuilder.h b/llvm/include/llvm/Passes/PassBuilder.h index 61417431f8a8f..56038416cd7aa 100644 --- a/llvm/include/llvm/Passes/PassBuilder.h +++ b/llvm/include/llvm/Passes/PassBuilder.h @@ -743,6 +743,90 @@ bool parseAnalysisUtilityPasses( return false; } + +// These are special since they are only for testing purposes. + +/// No-op module pass which does nothing. +struct NoOpModulePass : PassInfoMixin { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &) { + return PreservedAnalyses::all(); + } +}; + +/// No-op module analysis. +class NoOpModuleAnalysis : public AnalysisInfoMixin { + friend AnalysisInfoMixin; + static AnalysisKey Key; + +public: + struct Result {}; + Result run(Module &, ModuleAnalysisManager &) { return Result(); } +}; + +/// No-op CGSCC pass which does nothing. +struct NoOpCGSCCPass : PassInfoMixin { + PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &, + LazyCallGraph &, CGSCCUpdateResult &UR) { + return PreservedAnalyses::all(); + } +}; + +/// No-op CGSCC analysis. +class NoOpCGSCCAnalysis : public AnalysisInfoMixin { + friend AnalysisInfoMixin; + static AnalysisKey Key; + +public: + struct Result {}; + Result run(LazyCallGraph::SCC &, CGSCCAnalysisManager &, LazyCallGraph &G) { + return Result(); + } +}; + +/// No-op function pass which does nothing. +struct NoOpFunctionPass : PassInfoMixin { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &) { + return PreservedAnalyses::all(); + } +}; + +/// No-op function analysis. +class NoOpFunctionAnalysis : public AnalysisInfoMixin { + friend AnalysisInfoMixin; + static AnalysisKey Key; + +public: + struct Result {}; + Result run(Function &, FunctionAnalysisManager &) { return Result(); } +}; + +/// No-op loop nest pass which does nothing. +struct NoOpLoopNestPass : PassInfoMixin { + PreservedAnalyses run(LoopNest &L, LoopAnalysisManager &, + LoopStandardAnalysisResults &, LPMUpdater &) { + return PreservedAnalyses::all(); + } +}; + +/// No-op loop pass which does nothing. +struct NoOpLoopPass : PassInfoMixin { + PreservedAnalyses run(Loop &L, LoopAnalysisManager &, + LoopStandardAnalysisResults &, LPMUpdater &) { + return PreservedAnalyses::all(); + } +}; + +/// No-op loop analysis. +class NoOpLoopAnalysis : public AnalysisInfoMixin { + friend AnalysisInfoMixin; + static AnalysisKey Key; + +public: + struct Result {}; + Result run(Loop &, LoopAnalysisManager &, LoopStandardAnalysisResults &) { + return Result(); + } +}; } #endif diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index bfc97d5464c04..d8b28d2f2912c 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -300,109 +300,13 @@ cl::opt PrintPipelinePasses( "(best-effort only).")); } // namespace llvm -namespace { - -// The following passes/analyses have custom names, otherwise their name will -// include `(anonymous namespace)`. These are special since they are only for -// testing purposes and don't live in a header file. - -/// No-op module pass which does nothing. -struct NoOpModulePass : PassInfoMixin { - PreservedAnalyses run(Module &M, ModuleAnalysisManager &) { - return PreservedAnalyses::all(); - } - - static StringRef name() { return "NoOpModulePass"; } -}; - -/// No-op module analysis. -class NoOpModuleAnalysis : public AnalysisInfoMixin { - friend AnalysisInfoMixin; - static AnalysisKey Key; - -public: - struct Result {}; - Result run(Module &, ModuleAnalysisManager &) { return Result(); } - static StringRef name() { return "NoOpModuleAnalysis"; } -}; - -/// No-op CGSCC pass which does nothing. -struct NoOpCGSCCPass : PassInfoMixin { - PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &, - LazyCallGraph &, CGSCCUpdateResult &UR) { - return PreservedAnalyses::all(); - } - static StringRef name() { return "NoOpCGSCCPass"; } -}; - -/// No-op CGSCC analysis. -class NoOpCGSCCAnalysis : public AnalysisInfoMixin { - friend AnalysisInfoMixin; - static AnalysisKey Key; - -public: - struct Result {}; - Result run(LazyCallGraph::SCC &, CGSCCAnalysisManager &, LazyCallGraph &G) { - return Result(); - } - static StringRef name() { return "NoOpCGSCCAnalysis"; } -}; - -/// No-op function pass which does nothing. -struct NoOpFunctionPass : PassInfoMixin { - PreservedAnalyses run(Function &F, FunctionAnalysisManager &) { - return PreservedAnalyses::all(); - } - static StringRef name() { return "NoOpFunctionPass"; } -}; - -/// No-op function analysis. -class NoOpFunctionAnalysis : public AnalysisInfoMixin { - friend AnalysisInfoMixin; - static AnalysisKey Key; - -public: - struct Result {}; - Result run(Function &, FunctionAnalysisManager &) { return Result(); } - static StringRef name() { return "NoOpFunctionAnalysis"; } -}; - -/// No-op loop nest pass which does nothing. -struct NoOpLoopNestPass : PassInfoMixin { - PreservedAnalyses run(LoopNest &L, LoopAnalysisManager &, - LoopStandardAnalysisResults &, LPMUpdater &) { - return PreservedAnalyses::all(); - } - static StringRef name() { return "NoOpLoopNestPass"; } -}; - -/// No-op loop pass which does nothing. -struct NoOpLoopPass : PassInfoMixin { - PreservedAnalyses run(Loop &L, LoopAnalysisManager &, - LoopStandardAnalysisResults &, LPMUpdater &) { - return PreservedAnalyses::all(); - } - static StringRef name() { return "NoOpLoopPass"; } -}; - -/// No-op loop analysis. -class NoOpLoopAnalysis : public AnalysisInfoMixin { - friend AnalysisInfoMixin; - static AnalysisKey Key; - -public: - struct Result {}; - Result run(Loop &, LoopAnalysisManager &, LoopStandardAnalysisResults &) { - return Result(); - } - static StringRef name() { return "NoOpLoopAnalysis"; } -}; - AnalysisKey NoOpModuleAnalysis::Key; AnalysisKey NoOpCGSCCAnalysis::Key; AnalysisKey NoOpFunctionAnalysis::Key; AnalysisKey NoOpLoopAnalysis::Key; +namespace { + /// Whether or not we should populate a PassInstrumentationCallbacks's class to /// pass name map. /// diff --git a/llvm/unittests/CodeGen/CMakeLists.txt b/llvm/unittests/CodeGen/CMakeLists.txt index fa6c9cf7c5aeb..c78cbfcc28193 100644 --- a/llvm/unittests/CodeGen/CMakeLists.txt +++ b/llvm/unittests/CodeGen/CMakeLists.txt @@ -7,13 +7,16 @@ set(LLVM_LINK_COMPONENTS CodeGenTypes Core FileCheck + IRPrinter MC MIRParser Passes + ScalarOpts SelectionDAG Support Target TargetParser + TransformUtils ) add_llvm_unittest(CodeGenTests @@ -22,6 +25,7 @@ add_llvm_unittest(CodeGenTests AMDGPUMetadataTest.cpp AsmPrinterDwarfTest.cpp CCStateTest.cpp + CodeGenPassBuilderTest.cpp DIEHashTest.cpp DIETest.cpp DwarfStringPoolEntryRefTest.cpp diff --git a/llvm/unittests/CodeGen/CodeGenPassBuilderTest.cpp b/llvm/unittests/CodeGen/CodeGenPassBuilderTest.cpp new file mode 100644 index 0000000000000..9be3616cd362b --- /dev/null +++ b/llvm/unittests/CodeGen/CodeGenPassBuilderTest.cpp @@ -0,0 +1,141 @@ +//===- llvm/unittest/CodeGen/CodeGenPassBuilderTest.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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/CodeGenPassBuilder.h" +#include "llvm/CodeGen/MachinePassManager.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TargetParser/Host.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; + +namespace { + +class DummyCodeGenPassBuilder + : public CodeGenPassBuilder { +public: + DummyCodeGenPassBuilder(LLVMTargetMachine &TM, CGPassBuilderOption Opts, + PassInstrumentationCallbacks *PIC) + : CodeGenPassBuilder(TM, Opts, PIC){}; + + void addPreISel(AddIRPass &addPass) const { + addPass(NoOpModulePass()); + addPass(NoOpFunctionPass()); + addPass(NoOpFunctionPass()); + addPass(NoOpFunctionPass()); + addPass(NoOpModulePass()); + addPass(NoOpFunctionPass()); + } + + void addAsmPrinter(AddMachinePass &, CreateMCStreamer) const {} + + Error addInstSelector(AddMachinePass &) const { return Error::success(); } +}; + +class CodeGenPassBuilderTest : public testing::Test { +public: + LLVMTargetMachine *TM; + + static void SetUpTestCase() { + InitializeAllTargets(); + InitializeAllTargetMCs(); + + // TODO: Move this test to normal lit test when llc supports new pm. + static const char *argv[] = { + "test", + "-print-pipeline-passes", + }; + int argc = std::size(argv); + cl::ParseCommandLineOptions(argc, argv); + } + + void SetUp() override { + std::string TripleName = Triple::normalize(sys::getDefaultTargetTriple()); + std::string Error; + const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error); + if (!TheTarget) + GTEST_SKIP(); + + TargetOptions Options; + TM = static_cast( + TheTarget->createTargetMachine("", "", "", Options, std::nullopt)); + if (!TM) + GTEST_SKIP(); + } +}; + +TEST_F(CodeGenPassBuilderTest, basic) { + LoopAnalysisManager LAM; + FunctionAnalysisManager FAM; + CGSCCAnalysisManager CGAM; + ModuleAnalysisManager MAM; + + PassInstrumentationCallbacks PIC; + DummyCodeGenPassBuilder CGPB(*TM, getCGPassBuilderOption(), &PIC); + PipelineTuningOptions PTO; + PassBuilder PB(TM, PTO, std::nullopt, &PIC); + + PB.registerModuleAnalyses(MAM); + PB.registerCGSCCAnalyses(CGAM); + PB.registerFunctionAnalyses(FAM); + PB.registerLoopAnalyses(LAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + + ModulePassManager MPM; + MachineFunctionPassManager MFPM; + Error Err = + CGPB.buildPipeline(MPM, MFPM, outs(), nullptr, CodeGenFileType::Null); + EXPECT_FALSE(Err); + + std::string IRPipeline; + raw_string_ostream IROS(IRPipeline); + MPM.printPipeline(IROS, [&PIC](StringRef Name) { + auto PassName = PIC.getPassNameForClassName(Name); + return PassName.empty() ? Name : PassName; + }); + const char ExpectedIRPipeline[] = + "no-op-module,function(no-op-function," + "no-op-function,no-op-function),no-op-module"; + // TODO: Move this test to normal lit test when llc supports new pm. + EXPECT_TRUE(StringRef(IRPipeline).contains(ExpectedIRPipeline)); + + std::string MIRPipeline; + raw_string_ostream MIROS(MIRPipeline); + MFPM.printPipeline(MIROS, [&PIC](StringRef Name) { + auto PassName = PIC.getPassNameForClassName(Name); + return PassName.empty() ? Name : PassName; + }); + const char ExpectedMIRPipeline[] = + "FinalizeISelPass,EarlyTailDuplicatePass,OptimizePHIsPass," + "StackColoringPass,LocalStackSlotPass,DeadMachineInstructionElimPass," + "EarlyMachineLICMPass,MachineCSEPass,MachineSinkingPass," + "PeepholeOptimizerPass,DeadMachineInstructionElimPass," + "DetectDeadLanesPass,ProcessImplicitDefsPass,PHIEliminationPass," + "TwoAddressInstructionPass,RegisterCoalescerPass," + "RenameIndependentSubregsPass,MachineSchedulerPass,RAGreedyPass," + "VirtRegRewriterPass,StackSlotColoringPass," + "RemoveRedundantDebugValuesPass,PostRAMachineSinkingPass,ShrinkWrapPass," + "PrologEpilogInserterPass,BranchFolderPass,TailDuplicatePass," + "MachineLateInstrsCleanupPass,MachineCopyPropagationPass," + "ExpandPostRAPseudosPass,PostRASchedulerPass,MachineBlockPlacementPass," + "FEntryInserterPass,XRayInstrumentationPass,PatchableFunctionPass," + "FuncletLayoutPass,StackMapLivenessPass,LiveDebugValuesPass," + "MachineSanitizerBinaryMetadata,FreeMachineFunctionPass"; + // TODO: Check pipeline string when all pass names are populated. + // TODO: Move this test to normal lit test when llc supports new pm. + EXPECT_EQ(MIRPipeline, ExpectedMIRPipeline); +} + +} // namespace