Как написать пользовательский межмодульный проход в LLVM?
Я написал стандартный проход анализа в LLVM, расширив класс FunctionPass. Кажется, все имеет смысл.
Теперь я хотел бы написать пару межмодульных проходов, то есть проходов, которые позволяют мне анализировать более одного модуля за раз. Целью одного такого прохода является построение графа вызовов всего приложения. Цель другого такого прохода состоит в том, что у меня есть идея для оптимизации, включающей вызовы функций и их параметры.
Я знаю о межпроцедурных проходах в LLVM через расширение класса ModulePass, но это позволяет проводить анализ только в одном модуле.
Я знаю об оптимизации времени соединения (LTO) в LLVM, но (а) мне не совсем ясно, хочу ли я этого, и (б) я не нашел примеров или документации о том, как на самом деле написать пропуск LTO.
Как я могу написать межмодульный проход, то есть проход, который имеет доступ ко всем модулям в приложении, в LLVM?
3 ответа
Я нашел один способ достичь своей цели: написать простую программу, которая использует llvm::parseBitcodeFile()
читать в файле битового кода и создать Module
объект, который может быть пройден и проанализирован. Это не идеально, потому что это не Pass, который может быть запущен в рамках LLVM. Тем не менее, это способ достижения моей цели - анализ нескольких модулей одновременно.
Для будущих читателей вот что я сделал.
Создайте простой инструмент для чтения в файле битового кода и создайте 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;
}
Скомпилируйте инструмент
$ clang++ ReadBitcode.cpp -o reader `llvm-config --cxxflags --libs --ldflags --system-libs`
Создайте файл битового кода для анализа
$ 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
Запустить reader
инструмент на битовый код
$ ./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
Это можно сделать с помощью модуля pass. Ниже приведен мой код, и если вам нужна помощь в его запуске, вы можете посмотреть здесь.
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);
Выход:
| => 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
Вывод: если вы включите заголовочный файл, ссылающийся на 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
В LTO все модули объединены, и вы можете видеть всю программу IR в одном модуле.
Вам нужно написать проход модуля, как и любой проход модуля, и добавить его в список проходов LTO в функции populateLTOPassManager в PassManagerBuilder.cpp. Вот документация для PassManagerBuilder: http://llvm.org/docs/doxygen/html/classllvm_1_1PassManagerBuilder.html
Когда вы сделаете это, ваш проход будет выполнен с другими проходами LTO.