3

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.

JKRT
  • 1,179
  • 12
  • 25
Indiana Kernick
  • 5,041
  • 2
  • 20
  • 50

2 Answers2

6

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.

Indiana Kernick
  • 5,041
  • 2
  • 20
  • 50
  • Are you sure this is equivalent to the O3 pass? I don't have my rig here at the moment have you tried to compile a larger file using the latest clang with O3 and using the LLVM dif tool? – JKRT Dec 14 '18 at 11:39
  • @JKT It doesn't need to be *exactly* the same. This does a really good job at optimizing a few tests I've given it. It turned some nested loops into an integer multiplication! It's like magic! – Indiana Kernick Dec 14 '18 at 11:43
  • Right I see, but then it should be clarified in the answer, in case someone is looking to get the correct standard O3 functionality as stated in the question. For instance add roughly equivalent to the last line – JKRT Dec 14 '18 at 11:44
  • @JKT I haven't properly compared to two so it might be the same or it might not. I'm not sure. – Indiana Kernick Dec 14 '18 at 11:46
  • I will look into it, someday next week, great that you got it working! – JKRT Dec 14 '18 at 11:49
  • @JKT On second thought, they're probably very similar but not quite the same in some cases. – Indiana Kernick Dec 14 '18 at 11:49
  • @Kerndo73 I will keep my more complicated answer to this question for future reference, I saw your edit – JKRT Dec 14 '18 at 11:57
1

To see what the standard passes are for LLVM you can try to check to subclasses of the Pass interface. As far as I know there is no pass that run the clang specific passes in the LLVM API itself. For that you have to look at clang.

To figure out exactly what passes that you would like to add look at

llvm-as < /dev/null | opt -O3 -disable-output -debug-pass=Arguments  

See Where to find the optimization sequence for clang -OX?

Still, there is some hassle, finding the API you use and so on. The same can be applied for Clang -O3.

What you can do if it is possible for your project is to generate the LLVM IR to file on disk and then compiling the unoptimised LLVM IR with clang separately with the O3 flag.

This is how you can run some passes using the legacy pass manager. Assuming you have an LLVM context.

 module = llvm::make_unique<llvm::Module>("module",context); //Context is your LLVM context.
 functionPassMngr = llvm::make_unique<llvm::legacy::FunctionPassManager>(module.get());
 functionPassMngr->add(llvm::createPromoteMemoryToRegisterPass()); //SSA conversion
 functionPassMngr->add(llvm::createCFGSimplificationPass()); //Dead code elimination
 functionPassMngr->add(llvm::createSROAPass());
 functionPassMngr->add(llvm::createLoopSimplifyCFGPass());
 functionPassMngr->add(llvm::createConstantPropagationPass());
 functionPassMngr->add(llvm::createNewGVNPass());//Global value numbering
 functionPassMngr->add(llvm::createReassociatePass());
 functionPassMngr->add(llvm::createPartiallyInlineLibCallsPass()); //Inline standard calls
 functionPassMngr->add(llvm::createDeadCodeEliminationPass());
 functionPassMngr->add(llvm::createCFGSimplificationPass()); //Cleanup
 functionPassMngr->add(llvm::createInstructionCombiningPass());
 functionPassMngr->add(llvm::createFlattenCFGPass()); //Flatten the control flow graph.

These can then by run by

functionPassMngr->run(getLLVMFunc());

Were getLLVMFunc would return a llvm::Function* that you are currently generating. Note that I use the legacy pass manager here, the reason being that clang uses the legacy pass manager internally.

JKRT
  • 1,179
  • 12
  • 25
  • This isn't really helpful. If you could write an example of how to run some of the passes on a module, that would be helpful. There's PassManagers and PassBuilders and PassManagerBuilders and FunctionPassManagers and legacy::FunctionPassManagers. It's all very confusing and I can't seem to find any helpful resources on the subject. I don't really understand Loop passes either. – Indiana Kernick Dec 12 '18 at 22:35
  • There are no set of standard passes in the LLVM libraries, the clang passes you talk about exists in Clang. The other things are really other LLVM questions – JKRT Dec 13 '18 at 09:19
  • What I mean with that is the specific passes for Clang can't be run directly using the LLVM C++ API alone, you have to figure out these passes from your specific version of clang and then add them manually. The one way of running these passes would be to emit your LLVM IR to file and then run clang. Then you would not have to write a loop or manually listing anything – JKRT Dec 13 '18 at 09:27
  • I have the Clang C++ API installed with LLVM. I might be able to find the LLVM IR optimizer in there somewhere and call it directly rather than through a shell. – Indiana Kernick Dec 13 '18 at 10:10
  • Yes, it is possible. It is just about including the correct libraries. http://llvm.org/docs/WritingAnLLVMPass.html contains some useful information about the passes – JKRT Dec 13 '18 at 11:33
  • To see what the different passes done, please see https://stackoverflow.com/questions/50257850/whats-the-difference-between-modulepassmanagers-functionpassmanager-and-b/50514979#50514979 – JKRT Dec 13 '18 at 16:30