From 2f78dc686d1891aa737848bf3a7a5f95aecfad35 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sun, 4 Nov 2012 00:26:15 +0100 Subject: [PATCH] Use llvm::PassManagerBuilder for optimization scheduling. This commit is based on Kai's pull request #157. --- gen/optimizer.cpp | 317 ++++++++++++++++++++-------------------------- gen/optimizer.h | 6 +- 2 files changed, 141 insertions(+), 182 deletions(-) diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp index c5ae4a54..2ceb79ea 100644 --- a/gen/optimizer.cpp +++ b/gen/optimizer.cpp @@ -1,25 +1,28 @@ #include "gen/optimizer.h" #include "gen/cl_helpers.h" +#include "gen/irstate.h" #include "gen/logger.h" #include "gen/passes/Passes.h" #include "llvm/PassManager.h" #include "llvm/LinkAllPasses.h" -#include "llvm/Analysis/LoopPass.h" +#include "llvm/Module.h" +#include "llvm/ADT/Triple.h" #include "llvm/Analysis/Verifier.h" #if LDC_LLVM_VER >= 302 #include "llvm/DataLayout.h" #else #include "llvm/Target/TargetData.h" #endif +#include "llvm/Target/TargetLibraryInfo.h" +#include "llvm/Target/TargetMachine.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/PassNameParser.h" #include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "mars.h" // error() -#include "root.h" -#include // strcmp(); using namespace llvm; @@ -30,7 +33,7 @@ static cl::list cl::Hidden // to clean up --help output ); -static cl::opt optimizeLevel( +static cl::opt optimizeLevel( cl::desc("Setting the optimization level:"), cl::ZeroOrMore, cl::values( @@ -39,8 +42,10 @@ static cl::opt optimizeLevel( clEnumValN(1, "O1", "Simple optimizations"), clEnumValN(2, "O2", "Good optimizations"), clEnumValN(3, "O3", "Aggressive optimizations"), - clEnumValN(4, "O4", "Link-time optimization"), // not implemented? - clEnumValN(5, "O5", "Link-time optimization"), // not implemented? + clEnumValN(4, "O4", "Link-time optimization"), // Not implemented yet. + clEnumValN(5, "O5", "Link-time optimization"), // Not implemented yet. + clEnumValN(-1, "Os", "Like -O2 with extra optimizations for size"), + clEnumValN(-2, "Oz", "Like -Os but reduces code size further"), clEnumValEnd), cl::init(0)); @@ -75,44 +80,28 @@ enableInlining("inlining", cl::desc("(*) Enable function inlining in -O"), cl::ZeroOrMore); -#if LDC_LLVM_VER >= 301 static cl::opt -runVectorization("vectorize", cl::desc("Run vectorization passes")); +unitAtATime("unit-at-a-time", + cl::desc("Enable basic IPO"), + cl::init(true)); static cl::opt -useGVNAfterVectorization("use-gvn-after-vectorization", - cl::init(false), cl::Hidden, - cl::desc("Run GVN instead of Early CSE after vectorization passes")); -#endif +stripDebug("strip-debug", + cl::desc("Strip symbolic debug information before optimization")); -// Determine whether or not to run the inliner as part of the default list of -// optimization passes. -// If not explicitly specified, treat as false for -O0-2, and true for -O3. -bool doInline() { - return enableInlining == cl::BOU_TRUE - || (enableInlining == cl::BOU_UNSET && optimizeLevel >= 3); +static unsigned optLevel() { + // Use -O2 as a base for the size-optimization levels. + return optimizeLevel >= 0 ? optimizeLevel : 2; } -// Determine whether the inliner will be run. +static unsigned sizeLevel() { + return optimizeLevel < 0 ? -optimizeLevel : 0; +} + +// Determines whether or not to run the normal, full inlining pass. bool willInline() { - if (doInline()) - return true; - // It may also have been specified explicitly on the command line as an explicit pass - typedef cl::list PL; - for (PL::iterator I = passList.begin(), E = passList.end(); I != E; ++I) { - if (!std::strcmp((*I)->getPassArgument(), "inline")) - return true; - } - return false; -} - -// Some extra accessors for the linker: (llvm-ld version only, currently unused?) -int optLevel() { - return optimizeLevel; -} - -bool optimize() { - return optimizeLevel || doInline() || !passList.empty(); + return enableInlining == cl::BOU_TRUE || + (enableInlining != cl::BOU_UNSET && optLevel() > 1); } llvm::CodeGenOpt::Level codeGenOptLevel() { @@ -132,138 +121,84 @@ llvm::CodeGenOpt::Level codeGenOptLevel() { #endif } -static void addPass(PassManager& pm, Pass* pass) { +static inline void addPass(PassManagerBase& pm, Pass* pass) { pm.add(pass); if (verifyEach) pm.add(createVerifierPass()); } -// this function inserts some or all of the std-compile-opts passes depending on the -// optimization level given. -static void addPassesForOptLevel(PassManager& pm) { - // -O1 - if (optimizeLevel >= 1) - { - // Add alias analysis passes. - // This is at least required for FunctionAttrs pass. - addPass(pm, createTypeBasedAliasAnalysisPass()); - addPass(pm, createBasicAliasAnalysisPass()); - //addPass(pm, createStripDeadPrototypesPass()); - addPass(pm, createGlobalDCEPass()); - addPass(pm, createPromoteMemoryToRegisterPass()); - addPass(pm, createCFGSimplificationPass()); - if (optimizeLevel == 1) - addPass(pm, createPromoteMemoryToRegisterPass()); - else - addPass(pm, createScalarReplAggregatesPass()); - addPass(pm, createGlobalOptimizerPass()); - } +static void addStripExternalsPass(const PassManagerBuilder &builder, PassManagerBase &pm) { + if (builder.OptLevel >= 1) + addPass(pm, createStripExternalsPass()); +} - // -O2 - if (optimizeLevel >= 2) - { - addPass(pm, createIPConstantPropagationPass()); - addPass(pm, createDeadArgEliminationPass()); - addPass(pm, createInstructionCombiningPass()); - addPass(pm, createCFGSimplificationPass()); - addPass(pm, createPruneEHPass()); - - addPass(pm, createFunctionAttrsPass()); - - addPass(pm, createTailCallEliminationPass()); - addPass(pm, createCFGSimplificationPass()); - addPass(pm, createGVNPass()); - } - - // -inline - if (doInline()) { - addPass(pm, createFunctionInliningPass()); - - if (optimizeLevel >= 2) { - // Run some optimizations to clean up after inlining. - addPass(pm, createScalarReplAggregatesPass()); - addPass(pm, createInstructionCombiningPass()); - // -instcombine + gvn == devirtualization :) - addPass(pm, createGVNPass()); - - // Inline again, to catch things like now nonvirtual - // function calls, foreach delegates passed to inlined - // opApply's, etc. where the actual function being called - // wasn't known during the first inliner pass. - addPass(pm, createFunctionInliningPass()); - } - } - - if (optimizeLevel >= 2) { - if (!disableLangSpecificPasses) { - if (!disableSimplifyRuntimeCalls) - addPass(pm, createSimplifyDRuntimeCalls()); +static void addSimplifyDRuntimeCallsPass(const PassManagerBuilder &builder, PassManagerBase &pm) { + if (builder.OptLevel >= 2 && builder.SizeLevel == 0) + addPass(pm, createSimplifyDRuntimeCalls()); +} #if USE_METADATA - if (!disableGCToStack) - addPass(pm, createGarbageCollect2Stack()); -#endif // USE_METADATA - } - // Run some clean-up passes - addPass(pm, createInstructionCombiningPass()); - addPass(pm, createScalarReplAggregatesPass()); - addPass(pm, createCFGSimplificationPass()); - addPass(pm, createInstructionCombiningPass()); +static void addGarbageCollect2StackPass(const PassManagerBuilder &builder, PassManagerBase &pm) { + if (builder.OptLevel >= 2 && builder.SizeLevel == 0) + addPass(pm, createGarbageCollect2Stack()); +} +#endif + +/** + * Adds a set of optimization passes to the given module/function pass + * managers based on the given optimization and size reduction levels. + * + * The selection mirrors Clang behavior and is based on LLVM's + * PassManagerBuilder. + */ +static void addOptimizationPasses(PassManagerBase &mpm, FunctionPassManager &fpm, + unsigned optLevel, unsigned sizeLevel) { + fpm.add(createVerifierPass()); // Verify that input is correct + + PassManagerBuilder builder; + builder.OptLevel = optLevel; + builder.SizeLevel = sizeLevel; + + if (enableInlining == cl::BOU_FALSE) { + // If -disable-inlining has been explictly specified, don't perform + // any inlining at all. + } else if (willInline()) { + unsigned threshold = 225; + if (sizeLevel == 1) // -Os + threshold = 75; + else if (sizeLevel == 2) // -Oz + threshold = 25; + if (optLevel > 2) + threshold = 275; + builder.Inliner = createFunctionInliningPass(threshold); + } else { + builder.Inliner = createAlwaysInlinerPass(); } + builder.DisableUnitAtATime = !unitAtATime; + builder.DisableUnrollLoops = optLevel == 0; + /* builder.Vectorize is set in ctor from command line switch */ - // -O3 - if (optimizeLevel >= 3) - { - addPass(pm, createArgumentPromotionPass()); - addPass(pm, createSimplifyLibCallsPass()); - addPass(pm, createInstructionCombiningPass()); - addPass(pm, createJumpThreadingPass()); - addPass(pm, createCFGSimplificationPass()); - addPass(pm, createScalarReplAggregatesPass()); - addPass(pm, createInstructionCombiningPass()); - addPass(pm, createConstantPropagationPass()); + if (!disableLangSpecificPasses) { + if (!disableSimplifyRuntimeCalls) + builder.addExtension(PassManagerBuilder::EP_LoopOptimizerEnd, addSimplifyDRuntimeCallsPass); - addPass(pm, createReassociatePass()); - addPass(pm, createLoopRotatePass()); - addPass(pm, createLICMPass()); - addPass(pm, createLoopUnswitchPass()); - addPass(pm, createInstructionCombiningPass()); - addPass(pm, createIndVarSimplifyPass()); - addPass(pm, createLoopDeletionPass()); - addPass(pm, createLoopUnrollPass()); - addPass(pm, createInstructionCombiningPass()); - addPass(pm, createGVNPass()); - addPass(pm, createMemCpyOptPass()); - addPass(pm, createSCCPPass()); - - addPass(pm, createInstructionCombiningPass()); - addPass(pm, createConstantPropagationPass()); - - addPass(pm, createDeadStoreEliminationPass()); - addPass(pm, createAggressiveDCEPass()); - addPass(pm, createCFGSimplificationPass()); - addPass(pm, createConstantMergePass()); +#if USE_METADATA + if (!disableGCToStack) + Builder.addExtension(PassManagerBuilder::EP_LoopOptimizerEnd, addGarbageCollect2StackPass); +#endif // USE_METADATA } #if LDC_LLVM_VER >= 301 - // -vectorize - if (runVectorization) - { - addPass(pm, createBBVectorizePass()); - addPass(pm, createInstructionCombiningPass()); - if (optimizeLevel > 1 && useGVNAfterVectorization) - addPass(pm, createGVNPass()); // Remove redundancies - else - addPass(pm, createEarlyCSEPass()); // Catch trivial redundancies - } + // EP_OptimizerLast does not exist in LLVM 3.0, add it manually below. + builder.addExtension(PassManagerBuilder::EP_OptimizerLast, addStripExternalsPass); #endif - if (optimizeLevel >= 1) { - addPass(pm, createStripExternalsPass()); - addPass(pm, createGlobalDCEPass()); - } + builder.populateFunctionPassManager(fpm); + builder.populateModulePassManager(mpm); - // level -O4 and -O5 are linktime optimizations +#if LDC_LLVM_VER < 301 + addStripExternalsPass(builder, mpm); +#endif } ////////////////////////////////////////////////////////////////////////////////////////// @@ -271,52 +206,80 @@ static void addPassesForOptLevel(PassManager& pm) { // Returns true if any optimization passes were invoked. bool ldc_optimize_module(llvm::Module* m) { - if (!optimize()) - return false; + // Create a PassManager to hold and optimize the collection of + // per-module passes we are about to build. + PassManager mpm; - PassManager pm; + // Add an appropriate TargetLibraryInfo pass for the module's triple. + TargetLibraryInfo *tli = new TargetLibraryInfo(Triple(m->getTargetTriple())); - if (verifyEach) pm.add(createVerifierPass()); + // The -disable-simplify-libcalls flag actually disables all builtin optzns. + if (disableSimplifyRuntimeCalls) + tli->disableAllFunctions(); + mpm.add(tli); + // Add an appropriate TargetData instance for this module. #if LDC_LLVM_VER >= 302 - addPass(pm, new DataLayout(m)); + mpm.add(new DataLayout(m)); #else - addPass(pm, new TargetData(m)); + mpm.add(new TargetData(m)); #endif - bool optimize = optimizeLevel != 0 || doInline(); + // Also set up a manager for the per-function passes. + FunctionPassManager fpm(m); +#if LDC_LLVM_VER >= 302 + fpm.add(new DataLayout(m)); +#else + fpm.add(new TargetData(m)); +#endif - unsigned optPos = optimizeLevel != 0 - ? optimizeLevel.getPosition() - : enableInlining.getPosition(); + // If the -strip-debug command line option was specified, add it before + // anything else. + if (stripDebug) + addPass(mpm, createStripSymbolsPass(true)); - for (size_t i = 0; i < passList.size(); i++) { - // insert -O / -enable-inlining in right position - if (optimize && optPos < passList.getPosition(i)) { - addPassesForOptLevel(pm); - optimize = false; + bool defaultsAdded = false; + // Create a new optimization pass for each one specified on the command line + for (unsigned i = 0; i < passList.size(); ++i) { + if (optimizeLevel && optimizeLevel.getPosition() < passList.getPosition(i)) { + addOptimizationPasses(mpm, fpm, optLevel(), sizeLevel()); + defaultsAdded = true; } - const PassInfo* pass = passList[i]; - if (PassInfo::NormalCtor_t ctor = pass->getNormalCtor()) { - addPass(pm, ctor()); - } else { - const char* arg = pass->getPassArgument(); // may return null + const PassInfo *passInf = passList[i]; + Pass *pass = 0; + if (passInf->getNormalCtor()) + pass = passInf->getNormalCtor()(); + else { + const char* arg = passInf->getPassArgument(); // may return null if (arg) error("Can't create pass '-%s' (%s)", arg, pass->getPassName()); else error("Can't create pass (%s)", pass->getPassName()); - assert(0); // Should be unreachable; root.h:error() calls exit() + llvm_unreachable("pass creation failed"); + } + if (pass) { + addPass(mpm, pass); } } - // insert -O / -enable-inlining if specified at the end, - if (optimize) - addPassesForOptLevel(pm); - pm.run(*m); + // Add the default passes for the specified optimization level. + if (!defaultsAdded) + addOptimizationPasses(mpm, fpm, optLevel(), sizeLevel()); + // Run per-function passes. + fpm.doInitialization(); + for (llvm::Module::iterator F = m->begin(), E = m->end(); F != E; ++F) + fpm.run(*F); + fpm.doFinalization(); + + // Run per-module passes. + mpm.run(*m); + + // Verify the resulting module. verifyModule(m); + // Report that we run some passes. return true; } diff --git a/gen/optimizer.h b/gen/optimizer.h index 7fe7c73c..a45d22f4 100644 --- a/gen/optimizer.h +++ b/gen/optimizer.h @@ -12,13 +12,9 @@ namespace llvm { class Module; } bool ldc_optimize_module(llvm::Module* m); -// Determines whether the inliner will run in the -O list of passes -bool doInline(); -// Determines whether the inliner will be run at all. +// Returns whether the normal, full inlining pass will be run. bool willInline(); -int optLevel(); - bool optimize(); llvm::CodeGenOpt::Level codeGenOptLevel();