Learning LLVM:Genreate LLVM IR by IRBuilder

llvm生成ir。

LLVM version 11.0.0

Clang version 11.0.0

Target: x86_64-unknown-linux-gnu

Linux: ubuntu 20.04

Create LLVM IR By IRBuilder

LLVM version 11.0.0

Clang version 11.0.0

Target: x86_64-unknown-linux-gnu

Linux: ubuntu 20.04

写在开头

为了实现插桩,所以有了如何手动生成LLVM IR呢?

  • C++使用Instructions.h文件中的命令生成IR
  • 使用llvm提供的C接口生成IR
  • 使用IRBuilder生成IR

首先,回忆一下LLVM的基本组成吧,ModuleFunctionBasicBlockInstruction以及它们间的关系是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
	graph TD
	A(module)-->B1(function)
	A-->B2(function)
	A-->B3(more function...)
	B1-->C1(basic block)
	B1-->C2(more basic block...)
	B2-->D1(basic block)
	B2-->D2(more basic block...)
	C1-->E1(instruction)
	C1-->E2(more instruction...)
	D2-->F1(instruction)
	D2-->F2(more instruction...)

是的,一个module中包含多个functionfunction中又包含多个basic blockbasic block中又包含多条`instruction'.

preview

介绍一下一个简单的手动生成Demo流程,会使用到:

  1. 创建一个LLVMContext
  2. LLVMContext中创建一个Module
  3. Module中创建添加Function
    1. 一个Function是有函数类型FunctionType
    2. FunctionType可以解释为返回值类型returnType+参数argument+不定参数false/true
    3. 以及函数签名
  4. Function中创建Basic Block
    1. 一个Function有且只有一个labal为entry的基本块
    2. 所以,先创建一个label为entry的基本块
    3. 接着,创建更多需要的基本块
  5. Basic Block中创建Instruction
    1. 指令在Basic Block中,通过IRBuilder类创建

起飞

以一下main.cpp作为手动生成LLVM IR的代码例子.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int max(int a, int b){
	if(a > b)
		return a;
	else
		return b;
}

int main(){
	a = 10;
	b = 20;
	return max(a, b);
}

使用到的一些类

类名解释
LLVMContext上下文类,保存上下文符号
Module模块类,一般以文件一个模块,有函数列表和全局变量表
Function函数类
BasicBlock基本块类
IRBuilderIR建造类
Value各类型值的基类,函数、常量、变量、表达式等都可以转换为Value类型
Type类型类,各种内部类型或用户类型,Value可通过getType()取类型
Constant常量类,能够生产各种常量

step 1 创建一Mudule

1
2
3
4
static LLVMContext MyGlobalContext;
LLVMContext &context = MyGlobalContext;
// 创建一个module
Module *module = new Module("testing", context);

step 2 声明一个Function

在这里以int max(int a, int b)为例.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
SmallVector<Type *, 2> funcMaxArgs; // 函数的2个参数
funcMaxArgs.push_back(Type::getInt32Ty(context));
funcMaxArgs.push_back(Type::getInt32Ty(context));
Type *returnType = Type::getInt32Ty(context); // 返回值类型
unctionType *funcMaxType = FunctionType::get(returnType, funcMaxArgs, /*isVarArg*/ false);
// viod : FunctionType *funcType = FunctionType::get(builder.getVoidTy(),false);
// 在module中创建一个Function
// LinkageType是globalValue类下的一个链接类型,所有全局变量、函数都有一个链接类型
// GlobalValue::ExternalLinkage 表示该函数可以被其他模块引用。
Function *max_fun = Function::Create(funcMaxType, Function::ExternalLinkage, Twine("max"), module);
// 给两个参数名字
Function::arg_iterator argsItr = max_fun->arg_begin();
Value *arg_a = argsItr++;
arg_a->setName("a");
Value *arg_b = argsItr;
arg_b->setName("b");

step 3 创建Basic Block

label名字为entry的基本块为Function入口,一个Function有且只有一个labe为entry的基本块,并且不能作为跳转指令的目标,也不能存在phi指令.

在这里每一个 Basic Block分别使用IRBuilder生成指令,当然也可以不这么做.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
 // 创建函数 entry Basic Block
BasicBlock *entryMaxFunction = BasicBlock::Create(context, "entry", max_fun);
IRBuilder<> builderMaxFunc(entryMaxFunction);  // 创建一个IRBuilder用来创建指令
Value *cmpValue = builderMaxFunc.CreateICmpSGT(arg_a, arg_b); // 创建一条 有符号cmp命令
// if_then Basic Block 
BasicBlock *ifThen = BasicBlock::Create(context, "if_then", max_fun); 
IRBuilder<> builderIfThen(ifThen);
builderIfThen.CreateRet(arg_a); // ret a -->ret i32 %a
// ifElse Basic Block
BasicBlock *ifElse = BasicBlock::Create(context, "if_else", max_fun);
IRBuilder<> builderIfElse(ifElse);
builderIfElse.CreateRet(arg_b);
// 生成 br跳转指令
builderMaxFunc.CreateCondBr(cmpValue, ifThen, ifElse);

step 4 使用JIT引擎

当然,这里是为了执行生成的机器码,如果仅仅是想生成LLVM IR到step3就够了(PS记得释放new的内存).

1
2
3
4
5
6
7
8
9
InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();
InitializeNativeTargetAsmParser();
ExecutionEngine *ee = EngineBuilder(std::unique_ptr<Module>(module)).setEngineKind(EngineKind::JIT).create();
void *mainAddr = ee->getPointerToFunction(mainFunc);
typedef int (*FuncType)();
FuncType mainFuncTy = (FuncType)mainAddr;
ee->finalizeObject();
cout << mainFuncTy() << endl;

输出到文件中:

1
2
3
4
5
6
std::error_code ErrInfo;
raw_ostream *out = new raw_fd_ostream("main.bc", ErrInfo);
WriteBitcodeToFile(*module, *out);
out->flush();
delete out;
delete module;

最后

完整main.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/ExecutionEngine/MCJIT.h"
#include "llvm/Support/TargetSelect.h"
#include <iostream>

#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;
using namespace std;
int main()
{
    static LLVMContext MyGlobalContext;
    LLVMContext &context = MyGlobalContext;
    // 创建一个module
    Module *module = new Module("testing", context);
    IRBuilder<> builder(context);

    ///接下来声明一个函数
    SmallVector<Type *, 2> funcMaxArgs; // 函数的2个参数
    funcMaxArgs.push_back(Type::getInt32Ty(context));
    funcMaxArgs.push_back(Type::getInt32Ty(context));
    Type *returnType = Type::getInt32Ty(context); // 返回类型
    FunctionType *funcMaxType = FunctionType::get(returnType, funcMaxArgs, /*isVarArg*/ false);
    // viod : FunctionType *funcType = FunctionType::get(builder.getVoidTy(),false);
    Function *max_fun = Function::Create(funcMaxType, Function::ExternalLinkage, Twine("max"), module);

    Function::arg_iterator argsItr = max_fun->arg_begin();
    Value *arg_a = argsItr++;
    arg_a->setName("a");
    Value *arg_b = argsItr;
    arg_b->setName("b");

    // 创建函数 entry Basic Block
    BasicBlock *entryMaxFunction = BasicBlock::Create(context, "entry", max_fun);
    IRBuilder<> builderMaxFunc(entryMaxFunction);                 // 创建一个IRBuilder用来创建指令
    Value *cmpValue = builderMaxFunc.CreateICmpSGT(arg_a, arg_b); // 创建一条 有符号cmp命令
    // if_then Basic Block
    BasicBlock *ifThen = BasicBlock::Create(context, "if_then", max_fun);
    IRBuilder<> builderIfThen(ifThen);
    builderIfThen.CreateRet(arg_a); // ret a -->ret i32 %a
    // ifElse Basic Block
    BasicBlock *ifElse = BasicBlock::Create(context, "if_else", max_fun);
    IRBuilder<> builderIfElse(ifElse);
    builderIfElse.CreateRet(arg_b);
    // 生成 br跳转指令
    builderMaxFunc.CreateCondBr(cmpValue, ifThen, ifElse);
    // *******************************************************************

    FunctionType *mainType = FunctionType::get(builder.getInt32Ty(), false); //TypeBuilder<int(), false>::get(context);
    Function *mainFunc = Function::Create(mainType, Function::ExternalLinkage, "main", module);

    BasicBlock *entryMain = BasicBlock::Create(context, "entry", mainFunc);
    IRBuilder<> builderMainFunc(entryMain);

    Value *aValue = ConstantInt::get(Type::getInt32Ty(context), -10);
    Value *bValue = ConstantInt::get(Type::getInt32Ty(context), 20);

    vector<Value *> argsMax;
    argsMax.push_back(aValue);
    argsMax.push_back(bValue);
    ArrayRef<Value *> argsRef(argsMax);

    Value *ret = builderMainFunc.CreateCall(max_fun, argsRef);
    builderMainFunc.CreateRet(ret);
    //module->dump();

    InitializeNativeTarget();
    InitializeNativeTargetAsmPrinter();
    InitializeNativeTargetAsmParser();
    ExecutionEngine *ee = EngineBuilder(std::unique_ptr<Module>(module)).setEngineKind(EngineKind::JIT).create();
    void *mainAddr = ee->getPointerToFunction(mainFunc);
    typedef int (*FuncType)();
    FuncType mainFuncTy = (FuncType)mainAddr;
    ee->finalizeObject();
    cout << mainFuncTy() << endl;

    std::error_code ErrInfo;
    raw_ostream *out = new raw_fd_ostream("main.bc", ErrInfo);
    WriteBitcodeToFile(*module, *out);
    out->flush();
    delete out;
    delete module;
    return 0;
}

使用命令:

1
$ clang++ -O3 main.cpp -o main `llvm-config --cflags --ldflags` `llvm-config --libs` `llvm-config --system-libs`

使用命令:

1
2
$ ./main
$ llvm-dis main.bc -o main.ll  # .bc to .ll

最后,得到的main.ll.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
; ModuleID = 'main.bc'
source_filename = "testing"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

define i32 @max(i32 %a, i32 %b) {
entry:
  %0 = icmp sgt i32 %a, %b
  br i1 %0, label %if_then, label %if_else

if_then:                                          ; preds = %entry
  ret i32 %a

if_else:                                          ; preds = %entry
  ret i32 %b
}

define i32 @main() {
entry:
  %0 = call i32 @max(i32 -10, i32 20)
  ret i32 %0
}
; Function Attrs: nounwind
declare void @llvm.stackprotector(i8*, i8**) #0
attributes #0 = { nounwind }

额外的

还可以通过Instructions.h中提供的方法创建,参考于 第二篇博客.

Instructions.h中有很多类,比如AllocaInstLoadInstStoreInst等,具体可以查看参考,都是以IR名称+Inst为类名.

接下来,应该会去看Instructions.hIR的分类,如一元指令二元指令内存操作指令等,以及实现一个Pass 虽然已经写过HelloDemo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Verifier.h"
#include <iostream>

using namespace llvm;
using namespace std;

void createModule() {
    LLVMContext llvmContext;
    Module *module = new Module("testInst", llvmContext);
    {
        module->setDataLayout( "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128");
        module->setTargetTriple("x86_64-pc-linux-gnu");
    }
    // 创建函数类型(返回值类型+参数类型+是否有不定参数)
    SmallVector<Type*, 2> FuncArgs;
    FuncArgs.push_back(IntegerType::get(module->getContext(), 32));
    FuncArgs.push_back(IntegerType::get(module->getContext(), 32));
    IntegerType *returnType = IntegerType::get(module->getContext(), 32);
    FunctionType *funcType = FunctionType::get(returnType, FuncArgs, false);   
    
    // 创建函数
    Function * sumFunction = Function::Create(funcType, GlobalValue::ExternalLinkage, "sum", module);
    Function::arg_iterator argsIt = sumFunction->arg_begin();
    Value *param_1 = argsIt++;
    param_1->setName("a");
    Value *param_2 = argsIt;
    param_2->setName("b");
    ///////////////////////

    // 创建基本块
    BasicBlock *entryBlock = BasicBlock::Create(module->getContext(), "entry", sumFunction, 0);
    AllocaInst *ptrA = new AllocaInst(
        IntegerType::get(llvmContext,32), // 创建的内存空间类型
        module->getDataLayout().getAllocaAddrSpace(), // 对应平台地址空间
        "a.ddr",  // 变量名称
        entryBlock // 所属基本块
    );
    ptrA->setAlignment(Align(4)); // 4字节对齐

    AllocaInst *ptrB = new AllocaInst(
        IntegerType::get(llvmContext,32), // 创建的内存空间类型
        module->getDataLayout().getAllocaAddrSpace(), // 对应平台地址空间
        "b.ddr",  // 变量名称
        entryBlock // 所属基本块
    );
    ptrB->setAlignment(Align(4)); // 4字节对齐

    // Store IR
    StoreInst *st_0 = new StoreInst(param_1, ptrA, /*是否从静态存储区*/ false, entryBlock);
    st_0->setAlignment(Align(4));
    StoreInst *st_1 = new StoreInst(param_2, ptrB, /*是否从静态存储区*/ false, entryBlock);
    st_1->setAlignment(Align(4));
    
    // load IR
    LoadInst *ld_0 = new LoadInst(IntegerType::get(llvmContext, 32),ptrA, "", entryBlock);
    ld_0->setAlignment(Align(4));

    LoadInst *ld_1 = new LoadInst(IntegerType::get(llvmContext, 32),ptrB, "", entryBlock);
    ld_1->setAlignment(Align(4));

    // Add IR
    BinaryOperator *add = BinaryOperator::Create(Instruction::Add, ld_0, ld_1,"add", entryBlock);

    ReturnInst::Create(llvmContext, add, entryBlock);
    bool Result = verifyModule(*module);
    if(Result) {
        std::cout << "IR校验通过" << std::endl;
    }
    //module->dump();
    module->print(llvm::outs(), nullptr);


    delete module;
}

int main() {
    createModule();
    return 0;
}

结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
; ModuleID = 'testInst'
source_filename = "testInst"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

define i32 @sum(i32 %a, i32 %b) {
entry:
  %a.ddr = alloca i32, align 4
  %b.ddr = alloca i32, align 4
  store i32 %a, i32* %a.ddr, align 4
  store i32 %b, i32* %b.ddr, align 4
  %0 = load i32, i32* %a.ddr, align 4
  %1 = load i32, i32* %b.ddr, align 4
  %add = add i32 %0, %1
  ret i32 %add
}

暂时先整理这些,今后追补上.

REF

给了极大的帮助:IR API(一)——使用LLVM提供的C接口和IRBuilder来生成LLVM IR(if 和 while 语句)

以及参考了:LLVM IR 三部曲之二 — 创建IR

Instructions.h

Licensed under CC BY-NC-SA 4.0
哦吼是一首歌。
Built with Hugo
Theme Stack designed by Jimmy