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

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

查看所有标签

猜你喜欢:

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

ASP.NET 2.0入门经典

ASP.NET 2.0入门经典

哈特 / 清华大学出版社 / 2006-8 / 78.00元

该书是Wrox红皮书中的畅销品种, 从初版、1.0版、1.1版到目前的2.0版,已经3次升级,不仅内容更加完善、实用,还展现了.NET 2.0的最新技术和ASP.NET 2.0最新编程知识,是各种初学者学习ASP.NET的优秀教程,也是Web开发人员了解ASP.NET 2.0新技术的优秀参考书。该书与《ASP.NET 2.0高级编程(第4版)》及其早期版本,曾影响到无数中国Web程序员。一起来看看 《ASP.NET 2.0入门经典》 这本书的介绍吧!

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

各进制数互转换器

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

MD5 加密
MD5 加密

MD5 加密工具