LLVM -Learn-Work1
@2022-04-27 16:33:55
@sizaif
Work1:将其浮点数加法改为减法。(指令操作)
//main.c
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
puts("plz input two numbers");
size_t bufsize = 100;
char* buf = malloc(bufsize);
getline(&buf, &bufsize, stdin);
double f1 = atof(buf);
getline(&buf, &bufsize, stdin);
double f2 = atof(buf);
printf("%.2f + %.2f = %.2f \n", f1, f2, f1+f2);
free(buf);
return 0;
}
一) 思路
-
使用
Clang
生成IR文件clang -emit-llvm -S main.c -o main.ll
-
从
IR
文件中读取到Module类
中 -
拿到
main
函数 -
找到
fadd
指令,构造新的fsub
指令,添加到当前fadd
指令后,并删除fadd
指令-
法一:
通过main函数拿到所有的
BasicBlockList()
遍历所有
basicblock
块的所有指令(Instruction
) 查找fadd
指令 -
法二:
使用指令迭代器(
inst_iterator
),遍历所有main
函数的指令找到fadd
-
-
验证修改后的
Module
类 -
存入到新的
IR
文件中,执行lli aftermain.bc
二) 执行
2.1) 从IR
文件中读取到Module类
中
/**
* 2022-04-26 03:20:02
* 从IR中获取Module类
* */
M = parseIRFile(StringRef(argv[1]),Err,context);
if(M){
std::cout<<"M is not null"<<std::endl;
// M->print(llvm::outs(),nullptr);
}
else{
std::cout<<"M is null"<<std::endl;
Err.print(argv[0],errs());
}
2.2) 拿到main
函数
/**
* 2022-04-26 03:25:52
* 获取main函数
* */
auto main_fn = M->getFunction(StringRef("main"));
2.3) 找到fadd
指令,构造fsub
替换掉
法一:
/**
* 2022-04-26 16:56:34
* 方法一: 通过获得basicblock块list 进而获得指令
* 遍历查询inst指令,找到fadd
* %19 = load double, double* %4, align 8
* %20 = load double, double* %5, align 8
* %21 = fadd double %19, %20
* */
auto bkl = &main_fn->getBasicBlockList();
for (auto it = bkl->begin(); it != bkl->end();it++)
{
auto instl = &it->getInstList();
for(auto inst = instl->begin(); inst != instl->end();inst++){
if(inst->isBinaryOp() && inst->getOpcode() == Instruction::FAdd ){
new_inst = BinaryOperator::CreateFSub(
inst->getOperand(0),
inst->getOperand(1),
"");
old_inst = &*inst;
}
}
}
法二:
/**
* 2022-04-27 15:52:04
* 方法二: 指令迭代器遍历查找
* */
Instruction *old_inst,*new_inst;
for (inst_iterator I = inst_begin(*main_fn), E = inst_end(*main_fn); I != E; ++I){
if(I->isBinaryOp() && I->getOpcode()==Instruction::FAdd){
new_inst = BinaryOperator::CreateFSub(
I->getOperand(0),
I->getOperand(1),
"");
old_inst = &*I;
}
}
/**
* 2022-05-02 11:49:30
* #include "llvm/Transforms/Utils/BasicBlockUtils.h"
* 指令替换
* */
outs() <<" new op: "<< *new_inst<<" \n";
outs() <<" old op: "<< *old_inst << " \n";
ReplaceInstWithInst(old_inst,new_inst);
2.4) 验证修改后的Module
类
/**
* #include "llvm/IR/Verifier.h"
* 2022-05-02 11:43:34
* 修改完Module后进行验证Module的完整性
* verifyModule(*M,&errs()): true if the module is broken.
* 即如果验证正确返回一个false, 若存在问题则返回true
* */
if(!llvm::verifyModule(*M,&errs())){
outs()<<"verify is ok \n";
// M->print(outs(),nullptr);
}
2.5) 存入到新的IR
文件中,执行lli aftermain.ll
/**
* 将修改后的IR 存储到文件中, 执行
* */
std::error_code EC;
llvm::raw_fd_ostream OS("aftermain.bc", EC,llvm::sys::fs::OpenFlags());
llvm::WriteBitcodeToFile(*M,OS,true);
OS.flush();
2.6) 全部源码
/**
* 2022-04-26 01:02:37
* sizaif
*
* clang++ $(llvm-config --cxxflags --ldflags --system-libs --libs) -o main main.cpp
* ./main main.ll
* lli aftermain.bc
* */
#include <llvm/IR/IRBuilder.h>
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/SourceMgr.h"
#include <llvm/Support/raw_os_ostream.h>
#include "llvm/Support/WithColor.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include <iostream>
#include <string>
#include <fstream>
using namespace llvm;
int main(int argc,char **argv){
/**
* #include <llvm/IR/IRBuilder.h>
* #include "llvm/IR/LLVMContext.h"
* #include "llvm/IR/Module.h"
* #include "llvm/Support/SourceMgr.h"
* 初始化
* */
LLVMContext context;
IRBuilder<> builder(context);
std::unique_ptr<Module> M;
SMDiagnostic Err;
/**
* 2022-04-26 03:20:02
* 从IR中获取Module类
* */
M = parseIRFile(StringRef(argv[1]),Err,context);
if(M){
std::cout<<"M is not null"<<std::endl;
// M->print(llvm::outs(),nullptr);
}
else{
std::cout<<"M is null"<<std::endl;
Err.print(argv[0],errs());
}
/**
* 2022-04-26 03:25:52
* 获取main函数
* */
auto main_fn = M->getFunction("main");
/**
* #include "llvm/IR/InstIterator.h"
* 2022-04-27 15:52:04
* 方法二: 指令迭代器遍历查找,找到fadd指令
*
* */
Instruction *old_inst,*new_inst;
// for (inst_iterator I = inst_begin(*main_fn), E = inst_end(*main_fn); I != E; ++I){
// if(I->isBinaryOp() && I->getOpcode()==Instruction::FAdd){
// new_inst = BinaryOperator::CreateFSub(
// I->getOperand(0),
// I->getOperand(1),
// "");
// old_inst = &*I;
// }
// }
/**
*
* 2022-04-26 16:56:34
* 方法一: 通过获得basicblock块list 进而获得指令
* 遍历查询inst指令,找到fadd
* %19 = load double, double* %4, align 8
* %20 = load double, double* %5, align 8
* %21 = fadd double %19, %20
* */
auto bkl = &main_fn->getBasicBlockList();
for (auto it = bkl->begin(); it != bkl->end();it++)
{
auto instl = &it->getInstList();
for(auto inst = instl->begin(); inst != instl->end();inst++){
if(inst->isBinaryOp() && inst->getOpcode() == Instruction::FAdd ){
new_inst = BinaryOperator::CreateFSub(
inst->getOperand(0),
inst->getOperand(1),
"");
old_inst = &*inst;
}
}
}
/**
* #include "llvm/Transforms/Utils/BasicBlockUtils.h"
* 2022-05-02 11:49:30
* 指令替换
* */
outs() <<" new op: "<< *new_inst<<" \n";
outs() <<" old op: "<< *old_inst << " \n";
outs()<<"before replace InstructionCount(): " <<main_fn->getInstructionCount()<<"\n";
ReplaceInstWithInst(old_inst,new_inst);
outs()<<"after replace InstructionCount(): " <<main_fn->getInstructionCount()<<"\n";
/**
* #include "llvm/IR/Verifier.h"
* 2022-05-02 11:43:34
* 修改完Module后进行验证Module的完整性
* verifyModule(*M,&errs()): true if the module is broken.
* */
if(!llvm::verifyModule(*M,&errs())){
outs()<<"verify is ok \n";
M->print(outs(),nullptr);
}
/**
* #include "llvm/Bitcode/BitcodeWriter.h"
* 2022-05-02 12:11:57
* 将修改后的IR 存储到文件中, 执行
* */
std::error_code EC;
llvm::raw_fd_ostream OS("aftermain.bc", EC,llvm::sys::fs::OpenFlags());
llvm::WriteBitcodeToFile(*M,OS,true);
OS.flush();
return 0;
}
2.7) 效果
三) 总结
指令操作
核心是首先要找到对应的指令所在的位置;
构建新的指令,替换掉原来的指令操作,
a)构建新的操作指令使用BinaryOperator::CreateXXX
不需要指定插入位置
如果使用`BinaryOperator::Create`<font color='cornflowerblue'>则需要指定插入位置</font>
b)替换指令操作使用ReplaceInstWithInst()
设计头文件`"llvm/Transforms/Utils/BasicBlockUtils.h"`
相应的替换操作还有很多, 像`ReplaceInstWithValue`
c)将修改后的文件以bitcode
形式存放到文件中, 写入之前最好进行Module
的验证
这个过程中涉及到:
1. 文件读入到Module类;
2. 迭代器的使用
3. 获取指令的操作数
4. 创建新的指令
5. 指令替换
6. Module验证
7. 写入文件