mac上使用Xcode编写LLVM的简单pass

3,218 阅读2分钟

1、环境

macOS 10.15.7

Xcode 11.3

LLVM 8.0.0(下载地址:releases.llvm.org/download.ht…

2、本地下载LLVM源码,生成Xcode项目

(1)下载llvm8.0.0

访问 releases.llvm.org/download.ht… ,找到8.0.0的源码:

我这里是用Xcode11.3,所以下载LLVM8.0.0,如果你用Xcode12可以下载LLVM10.0.0,不同的Xcode版本对应不同的LLVM版本。可以在这里(en.wikipedia.org/wiki/Xcode)查看对应关系。

(2)编译生成Xcode工程

已下载的LLVM8.0.0文件,解压到你的任意路径下,在同级目录下创建一个 xcode_build_8.0 文件夹,并打开终端 cd 到xcode_build_8.0 路径下,并使用cmake命令生成Xcode工程:

cmake -G Xcode CMAKE_BUILD_TYPE="Debug" ../llvm

双击 LLVM.xcodeproj 文件打开工程,按照下图选择:

找到 ALL_BUILD ,并执行编译(快捷键:comd+B),编译整个LLVM工程,需要较长的时间:

编译完成后,可在Debug路径下看到编译后的文件:

3、编写一个pass

(1)在LLVM工程中找到测试用例Pass项目Hello:

在Hello.cpp文件代码如下:

#include "llvm/IR/IRBuilder.h"#include "llvm/IR/IntrinsicInst.h#include "llvm/ADT/Statistic.h"#include "llvm/IR/Function.h"#include "llvm/Pass.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/LoopPass.h"#include "llvm/PassAnalysisSupport.h"
using namespace llvm;#define DEBUG_TYPE "hello"STATISTIC(HelloCounter, "Counts number of functions greeted");
//这个pass的功能:当 c 文件里没有 main 方法的时候,自动生成 main并且在main中调用当前 module 里的所有函数。

//思路是,先看看有没有 main,有的话就终止操作,没有的话就创造一个 main,之后创建BB、创建Inst、完成调用。
namespace {    class SimpleInvoker : public ModulePass {    public:        static char ID;        SimpleInvoker() : ModulePass(ID) {}        bool runOnModule(Module &M) override{               Function *F = M.getFunction("main");               if (F) {                   errs() << "Main Function Found! So return.\n";                   return true;               }               errs() << "Main Function Not Found! So create.\n";               FunctionType *FT = FunctionType::get(Type::getInt32Ty(M.getContext()), false);               F = Function::Create(FT, GlobalValue::LinkageTypes::ExternalLinkage, "main", &M);               BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "EntryBlock", F);               IRBuilder<> IRB(EntryBB);               for (Function &FF:M) {                   if(FF.getName() == "main")                       continue;                   if(FF.empty())                       continue;                   IRB.CreateCall(&FF);               }               IRB.CreateRet(ConstantInt::get(Type::getInt32Ty(M.getContext()), 0));               return true;           }
        virtual StringRef getPassName() const override {            return "Simple Invoker";        }    };    Pass *createSimpleInvoker() {        return new SimpleInvoker();    }    }
char SimpleInvoker::ID = 0;static RegisterPass<SimpleInvoker> Y("simpleInvoker", "SimpleInvoker Pass");

(2)Xcode选中LLVMHello 并编译,生成 LLVMHello.dylib 文件:

这就是我们编写的pass了。这个pass的功能是当 c 文件里没有 main 方法的时候,自动生成 main并且在main中调用当前 module 里的所有函数。

4、测试pass功能

(1)创建test.c文件

这个文件中没有main函数,只用f1()和f2()两个方法,代码如下:

void f1(){
	printf("f1--printf\n");
}
 
int f2(){
	printf("f2---printf\n");
	return 0;
}

(2)用 Xcode 中的 clang 编译 test.c 生成 test.bc 中间码文件

/path/to/clang -emit-llvm -c ./test.c -o ./test.bc

(3)使用 opt 加载 LLVMHello.dylib 处理 test.bc 中间码文件,得到 test1.bc 文件

/path/to/xcode_build_8.0/Debug/bin/opt -load /path/to/xcode_build_8.0/Debug/lib/LLVMHello.dylib -simpleInvoker ./test.bc -o ./test1.bc

这个 opt 在是你LLVM工程编译后生成的一个可执行文件。

对test.bc的中间码做了处理,遍历是否包含main函数,不过不包含则插入一个main函数并在其中调用f1()和f2()这两个方法,生成test1.bc 文件。

(4)用 lli 执行 test1.bc 文件

/path/to/xcode_build_8.0/Debug/bin/lli ./test1.bc

这个 lli 在是你LLVM工程编译后生成的一个可执行文件。

终端输出:

f1--printf
f2---printf

说明正确添加了main()函数,并成功调用了f1()和f2()这两个方法。

本文部分内容参考自:

www.leadroyal.cn/?p=719