diff --git a/llvm/include/llvm/CodeGen/TargetPassConfig.h b/llvm/include/llvm/CodeGen/TargetPassConfig.h --- a/llvm/include/llvm/CodeGen/TargetPassConfig.h +++ b/llvm/include/llvm/CodeGen/TargetPassConfig.h @@ -161,8 +161,9 @@ static bool hasLimitedCodeGenPipeline(); /// Returns true if none of the `-stop-before` and `-stop-after` options is - /// set. - static bool willCompleteCodeGenPipeline(); + /// set. If one of them is set and `StopOpt` is not null, return the specified + /// pass in `StopOpt`. + static bool willCompleteCodeGenPipeline(std::string *StopOpt = nullptr); /// If hasLimitedCodeGenPipeline is true, this method /// returns a string with the name of the options, separated diff --git a/llvm/include/llvm/Target/TargetMachine.h b/llvm/include/llvm/Target/TargetMachine.h --- a/llvm/include/llvm/Target/TargetMachine.h +++ b/llvm/include/llvm/Target/TargetMachine.h @@ -376,6 +376,27 @@ return make_error("buildCodeGenPipeline is not overriden", inconvertibleErrorCode()); } + + virtual Expected + parseIRPipeline(StringRef PipelineText, CGPassBuilderOption Opts, + MachineFunctionAnalysisManager &MFAM, + PassInstrumentationCallbacks *PIC) { + return make_error("parseIRPipeline is not overriden", + inconvertibleErrorCode()); + } + + virtual Expected + parseMIRPipeline(StringRef PipelineText, CGPassBuilderOption Opts, + MachineFunctionAnalysisManager &MFAM, + PassInstrumentationCallbacks *PIC) { + return make_error("parseMIRPipeline is not overriden", + inconvertibleErrorCode()); + } + + virtual bool isMachinePass(StringRef PassName) const { + llvm_unreachable("isMachinePass is not overriden"); + } + /// Add passes to the specified pass manager to get machine code emitted with /// the MCJIT. This method returns true if machine code is not supported. It /// fills the MCContext Ctx pointer which can be used to build custom diff --git a/llvm/lib/CodeGen/TargetPassConfig.cpp b/llvm/lib/CodeGen/TargetPassConfig.cpp --- a/llvm/lib/CodeGen/TargetPassConfig.cpp +++ b/llvm/lib/CodeGen/TargetPassConfig.cpp @@ -646,8 +646,15 @@ "triple set?"); } -bool TargetPassConfig::willCompleteCodeGenPipeline() { - return StopBeforeOpt.empty() && StopAfterOpt.empty(); +bool TargetPassConfig::willCompleteCodeGenPipeline(std::string *StopOpt) { + bool Ret = StopBeforeOpt.empty() && StopAfterOpt.empty(); + if (!Ret && StopOpt) { + if (!StopBeforeOpt.empty()) + *StopOpt = StopBeforeOpt; + if (!StopAfterOpt.empty()) + *StopOpt = StopAfterOpt; + } + return Ret; } bool TargetPassConfig::hasLimitedCodeGenPipeline() { diff --git a/llvm/tools/llc/CMakeLists.txt b/llvm/tools/llc/CMakeLists.txt --- a/llvm/tools/llc/CMakeLists.txt +++ b/llvm/tools/llc/CMakeLists.txt @@ -22,6 +22,7 @@ add_llvm_tool(llc llc.cpp + NewPMDriver.cpp DEPENDS intrinsics_gen diff --git a/llvm/tools/llc/NewPMDriver.h b/llvm/tools/llc/NewPMDriver.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llc/NewPMDriver.h @@ -0,0 +1,49 @@ +//===- NewPMDriver.h - Function to drive llc with the new PM --------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// A single function which is called to drive the llc behavior for the new +/// PassManager. +/// +/// This is only in a separate TU with a header to avoid including all of the +/// old pass manager headers and the new pass manager headers into the same +/// file. Eventually all of the routines here will get folded back into +/// llc.cpp. +/// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_OPT_NEWPMDRIVER_H +#define LLVM_TOOLS_OPT_NEWPMDRIVER_H + +#include "llvm/IR/DiagnosticHandler.h" +#include "llvm/Support/CodeGen.h" +#include +#include + +namespace llvm { +class Module; +class TargetLibraryInfoImpl; +class TargetMachine; +class ToolOutputFile; +class LLVMContext; +class MIRParser; + +struct LLCDiagnosticHandler : public DiagnosticHandler { + bool *HasError; + LLCDiagnosticHandler(bool *HasErrorPtr) : HasError(HasErrorPtr) {} + bool handleDiagnostics(const DiagnosticInfo &DI) override; +}; + +int compileModuleWithNewPM( + StringRef Arg0, std::unique_ptr M, std::unique_ptr MIR, + std::unique_ptr Target, std::unique_ptr Out, + std::unique_ptr DwoOut, LLVMContext &Context, + const TargetLibraryInfoImpl &TLII, bool NoVerify, bool CompileTwice, + const std::vector &RunPassNames, CodeGenFileType FileType); +} // namespace llvm + +#endif \ No newline at end of file diff --git a/llvm/tools/llc/NewPMDriver.cpp b/llvm/tools/llc/NewPMDriver.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llc/NewPMDriver.cpp @@ -0,0 +1,267 @@ +//===- NewPMDriver.cpp - Driver for llc using new PM ----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file is just a split of the code that logically belongs in llc.cpp but +/// that includes the new pass manager headers. +/// +//===----------------------------------------------------------------------===// + +#include "NewPMDriver.h" +#include "llvm/Analysis/CGSCCPassManager.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/CodeGen/CGPassBuilderOption.h" +#include "llvm/CodeGen/CommandFlags.h" +#include "llvm/CodeGen/MIRParser/MIRParser.h" +#include "llvm/CodeGen/MIRPrinter.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachinePassManager.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/IRPrintingPasses.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/IR/Verifier.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/StandardInstrumentations.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include "llvm/Transforms/Scalar/LoopPassManager.h" +#include "llvm/Transforms/Utils/Cloning.h" + +using namespace llvm; + +static cl::opt PassPipeline( + "passes", cl::desc("A textual description of the codegen IR pass pipeline"), + cl::Hidden); + +static cl::opt RegAlloc( + "regalloc2", cl::desc("Register allocator to use for new pass manager"), + cl::Hidden, cl::ValueOptional, cl::init(RegAllocType::Default), + cl::values( + clEnumValN(RegAllocType::Default, "default", + "pick register allocator based on -O option"), + clEnumValN(RegAllocType::Basic, "basic", "basic register allocator"), + clEnumValN(RegAllocType::Fast, "fast", "fast register allocator"), + clEnumValN(RegAllocType::Greedy, "greedy", "greedy register allocator"), + clEnumValN(RegAllocType::PBQP, "pbqp", "PBQP register allocator"))); + +static cl::opt + DebugPM("debug-pass-manager", cl::Hidden, + cl::desc("Print pass management debugging information")); + +bool LLCDiagnosticHandler::handleDiagnostics(const DiagnosticInfo &DI) { + if (DI.getSeverity() == DS_Error) + *HasError = true; + + if (auto *Remark = dyn_cast(&DI)) + if (!Remark->isEnabled()) + return true; + + DiagnosticPrinterRawOStream DP(errs()); + errs() << LLVMContext::getDiagnosticMessagePrefix(DI.getSeverity()) << ": "; + DI.print(DP); + errs() << "\n"; + return true; +} + +llvm::ExitOnError ExitOnErr; + +static void RunPasses(bool CompileTwice, bool BOS, ToolOutputFile *Out, + Module *M, LLVMContext &Context, + SmallVector &Buffer, ModulePassManager *MPM, + ModuleAnalysisManager *MAM, + MachineFunctionPassManager *MFPM, + MachineFunctionAnalysisManager *MFAM) { + auto RunPM = [=]() { + if (MPM) { + assert(MAM); + MPM->run(*M, *MAM); + } + + if (MFPM) { + assert(MFAM); + ExitOnErr(MFPM->run(*M, *MFAM)); + } + }; + + assert(M); + + // Before executing passes, print the final values of the LLVM options. + cl::PrintOptionValues(); + + // If requested, run the pass manager over the same module again, + // to catch any bugs due to persistent state in the passes. Note that + // opt has the same functionality, so it may be worth abstracting this out + // in the future. + SmallVector CompileTwiceBuffer; + if (CompileTwice) { + std::unique_ptr M2(llvm::CloneModule(*M)); + RunPM(); + CompileTwiceBuffer = Buffer; + Buffer.clear(); + } + + RunPM(); + + auto HasError = + ((const LLCDiagnosticHandler *)(Context.getDiagHandlerPtr()))->HasError; + if (*HasError) + exit(1); + + // Compare the two outputs and make sure they're the same + if (CompileTwice) { + if (Buffer.size() != CompileTwiceBuffer.size() || + (memcmp(Buffer.data(), CompileTwiceBuffer.data(), Buffer.size()) != + 0)) { + errs() << "Running the pass manager twice changed the output.\n" + "Writing the result of the second run to the specified output\n" + "To generate the one-run comparison binary, just run without\n" + "the compile-twice option\n"; + Out->os() << Buffer; + Out->keep(); + exit(1); + } + } + + if (BOS) { + Out->os() << Buffer; + } +} + +int llvm::compileModuleWithNewPM( + StringRef Arg0, std::unique_ptr M, std::unique_ptr MIR, + std::unique_ptr Target, std::unique_ptr Out, + std::unique_ptr DwoOut, LLVMContext &Context, + const TargetLibraryInfoImpl &TLII, bool NoVerify, bool CompileTwice, + const std::vector &RunPassNames, CodeGenFileType FileType) { + + if (!RunPassNames.empty() && PassPipeline.getNumOccurrences()) { + WithColor::warning(errs(), Arg0) + << "could not specify both -run-pass and -passes\n"; + return 1; + } + + if (!RunPassNames.empty() || PassPipeline.getNumOccurrences()) + if (TargetPassConfig::hasLimitedCodeGenPipeline()) { + WithColor::warning(errs(), Arg0) + << "run-pass cannot be used with " + << TargetPassConfig::getLimitedCodeGenPipelineReason(" and ") + << ".\n"; + return 1; + } + + LLVMTargetMachine &LLVMTM = static_cast(*Target); + + { + raw_pwrite_stream *OS = &Out->os(); + + // Manually do the buffering rather than using buffer_ostream, + // so we can memcmp the contents in CompileTwice mode + SmallVector Buffer; + std::unique_ptr BOS; + if ((codegen::getFileType() != CGFT_AssemblyFile && + !Out->os().supportsSeeking()) || + CompileTwice) { + BOS = std::make_unique(Buffer); + OS = BOS.get(); + } + + // Fetch options from TargetPassConfig + CGPassBuilderOption Opt = getCGPassBuilderOption(); + Opt.DisableVerify = NoVerify; + Opt.DebugPM = DebugPM; + Opt.RegAlloc = RegAlloc; + + PassInstrumentationCallbacks PIC; + StandardInstrumentations SI; + SI.registerCallbacks(PIC); + registerCodeGenCallback(PIC); + + LoopAnalysisManager LAM(Opt.DebugPM); + FunctionAnalysisManager FAM(Opt.DebugPM); + CGSCCAnalysisManager CGAM(Opt.DebugPM); + ModuleAnalysisManager MAM(Opt.DebugPM); + PassBuilder PB(Target.get(), PipelineTuningOptions(), None, &PIC); + PB.registerModuleAnalyses(MAM); + PB.registerCGSCCAnalyses(CGAM); + PB.registerFunctionAnalyses(FAM); + PB.registerLoopAnalyses(LAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + + FAM.registerPass([&] { return TargetLibraryAnalysis(TLII); }); + MAM.registerPass([&] { return MachineModuleAnalysis(&LLVMTM); }); + + MachineFunctionAnalysisManager MFAM(FAM, MAM, Opt.DebugPM); + + if (!RunPassNames.empty()) { + // Construct a custom pass pipeline that starts after instruction + // selection. + + if (!MIR) { + WithColor::warning(errs(), Arg0) << "run-pass is for .mir file only.\n"; + return 1; + } + + MachineFunctionPassManager MFPM = ExitOnErr(LLVMTM.parseMIRPipeline( + llvm::join(RunPassNames, ","), Opt, MFAM, &PIC)); + MFPM.addPass(PrintMIRPass(*OS)); + + auto &MMI = MFAM.getResult(*M); + if (MIR->parseMachineFunctions(*M, MMI)) + return 1; + + RunPasses(CompileTwice, BOS.get(), Out.get(), M.get(), Context, Buffer, + nullptr, nullptr, &MFPM, &MFAM); + } else if (PassPipeline.getNumOccurrences()) { + // Construct a custom pass pipeline that ends before instruction + // selection. + + ModulePassManager MPM = + ExitOnErr(LLVMTM.parseIRPipeline(PassPipeline, Opt, MFAM, &PIC)); + MPM.addPass(PrintModulePass(*OS)); + + RunPasses(CompileTwice, BOS.get(), Out.get(), M.get(), Context, Buffer, + &MPM, &MAM, nullptr, nullptr); + } else { + std::pair PMPair = + ExitOnErr( + LLVMTM.buildCodeGenPipeline(*OS, DwoOut ? &DwoOut->os() : nullptr, + FileType, Opt, MFAM, &PIC)); + + // Add printing pass according the pass type: IR or machine pass. + std::string StopPass; + if (!TargetPassConfig::willCompleteCodeGenPipeline(&StopPass)) { + if (LLVMTM.isMachinePass(StopPass)) + PMPair.second.addPass(PrintMIRPass(*OS)); + else + PMPair.first.addPass(PrintModulePass(*OS)); + } + + RunPasses(CompileTwice, BOS.get(), Out.get(), M.get(), Context, Buffer, + &PMPair.first, &MAM, &PMPair.second, &MFAM); + } + } + + // Declare success. + Out->keep(); + if (DwoOut) + DwoOut->keep(); + + return 0; +} diff --git a/llvm/tools/llc/llc.cpp b/llvm/tools/llc/llc.cpp --- a/llvm/tools/llc/llc.cpp +++ b/llvm/tools/llc/llc.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "NewPMDriver.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/Analysis/TargetLibraryInfo.h" @@ -164,6 +165,9 @@ cl::desc("The format used for serializing remarks (default: YAML)"), cl::value_desc("format"), cl::init("yaml")); +static cl::opt EnableNewPassManager( + "enable-new-pm", cl::desc("Enable the new pass manager"), cl::init(false)); + namespace { static ManagedStatic> RunPassNames; @@ -255,25 +259,6 @@ return FDOut; } -struct LLCDiagnosticHandler : public DiagnosticHandler { - bool *HasError; - LLCDiagnosticHandler(bool *HasErrorPtr) : HasError(HasErrorPtr) {} - bool handleDiagnostics(const DiagnosticInfo &DI) override { - if (DI.getSeverity() == DS_Error) - *HasError = true; - - if (auto *Remark = dyn_cast(&DI)) - if (!Remark->isEnabled()) - return true; - - DiagnosticPrinterRawOStream DP(errs()); - errs() << LLVMContext::getDiagnosticMessagePrefix(DI.getSeverity()) << ": "; - DI.print(DP); - errs() << "\n"; - return true; - } -}; - static void InlineAsmDiagHandler(const SMDiagnostic &SMD, void *Context, unsigned LocCookie) { bool *HasError = static_cast(Context); @@ -541,16 +526,12 @@ } } - // Build up all of the passes that we want to do to the module. - legacy::PassManager PM; - // Add an appropriate TargetLibraryInfo pass for the module's triple. TargetLibraryInfoImpl TLII(Triple(M->getTargetTriple())); // The -disable-simplify-libcalls flag actually disables all builtin optzns. if (DisableSimplifyLibCalls) TLII.disableAllFunctions(); - PM.add(new TargetLibraryInfoWrapperPass(TLII)); // Verify module immediately to catch problems before doInitialization() is // called on any passes. @@ -569,6 +550,17 @@ WithColor::warning(errs(), argv[0]) << ": warning: ignoring -mc-relax-all because filetype != obj"; + bool RunPassNone = !RunPassNames->empty() && RunPassNames->at(0) == "none"; + if (EnableNewPassManager && !RunPassNone) + return compileModuleWithNewPM( + argv[0], std::move(M), std::move(MIR), std::move(Target), + std::move(Out), std::move(DwoOut), Context, TLII, NoVerify, + CompileTwice, *RunPassNames, codegen::getFileType()); + + // Build up all of the passes that we want to do to the module. + legacy::PassManager PM; + PM.add(new TargetLibraryInfoWrapperPass(TLII)); + { raw_pwrite_stream *OS = &Out->os();