0x01 Xcode调试一个LLVM Pass

栏目: IOS · 发布时间: 7年前

内容简介:0x01 Xcode调试一个LLVM Pass

之前写过一篇《关于LLVM,这些东西你必须知道!》,先已收录在《iOS成长之路》。

其中讲解了编译器的编译过程,以及通过LLVM可以做哪些有意思的事情,今天继续来跟着官方的文档继续了解Pass。

获取代码:

git clone http://llvm.org/git/llvm.git
git clone http://llvm.org/git/clang.git llvm/tools/clang
git clone http://llvm.org/git/clang-tools-extra.git llvm/tools/clang/tools/extra
git clone http://llvm.org/git/compiler-rt.git llvm/projects/compiler-rt

编译成 Xcode项目 :

mkdir build
cd build
cmake -G Xcode CMAKE_BUILD_TYPE="Debug" ../llvm

open LLVM.xcodeproj

然后运行 clang ,准备工作做好后,继续Pass的学习。

什么是Pass?

LLVM Pass是LLVM系统中非常重要和有趣的一个模块,Pass处理编译过程中代码的转换以及优化工作。

所有的Pass都是 Pass 的子类,不同的Pass实现不同的作用可以继承 ModulePass , CallGraphSCCPass , FunctionPass , LoopPass , RegionPass , BasicBlockPass 等类。

接下来开始介绍如何编写一个Pass,编译,加载以及运行的过程。

编写hello world

先编写一个“Hello” Pass,只是简单的输出程序中每个非外部方法的方法名,不会修改程序本身的功能。官方Demo的源代码存在 lib/Transforms/Hello 文件夹。

配置编译环境

首先来看看环境的配置,如果你想编译一个新的Pass,你可以在 lib/Transforms/ 下新建一个文件夹,这里我自己创建的是 PassDemo ,然后配置编译脚本去编译你的源代码。 在新建的目录下创建文件 CMakeLists.txt :

add_llvm_loadable_module(LLVMPassDemo
    Hello.cpp

    PLUGIN_TOOL
    opt
)

然后增加下面一行代码到 lib/Transforms/CMakeLists.txt

add_subdirectory(PassDemo)

编译脚本指定了编译源文件 Hello.cpp 去编译生成动态库 $(LEVEL)/lib/LLVMPassDemo.dylib 。该文件可以被 opt 通过 -load 参数动态加载。如果不是Mac平台生成的后缀会不一样,比如 Linux 下是 so

配置好编译脚本后,打开 Xcode 项目,按 command + B 编译,然后在项目里面就可以看到刚刚增加的 Target ,打开 Hello.cpp 文件就可以编写代码了。

0x01 Xcode调试一个LLVM Pass

基础代码

首先编辑 Hello.cpp 文件,导入头文件:

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

因为需要编一个 Pass 然后操作 Function , 还可能输出一些东西。

using namespace llvm;

由于导入文件里面的方法是属于 llvm 命名空间的,所以先指定使用的命名空间。

namespace {

编写一个匿名的命名空间, C++ 中的匿名命名空间和 C 语言里面的 static 关键词一样,定义在匿名空间中的变量仅仅对当前文件可见。

struct Hello : public FunctionPass{

定义 Hello 类继承 FunctionPass ,后面会讲到不同用处的Pass会继承不同的父类,现在只需要知道,继承自 FunctionPass 可以操作程序中的方法。

static char ID;
Hello() : FunctionPass(ID){}

定义可供LLVM标示的Pass ID。

        bool runOnFunction(Function &F) override{
            errs() << "Hello: ";
            errs().write_escaped(F.getName()) << "\n";
            return false;
        }
    };
}

这里定义了一个 runOnFunction ,重载继承自父类的抽象虚函数。在这个函数里面可以做针对于函数的特殊处理,这里只是打印出每个方法的名字。

char Hello::ID = 0;

初始化Pass ID,LLVM使用ID的地址去识别一个Pass,所以初始化的值不重要。

static RegisterPass<Hello> X("hello", "Hello World Pass", false/* Only looks at CFG */, false/* Analysis Pass */);

最后, 注册我们的class Hello, 指定命令行参数为 hello ,名字说明 Hello World Pass

整个 Hello.cpp 文件如下:

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

using namespace llvm;

namespace {
    struct Hello : public FunctionPass{
        static char ID;
        
        Hello() : FunctionPass(ID){}
        
        bool runOnFunction(Function &F) override{
            errs() << "Hello: ";
            errs().write_escaped(F.getName()) << "\n";
            return false;
        }
    };
}

char Hello::ID = 0;
static RegisterPass<Hello> X("hello", "Hello World Pass", false/* Only looks at CFG */, false/* Analysis Pass */);

选中 Target LLVMPassDemocommand + B 编译,编译完后会生成文件 build/Debug/lib/LLVMPassDemo.dylib

使用 opt 加载

上面在代码中通过 RegisterPass 注册了Pass,所以可以通过 opt -load 去加载动态库并指定参数 hello

准备源文件:

#include <stdio.h>

int add(int x, int y) {
    return x + y;
}

int main(){
    printf("%d",add(3,4));
    return 0;
}

编译源文件生成 bitcode :

path/to/build/Debug/bin/clang -emit-llvm -c test.mm -o test.bc

然后编译启动参数, 选择 opt target, 点击 Edit Scheme

0x01 Xcode调试一个LLVM Pass

填入启动参数:

0x01 Xcode调试一个LLVM Pass

command + R 运行。

输出如下:

Hello: _Z3addii
Hello: main

当然还可以断点调试啦~

0x01 Xcode调试一个LLVM Pass

通过 -help 可以看到注册的pass参数:

opt -load lib/LLVMPassDemo.dylib -help
......
  Optimizations available:
    -hello                                          - Hello World Pass
......

PassManager 还提供 --time-passes 参数输出你的Pass和其它Pass执行时间的对比。

opt -load lib/LLVMPassDemo.dylib -hello -time-passes -disable-output test.bc
Hello: _Z3addii
Hello: main
===-------------------------------------------------------------------------===
                      ... Pass execution timing report ...
===-------------------------------------------------------------------------===
  Total Execution Time: 0.0003 seconds (0.0003 wall clock)

   ---User Time---   --System Time--   --User+System--   ---Wall Time---  --- Name ---
   0.0002 ( 75.3%)   0.0000 ( 27.9%)   0.0002 ( 62.1%)   0.0002 ( 62.7%)  Module Verifier
   0.0001 ( 24.7%)   0.0001 ( 72.1%)   0.0001 ( 37.9%)   0.0001 ( 37.3%)  Hello World Pass
   0.0002 (100.0%)   0.0001 (100.0%)   0.0003 (100.0%)   0.0003 (100.0%)  Total

===-------------------------------------------------------------------------===

tips:

为了在执行 opt 时能自动检测 LLVMPassDemo 模块有没有被修改,如果修改了,重新编译 LLVMPassDemo 模块,我们把 LLVMPassDemo 模块加到 opt 的依赖里面。

编辑 tools/opt/CMakeLists.txt :

add_llvm_tool(opt
  AnalysisWrappers.cpp
  BreakpointPrinter.cpp
  GraphPrinters.cpp
  NewPMDriver.cpp
  PassPrinters.cpp
  PrintSCC.cpp
  opt.cpp

  DEPENDS
  intrinsics_gen
  LLVMPassDemo
  )

更多Pass类

当你写一个新的Pass的时候,你需要根据需要去选择需要继承的父类,根据不同的作用有 ImmutablePassModulePassCallGraphSCCPassFunctionPassLoopPassRegionPassBasicBlockPassMachineFunctionPass 。具体可以参考 官方文档

调用其它Pass

如果在编写的Pass中需要使用到其它Pass提供的函数功能,需要在:

virtual void getAnalysisUsage(AnalysisUsage &AU) const;

说明,比如像要获取程序中存在循环的信息,可以在该函数里面申请需要依赖的Pass。

以上面的Pass为例,导入头文件 #include "llvm/Analysis/LoopInfo.h" ,在 getAnalysisUsage 中调用:

AU.addRequired<LoopInfoWrapperPass>();
AU.setPreservesAll();

然后就可以通过它提供接口获取存在循环的个数。

LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
int loopCounter = 0;
for (LoopInfo::iterator i = LI.begin(), e = LI.end(); i != e; ++i) {
    loopCounter++;
}
errs() << "loop num:" << loopCounter << "\n";

测试程序:

#include <stdio.h>

int add(int x, int y) {
    for (int i = 0; i < 10; i++) {
        printf("%d\n",i);
    }
    return x + y;
}

int main(){
    printf("%d",add(3,4));
    return 0;
}

输出如下:

Hello: _Z3addii
loop num:1
Hello: main
loop num:0
Program ended with exit code: 0

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Google's PageRank and Beyond

Google's PageRank and Beyond

Amy N. Langville、Carl D. Meyer / Princeton University Press / 2006-7-23 / USD 57.50

Why doesn't your home page appear on the first page of search results, even when you query your own name? How do other web pages always appear at the top? What creates these powerful rankings? And how......一起来看看 《Google's PageRank and Beyond》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具