【LLVM】编写自己的pass

【LLVM】编写自己的pass

LLVM的Pass框架是LLVM中的重要部分,多个pass一起完成了LLVM的优化与代码转换工作。每个pass都会完成指定的优化工作。在某些情况下,pass之间会有对应的依赖关系,即新的pass会与其他原有的pass产生关联:例如在运行此pass之前必须先运行某一个pass。类似这种情况,都有相应的接口可以实现。参考这里

另外还有一种情况,当我们需要一种更高的level去控制pass时,比如将特定功能的pass进行分组,一个pass可以被重复注册到不同的分组中去。参考这里

如上介绍的那样,LLVM的Pass架构具有良好的可重用性与控制性。它允许我们嵌入自己编写的pass或者关闭一些默认提供的pass。同时,单个pass的开发以及调试也很独立,所以不必担心破坏整个LLVM的源码结构,只需要学会操作LLVM IR即可实现自己的pass。

Pass的分类

所有的pass可以分成两类:分析和转换。
分析类的pass以提供信息为主,转换类的pass会修改中间代码。可以实现的具体的pass类型如下所示:
ImmutablePass
ModulePass
CallGraphSCCPass
FunctionPass
LoopPass
RegionPass
BasicBlockPass
MachineFunctionPass
通过集成指定的类实现相关的虚函数即可。以上的链接对每一种pass的作用范围以及实现途径做了一个简单的介绍。

编写Pass

首先在llvm/lib/Transforms目录下创建TestPass目录:
cd到TestPass目录下,创建三个文件:
CMakeLists.txt:

add_llvm_loadable_module( LLVMMyPass
  MyPass.cpp
)

Makefile:

LEVEL = ../../..
LIBRARYNAME = LLVMTest
LOADABLE_MODULE = 1
USEDLIBS =
include $(LEVEL)/Makefile.common

testPass.cpp:

#define DEBUG_TYPE "hello"
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Instructions.h"
using namespace llvm;

namespace {
    // Hello - The first implementation, without getAnalysisUsage.
    struct Hello : public FunctionPass {
        static char ID; // Pass identification, replacement for typeid
        Hello() : FunctionPass(ID) {}

        virtual bool runOnFunction(Function &F) {
            //主要是打印出函数名称
            errs() << "Hello: ";
            errs() << F.getName() << '\n';

            Function *tmp = &F;
            // 遍历函数中的所有基本块
            for (Function::iterator bb = tmp->begin(); bb != tmp->end(); ++bb) {
                // 遍历基本块中的每条指令
                for (BasicBlock::iterator inst = bb->begin(); inst != bb->end(); ++inst) {
                    // 是否是add指令
                    if (inst->isBinaryOp()) {
                        if (inst->getOpcode() == Instruction::Add) {
                            ob_add(cast<BinaryOperator>(inst));
                        }
                    }
                }
            }

            return false;
        }
        // a+b === a-(-b)
        bool ob_add(BinaryOperator *bo) {
            BinaryOperator *op = NULL;

            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);

                op->setHasNoSignedWrap(bo->hasNoSignedWrap());
                op->setHasNoUnsignedWrap(bo->hasNoUnsignedWrap());
            }

            // 替换所有出现该指令的地方
            bo->replaceAllUsesWith(op);
        }
    };
}

char Hello::ID = 0;
static RegisterPass<Hello> X("hello", "Hello World Pass");

都写好之后cd ..退出到上层目录下,修改CMakeLists.txt文件,添加add_subdirectory(TestPass)语句。
接下来进入到之前的build文件目录下,直接在终端运行make命令。待编译结束后会生成一个LLVMMyPass.dylib文件,它在build/lib文件夹下。

运行编写的Pass

首先自己随便写一个.c文件:
test.c:

include<stdio.h>

int sum(int a, int b) {
  return a+b;
}

int main() {
  int a = 10,b = 10;
  a = sum(a,b);
  printf("%d",a);
}

使用命令将test.c文件生成test.bc文件,生成步骤见这里
将编写的pass作用到对应的test.bc文件上:
./opt -load ../lib/LLVMMyPass.dylib -test < test.bc > charge_test.bc
这样即可使用opt工具完成pass的功能,此pass的作用是实现了一个转换的pass,它将程序中的加法转换成了减法。即A+B -> A-(-B)
对于pass作用后生成的charge_test.bc文件,可以使用如下命令进行查看(因为.bc字节码文件阅读性太差,几乎不可阅读):

  • 生成对应的汇编文件(使用汇编代码查看):
llc charge_test.bc -o charge_test.s
  • 生成对应的IR代码文件(.ll文件):
lli charge_test.bc

这样就可以将pass作用后的文件与没作用的文件进行对比,查看在底层是否已达到想要的效果。

推荐阅读更多精彩内容

  • 前言 2000年,伊利诺伊大学厄巴纳-香槟分校(University of Illinois at Urbana-...
    星光社的戴铭阅读 12,463评论 7 166
  • linux资料总章2.1 1.0写的不好抱歉 但是2.0已经改了很多 但是错误还是无法避免 以后资料会慢慢更新 大...
    O感悟人生O阅读 8,545评论 2 29
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 103,703评论 13 125
  • JMETER中文手册 1. 简介 Apache JMeter是100%纯java桌面应用程序,被设计用来测试客户端...
    捉虫师阅读 16,650评论 0 24
  • 最近看到一篇文章《北京雾霾的危害,远远不如热腾腾的饭菜》,我笑了,标题党真是无所不用其极啊!且不说雾霾已经引起...
    叶塞尼娅阅读 125评论 9 9