抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

LLVM -Learn-Work2

@2022-04-28 03:05:00

@sizaif

Work2:如果加法结果大于 100,则将结果改为 100。(基本块操作)

//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;
}

一) 思路

整体思想为: 获得fadd指令的结果,if语句判断一下结果与100的大小,如果大于100,则修改指令为100

一种思想:

​ 先找到fadd 指令与fadd指令的后一个指令call printf

  1. 根据源码,main 只有一个默认基本块
  2. fadd 指令前,先存入常数值100.0
  3. fadd 指令后面(call printf指令前添加 fcmp判断指令
  4. call printf指令开始将默认块一分为: defaut 块和defaut_ret
  5. 插入两个新的基本块change_prtdefaut_prt
  6. change_prt基本块的内容为读取100, 调用printf函数输出新的值,跳转到defaut_ret free内存
  7. defaut_prt基本块的内容为读取fadd的结果,调用printf函数输出,跳转到defaut_ret free内存

关系图如下:

image-20220503152039699

原来IR代码

%20 = load double, double* %5, align 8
%21 = fadd double %19, %20
%22 = call i32 (i8*, ...) @printf(8* noundef getelementptr inbounds ([21 x i8], [21 x i8]* @.str.1, i64 0, i64 0), double noundef %17, double noundef %18, double noundef %21)
%23 = load i8*, i8** %3, align 8
call void @free(i8* noundef %23) #4
ret i32 0

目标IR代码

·····
  store double %15, double* %4, align 8
  %16 = load double, double* %3, align 8
  %17 = load double, double* %4, align 8
  %18 = load double, double* %3, align 8
  %19 = load double, double* %4, align 8
  %ina1 = alloca double, align 8
  store double 1.000000e+02, double* %ina1, align 8
  %20 = fadd double %18, %19
  %inf1 = fcmp ugt double %20, 1.000000e+02
  br i1 %inf1, label %change_prt, label %defaut_prt

defaut_ret:                                       ; preds = %defaut_prt, %change_prt
  %21 = load i8*, i8** %2, align 8
  call void @free(i8* noundef %21) #4
  ret i32 0

change_prt:                                       ; preds = %defaut
  %22 = load double, double* %ina1, align 8
  %23 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @0, i32 0, i32 0), double %16, double %17, double %22)
  br label %defaut_ret

defaut_prt:                                       ; preds = %defaut
  %24 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @0, i32 0, i32 0), double %16, double %17, double %20)
  br label %defaut_ret
}
·····

二) 执行

2.1 初始化(前置)

LLVMContext context;
IRBuilder<> builder(context);
std::unique_ptr<Module> M;
std::unique_ptr<MIRParser> MIR;

SMDiagnostic Err;
/**
 * #include "llvm/IRReader/IRReader.h"
 * 2022-04-26 03:04:32
 * TODO:
 * from IR file get Moudle class
 * */
M = parseIRFile(StringRef(argv[1]),Err,context);
if(M){
    std::cout<<"debug: 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 找到fadd指令并添加相关必要指令

/**
 * 2022-04-26 03:25:52
 * TODO:
 * 获取main函数
 * */
auto main_fn = M->getFunction(StringRef("main"));

/**
 * 2022-04-28 02:14:46
 * TODO:
 * 找到fadd指令,与fadd的后一个指令
 * */
Instruction *in_fadd,*in_prt;
for (inst_iterator I = inst_begin(*main_fn), E = inst_end(*main_fn); I != E; ++I){
    if(I->isBinaryOp() && I->getOpcode()==Instruction::FAdd){
        // outs() << *I << "\n";
        in_fadd = &*I;
        in_prt = &*(I.operator++());
    }
}

/**
 * 2022-04-28 02:42:32
 * TODO:
 * %20 = load double, double* %5, align 8
 * %21 = fadd double %19, %20
 * 在fadd指令前面
 * 添加以下两个指令:
 * <x>  = alloca double, align 8
 * store double 1.000000e+02, double* <x>, align 8
 * 
 * */
auto f32 = builder.getDoubleTy();
auto const100 = ConstantFP::get(f32,double(100));

builder.SetInsertPoint(in_fadd);
auto in_alloc = builder.CreateAlloca(f32,0,"ina1");
auto in_store = builder.CreateStore(const100,in_alloc);

/**
 * 2022-05-03 12:47:30
 * TODO:
 * 在printf指令前添加条件判断(fcmp)指令
 * %21 = call i32 (i8*, ...) @printf(i8* noundef ····
 * fcmp:
 * <x> = fcmp ugt double <fadd>, 1.000000e+02
 * */
builder.SetInsertPoint(in_prt);
auto in_fcmp = builder.CreateFCmpUGT(in_fadd,const100,"inf1");

2.3 切割基本块

/**
 * 2022-05-03 12:49:38
 * TODO:
 * 将main中默认块分为2块: 主体块和默认ret块
 * 并添加两个新块: 修改输出块和默认输出块
 * */
auto oldblk = &main_fn->getEntryBlock();
oldblk->setName("defaut");
auto bb2 = SplitBlock(oldblk,in_prt);
if(!bb2){
    errs()<<"debug: split block wrong! \n";
}else{
    bb2->setName("defaut_ret");
}

BasicBlock *change_prt = BasicBlock::Create(context, "change_prt", main_fn);
BasicBlock *defaut_prt = BasicBlock::Create(context, "defaut_prt", main_fn);

切割基本块SplitBlock(oldblk,in_prt);函数,此时切割效果如下:

 #     auto bb2 = SplitBlock(oldblk,in_prt);
·······
  store double %15, double* %4, align 8
  %16 = load double, double* %3, align 8
  %17 = load double, double* %4, align 8
  %18 = load double, double* %3, align 8
  %19 = load double, double* %4, align 8
  %ina1 = alloca double, align 8
  store double 1.000000e+02, double* %ina1, align 8
  %20 = fadd double %18, %19
  %inf1 = fcmp ugt double %20, 1.000000e+02
  br label %.split

.split:                                           ; preds = %0
  %21 = call i32 (i8*, ...) @printf(i8* noundef getelementptr inbounds ([21 x i8], [21 x i8]* @.str.1, i64 0, i64 0), double noundef %18, double noundef %19, double noundef %20)
  %22 = load i8*, i8** %3, align 8
  call void @free(i8* noundef %23) #4
  ret i32 0
}

补充后效果如下:

defuat
·····
  store double %15, double* %4, align 8
  %16 = load double, double* %3, align 8
  %17 = load double, double* %4, align 8
  %18 = load double, double* %3, align 8
  %19 = load double, double* %4, align 8
  %ina1 = alloca double, align 8
  store double 1.000000e+02, double* %ina1, align 8
  %20 = fadd double %18, %19
  %inf1 = fcmp ugt double %20, 1.000000e+02
  br label %.split

defaut_ret:                                       ; preds = %defaut
  %21 = load i8*, i8** %2, align 8
  call void @free(i8* noundef %21) #4
  ret i32 0

change_prt:                                       ; 


defaut_prt:                                       ; 

}
·····

2.4 在块中填充内容

/**
    * 2022-05-03 12:51:10
    * TODO:
    * 修改输出块:
    * 读取前面存储默认100值,
    * 构造printf函数输出
    * */
   builder.SetInsertPoint(change_prt);
   auto in_load = builder.CreateLoad(f32,in_alloc);

   auto i32p = builder.getIntPtrTy(M->getDataLayout(),0);
   auto printf_prototype = FunctionType::get(i32p, true);
   auto printf_fn = M->getFunction("printf");
   if(!printf_fn){
       printf_fn = Function::Create(printf_prototype,Function::ExternalLinkage, "printf", M.get());
   }
   auto format_str = builder.CreateGlobalStringPtr("%.2f + %.2f = %.2f \n");
   
   builder.CreateCall(printf_fn, {format_str,in_prt->getOperand(1),in_prt->getOperand(2),in_load});
   builder.CreateBr(bb2);
   
   /**
    * 2022-05-03 12:51:57
    * TODO:
    * 默认输出块:
    * 构造printf默认函数输出
    * */
   builder.SetInsertPoint(defaut_prt);
   builder.CreateCall(printf_fn, {format_str,in_prt->getOperand(1),in_prt->getOperand(2),in_fadd});
   builder.CreateBr(bb2);

   
   /**
    * 2022-05-03 12:52:18
    * TODO:
    * 构造新的br跳转指令替换默认分块指令
    * br label defaue_ret -> br <cond> label A lable B
    * */
   auto old_cond_br = &*(oldblk->getIterator()->rbegin());
   auto new_cond_br = BranchInst::Create(change_prt,defaut_prt,in_fcmp);
   ReplaceInstWithInst(old_cond_br,new_cond_br);

   /**
    * 2022-05-03 14:54:46
    * TODO:
    * 删除原先printf的调用指令
    * */
   in_prt->eraseFromParent();

2.5 修改后Module验证并写入文件中

/**
 * #include "llvm/IR/Verifier.h"
 * 2022-05-02 11:43:34
 * TODO:
 * 修改完Module后进行验证Module的完整性
 * verifyModule(*M,&errs()): true if the module is broken. 
 * */
if(!llvm::verifyModule(*M,&errs())){
    outs()<<"debug: Module verify is ok \n";
    // M->print(outs(),nullptr);
}

/**
 * #include "llvm/Bitcode/BitcodeWriter.h"
 * 2022-05-03 12:55:33
 * TODO:
 * 将修改后的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.x) 全部源码

/**
 * 2022-04-26 01:02:37
 * 2022-05-03 15:55:43
 * sizaif
 * 
 * clang++ $(llvm-config --cxxflags --ldflags --system-libs --libs)  -o main main.cpp
 * ./main main.ll
 * lli aftermain.bc
 * */

#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include <llvm/IR/IRBuilder.h>
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/CodeGen/MIRParser/MIRParser.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/SourceMgr.h"
#include <llvm/Support/raw_os_ostream.h>
#include "llvm/Bitcode/BitcodeWriter.h"

#include <iostream>
#include <string>
#include <fstream>
#include <cstdio>

using namespace llvm;


int main(int argc,char **argv){

 
    LLVMContext context;
    IRBuilder<> builder(context);
    std::unique_ptr<Module> M;
    std::unique_ptr<MIRParser> MIR;

    SMDiagnostic Err;
    /**
     * #include "llvm/IRReader/IRReader.h"
     * 2022-04-26 03:04:32
     * TODO:
     * from IR file get Moudle class
     * */
    M = parseIRFile(StringRef(argv[1]),Err,context);
    if(M){
        std::cout<<"debug: 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
     * TODO:
     * 获取main函数
     * */
    auto main_fn = M->getFunction(StringRef("main"));
    
    /**
     * 2022-04-28 02:14:46
     * TODO:
     * 找到fadd指令,与fadd的后一个指令
     * */
    Instruction *in_fadd,*in_prt;
    for (inst_iterator I = inst_begin(*main_fn), E = inst_end(*main_fn); I != E; ++I){
        if(I->isBinaryOp() && I->getOpcode()==Instruction::FAdd){
            // outs() << *I << "\n";
            in_fadd = &*I;
            in_prt = &*(I.operator++());
    }
    
    /**
     * 2022-04-28 02:42:32
     * TODO:
     * %20 = load double, double* %5, align 8
     * %21 = fadd double %19, %20
     * 在fadd指令前面
     * 添加以下两个指令:
     * <x>  = alloca double, align 8
     * store double 1.000000e+02, double* <x>, align 8
     * 
     * */
    auto f32 = builder.getDoubleTy();
    auto const100 = ConstantFP::get(f32,double(100));

    builder.SetInsertPoint(in_fadd);
    auto in_alloc = builder.CreateAlloca(f32,0,"ina1");
    auto in_store = builder.CreateStore(const100,in_alloc);

    /**
     * 2022-05-03 12:47:30
     * TODO:
     * 在printf指令前添加条件判断(fcmp)指令
     * %21 = call i32 (i8*, ...) @printf(i8* noundef ····
     * fcmp:
     * <x> = fcmp ugt double <fadd>, 1.000000e+02
     * */
    builder.SetInsertPoint(in_prt);
    auto in_fcmp = builder.CreateFCmpUGT(in_fadd,const100,"inf1");
    

    outs()<<"debug: fadd op : "<<*in_fadd<<" \n";
    outs()<<"debug: alloc op : "<<*in_alloc<<" \n";
    outs()<<"debug: store op : "<<*in_store<<" \n";
    outs()<<"debug: fcmp op : "<<*in_fcmp<<" \n";
    
    /**
     * 2022-05-03 12:49:38
     * TODO:
     * 将main中默认块分为2块: 主体块和默认ret块
     * 并添加两个新块: 修改输出块和默认输出块
     * */
    auto oldblk = &main_fn->getEntryBlock();
    oldblk->setName("defaut");
    auto bb2 = SplitBlock(oldblk,in_prt);
    if(!bb2){
        errs()<<"debug: split block wrong! \n";
    }else{
        bb2->setName("defaut_ret");
    }
    
    BasicBlock *change_prt = BasicBlock::Create(context, "change_prt", main_fn);
    BasicBlock *defaut_prt = BasicBlock::Create(context, "defaut_prt", main_fn);

    /**
     * 2022-05-03 12:51:10
     * TODO:
     * 修改输出块:
     * 读取前面存储默认100值,
     * 构造printf函数输出
     * */
    builder.SetInsertPoint(change_prt);
    auto in_load = builder.CreateLoad(f32,in_alloc);

    auto i32p = builder.getIntPtrTy(M->getDataLayout(),0);
    auto printf_prototype = FunctionType::get(i32p, true);
    auto printf_fn = M->getFunction("printf");
    if(!printf_fn){
        printf_fn = Function::Create(printf_prototype,Function::ExternalLinkage, "printf", M.get());
    }
    auto format_str = builder.CreateGlobalStringPtr("%.2f + %.2f = %.2f \n");
    
    builder.CreateCall(printf_fn, {format_str,in_prt->getOperand(1),in_prt->getOperand(2),in_load});
    builder.CreateBr(bb2);
    
    /**
     * 2022-05-03 12:51:57
     * TODO:
     * 默认输出块:
     * 构造printf默认函数输出
     * */
    builder.SetInsertPoint(defaut_prt);
    builder.CreateCall(printf_fn, {format_str,in_prt->getOperand(1),in_prt->getOperand(2),in_fadd});
    builder.CreateBr(bb2);

    
    /**
     * 2022-05-03 12:52:18
     * TODO:
     * 构造新的br跳转指令替换默认分块指令
     * br label defaue_ret -> br <cond> label A lable B
     * */
    auto old_cond_br = &*(oldblk->getIterator()->rbegin());
    outs()<<"debug: old cond br : "<<*old_cond_br<<" \n";
    auto new_cond_br = BranchInst::Create(change_prt,defaut_prt,in_fcmp);
    outs()<<"debug: new cond br : "<<*new_cond_br<<" \n";
    ReplaceInstWithInst(old_cond_br,new_cond_br);

    /**
     * 2022-05-03 14:54:46
     * TODO:
     * 删除原先printf的调用指令
     * */
    in_prt->eraseFromParent();
    
    
    /**
     * #include "llvm/IR/Verifier.h"
     * 2022-05-02 11:43:34
     * TODO:
     * 修改完Module后进行验证Module的完整性
     * verifyModule(*M,&errs()): true if the module is broken. 
     * */
    if(!llvm::verifyModule(*M,&errs())){
        outs()<<"debug: Module verify is ok \n";
        // M->print(outs(),nullptr);
    }

    /**
     * #include "llvm/Bitcode/BitcodeWriter.h"
     * 2022-05-03 12:55:33
     * TODO:
     * 将修改后的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.6 效果

image-20220503160126584

三) 总结

基本块操作涉及到:

  1. 对基本块的遍历(迭代器应用)

  2. 对基本块的拆分, 基本块的拆分时,根据方法的使用会将当前指令放到新基本块或旧基本块(注意区分)

  3. 构造指令插入(可以使用IRbuilder) 来构造基本指令与函数或基本块

  4. 指令删除操作

  5. 相应的修改后Module 需要进行验证

评论吧



本站总访问量为 访客数为

鲁 ICP 备 20018157 号-1
Copyright 2021 - 2022 sizaif. All Rights Reserved