From 4e728be2bfb9b2dee209b563c573dc5a7ae00730 Mon Sep 17 00:00:00 2001 From: Chandler Carruth Date: Sat, 11 Jan 2014 08:16:35 +0000 Subject: [PATCH] [PM] Add (very skeletal) support to opt for running the new pass manager. I cannot emphasize enough that this is a WIP. =] I expect it to change a great deal as things stabilize, but I think its really important to get *some* functionality here so that the infrastructure can be tested more traditionally from the commandline. The current design is looking something like this: ./bin/opt -passes='module(pass_a,pass_b,function(pass_c,pass_d))' So rather than custom-parsed flags, there is a single flag with a string argument that is parsed into the pass pipeline structure. This makes it really easy to have nice structural properties that are very explicit. There is one obvious and important shortcut. You can start off the pipeline with a pass, and the minimal context of pass managers will be built around the entire specified pipeline. This makes the common case for tests super easy: ./bin/opt -passes=instcombine,sroa,gvn But this won't introduce any of the complexity of the fully inferred old system -- we only ever do this for the *entire* argument, and we only look at the first pass. If the other passes don't fit in the pass manager selected it is a hard error. The other interesting aspect here is that I'm not relying on any registration facilities. Such facilities may be unavoidable for supporting plugins, but I have alternative ideas for plugins that I'd like to try first. My plan is essentially to build everything without registration until we hit an absolute requirement. Instead of registration of pass names, there will be a library dedicated to parsing pass names and the pass pipeline strings described above. Currently, this is directly embedded into opt for simplicity as it is very early, but I plan to eventually pull this into a library that opt, bugpoint, and even Clang can depend on. It should end up as a good home for things like the existing PassManagerBuilder as well. There are a bunch of FIXMEs in the code for the parts of this that are just stubbed out to make the patch more incremental. A quick list of what's coming up directly after this: - Support for function passes and building the structured nesting. - Support for printing the pass structure, and FileCheck tests of all of this code. - The .def-file based pass name parsing. - IR priting passes and the corresponding tests. Some obvious things that I'm not going to do right now, but am definitely planning on as the pass manager work gets a bit further: - Pull the parsing into library, including the builders. - Thread the rest of the target stuff into the new pass manager. - Wire support for the new pass manager up to llc. - Plugin support. Some things that I'd like to have, but are significantly lower on my priority list. I'll get to these eventually, but they may also be places where others want to contribute: - Adding nice error reporting for broken pass pipeline descriptions. - Typo-correction for pass names. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@198998 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Other/pass-pipeline-parsing.ll | 2 + tools/opt/CMakeLists.txt | 2 + tools/opt/NewPMDriver.cpp | 46 ++++++++++++++ tools/opt/NewPMDriver.h | 42 +++++++++++++ tools/opt/Passes.cpp | 93 +++++++++++++++++++++++++++++ tools/opt/Passes.h | 56 +++++++++++++++++ tools/opt/opt.cpp | 21 ++++++- 7 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 test/Other/pass-pipeline-parsing.ll create mode 100644 tools/opt/NewPMDriver.cpp create mode 100644 tools/opt/NewPMDriver.h create mode 100644 tools/opt/Passes.cpp create mode 100644 tools/opt/Passes.h diff --git a/test/Other/pass-pipeline-parsing.ll b/test/Other/pass-pipeline-parsing.ll new file mode 100644 index 00000000000..c490f56ba84 --- /dev/null +++ b/test/Other/pass-pipeline-parsing.ll @@ -0,0 +1,2 @@ +; RUN: opt -disable-output -passes=no-op-module,no-op-module %s +; RUN: opt -disable-output -passes='module(no-op-module,no-op-module)' %s diff --git a/tools/opt/CMakeLists.txt b/tools/opt/CMakeLists.txt index f96f9b35ef5..c007e0fa2e7 100644 --- a/tools/opt/CMakeLists.txt +++ b/tools/opt/CMakeLists.txt @@ -23,6 +23,8 @@ set(LLVM_NO_DEAD_STRIP 1) add_llvm_tool(opt AnalysisWrappers.cpp GraphPrinters.cpp + NewPMDriver.cpp + Passes.cpp PrintSCC.cpp opt.cpp ) diff --git a/tools/opt/NewPMDriver.cpp b/tools/opt/NewPMDriver.cpp new file mode 100644 index 00000000000..e577995e260 --- /dev/null +++ b/tools/opt/NewPMDriver.cpp @@ -0,0 +1,46 @@ +//===- NewPMDriver.cpp - Driver for opt with new PM -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file is just a split of the code that logically belongs in opt.cpp but +/// that includes the new pass manager headers. +/// +//===----------------------------------------------------------------------===// + +#include "NewPMDriver.h" +#include "Passes.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ToolOutputFile.h" + +using namespace llvm; + +bool llvm::runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M, + tool_output_file *Out, StringRef PassPipeline, + bool NoOutput) { + // Before executing passes, print the final values of the LLVM options. + cl::PrintOptionValues(); + + ModulePassManager MPM; + if (!parsePassPipeline(MPM, PassPipeline)) { + errs() << Arg0 << ": unable to parse pass pipeline description.\n"; + return false; + } + + // Now that we have all of the passes ready, run them. + MPM.run(&M); + + // Declare success. + if (!NoOutput) + Out->keep(); + return true; +} diff --git a/tools/opt/NewPMDriver.h b/tools/opt/NewPMDriver.h new file mode 100644 index 00000000000..17ac75be549 --- /dev/null +++ b/tools/opt/NewPMDriver.h @@ -0,0 +1,42 @@ +//===- NewPMDriver.h - Function to drive opt with the new PM ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// A single function which is called to drive the opt 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 +/// opt.cpp. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OPT_NEW_PM_DRIVER_H +#define LLVM_TOOLS_OPT_NEW_PM_DRIVER_H + +#include "llvm/ADT/StringRef.h" + +namespace llvm { +class LLVMContext; +class Module; +class tool_output_file; + +/// \brief Driver function to run the new pass manager over a module. +/// +/// This function only exists factored away from opt.cpp in order to prevent +/// inclusion of the new pass manager headers and the old headers into the same +/// file. It's interface is consequentially somewhat ad-hoc, but will go away +/// when the transition finishes. +bool runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M, + tool_output_file *Out, StringRef PassPipeline, + bool NoOutput); +} + +#endif diff --git a/tools/opt/Passes.cpp b/tools/opt/Passes.cpp new file mode 100644 index 00000000000..49751269df9 --- /dev/null +++ b/tools/opt/Passes.cpp @@ -0,0 +1,93 @@ +//===- Passes.cpp - Parsing, selection, and running of passes -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file provides the infrastructure to parse and build a custom pass +/// manager based on a commandline flag. It also provides helpers to aid in +/// analyzing, debugging, and testing pass structures. +/// +//===----------------------------------------------------------------------===// + +#include "Passes.h" +#include "llvm/IR/PassManager.h" + +using namespace llvm; + +namespace { + + /// \brief No-op module pass which does nothing. +struct NoOpModulePass { + PreservedAnalyses run(Module *M) { return PreservedAnalyses::all(); } +}; + +} // End anonymous namespace. + +// FIXME: Factor all of the parsing logic into a .def file that we include +// under different macros. +static bool isModulePassName(StringRef Name) { + if (Name == "no-op-module") return true; + + return false; +} + +static bool parseModulePassName(ModulePassManager &MPM, StringRef Name) { + assert(isModulePassName(Name)); + if (Name == "no-op-module") { + MPM.addPass(NoOpModulePass()); + return true; + } + return false; +} + +static bool parseModulePassPipeline(ModulePassManager &MPM, + StringRef &PipelineText) { + for (;;) { + // Parse nested pass managers by recursing. + if (PipelineText.startswith("module(")) { + PipelineText = PipelineText.substr(strlen("module(")); + if (!parseModulePassPipeline(MPM, PipelineText)) + return false; + assert(!PipelineText.empty() && PipelineText[0] == ')'); + PipelineText = PipelineText.substr(1); + } else { + // Otherwise try to parse a pass name. + size_t End = PipelineText.find_first_of(",)"); + if (!parseModulePassName(MPM, PipelineText.substr(0, End))) + return false; + + PipelineText = PipelineText.substr(End); + } + + if (PipelineText.empty() || PipelineText[0] == ')') + return true; + + assert(PipelineText[0] == ','); + PipelineText = PipelineText.substr(1); + } +} + +// Primary pass pipeline description parsing routine. +// FIXME: Should this routine accept a TargetMachine or require the caller to +// pre-populate the analysis managers with target-specific stuff? +bool llvm::parsePassPipeline(ModulePassManager &MPM, StringRef PipelineText) { + // Look at the first entry to figure out which layer to start parsing at. + if (PipelineText.startswith("module(")) + return parseModulePassPipeline(MPM, PipelineText); + + // FIXME: Support parsing function pass manager nests. + + // This isn't a direct pass manager name, look for the end of a pass name. + StringRef FirstName = PipelineText.substr(0, PipelineText.find_first_of(",")); + if (isModulePassName(FirstName)) + return parseModulePassPipeline(MPM, PipelineText); + + // FIXME: Support parsing function pass names. + + return false; +} diff --git a/tools/opt/Passes.h b/tools/opt/Passes.h new file mode 100644 index 00000000000..6016b74c80b --- /dev/null +++ b/tools/opt/Passes.h @@ -0,0 +1,56 @@ +//===- Passes.h - Parsing, selection, and running of passes -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// Interfaces for producing common pass manager configurations and parsing +/// textual pass specifications. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OPT_PASSES_H +#define LLVM_TOOLS_OPT_PASSES_H + +#include "llvm/ADT/StringRef.h" + +namespace llvm { +class ModulePassManager; + +/// \brief Parse a textual pass pipeline description into a \c ModulePassManager. +/// +/// The format of the textual pass pipeline description looks something like: +/// +/// module(function(instcombine,sroa),dce,cgscc(inliner,function(...)),...) +/// +/// Pass managers have ()s describing the nest structure of passes. All passes +/// are comma separated. As a special shortcut, if the very first pass is not +/// a module pass (as a module pass manager is), this will automatically form +/// the shortest stack of pass managers that allow inserting that first pass. +/// So, assuming function passes 'fpassN', CGSCC passes 'cgpassN', and loop passes +/// 'lpassN', all of these are valid: +/// +/// fpass1,fpass2,fpass3 +/// cgpass1,cgpass2,cgpass3 +/// lpass1,lpass2,lpass3 +/// +/// And they are equivalent to the following (resp.): +/// +/// module(function(fpass1,fpass2,fpass3)) +/// module(cgscc(cgpass1,cgpass2,cgpass3)) +/// module(function(loop(lpass1,lpass2,lpass3))) +/// +/// This shortcut is especially useful for debugging and testing small pass +/// combinations. Note that these shortcuts don't introduce any other magic. If +/// the sequence of passes aren't all the exact same kind of pass, it will be +/// an error. You cannot mix different levels implicitly, you must explicitly +/// form a pass manager in which to nest passes. +bool parsePassPipeline(ModulePassManager &MPM, StringRef PipelineText); + +} + +#endif diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 9e764512296..5138ef68c04 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -#include "llvm/IR/LLVMContext.h" +#include "NewPMDriver.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Triple.h" #include "llvm/Analysis/CallGraph.h" @@ -24,6 +24,7 @@ #include "llvm/CodeGen/CommandFlags.h" #include "llvm/DebugInfo.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/PrintModulePass.h" #include "llvm/IRReader/IRReader.h" @@ -55,6 +56,15 @@ using namespace llvm; static cl::list PassList(cl::desc("Optimizations available:")); +// This flag specifies a textual description of the optimization pass pipeline +// to run over the module. This flag switches opt to use the new pass manager +// infrastructure, completely disabling all of the flags specific to the old +// pass management. +static cl::opt PassPipeline( + "passes", + cl::desc("A textual description of the pass pipeline for optimizing"), + cl::Hidden); + // Other command line options... // static cl::opt @@ -660,6 +670,15 @@ int main(int argc, char **argv) { if (CheckBitcodeOutputToConsole(Out->os(), !Quiet)) NoOutput = true; + if (PassPipeline.getNumOccurrences() > 0) + // The user has asked to use the new pass manager and provided a pipeline + // string. Hand off the rest of the functionality to the new code for that + // layer. + return runPassPipeline(argv[0], Context, *M.get(), Out.get(), PassPipeline, + NoOutput) + ? 0 + : 1; + // Create a PassManager to hold and optimize the collection of passes we are // about to build. // -- 2.34.1