Index: include/llvm/Analysis/CallGraphSCCPass.h =================================================================== --- include/llvm/Analysis/CallGraphSCCPass.h +++ include/llvm/Analysis/CallGraphSCCPass.h @@ -41,6 +41,10 @@ Pass *createPrinterPass(raw_ostream &OS, const std::string &Banner) const override; + /// createGitCommitModulePass - Get a git-commit module pass. + Pass *createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const override; + using llvm::Pass::doInitialization; using llvm::Pass::doFinalization; Index: include/llvm/Analysis/LoopPass.h =================================================================== --- include/llvm/Analysis/LoopPass.h +++ include/llvm/Analysis/LoopPass.h @@ -35,6 +35,10 @@ Pass *createPrinterPass(raw_ostream &O, const std::string &Banner) const override; + /// createGitCommitModulePass - Get a git-commit module pass. + Pass *createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const override; + // runOnLoop - This method should be implemented by the subclass to perform // whatever action is necessary for the specified Loop. virtual bool runOnLoop(Loop *L, LPPassManager &LPM) = 0; Index: include/llvm/Analysis/RegionPass.h =================================================================== --- include/llvm/Analysis/RegionPass.h +++ include/llvm/Analysis/RegionPass.h @@ -58,6 +58,10 @@ Pass *createPrinterPass(raw_ostream &O, const std::string &Banner) const override; + /// createGitCommitModulePass - Get a git-commit module pass. + Pass *createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const override; + using llvm::Pass::doInitialization; using llvm::Pass::doFinalization; Index: include/llvm/IR/GitCommitModule.h =================================================================== --- /dev/null +++ include/llvm/IR/GitCommitModule.h @@ -0,0 +1,89 @@ +//===--- GitCommitModule.h --- Write and Commit Module into Git -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file defines passes to write out IR and commit it into a Git +/// repository. The GitCommitModulePass pass simply initialize a Git repository +/// when created, then write out and commit the entire module when it is +/// executed. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_IR_GITCOMMITMODULE_H +#define LLVM_IR_GITCOMMITMODULE_H + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include + +namespace llvm { +class Module; +class PreservedAnalyses; +template class AnalysisManager; + +/// \brief Pass for printing and committing a Module as LLVM's text IR assembly. +/// +/// Note: This pass is for use with the new pass manager. Use the create...Pass +/// functions above to create passes for use with the legacy pass manager. +class GitCommitModulePass { + SmallString<128> GitRepo; + std::string Message; + bool ShouldPreserveUseListOrder; + +protected: + bool init(); + bool printModule(llvm::Module &M); + bool commit(); + + llvm::PreservedAnalyses run(Module &M); + +public: + GitCommitModulePass(); + GitCommitModulePass(const std::string &GitRepo, const std::string &Message, + bool ShouldPreserveUseListOrder = false); + + llvm::PreservedAnalyses run(Module &M, AnalysisManager &); + + static llvm::StringRef name() { return "GitCommitModulePass"; } +}; +} // namespace llvm + +// Provides the implementation of the wrapper pass (wrap it into a KindPass). +// +// That is split into pieces to handle RegionPass, LoopPass and CallGraphSCCPass +#define GIT_COMMIT_MODULE_PASS_WRAPPER(Kind, GetModule) \ + GIT_COMMIT_MODULE_PASS_WRAPPER_INTERNAL( \ + Kind, GetModule, GIT_COMMIT_MODULE_PASS_WRAPPER_RUN_ON) +#define GIT_COMMIT_MODULE_PASS_WRAPPER_RUN_ON(Kind, K) runOn##Kind(Kind &K) +#define GIT_COMMIT_MODULE_PASS_WRAPPER_INTERNAL(Kind, GetModule, RUN_ON) \ + namespace { \ + class GitCommit##Kind##PassWrapper : public Kind##Pass { \ + GitCommitModulePass P; \ + \ + public: \ + static char ID; \ + GitCommit##Kind##PassWrapper(const std::string &GitRepo, \ + const std::string &Message, \ + bool ShouldPreserveUseListOrder = false) \ + : Kind##Pass(ID), P(GitRepo, Message, ShouldPreserveUseListOrder) {} \ + \ + bool RUN_ON(Kind, K) override { \ + ModuleAnalysisManager DummyMAM; \ + P.run(GetModule(K), DummyMAM); \ + return false; \ + } \ + \ + void getAnalysisUsage(AnalysisUsage &AU) const override { \ + AU.setPreservesAll(); \ + } \ + }; \ + } \ + char GitCommit##Kind##PassWrapper::ID = 0; + +#endif Index: include/llvm/Pass.h =================================================================== --- include/llvm/Pass.h +++ include/llvm/Pass.h @@ -124,6 +124,11 @@ virtual Pass *createPrinterPass(raw_ostream &OS, const std::string &Banner) const = 0; + /// createGitCommitModulePass - Get a Pass to print and commit the IR + /// of the entire module. + virtual Pass *createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const = 0; + /// Each pass is responsible for assigning a pass manager to itself. /// PMS is the stack of available pass manager. virtual void assignPassManager(PMStack &, @@ -233,6 +238,10 @@ Pass *createPrinterPass(raw_ostream &OS, const std::string &Banner) const override; + /// createGitCommitModulePass - Get a git-commit module pass. + Pass *createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const override; + /// runOnModule - Virtual method overriden by subclasses to process the module /// being operated on. virtual bool runOnModule(Module &M) = 0; @@ -290,6 +299,10 @@ Pass *createPrinterPass(raw_ostream &OS, const std::string &Banner) const override; + /// createGitCommitModulePass - Get a git-commit module function pass. + Pass *createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const override; + /// runOnFunction - Virtual method overriden by subclasses to do the /// per-function processing of the pass. virtual bool runOnFunction(Function &F) = 0; @@ -324,6 +337,10 @@ Pass *createPrinterPass(raw_ostream &OS, const std::string &Banner) const override; + /// createGitCommitModulePass - Get a git-commit module basic block pass. + Pass *createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const override; + using llvm::Pass::doInitialization; using llvm::Pass::doFinalization; Index: lib/Analysis/CallGraphSCCPass.cpp =================================================================== --- lib/Analysis/CallGraphSCCPass.cpp +++ lib/Analysis/CallGraphSCCPass.cpp @@ -22,6 +22,7 @@ #include "llvm/Analysis/CallGraph.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/Function.h" +#include "llvm/IR/GitCommitModule.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/LegacyPassManagers.h" @@ -645,6 +646,14 @@ return new PrintCallGraphPass(Banner, OS); } +static Module &getModule(CallGraphSCC &SCC) { + return SCC.getCallGraph().getModule(); +} + +#define RUN_ON(CallGraphSCC, SCC) runOnSCC(CallGraphSCC &SCC) +GIT_COMMIT_MODULE_PASS_WRAPPER_INTERNAL(CallGraphSCC, getModule, RUN_ON) +#undef RUN_ON + bool CallGraphSCCPass::skipSCC(CallGraphSCC &SCC) const { return !SCC.getCallGraph().getModule() .getContext() @@ -652,6 +661,12 @@ .shouldRunPass(this, SCC); } +Pass * +CallGraphSCCPass::createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const { + return new GitCommitCallGraphSCCPassWrapper(GitRepo, Message); +} + char DummyCGSCCPass::ID = 0; INITIALIZE_PASS(DummyCGSCCPass, "DummyCGSCCPass", "DummyCGSCCPass", false, Index: lib/Analysis/LoopPass.cpp =================================================================== --- lib/Analysis/LoopPass.cpp +++ lib/Analysis/LoopPass.cpp @@ -16,6 +16,7 @@ #include "llvm/Analysis/LoopPass.h" #include "llvm/Analysis/LoopAnalysisManager.h" #include "llvm/IR/Dominators.h" +#include "llvm/IR/GitCommitModule.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/OptBisect.h" @@ -60,6 +61,11 @@ char PrintLoopPassWrapper::ID = 0; } +static Module &getModule(Loop *L) { return *L->getHeader()->getModule(); } +#define RUN_ON(Loop, L) runOn##Loop(Loop *L, LPPassManager &) +GIT_COMMIT_MODULE_PASS_WRAPPER_INTERNAL(Loop, getModule, RUN_ON) +#undef RUN_ON + //===----------------------------------------------------------------------===// // LPPassManager // @@ -294,6 +300,11 @@ return new PrintLoopPassWrapper(O, Banner); } +Pass *LoopPass::createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const { + return new GitCommitLoopPassWrapper(GitRepo, Message); +} + // Check if this pass is suitable for the current LPPassManager, if // available. This pass P is not suitable for a LPPassManager if P // is not preserving higher level analysis info used by other Index: lib/Analysis/RegionPass.cpp =================================================================== --- lib/Analysis/RegionPass.cpp +++ lib/Analysis/RegionPass.cpp @@ -14,6 +14,7 @@ // //===----------------------------------------------------------------------===// #include "llvm/Analysis/RegionPass.h" +#include "llvm/IR/GitCommitModule.h" #include "llvm/IR/OptBisect.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Timer.h" @@ -281,6 +282,19 @@ return new PrintRegionPass(Banner, O); } +static Module &getModule(Region *R) { + return *R->getEntry()->getParent()->getParent(); +} + +#define RUN_ON(Region, R) runOn##Region(Region *R, RGPassManager &) +GIT_COMMIT_MODULE_PASS_WRAPPER_INTERNAL(Region, getModule, RUN_ON) +#undef RUN_ON + +Pass *RegionPass::createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const { + return new GitCommitRegionPassWrapper(GitRepo, Message); +} + bool RegionPass::skipRegion(Region &R) const { Function &F = *R.getEntry()->getParent(); if (!F.getContext().getOptBisect().shouldRunPass(this, R)) Index: lib/IR/CMakeLists.txt =================================================================== --- lib/IR/CMakeLists.txt +++ lib/IR/CMakeLists.txt @@ -22,6 +22,7 @@ DiagnosticPrinter.cpp Dominators.cpp Function.cpp + GitCommitModule.cpp GVMaterializer.cpp Globals.cpp IRBuilder.cpp Index: lib/IR/GitCommitModule.cpp =================================================================== --- /dev/null +++ lib/IR/GitCommitModule.cpp @@ -0,0 +1,99 @@ +//===---- GitCommitModule.cpp ------ Write and Commit Module into Git -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// GitCommitModulePass implementation. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/GitCommitModule.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Pass.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" +#define DEBUG_TYPE "git-commit-module" +using namespace llvm; + +GitCommitModulePass::GitCommitModulePass() {} +GitCommitModulePass::GitCommitModulePass(const std::string &GitRepo, + const std::string &Message, + bool ShouldPreserveUseListOrder) + : GitRepo(GitRepo), Message(Message), + ShouldPreserveUseListOrder(ShouldPreserveUseListOrder) { + if(!init()) + report_fatal_error("Folder is not writeable?"); +} + +#ifndef NDEBUG +void dumpCommand(std::string Prog, ArrayRef Args) { + dbgs() << "(" << Prog << ")"; + for(auto Arg : Args) + if (Arg) + dbgs() << " '" << Arg << "'"; + dbgs() << "\n"; +} +#endif + +template static bool git(T... Args) { + auto Git = sys::findProgramByName("git"); + if (!Git) { + DEBUG(dbgs() << "Cannot find 'git' in PATH: " << Git.getError().message() + << "\n"); + return false; + } + const char *FullArgs[] = {"git", Args..., nullptr}; + DEBUG(dumpCommand(*Git, FullArgs)); + return (sys::ExecuteAndWait(*Git, FullArgs) == 0); +} + +bool GitCommitModulePass::init() { + return git("init", "--quiet", GitRepo.c_str()); +} + +bool GitCommitModulePass::printModule(Module &M) { + SmallString<128> FileName(M.getName()); + sys::path::replace_extension(FileName, "ll"); + + SmallString<128> AbsFileName(GitRepo); + sys::path::append(AbsFileName, FileName); + std::error_code EC; + raw_fd_ostream OS(AbsFileName, EC, sys::fs::F_Text); + + if (EC) + return false; + + if (llvm::isFunctionInPrintList("*")) + M.print(OS, nullptr, ShouldPreserveUseListOrder); + else { + for (const auto &F : M.functions()) + if (llvm::isFunctionInPrintList(F.getName())) + F.print(OS); + } + + return git("-C", GitRepo.c_str(), "add", FileName.c_str()); +} + +bool GitCommitModulePass::commit() { + return git("-C", GitRepo.c_str(), "commit", "--allow-empty", "--quiet", "-m", + Message.c_str()); +} + +PreservedAnalyses GitCommitModulePass::run(Module &M, + AnalysisManager &) { + if (!printModule(M)) + report_fatal_error("File is not writeable?"); + if (!commit()) + report_fatal_error("Git repository is broken?"); + return PreservedAnalyses::all(); +} Index: lib/IR/LegacyPassManager.cpp =================================================================== --- lib/IR/LegacyPassManager.cpp +++ lib/IR/LegacyPassManager.cpp @@ -82,6 +82,14 @@ llvm::cl::desc("Print IR after each pass"), cl::init(false), cl::Hidden); +static cl::opt + GitRepo("git-repo", llvm::cl::desc("Path to Git repository to initialize"), + cl::init("passes"), cl::Hidden); +static cl::opt GitCommitModuleAfterAll( + "git-commit-after-all", + llvm::cl::desc("Print and commit IR after each pass"), cl::init(false), + cl::Hidden); + static cl::opt PrintModuleScope("print-module-scope", cl::desc("When printing IR for print-[before|after]{-all} " @@ -254,6 +262,13 @@ return createPrintFunctionPass(O, Banner); } + /// createGitCommitModulePass - Get a git-commit module pass. + Pass *createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const override { + // Pass managers don't need to do git commit + return nullptr; + } + // Prepare for running an on the fly pass, freeing memory if needed // from a previous run. void releaseMemoryOnTheFly(); @@ -322,6 +337,13 @@ return createPrintModulePass(O, Banner); } + /// createGitCommitModulePass - Get a git-commit module pass. + Pass *createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const override { + // Pass managers don't need to do git commit + return nullptr; + } + /// run - Execute all of the passes scheduled for execution. Keep track of /// whether any of the passes modifies the module, and if so, return true. bool runOnModule(Module &M); @@ -410,6 +432,13 @@ return createPrintModulePass(O, Banner); } + /// createGitCommitModulePass - Get a git-commit module pass. + Pass *createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const override { + // Pass managers don't need to do git commit + return nullptr; + } + /// run - Execute all of the passes scheduled for execution. Keep track of /// whether any of the passes modifies the module, and if so, return true. bool run(Module &M); @@ -704,6 +733,13 @@ dbgs(), ("*** IR Dump After " + P->getPassName() + " ***").str()); PP->assignPassManager(activeStack, getTopLevelPassManagerType()); } + + if (PI && !PI->isAnalysis() && GitCommitModuleAfterAll) { + // TODO Use the pass options as commit message instead + Pass *PP = P->createGitCommitModulePass(GitRepo, P->getPassName()); + if (PP) + PP->assignPassManager(activeStack, getTopLevelPassManagerType()); + } } /// Find the pass that implements Analysis AID. Search immutable Index: lib/IR/Pass.cpp =================================================================== --- lib/IR/Pass.cpp +++ lib/IR/Pass.cpp @@ -17,11 +17,13 @@ #include "llvm/IR/Attributes.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Function.h" +#include "llvm/IR/GitCommitModule.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/LegacyPassNameParser.h" #include "llvm/IR/Module.h" #include "llvm/IR/OptBisect.h" +#include "llvm/IR/PassManager.h" #include "llvm/PassInfo.h" #include "llvm/PassRegistry.h" #include "llvm/PassSupport.h" @@ -51,6 +53,14 @@ return createPrintModulePass(OS, Banner); } +static Module &getModule(Module &M) { return M; } +GIT_COMMIT_MODULE_PASS_WRAPPER(Module, getModule); + +Pass *ModulePass::createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const { + return new GitCommitModulePassWrapper(GitRepo, Message); +} + PassManagerType ModulePass::getPotentialPassManagerType() const { return PMT_ModulePassManager; } @@ -150,6 +160,15 @@ return createPrintFunctionPass(OS, Banner); } +static Module &getModule(Function &F) { return *F.getParent(); } +GIT_COMMIT_MODULE_PASS_WRAPPER(Function, getModule); + +Pass * +FunctionPass::createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const { + return new GitCommitFunctionPassWrapper(GitRepo, Message); +} + PassManagerType FunctionPass::getPotentialPassManagerType() const { return PMT_FunctionPassManager; } @@ -175,6 +194,18 @@ return createPrintBasicBlockPass(OS, Banner); } +static Module &getModule(BasicBlock &BB) { + return *BB.getParent()->getParent(); +} + +GIT_COMMIT_MODULE_PASS_WRAPPER(BasicBlock, getModule); + +Pass * +BasicBlockPass::createGitCommitModulePass(const std::string &GitRepo, + const std::string &Message) const { + return new GitCommitBasicBlockPassWrapper(GitRepo, Message); +} + bool BasicBlockPass::doInitialization(Function &) { // By default, don't do anything. return false;