Search code examples
c++clangllvmllvm-c++-api

Run default optimization pipeline using modern LLVM


I'm using LLVM 7 and I have an llvm::Module that I'd like to optimize using the standard optimization pipeline. Unfortunately, there isn't a llvm::runDefaultOptimizations function that I can call. There seems to be a bajillion ways to optimize a module in LLVM. My searches on this topic have found many old/depreciated APIs and some examples that don't work on my system.

I want to run all of the standard optimizations at -O3 with the least amount of hassle possible. I don't want to manually list all of the passes or even write a for loop. I thought llvm::PassBuilder::buildModuleOptimizationPipeline might be the solution but I get a linker error when I try to use that function which I think is really strange.


Solution

  • I ended up taking the source of the opt tool (found here) and stripping everything I didn't need. I ended up with this:

    #include <llvm/IR/Verifier.h>
    #include <llvm/Transforms/IPO.h>
    #include <llvm/IR/LegacyPassManager.h>
    #include <llvm/Target/TargetMachine.h>
    #include <llvm/Analysis/TargetLibraryInfo.h>
    #include <llvm/Analysis/TargetTransformInfo.h>
    #include <llvm/Transforms/IPO/PassManagerBuilder.h>
    
    namespace {
    
    void addOptPasses(
      llvm::legacy::PassManagerBase &passes,
      llvm::legacy::FunctionPassManager &fnPasses,
      llvm::TargetMachine *machine
    ) {
      llvm::PassManagerBuilder builder;
      builder.OptLevel = 3;
      builder.SizeLevel = 0;
      builder.Inliner = llvm::createFunctionInliningPass(3, 0, false);
      builder.LoopVectorize = true;
      builder.SLPVectorize = true;
      machine->adjustPassManager(builder);
    
      builder.populateFunctionPassManager(fnPasses);
      builder.populateModulePassManager(passes);
    }
    
    void addLinkPasses(llvm::legacy::PassManagerBase &passes) {
      llvm::PassManagerBuilder builder;
      builder.VerifyInput = true;
      builder.Inliner = llvm::createFunctionInliningPass(3, 0, false);
      builder.populateLTOPassManager(passes);
    }
    
    }
    
    void optimizeModule(llvm::TargetMachine *machine, llvm::Module *module) {
      module->setTargetTriple(machine->getTargetTriple().str());
      module->setDataLayout(machine->createDataLayout());
    
      llvm::legacy::PassManager passes;
      passes.add(new llvm::TargetLibraryInfoWrapperPass(machine->getTargetTriple()));
      passes.add(llvm::createTargetTransformInfoWrapperPass(machine->getTargetIRAnalysis()));
    
      llvm::legacy::FunctionPassManager fnPasses(module);
      fnPasses.add(llvm::createTargetTransformInfoWrapperPass(machine->getTargetIRAnalysis()));
    
      addOptPasses(passes, fnPasses, machine);
      addLinkPasses(passes);
    
      fnPasses.doInitialization();
      for (llvm::Function &func : *module) {
        fnPasses.run(func);
      }
      fnPasses.doFinalization();
    
      passes.add(llvm::createVerifierPass());
      passes.run(*module);
    }
    

    This is roughly equivalent to passing -O3 to opt. It's using some legacy stuff but I don't really mind.