LLVM-Pass进阶1(遍历BasicBlocks&指令替换)

发布于 2019-05-29  121 次阅读


一键重新编译Pass脚本

因为每次都要重新cmake和make,不断切换目录很繁琐,于是自己写了一个makefile文件提升效率

.PHONY=make so all

BUILD_PATH=/home/happy/Documents/llvm8_build
SOURCE_PATH=/home/happy/Documents/llvm-8.0.0.src/lib/Transforms


all:cmake    so

cmake:
    cd $(BUILD_PATH) && cmake -G "Unix Makefiles" --enable-optimized --enable-targets=host-only -DCMAKE_BUILD_TYPE=Release ../llvm-8.0.0.src

so:
    cd $(BUILD_PATH)/lib/Transforms && make 

edit:
    @ gedit $(SOURCE_PATH)/mypass/Mypass.cpp

show_source:
    nautilus $(SOURCE_PATH)/mypass
show:
    nautilus $(BUILD_PATH)/lib

path:
    -@ echo $(SOURCE_PATH)/../Mypass.so

gll:
    clang -emit-llvm -S 1.c

opt:
    opt -load=/home/happy/Documents/llvm8_build/lib/Mypass.so -Mypass < 1.ll >/dev/null

使用

  • 重新生成cmake
make cmake
  • 编译pass文件到so文件
make so
  • 一键重新生成pass(包括cmake的生成)
make 
  • 打开生成后的so文件目录
make show

只要修改相应的目录和文件名即可使用。

vscode开发环境配置

修改相应的目录,在Windows中安装clang,并将llvm的inlcude下的文件夹(头文件)拷贝到某个目录即可

{
    "configurations": [
        {
            "name": "Win32",
            "includePath": [
                "${workspaceFolder}/**",
                "E:\\LLVM_Project",
                "E:\\LLVM_Project\\llvm\\**",
                "E:\\LLVM_Project\\llvm-c\\**"
            ],
            "defines": [
                "_DEBUG",
                "UNICODE",
                "_UNICODE"
            ],
            "compilerPath": "D:\\LLVM\\bin\\clang.exe",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "clang-x64"
        }
    ],
    "version": 4
}

pass的分类

所有的pass大致可以分为两类:分析和转换
分析类的pass以提供信息为主,转换类的会修改中间代码。
可以实现的具体的pass类型如下:

PASS片段

指令遍历和替换

此处遍历所有指令并将加法指令(a+b)替换为减法a-(-b)

#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Instructions.h"

using namespace llvm;

#define DEBUG_TYPE "mypass"

namespace {
    struct Mypass : public FunctionPass {
        static char ID; // Pass identification, replacement for typeid

        Mypass() : FunctionPass(ID) {}

        bool runOnFunction(Function &F) override {
            Function *tmp = &F;
            // 遍历函数中的所有基本块
            for (Function::iterator bb = tmp->begin(); bb != tmp->end(); ++bb) {
                // 遍历基本块中的每条指令
                errs() << "I am running on a block...\n";
                for (BasicBlock::iterator inst = bb->begin(); inst != bb->end(); ++inst) {
                    // 是否是add指令
                    errs() << "I am running in Instruction...\n";
                    if (inst->isBinaryOp()) {
                        errs() << "Find OP\n"; 
                        if (inst->getOpcode() == Instruction::Add) {
                            errs() << "Find ADD instruction\n";
                            ob_add(cast<BinaryOperator>(inst));
                        }
                    }
                }
            }

            return false;
        }

        // a+b === a-(-b)
        bool ob_add(BinaryOperator *bo) {
            BinaryOperator *op = NULL;
             errs() << "Modify~~\n";
            if (bo->getOpcode() == Instruction::Add) {
                // 生成 (-b)
                op = BinaryOperator::CreateNeg(bo->getOperand(1), "", bo);
                // 生成 a-(-b)
                op = BinaryOperator::Create(Instruction::Sub, bo->getOperand(0), op, "", bo);
                  errs() << "Modify2\n";
                op->setHasNoSignedWrap(bo->hasNoSignedWrap());
                op->setHasNoUnsignedWrap(bo->hasNoUnsignedWrap());
            }
              errs() << "Modify3\n";
            // 替换所有出现该指令的地方
            bo->replaceAllUsesWith(op);
            errs() << "Modify Finish\n";
            return true;
        }
    };
}
char Mypass::ID = 0;
//注册命令行选项Mypass
static RegisterPass<Mypass> X("Mypass", "DIY pass");

执行效果
原来的加法

  %5 = load i32, i32* %2, align 4
  %6 = load i32, i32* %3, align 4
  %7 = add nsw i32 %5, %6
  store i32 %7, i32* %4, align 4

pass后

  %5 = load i32, i32* %2, align 4
  %6 = load i32, i32* %3, align 4
  %7 = sub i32 0, %6
  %8 = sub nsw i32 %5, %7
  %9 = add nsw i32 %5, %6
  store i32 %8, i32* %4, align 4