LLVM IR 三部曲之二 --- 創(chuàng)建IR

前言

上一篇文章我們講了IR的基本語法規(guī)則裹刮,這篇文章我們講一下棘捣,如何手動去生成IR!
生成IR有以下幾種方式:
1诗赌、通過c++直接使用Instructions.h文件中的命令來生成IR
2、使用llvm提供的c接口來生成IR LLVM官方文檔
3炊苫、使用IRBuilder來生成IR IRBuilder官方文檔
三種方式容达,其實這三種方式古涧,最復雜的就在于如何創(chuàng)建IR中的命令,我們查閱2花盐、3中的文檔時會發(fā)現羡滑,LLVM提供分API大部分都是創(chuàng)建Instruction的。
我們分別就1算芯、3方式給出示例(2大家自己看文檔吧)柒昏,教實現一個IR生成器。

1也祠、直接使用Instructions.h中的命令

這種方式可以讓我們清楚的看到IR每一步的實現過程昙楚,方便我們學習如何生成IR。

1)來實現一個sum函數:(代碼中我做了詳細的注解)
//1诈嘿、創(chuàng)建module
Module *createLLVMModule() {
    LLVMContext context ;
    Module *module = new Module("haoyuTestLLVMIR",context);
    { //設置datalayout和三元組
        module ->setDataLayout("e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128");
        module ->setTargetTriple("x86_64-apple-macosx10.14.0");
    }
    //2堪旧、創(chuàng)建函數的類型(指定返回值類型削葱、參數類型及數量、是否有可變參數)
    SmallVector<Type *, 2>FunctionArgs;
    //各種類型都是通過get方法來構造對應的實例
    FunctionArgs.push_back(IntegerType::get(module->getContext(), 32)); //32為整型參數Tina及到vector數組中
    FunctionArgs.push_back(IntegerType::get(module->getContext(), 32));

    IntegerType *returnType = IntegerType::get(module->getContext(), 32);//返回值類型
    FunctionType *funcType = FunctionType::get(returnType, FunctionArgs, /*isVarArg*/ false);

    //3淳梦、創(chuàng)建一個函數
    // LinkageType是globalValue類下的一個鏈接類型析砸,所有全局變量、函數都有一個鏈接類型
    // GlobalValue::ExternalLinkage 表示該函數可以被其他模塊引用爆袍。
    Function *customFunc = Function::Create(funcType, GlobalValue::ExternalLinkage, /* 函數名 = */"haoyuSum", module);
    customFunc->setCallingConv(CallingConv::C); //CallingConv類是一個枚舉類首繁,定義了所有的函數調用公約!文檔上有說明陨囊。

    //4弦疮、存儲參數(獲取參數的引用)
    Function::arg_iterator argsIT = customFunc->arg_begin();
    Value *param1 = argsIT ++;
    param1->setName("a");

    Value *param2 = argsIT ++;
    param2->setName("b");

    //5、創(chuàng)建基本塊(需要制定其所屬的function)
    BasicBlock *entryBlock = BasicBlock::Create(module->getContext(),"entry",customFunc,0);

    //6蜘醋、添加指令胁塞,兩種方式:1、直接使用具體的指令 2压语、使用IRBuilder(需要重點研究)
    //6.1)直接使用指令方式(便于呈現原始接口)
    //指令:alloca指令使用(操作變量必須用到指針啸罢,同OC是一個道理)
    AllocaInst *ptrA = new AllocaInst(/*要創(chuàng)建的內存空間的類型*/ IntegerType::get(module->getContext(), 32),/*地址空間*/module->getDataLayout().getAllocaAddrSpace(),/*名稱*/"a.addr",/*BasicBlock*/entryBlock);
    ptrA->setAlignment(Align(4)); //4字節(jié)對齊的32位元素

    AllocaInst *ptrB = new AllocaInst(/*要創(chuàng)建的內存空間的類型*/ IntegerType::get(module->getContext(), 32),/*地址空間*/module->getDataLayout().getAllocaAddrSpace(),/*名稱*/"a.addr",/*BasicBlock*/entryBlock);
    ptrB->setAlignment(Align(4));

    //7、使用store命令
    StoreInst *st0 = new StoreInst(param1,ptrA,/* 指定是否是靜態(tài)存儲區(qū) */false,entryBlock);
    st0->setAlignment(Align(4));
    StoreInst *st1 = new StoreInst(param2,ptrB,/* 指定是否是靜態(tài)存儲區(qū) */false,entryBlock);
    st1->setAlignment(Align(4));

    //8胎食、使用load命令
    LoadInst *ld0 = new LoadInst(/*類型*/IntegerType::get(context, 32), ptrA, /*名稱*/"",false, entryBlock);
    ld0->setAlignment(Align(4));
    LoadInst *ld1 = new LoadInst(/*類型*/IntegerType::get(context, 32), ptrB, /*名稱*/"",false, entryBlock);
    ld1->setAlignment(Align(4));

    //9扰才、添加操作
    BinaryOperator *addRes = BinaryOperator::Create(Instruction::Add, ld0, ld1, "add", entryBlock);
    //10、設置返回值
    ReturnInst::Create(module->getContext(), addRes, entryBlock);
    
    //11厕怜、校驗生成的IR
    bool Result = llvm::verifyModule(*module);
    if(Result) {
        std::cout << "IR校驗通過" << std::endl;
    }
    module->dump();
    return module;
}

dump一下結果是:

; ModuleID = 'haoyuTestLLVMIR'
source_filename = "haoyuTestLLVMIR"
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.14.0"

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

2)創(chuàng)建一個while函數的IR

C++原始代碼如下:

int whileTest() {
    int a = 10,i = 20;
    while (i < 10) {
        i=i+1;
        a=a*2;
    }
    return 0;
}

我們IR生成代碼:(代碼中同樣做了詳細解釋)

void customWhileIR () {
    LLVMContext context ;
    Module *module = new Module("haoyuWhile",context);
    IRBuilder<> builder(context);
    //創(chuàng)建void函數
    FunctionType *funcType = FunctionType::get(builder.getVoidTy(),false);
    Function *customFunc = Function::Create(funcType, Function::ExternalLinkage, "whileTest",module);
    //創(chuàng)建最外層函數的basic
    BasicBlock *entryBlock = BasicBlock::Create(module->getContext(),"entry",customFunc,0);
    //創(chuàng)建br用的basicblock
    BasicBlock *BB1 = BasicBlock::Create(module->getContext(),"label1",customFunc,0);
    BasicBlock *BB2 = BasicBlock::Create(module->getContext(),"label2",customFunc,0);
    BasicBlock *BB3 = BasicBlock::Create(module->getContext(),"label3",customFunc,0);
    //添加Instruction
    // 1) alloca指令
    AllocaInst *ptr1 = new AllocaInst(/*要創(chuàng)建的內存空間的類型*/ IntegerType::get(module->getContext(), 32),/*地址空間*/module->getDataLayout().getAllocaAddrSpace(),/*名稱*/"1",/*BasicBlock*/entryBlock);
    AllocaInst *ptr2 = new AllocaInst(builder.getInt32Ty(),module->getDataLayout().getAllocaAddrSpace(),"2",entryBlock);
    AllocaInst *ptr3 = new AllocaInst(builder.getInt32Ty(),module->getDataLayout().getAllocaAddrSpace(),"3",entryBlock);
    // 2) store指令
    auto *IntType = builder.getInt32Ty();
    StoreInst *st1 = new StoreInst(ConstantInt::get(IntType,0),ptr1,entryBlock);
    StoreInst *st2 = new StoreInst(ConstantInt::get(IntType,10),ptr2,entryBlock);
    StoreInst *st3 = new StoreInst(ConstantInt::get(IntType, 20),ptr3,false,entryBlock);
    //3)br指令
    BranchInst::Create(BB1,entryBlock);

    //2.1)設置BB1
    //load指令
    LoadInst *load1 = new LoadInst(IntegerType::get(context, 32), ptr3, /*名稱*/"",false, BB1);
    //icmp指令
    ICmpInst *icmp = new ICmpInst(*BB1,ICmpInst::ICMP_SLT,load1,ConstantInt::get(IntType,10) );
    //br
    BranchInst::Create(BB2,BB3,icmp,BB1); //!!!:- 如果使用 BranchInst::Create(BB2,BB3,icmp) 這個構造函數會觸發(fā)ICmp的assert

    //2.2)設置BB2
    //load指令
    LoadInst *load8 = new LoadInst(IntegerType::get(context, 32), ptr3, /*名稱*/"",false, BB2);
    Instruction *add9 = BinaryOperator::Create(Instruction::Add, load8, ConstantInt::get(IntType, 1), "add", BB2);
    StoreInst *stBB2 = new StoreInst(add9,ptr3,BB2);
    LoadInst *load10 = new LoadInst(IntegerType::get(context, 32), ptr2, /*名稱*/"",false, BB2);
    Instruction *mul11 = BinaryOperator::Create(Instruction::Mul, load10, ConstantInt::get(IntType, 2), "MUL", BB2);
    StoreInst *stBB3 = new StoreInst(mul11, ptr2,BB2);
    BranchInst::Create(BB1,entryBlock);
//
//    //2.3)設置BB3
    ReturnInst::Create(context,ConstantInt::get(IntType, 0),BB3);
    
    module->dump();
    
    
}

最終生產能的IR如下:

; ModuleID = 'haoyuWhile'
source_filename = "haoyuWhile"

define void @whileTest() {
entry:
  %"1" = alloca i32
  %"2" = alloca i32
  %"3" = alloca i32
  store i32 0, i32* %"1"
  store i32 10, i32* %"2"
  store i32 20, i32* %"3"
  br label %label1
  br label %label1

label1:                                           ; preds = %entry, %entry
  %0 = load i32, i32* %"3"
  %1 = icmp slt i32 %0, 10
  br i1 %1, label %label2, label %label3

label2:                                           ; preds = %label1
  %2 = load i32, i32* %"3"
  %add = add i32 %2, 1
  store i32 %add, i32* %"3"
  %3 = load i32, i32* %"2"
  %MUL = mul i32 %3, 2
  store i32 %MUL, i32* %"2"

label3:                                           ; preds = %label1
  ret i32 0
}

上面的結果是跟我們之前一篇文章中的while生成IR是一樣的衩匣,可以參考下。

另外在編寫IR生成代碼時酣倾,可能遇到很多命令不知道該如何使用舵揭,一個Tip是:去LLVM工程中搜索相關的示例谤专,看一下官方是如何使用的

2躁锡、使用IRBuilder來生成IR

IRBuilder是LLVM中專門提供用來生產Instruction命令的,對比上一種方式置侍,使用IRBuilder會更加方便映之,它提供了更加友好的封裝,省去了我們直接一個個手動調用原始命令的繁瑣和枯燥蜡坊!
下面是一段生成代碼杠输,同樣在代碼中給出詳細注釋:

void createIRWithIRBuilder() {
    LLVMContext Context;
    Module *mod = new Module("sum.ll", Context);
    
    //1、創(chuàng)建IRBuilder
    IRBuilder<> builder(Context);
    //2秕衙、創(chuàng)建main函數
    FunctionType *ft = FunctionType::get(builder.getInt32Ty(),false);
    Function *mainfunc = Function::Create(ft, Function::ExternalLinkage, "main", mod);
    //到此為止之創(chuàng)建了main函數蠢甲,但是函數體內的包含的Instruction沒有添加,因此需要添加据忘。
    
    //3鹦牛、創(chuàng)建基本塊(這個基本塊是空的無內容)
    BasicBlock *entry = BasicBlock::Create(Context,"entrypoint",mainfunc);
    
    //4搞糕、設置插入點:插入點設置成相應BasicBlock,<#后面用builder創(chuàng)建的指令都會追加到這個BasicBlock里了#>
    //!!!: - 理解:上面的方式是通過直接往BasicBloock中添加Instruction方式來構造基本的basicBlock曼追,這里借助IRBuilder方式窍仰,往basicBlock中添加命令。
    builder.SetInsertPoint(entry);
    
    //5礼殊、添加全局字符串(IR中字符串全部為全局變量驹吮,使用數據序列來表示,每個元素是一個char類型)
    Value *helloWorld = builder.CreateGlobalStringPtr("hello world!\n");
    //6晶伦、創(chuàng)建put函數
    //1)指定函數參數類型碟狞,裝在一個數組中`
    std::vector<Type*> putsargs;
    putsargs.push_back(builder.getInt8Ty()->getPointerTo());
    ArrayRef<Type*>  argsRef(putsargs);
    //2)指定函數返回值類型
    FunctionType *putsType = FunctionType::get(builder.getInt32Ty(),argsRef,false);
    //3)創(chuàng)建“函數調用”,而不是創(chuàng)建函數
    FunctionCallee putsFunc = mod->getOrInsertFunction("puts", putsType);
    
    //7婚陪、調用函數(<#理解:通過createXXX創(chuàng)建出來的所有指令都在SetInsertPoint后面#>)
    builder.CreateCall(putsFunc,helloWorld); //這是創(chuàng)建方法的指令
    
    //8篷就、創(chuàng)建返回ret指令
    ConstantInt *zero = ConstantInt::get(IntegerType::getInt32Ty(Context), 0);
    builder.CreateRet(zero);
    
    //9、驗證近忙。這一步待定竭业!
    llvm::VerifierAnalysis::Result Res;
    Res.IRBroken = llvm::verifyModule(*mod, &dbgs(), &Res.DebugInfoBroken);

    mod->dump();
 
}

生成IR如下:

; ModuleID = 'sum.ll'
source_filename = "sum.ll"

@0 = private unnamed_addr constant [14 x i8] c"hello world!\0A\00", align 1

define i32 @main() {
entrypoint:
  %0 = call i32 @puts(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @0, i32 0, i32 0))
  ret i32 0
}

declare i32 @puts(i8* %0)

綜上示例,例舉了生成IR的幾種方式及舍,以及一些常用命令的使用未辆,更加復雜的IR編寫,需要在開發(fā)時查閱上面給出的文檔锯玛。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末咐柜,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子攘残,更是在濱河造成了極大的恐慌拙友,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件歼郭,死亡現場離奇詭異遗契,居然都是意外死亡,警方通過查閱死者的電腦和手機病曾,發(fā)現死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門牍蜂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人泰涂,你說我怎么就攤上這事鲫竞。” “怎么了逼蒙?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵从绘,是天一觀的道長。 經常有香客問我,道長僵井,這世上最難降的妖魔是什么赁还? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮驹沿,結果婚禮上艘策,老公的妹妹穿的比我還像新娘。我一直安慰自己渊季,他們只是感情好朋蔫,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著却汉,像睡著了一般驯妄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上合砂,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天青扔,我揣著相機與錄音,去河邊找鬼翩伪。 笑死微猖,一個胖子當著我的面吹牛,可吹牛的內容都是我干的缘屹。 我是一名探鬼主播凛剥,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼轻姿!你這毒婦竟也來了犁珠?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤互亮,失蹤者是張志新(化名)和其女友劉穎犁享,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體豹休,經...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡炊昆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了慕爬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窑眯。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖医窿,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情炊林,我是刑警寧澤姥卢,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響独榴,放射性物質發(fā)生泄漏僧叉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一棺榔、第九天 我趴在偏房一處隱蔽的房頂上張望瓶堕。 院中可真熱鬧,春花似錦症歇、人聲如沸郎笆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宛蚓。三九已至,卻和暖如春设塔,著一層夾襖步出監(jiān)牢的瞬間凄吏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工闰蛔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留痕钢,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓序六,卻偏偏與公主長得像盖喷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子难咕,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內容