How to write custom intermodular pass in LLVM?

I wrote a standard analysis pass in LLVM, extending the FunctionPass class. Everything seems to make sense.

Now, what I would like to do is write a couple of intermodular passes, that is, passages that allow me to analyze more than one module at a time. The goal of one such passage is to plot the calls of the entire application. The goal of another such passage is that I have an idea for optimization that includes function calls and their parameters.

I know about interprocedural passes in LLVM, extending the ModulePass class, but this allows analysis in only one module.

I know about optimization of connection time (LTO) in LLVM, but (a) I do not quite understand if this is what I want and (b) I did not find examples or documentation on how to actually write an LTO pass.

How can I write an intermodular passage, i.e. pass that has access to all application modules in LLVM?

+9
source share
3 answers

I found one way to achieve my goal: write a simple program that uses llvm::parseBitcodeFile() to read the bitcode in a file and create a Module object that can be passed and analyzed. This is not ideal, because it is not a pass that can be run within the LLVM. Nevertheless, this is a way to achieve my goal of analyzing several modules at once.

For future readers, here's what I did.

Create a simple tool to read in a bitcode file and create a Module

 //ReadBitcode.cpp #include <iostream> #include "llvm/IR/Module.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Bitcode/ReaderWriter.h" using namespace llvm; int main(int argc, char *argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " bitcode_filename" << std::endl; return 1; } StringRef filename = argv[1]; LLVMContext context; ErrorOr<std::unique_ptr<MemoryBuffer>> fileOrErr = MemoryBuffer::getFileOrSTDIN(filename); if (std::error_code ec = fileOrErr.getError()) { std::cerr << "Error opening input file: " + ec.message() << std::endl; return 2; } ErrorOr<llvm::Module *> moduleOrErr = parseBitcodeFile(fileOrErr.get()->getMemBufferRef(), context); if (std::error_code ec = fileOrErr.getError()) { std::cerr << "Error reading Module: " + ec.message() << std::endl; return 3; } Module *m = moduleOrErr.get(); std::cout << "Successfully read Module:" << std::endl; std::cout << " Name: " << m->getName().str() << std::endl; std::cout << " Target triple: " << m->getTargetTriple() << std::endl; for (auto iter1 = m->getFunctionList().begin(); iter1 != m->getFunctionList().end(); iter1++) { Function &f = *iter1; std::cout << " Function: " << f.getName().str() << std::endl; for (auto iter2 = f.getBasicBlockList().begin(); iter2 != f.getBasicBlockList().end(); iter2++) { BasicBlock &bb = *iter2; std::cout << " BasicBlock: " << bb.getName().str() << std::endl; for (auto iter3 = bb.begin(); iter3 != bb.end(); iter3++) { Instruction &i = *iter3; std::cout << " Instruction: " << i.getOpcodeName() << std::endl; } } } return 0; } 

Compile tool

 $ clang++ ReadBitcode.cpp -o reader `llvm-config --cxxflags --libs --ldflags --system-libs` 

Create a bitcode file for analysis

 $ cat foo.c int my_fun(int arg1){ int x = arg1; return x+1; } int main(){ int a = 11; int b = 22; int c = 33; int d = 44; if (a > 10){ b = c; } else { b = my_fun(d); } return b; } $ clang -emit-llvm -o foo.bc -c foo.c 

Run the reader tool on the bitcode

 $ ./reader foo.bc Successfully read Module: Name: foo.bc Target triple: x86_64-pc-linux-gnu Function: my_fun BasicBlock: Instruction: alloca Instruction: alloca Instruction: store Instruction: load Instruction: store Instruction: load Instruction: add Instruction: ret Function: main BasicBlock: Instruction: alloca Instruction: alloca Instruction: alloca Instruction: alloca Instruction: alloca Instruction: store Instruction: store Instruction: store Instruction: store Instruction: store Instruction: load Instruction: icmp Instruction: br BasicBlock: Instruction: load Instruction: store Instruction: br BasicBlock: Instruction: load Instruction: call Instruction: store Instruction: br BasicBlock: Instruction: load Instruction: ret 
+3
source

This can be done using the module pass. Below is my code, and if you need help, you can look here .

bar.c

 int your_fun(int arg2) { int x = arg2; return x+2; } 

Skeleton.cpp

 #include "llvm/Pass.h" #include "llvm/IR/Module.h" #include "llvm/Support/raw_ostream.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" using namespace llvm; namespace { struct SkeletonPass : public ModulePass { static char ID; SkeletonPass() : ModulePass(ID) {} virtual bool runOnModule(Module &M) { for (auto& F : M) { errs() << "\tFunction: " << F.getName() << "\n"; for (auto& BB : F) { errs() << "\t\tBasic Block: " << BB.getName() << "\n"; for (auto& I : BB) { errs() << "\t\t\tInstruction: " << I.getOpcodeName() << "\n"; } } } return false; } }; } char SkeletonPass::ID = 0; // Automatically enable the pass. // http://adriansampson.net/blog/clangpass.html static void registerSkeletonPass(const PassManagerBuilder &, legacy::PassManagerBase &PM) { PM.add(new SkeletonPass()); } static RegisterStandardPasses RegisterMyPass(PassManagerBuilder::EP_ModuleOptimizerEarly, registerSkeletonPass); static RegisterStandardPasses RegisterMyPass1(PassManagerBuilder::EP_EnabledOnOptLevel0, registerSkeletonPass); 

Conclusion:

 | => clang -Xclang -load -Xclang build/skeleton/libSkeletonPass.so foo.c bar.c Module: foo.c! Function: my_fun! Basicblock: entry! Instruction: alloca Instruction: alloca Instruction: store Instruction: load Instruction: store Instruction: load Instruction: add Instruction: ret Function: main! Basicblock: entry! Instruction: alloca Instruction: alloca Instruction: alloca Instruction: alloca Instruction: alloca Instruction: store Instruction: store Instruction: store Instruction: store Instruction: store Instruction: load Instruction: icmp Instruction: br Basicblock: if.then! Instruction: load Instruction: store Instruction: br Basicblock: if.else! Instruction: load Instruction: call Instruction: store Instruction: br Basicblock: if.end! Instruction: load Instruction: ret Module: bar.c! Function: your_fun! Basicblock: entry! Instruction: alloca Instruction: alloca Instruction: store Instruction: load Instruction: store Instruction: load Instruction: add Instruction: ret 

Exit: if you include a link to the header file in bar.c

 Module: foo.c! Function: your_fun! Basicblock: entry! Instruction: alloca Instruction: alloca Instruction: store Instruction: load Instruction: store Instruction: load Instruction: add Instruction: ret Function: my_fun! Basicblock: entry! Instruction: alloca Instruction: alloca Instruction: store Instruction: load Instruction: store Instruction: load Instruction: add Instruction: ret Function: main! Basicblock: entry! Instruction: alloca Instruction: alloca Instruction: alloca Instruction: alloca Instruction: alloca Instruction: store Instruction: store Instruction: store Instruction: store Instruction: store Instruction: load Instruction: icmp Instruction: br Basicblock: if.then! Instruction: load Instruction: store Instruction: br Basicblock: if.else! Instruction: load Instruction: call Instruction: store Instruction: load Instruction: call Instruction: store Instruction: br Basicblock: if.end! Instruction: load Instruction: ret 
+2
source

In LTO, all modules are combined, and you can see the entire IR program in one module.

You need to write a module pass, like any module pass, and add it to the LTO skip list in the populateLTOPassManager function in PassManagerBuilder.cpp. Here is the document for PassManagerBuilder: http://llvm.org/docs/doxygen/html/classllvm_1_1PassManagerBuilder.html

When you do this, your pass will be completed with other LTO passes.

+1
source

All Articles