参考内容

前期准备

  1. 进入官网下载llvm页面(下载地址)
  2. 下载:LLVM source code (.sig)
  3. 将源码解压放到一个文件夹中(我选择新建一个LLVMPass的文件夹)
    解压后的文件夹显示为llvm-7.0.0.src,将该文件夹重命名为llvm
  4. 在LLVMPass文件夹中新建一个build文件夹,用来存放编译的信息,和最终编译出的内容。
    当前目录结构如下:
    > LLVMPass
      > llvm
      > build

编写pass

  1. 进入: LLVMPass/llvm/lib/Transforms
    该文件夹就是存放pass的文件夹
  2. 在LLVMPass/llvm/lib/Transforms文件夹中创建一个新文件夹,命名为Hello(下载的源码包中,此位置处有实例文件——Hello文件夹,删除原始文件夹新创建一个)。
  3. 在LLVMPass/llvm/lib/Transforms/Hello 文件夹中创建文件Hello.cpp,文件内容1如下:
/* Hello.cpp */
// llvm必须的三个组件
#include "llvm/Pass.h"  // 因为我们要写一个Pass
#include "llvm/IR/Function.h"   // 因为要对函数做一些操作
#include "llvm/Support/raw_ostream.h"   // 因为将打印一些输出using namespace llvm;   // 使用的函数在llvm命名空间中namespace { // 声明匿名空间,使得被声明的内容仅在该文件内部可见struct Hello : public FunctionPass{ // 使用struct声明,表示默认共有继承。FunctionPass 一次操作一个函数。//声明了LLVM用于标识传递的传递标识符。这允许LLVM避免使用昂贵的C++运行时信息。static char ID;Hello() : FunctionPass(ID){}    // 构造函数,调用父函数FunctionPass() 的构造函数初始化。// 该方法覆盖了从FunctionPass继承的抽象虚拟方法。这是我们应该做事情的地方,所以我们只用每个函数的名称打印出我们的消息。bool runOnFunction(Function &F) override {errs() << "Hello:";errs().write_escaped(F.getName()) << '\n';return false;}}; // end of struct Hello }   // end of anonymous namespace// 初始化pass ID。 LLVM 使用 ID 的地址来识别通行证,所以初始化值并不重要。
char Hello::ID = 0;// 注册类 Hello ,给它命令行参数 hello ,和名字 Hello World Pass 。最后两个参数描述它的行为,如果一个pass在没有修改它的情况下遍历CFG,那么第三个参数设置为true;如果传递是分析传递,例如支配树传递,则 true 作为第四个参数提供。
static RegisterPass<Hello> X("hello", "Hello World Pass", false /* Only looks at CFG*/, false /* Analysis Pass*/);
  1. 在LLVMPass/llvm/lib/Transforms/Hello中创建文件CMakeLists.txt,文件内容如下:
add_llvm_loadable_module( LLVMHelloHello.cppPLUGIN_TOOLopt
)

注:LLVMHello为最终生成Pass的前缀名
  Hello.cpp为依赖的文件,后面另外两项为LLVM的一些东西(目前我还不清楚)。
9. 在LLVMPass/llvm/lib/Transforms中打开CMakeLists.txt,将原来的“add_subdirectory(Hello)”删除,此处的Hello为共享库的名字。LLVMPass/llvm/lib/Transforms/CMakeLists.txt文件内容如下:

add_subdirectory(Utils)
add_subdirectory(Instrumentation)
add_subLLVMPass/builddirectory(AggressiveInstCombine)
add_subdirectory(InstCombine)
add_subdirectory(Scalar)
add_subdirectory(IPO)
add_subdirectory(Vectorize)
add_subdirectory(ObjCARC)
add_subdirectory(Coroutines)
add_subdirectory(Hello)
 除过最后一行,其余的都是原始文件自带的。也即是以后想添加新的pass,需要在该文件夹添加。

编译

  1. 回退到路径LLVMPass/build下。使用如下命令构建Makefile。
cmake ../llvm -DLLVM_TARGETS_TO_BUILD=X86 -DCMAKE_BUILD_TYPE=Release

注:
-DLLVM_TARGET_TO_BUILD_X86:该参数为制定生成平台,此处为生成X86平台下的文件。若不指定则为默认,生成多种平台的文件。支持的平台

-DCMAKE_BUILD=Release : 为指定构造类型。三种类型介绍

  1. 开始编译:在LLVMPass/build路径下执行如下命令:
make -j<n>LLVMPass/build

注:-j<n>为多线程编译参数(多线程编译能够加快编译速度),如果不加,即为单线程编译,<n>的最佳情况为CPU核心数的两倍。但是如果进行多线程编译项目代码容易编译报错(我多线程编译报错)。如果报错可不加参数。

  1. 编译完成后,会生成文件:LLVMPass/build/lib/LLVMHello.so

运行pass

运行pass需要使用opt工具,pass运行在IR层面,所以需要先生成一个IR代码。
14. 先创建一个hello.c文件在路径LLVMPass下,该文件内容如下:

#include <stdio.h>int main() {printf("hello world\n");return 0;
}
  1. 将该文件编译成LLVM字节码,命令如下:
clang -O3 -emit-llvm hello.c -c -o hello.bc

  此时会生成文件hello.bc,就是hello.c转译成的IR字节码。IR字节码可通过如下命令转换成IR汇编代码:

llvm-dis hello.bc -o hello.ll

生成的hello.ll内容如下(不同版本可能内容会有些许差异):

; ModuleID = 'hello.bc'
source_filename = "hello.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"@str = private unnamed_addr constant [12 x i8] c"hello world\00"; Function Attrs: nounwind uwtable
define dso_local i32 @main() local_unnamed_addr #0 {%1 = tail call i32 @puts(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @str, i64 0, i64 0))ret i32 0
}; Function Attrs: nounwind
declare i32 @puts(i8* nocapture readonly) local_unnamed_addr #1attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind }!llvm.module.flags = !{!0}
!llvm.ident = !{!1}!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 7.0.0 (tags/RELEASE_700/final)"}

此时目录结构如下:
> LLVMPass
   > llvm
   > build
   hello.c
   hello.bc
   hello.ll

16.切换到LLVMPass目录, 使用opt命令运行Pass

opt -load build/lib/LLVMHello.so -hello < hello.bc > /dev/null

【注】:-load选项明确opt应该加载共享目标文件作为pass,“-hello”是一个合法的命令行参数(在Pass中,通过registerPass注册时起的名字。)表示使用hello这个pass。因为我们不修改程序,所以将opt的优化输出发送给/dev/null。

得到如下输出:
$Hello:main

刚好hello.c中只有一个函数,所以只有一行输出
brand
再在LLVMPass目录下创建文件quickSort.c,内容如下:

//快速排序
#include<stdio.h>void swap(int *x, int *y) {int tmp = *x;*x = *y;*y = tmp;
}//分治法把数组分成两份
int test_patition(int *a, int left,int right) {int j = left;    //用来遍历数组int i = j - 1;    //用来指向小于基准元素的位置int key = a[right];    //基准元素//从左到右遍历数组,把小于等于基准元素的放到左边,大于基准元素的放到右边for (; j < right; ++j) {if (a[j] <= key)swap(&a[j], &a[++i]);}//把基准元素放到中间swap(&a[right], &a[++i]);https://releases.llvm.org/7.0.0/docs/WritingAnLLVMPass.html//返回数组中间位置return i;
}
//快速排序
void test_quickSort(int *a,int left,int right) {if (left>=right)return;int mid = test_patition(a,left,right);test_quickSort(a, left, mid - 1);test_quickSort(a, mid + 1, right);
}
int main() {int a[] = { 10,6,5,7,12,8,1,3,11,4,2,9,16,13,15,14 };int n = sizeof(a) / sizeof(int);test_quickSort(a, 0,n-1);printf("排序好的数组为:");for (int l = 0; l < n; l++) {printf("%d ", a[l]);}printf("\n");return 0;
}

编译成IR字节码:

clang -O3 -emit-llvm quickSort.c -c -o quickSort.bc

使用opt运行:

opt -load build/lib/LLVMHello.so -hello < quickSort.bc > /dev/null

运行结果如下:
Hello:swap
Hello:test_patition
Hello:test_quickSort
Hello:main
由此可见,该Pass打印函数名成功。

使用:

opt -load build/lib/LLVMHello.so -help

可查看其他参数。

PassManager 提供了一个不错的命令行选项 (–time-passes),它允许您获取有关您的传递以及您排队的其他传递的执行时间的信息。

$ opt -load build/lib/LLVMHello.so -hello -time-passes < quickSort.bc > /dev/null
Hello:swap
Hello:test_patition
Hello:test_quickSort
Hello:main
===-------------------------------------------------------------------------===... Pass execution timing report ...
===-------------------------------------------------------------------------===Total Execution Time: 0.0009 seconds (0.0009 wall clock)---User Time---   --System Time--   --User+System--   ---Wall Time---  --- Name ---0.0006 ( 76.3%)   0.0000 (  0.0%)   0.0006 ( 68.4%)   0.0006 ( 68.6%)  Bitcode Writer0.0001 ( 12.2%)   0.0000 ( 52.1%)   0.0001 ( 16.3%)   0.0001 ( 16.1%)  Hello World Pass0.0001 ( 11.6%)   0.0000 ( 47.9%)   0.0001 ( 15.3%)   0.0001 ( 15.3%)  Module Verifier0.0008 (100.0%)   0.0001 (100.0%)   0.0009 (100.0%)   0.0009 (100.0%)  Total===-------------------------------------------------------------------------===LLVM IR Parsing
===-------------------------------------------------------------------------===Total Execution Time: 0.0007 seconds (0.0007 wall clock)---User Time---   --System Time--   --User+System--   ---Wall Time---  --- Name ---0.0005 (100.0%)   0.0002 (100.0%)   0.0007 (100.0%)   0.0007 (100.0%)  Parse IR0.0005 (100.0%)   0.0002 (100.0%)   0.0007 (100.0%)   0.0007 (100.0%)  Total

Pass 的分类和要求

The ImmutablePass class——不可变Pass类

此种类型用于不必运行,不更改状态且永远不需要更新的传递。可以提供有关当前编译器配置的信息。

尽管此传递类很少使用,但它对于提供有关正在编译的当前目标机器的信息以及可能影响各种转换的其他静态信息非常重要。

ImmutablePasses 永远不会使其他转换失效,永远不会失效,也永远不会“运行”。

The ModulePass class——组件Pass类

ModulePass 类是您可以使用的所有超类中最通用的。 从 ModulePass 派生表示你的 pass 使用整个程序作为一个单元,以不可预测的顺序引用函数体,或者添加和删除函数。 由于对 ModulePass 子类的行为一无所知,因此无法对其执行进行优化。

如果函数传递不需要任何模块或不可变传递,则模块传递可以使用 getAnalysis 接口 getAnalysis(llvm::Function *) 使用函数级传递(例如支配者)来提供检索分析结果的函数。 请注意,这只能对运行分析的函数执行,例如 在支配者的情况下,您应该只要求 DominatorTree 进行函数定义,而不是声明。

要编写正确的 ModulePass 子类,请从 ModulePass 派生并使用以下签名重载 runOnModule 方法:

the runOnModule method

virtual bool runOnModule(Module &M) = 0;

runOnModule 方法执行传递的有趣工作。 如果模块被转换修改,则返回 true,否则返回 false。

The CallGraphSCCPass class

SCC:strongly connected component,强连接分量2。所以这个Pass被称作调用图强连接分量Pass。

doInitialization(CallGraph &) method

virtual bool doInitialization(CallGraph & CG);

可以添加和删除函数,获取指向函数的指针等。

runOnSCC 函数

virtual bool runOnSCC(CallGraphSCC &SCC) = 0;

在传递的过程中执行工作,如果模块被转换修改返回true,否则返回false。

doFinalization(CallGraph &) method

virtual bool doFinalization(CallGraph & CG);

一种不常用方法,当传递框架为正在编译的程序中的每个SCC完成调用runOnSCC时,将调用该方法。

The FunctionPass class

与ModulePass子类相反,FunctionPass子类具有系统可以预测的本地行为。所有的函数Pass在每个函数上执行,独立于程序中的所有其他函数。且不要求它们按照特定顺序执行,并且函数Pass不修改外部函数。

明确的说,函数Pass子类不允许:

 检查或修改当前正在处理的函数以外的函数。从当前模块中添加或删除功能。从当前模块中添加或删除全局变量。在 runOnFunction 的调用(包括全局数据)之间维护状态。

实现 FunctionPass 通常很简单(例如,参见 Hello World pass)。 FunctionPasses 可能会重载三个虚方法来完成它们的工作。 如果它们修改了程序,所有这些方法都应该返回 true,如果没有,则返回 false。

The runOnFunction method

virtual bool runOnFunction(Function &F) = 0;

runOnFunction 方法必须由您的子类实现,以完成您的传递的转换或分析工作。 像往常一样,如果函数被修改,应该返回一个真值。

The doFinalization(Module &) method

virtual bool doFinalization(Module &M);

doFinalization 方法是一个不常用的方法,当传递框架为正在编译的程序中的每个函数完成调用 runOnFunction 时调用它。

LoopPass

所有 LoopPass 在函数中的每个循环上执行,独立于函数中的所有其他循环。 LoopPass 以循环嵌套顺序处理循环,以便最后处理最外层的循环。

LoopPass 子类可以使用 LPPassManager 接口更新循环嵌套。 实现循环传递通常很简单。 LoopPasses 可能会重载三个虚拟方法来完成它们的工作。 如果它们修改了程序,所有这些方法都应该返回 true,如果没有,则返回 false。

打算作为主循环传递管道的一部分运行的 LoopPass 子类需要保留其他循环在其管道中传递所需的所有相同函数分析。 为了使这更容易,LoopUtils.h 提供了一个 getLoopAnalysisUsage 函数。 可以在子类的 getAnalysisUsage 覆盖中调用它以获得一致和正确的行为。 类似地, INITIALIZE_PASS_DEPENDENCY(LoopPass) 将初始化这组函数分析。

The doInitialization(Loop *, PassManager &) method

virtual bool doInitialization(Loop *, LPPassManager &LPM);

The runOnLoop method

virtual bool runOnLoop(Loop *, LPPassManager &LPM) = ;

The doFinalization() method

virtual bool doFinalization();

doFinalization 方法是一个不常用的方法,当传递框架为正在编译的程序中的每个循环完成调用 runOnLoop 时,将调用该方法。

The RegionPass class

RegionPass 类似于 LoopPass,但在函数中的每个单入口单出口区域上执行。 RegionPass 以嵌套顺序处理区域,以便最后处理最外面的区域。

允许 RegionPass 子类使用 RGPassManager 接口更新区域树。 您可以重载 RegionPass 的三个虚拟方法来实现您自己的区域通行证。 如果它们修改了程序,所有这些方法都应该返回 true,否则返回 false。

The doInitialization methed

virtual bool doInitialization(Region *, RGPassManager &) method

the runOnRegion method

doFinalization() method

The BasicBlockPass class

BasicBlockPasses 就像 FunctionPass?s 一样,除了它们必须将它们的检查和修改范围限制为一次单个基本块。 因此,他们不得有以下任何行为:

 修改或检查当前块之外的任何基本块。在 runOnBasicBlock 的调用之间保持状态。修改控制流图(通过改变终止符指令)FunctionPass 禁止的任何事情。

BasicBlockPasses 对传统的本地和“窥视孔”很有用。 优化。 它们可能会覆盖 FunctionPass 具有的相同的 doInitialization(Module &) 和 doFinalization(Module &) 方法,但也可以实现以下虚拟方法:

doInitialization(Function &) method

runOnBasicBlock method

doFinalization(Function &) method

The MachineFunctionPass class

MachineFunctionPass 是 LLVM 代码生成器的一部分,它在程序中每个 LLVM 函数的机器相关表示上执行。

代码生成器通道由 TargetMachine::addPassesToEmitFile 和类似例程专门注册和初始化,因此它们通常不能从 opt 或 bugpoint 命令运行。

MachineFunctionPass 也是 FunctionPass,因此适用于 FunctionPass 的所有限制也适用于它。 MachineFunctionPasses 也有额外的限制。 特别是,MachineFunctionPasses 不允许执行以下任何操作:

 修改或创建任何 LLVM IR 指令、基本块、参数、函数、全局变量、全局别名或模块。修改当前正在处理的 MachineFunction 以外的 MachineFunction。在 runOnMachineFunction 的调用中维护状态(包括全局数据)。

runOnMachineFunction(MachineFunction &MF) method

virtual bool runOnMachineFunction(MachineFunction &MF) = 0;

runOnMachineFunction 可以被认为是 MachineFunctionPass 的主要入口点; 也就是说,你应该覆盖这个方法来完成你的 MachineFunctionPass 的工作。

在模块中的每个 MachineFunction 上调用 runOnMachineFunction 方法,以便 MachineFunctionPass 可以对函数的依赖于机器的表示执行优化。 如果您想获得您正在处理的 MachineFunction 的 LLVM 函数,请使用 MachineFunction 的 getFunction() 访问器方法? 但请记住,您不能从 MachineFunctionPass 修改 LLVM 函数或其内容。

Pass 注册

在 Hello World 示例 pass 中,我们说明了 pass 注册的工作原理,并讨论了使用它的一些原因以及它的作用。 在这里,我们讨论如何以及为何注册通行证。

正如我们在上面看到的,通行证是用 RegisterPass 模板注册的。 模板参数是要在命令行上使用的通行证的名称,用于指定应将通行证添加到程序中(例如,使用 opt 或 bugpoint)。 第一个参数是传递的名称,它用于程序的 -help 输出,以及由 -debug-pass 选项生成的调试输出。

如果您希望您的通行证易于转储,您应该实现虚拟打印方法:

The print method

virtual void print(llvm::raw_ostream &O, const Module *M) const;

打印方法必须通过“分析” 为了打印分析结果的人类可读版本。 这对于调试分析本身以及其他人了解分析的工作原理非常有用。 使用 opt -analyze 参数调用此方法。

llvm::raw_ostream 参数指定将结果写入的流,Module 参数提供指向已分析程序的顶级模块的指针。 但是请注意,此指针在某些情况下可能为 NULL(例如从调试器调用 Pass::dump()),因此它应该仅用于增强调试输出,不应依赖它。

明确pass之间的特殊交互

PassManager 的主要职责之一是确保通行证彼此正确交互。 因为 PassManager 试图优化通道的执行,所以它必须知道通道之间如何交互以及各个通道之间存在哪些依赖关系。 为了跟踪这一点,每个pass都可以声明需要在当前pass之前执行的pass集,以及被当前pass无效的pass。

通常,此功能用于要求在运行过程之前计算分析结果。 运行任意转换过程可以使计算的分析结果无效,这是无效集指定的。 如果传递未实现 getAnalysisUsage 方法,则默认为没有任何先决传递,并使所有其他传递无效。

The getAnalysisUsage method

virtual void getAnalysisUsage(AnalysisUsage &info) const;

通过实现 getAnalysisUsage 方法,可以为您的转换指定必需的和无效的集合。 实现应该在 AnalysisUsage 对象中填写有关哪些传递是必需的而不是无效的信息。 为此,传递可以调用 AnalysisUsage 对象上的以下任何方法:

AnalysisGroupPass

Pass Statistis

统计pass
Statistic 类旨在成为一种从传递中公开各种成功指标的简单方法。 当在命令行上启用 -stats 命令行选项时,将在运行结束时打印这些统计信息。 有关详细信息,请参阅程序员手册中的统计部分。

PassManager做什么事情

PassManager 类接受一个传递列表,确保正确设置它们的先决条件,然后安排传递以有效运行。 所有运行 pass 的 LLVM 工具都使用 PassManager 来执行这些 pass。

PassManager主要做两点去减少一系列的pass的执行时间:

  1. 共享分析结果。PassManager 会尽量避免重新计算分析结果。 这意味着跟踪哪些分析已经可用,哪些分析无效,以及需要运行哪些分析才能通过。 工作的一个重要部分是 PassManager 跟踪所有分析结果的确切生命周期,允许它在不再需要时释放分配给保存分析结果的内存。
  2. 在程序中顺序执行pass。PassManager 尝试通过将传递一起流水线化来从一系列传递中获得更好的缓存和内存使用行为。 这意味着,给定一系列连续的FunctionPass,它将执行第一个函数上的所有FunctionPass,然后执行第二个函数上的所有FunctionPass,等等…… 直到整个程序通过pass。
    这改进了编译器的缓存行为,因为它一次只接触单个函数的 LLVM 程序表示,而不是遍历整个程序。 它减少了编译器的内存消耗,因为例如一次只需要计算一个 DominatorSet。 这也使得在未来实现一些有趣的增强成为可能。

PassManager 类公开了一个 --debug-pass 命令行选项,可用于调试传递执行、查看事情如何工作以及诊断何时应该保留比当前更多的分析。 (要获取有关 --debug-pass 选项的所有变体的信息,只需键入 “opt -help-hidden”)。

例如通过使用-debug-pass=Structure 选项,我们可以看到我们的Hello World传递如何与其他传递交互。让我们用 gvn 和 licm pass 试试:

$ opt -load build/lib/LLVMHello.so -gvn -licm --debug-pass=Structure < quickSort.bc > /dev/null
Pass Arguments:  -targetlibinfo -tti -targetpassconfig -assumption-cache-tracker -domtree -phi-values -basicaa -aa -memdep -loops -lazy-branch-prob -lazy-block-freq -opt-remark-emitter -gvn -loops -loop-simplify -lcssa-verification -lcssa -basicaa -aa -scalar-evolution -licm -verify -write-bitcode
Target Library Information
Target Transform Information
Target Pass Configuration
Assumption Cache TrackerModulePass ManagerFunctionPass ManagerDominator Tree ConstructionPhi Values AnalysisBasic Alias Analysis (stateless AA impl)Function Alias Analysis ResultsMemory Dependence AnalysisNatural Loop InformationLazy Branch Probability AnalysisLazy Block Frequency AnalysisOptimization Remark EmitterGlobal Value NumberingNatural Loop InformationCanonicalize natural loopsLCSSA VerifierLoop-Closed SSA Form PassBasic Alias Analysis (stateless AA impl)Function Alias Analysis ResultsScalar Evolution AnalysisLoop Pass ManagerLoop Invariant Code MotionModule VerifierBitcode Writer

此输出向我们展示了何时构建Pass。 在这里我们看到 GVN 使用支配树信息来完成它的工作。 LICM pass 使用自然循环信息,它也使用支配树。

LICM 通过后,模块验证器运行(由 opt 工具自动添加),它使用支配树来检查生成的 LLVM 代码是否格式正确。 请注意,支配树计算一次,并由三遍共享。

让我们看看当我们在两次传递之间运行 Hello World 传递时会发生什么变化:

$ opt -load build/lib/LLVMHello.so -gvn -hello -licm --debug-pass=Structure < quickSort.bc > /dev/null
Pass Arguments:  -targetlibinfo -tti -targetpassconfig -assumption-cache-tracker -domtree -phi-values -basicaa -aa -memdep -loops -lazy-branch-prob -lazy-block-freq -opt-remark-emitter -gvn -hello -domtree -loops -loop-simplify -lcssa-verification -lcssa -basicaa -aa -scalar-evolution -licm -verify -write-bitcode
Target Library Information
Target Transform Information
Target Pass Configuration
Assumption Cache TrackerModulePass ManagerFunctionPass ManagerDominator Tree ConstructionPhi Values AnalysisBasic Alias Analysis (stateless AA impl)Function Alias Analysis ResultsMemory Dependence AnalysisNatural Loop InformationLazy Branch Probability AnalysisLazy Block Frequency AnalysisOptimization Remark EmitterGlobal Value NumberingHello World PassDominator Tree ConstructionNatural Loop InformationCanonicalize natural loopsLCSSA VerifierLoop-Closed SSA Form PassBasic Alias Analysis (stateless AA impl)Function Alias Analysis ResultsScalar Evolution AnalysisLoop Pass ManagerLoop Invariant Code MotionModule VerifierBitcode Writer
Hello:swap
Hello:test_patition
Hello:test_quickSort
Hello:main

在这里我们看到 Hello World 传递隔开了 Dominator Tree 传递,即使它根本没有修改代码! 为了解决这个问题,我们需要将以下 getAnalysisUsage 方法添加到我们的传递中:

/// we don't modify the program, so we presserve all analyses
void getAnalysisUsage(AnalysisUsage &AU) const override {AU.setPreservesAll();
}

再次运行我们的Pass:

opt -load build/lib/LLVMHello.so -gvn -hello -licm --debug-pass=Structure < quickSort.bc > /dev/null
Pass Arguments:  -targetlibinfo -tti -targetpassconfig -assumption-cache-tracker -domtree -phi-values -basicaa -aa -memdep -loops -lazy-branch-prob -lazy-block-freq -opt-remark-emitter -gvn -hello -loops -loop-simplify -lcssa-verification -lcssa -basicaa -aa -scalar-evolution -licm -verify -write-bitcode
Target Library Information
Target Transform Information
Target Pass Configuration
Assumption Cache TrackerModulePass ManagerFunctionPass ManagerDominator Tree ConstructionPhi Values AnalysisBasic Alias Analysis (stateless AA impl)Function Alias Analysis ResultsMemory Dependence AnalysisNatural Loop InformationLazy Branch Probability AnalysisLazy Block Frequency AnalysisOptimization Remark EmitterGlobal Value NumberingHello World PassNatural Loop InformationCanonicalize natural loopsLCSSA VerifierLoop-Closed SSA Form PassBasic Alias Analysis (stateless AA impl)Function Alias Analysis ResultsScalar Evolution AnalysisLoop Pass ManagerLoop Invariant Code MotionModule VerifierBitcode Writer
Hello:swap
Hello:test_patition
Hello:test_quickSort
Hello:main

这表明我们不再意外地使支配者信息无效,因此不必计算两次。

The releaseMemory method

virtual void releaseMemory();

用于释放内存

PassManager 自动确定何时计算分析结果,以及将它们保留多长时间。 因为 pass 对象本身的生命周期实际上是编译过程的整个持续时间,所以当分析结果不再有用时,我们需要一些方法来释放它们。 releaseMemory 虚拟方法是执行此操作的方法。

如果您正在编写一个分析或任何其他保留大量状态的传递(供另一个传递使用"requires"您的传递并使用 getAnalysis 方法),您应该实现 releaseMemory 以释放分配来维护此状态的内存 内部状态。 此方法在类的 run* 方法之后调用,在您的 pass 中下一次调用 run* 之前。

注册动态加载pass

Using existing registries

有预定义的注册表来跟踪指令调度(RegisterScheduler)和寄存器分配(RegisterRegAlloc)机器通过。 这里我们将描述如何注册一个寄存器分配器机器pass。

实现您的注册分配器机器通行证。 在您的注册分配器 .cpp 文件中添加以下内容:
……

创建新的注册器

使用GDB动态加载pass

不幸的是,将 GDB 与动态加载的 pass 一起使用并不像它应该的那么容易。 首先,您不能在尚未加载的共享对象中设置断点,其次,共享对象中的内联函数存在问题。 以下是使用 GDB 调试您的通行证的一些建议。

为便于讨论,我将假设您正在调试由 opt 调用的转换,尽管此处描述的内容不依赖于此。

在pass上设置断点

首先要做的就是在opt进程中打开gdb:

$ gdb opt
GNU gdb (GDB) 10.2
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from opt...
(No debugging symbols found in opt)
(gdb) 

上面是我的输出,猜测我使用的opt没有debug信息,所以不行,如果要调试的话,应该要自己编译一遍llvm,并且参数“-DCMAKE_BUILD_TYPE=Debug”,这样一来产生的文件估计就挺大了,我的硬盘空间不足,没有尝试。

参考文献


  1. LLVM7.0官方文档 ↩︎

  2. LLVM中的Pass及其管理机制 ↩︎

llvm Pass简单实例相关推荐

  1. Writing an LLVM Pass

    目录 1.简介 - 什么是pass? 2.快速入门 - 写下hello world 3.设置构建环境 4.Basic code required 5.Pass类和要求 6.ImmutablePass类 ...

  2. LLVM pass pwn 入门 (4)

    有了前面两道题的分析基础之后,我们不难发现,LLVM实际上就是一类基于C++的VM pwn,我们通过定义不同名字的函数或写入不同类型的指令让vm做一些事情,其中就包含触发漏洞.这篇文章笔者来分析一下2 ...

  3. 程序设计-在校整理-06 最常公共子序列与子串+2048小游戏+KNN简单实例

    [在校整理-06 最常公共子序列与子串+2048小游戏+KNN简单实例](注:仅供参考学习使用) 一.课题内容和要求 1.问题描述 1.1最长公共子序列与子串 1.2 2048小游戏 1.3 K-NN ...

  4. 【转载】基于LLVM Pass实现控制流平坦化

    基于LLVM Pass实现控制流平坦化 文章目录 基于LLVM Pass实现控制流平坦化 0x00. 什么是LLVM和LLVM Pass 0x01. 首先写一个能跑起来的LLVM Pass 0x02. ...

  5. windows上的LLVM pass瞎折腾记录

    windows上的LLVM pass瞎折腾记录 想了解下LLVM的代码风格和优化过程,就从写LLVM pass入手吧,做个踩坑记录- 文章最后有逐步骤的操作记录,Tips内记录关键点和思路 Tips: ...

  6. java查询mysql装载bean_jsp与javabean链接mysql数据库并查询数据表的简单实例源码

    jsp与javabean链接mysql数据库并查询数据表的简单实例源码.这个简单的实例是给新手学习的,或者一些高手临时忘记怎么使用jsp操作mysql数据库时候查找的,包括了建立mysql数据库连接的 ...

  7. js入门·循环与判断/利用函数的简单实例/使用对象/列举对象属性的名称

    1,列举对象属性的名称 <script language="javascript"> var obj=new Object(); obj.a="您好,我是田洪 ...

  8. 关于webservice的异步调用简单实例

    于webservice的异步调用简单实例 无论在任何情况下,被调用方的代码无论是被异步调用还是同步调用的情况下,被调用方的代码都是一样的, 下面,我们就以异步调用一个webservice 为例作说明. ...

  9. (三)AJAX基本介绍和简单实例03

    (三)AJAX基本介绍和简单实例03-----Ajax与数据库的动态应用 前台显示界面: 选择所有客户之后: 选择其中一个客户---杜森: Demo03.html代码 <html> < ...

  10. protobuf和socket通信简单实例

    protobuf和socket通信简单实例   protobuf是 Google 公司内部的混合语言数据标准,可以用来定义通信的协议,由于其有序列化和反序列化的操作,减小了存储或通信的数据量,从而达到 ...

最新文章

  1. bzoj 4602: [Sdoi2016]齿轮
  2. 借助C/C++进行Android开发:NDK的配置与使用
  3. Python编程语言简介
  4. Go语言 命令行解析(二)
  5. access函数_ACCESS中的DLookUp函数是如何运算的?
  6. 光纤收发器模块如何选购,光纤收发器模块选购原则
  7. 文档丨暴力破解性能问题
  8. 【LeetCode】241. Different Ways to Add Parentheses
  9. opencv的一些滤镜效果
  10. python可以调用windows资源吗_在Windows中使用Python设置文件夹权限
  11. Java全套学习资料
  12. 4米乘以12米CAD图_【超干货】CAD铺装排版下料之路径阵列
  13. C++编程练习:2-华氏度转摄氏度
  14. 芯片k66中文数据手册_IXFK66N50Q2
  15. 高斯混合模型聚类算法和K-Means聚类算法
  16. 单周期CPU设计【Verilog】
  17. 电商小程序实战教程-需求分析
  18. SAP 财务月结与年结过程
  19. mysql中工资表,MySQL操作工资表,获取累计工资和月平均工资
  20. 计算机网络知识全面讲解:域名的结构

热门文章

  1. 英飞凌 AURIX 系列单片机的HSM详解(3)——开发方法
  2. 定理在数学中的简写形式_初中数学常用的定理大全
  3. 学习Android studio时的报错Binary XML file line #10: Error inflating class fragment
  4. canvas-八卦图和时钟实现
  5. 如何使用 CSS 使表格居中(快速指南)
  6. mysql仿网易评论_仿网易新闻客户端评论盖楼
  7. C++ tic toc计时
  8. The following paths are ignored by one of your .gitignore
  9. mysql MVCC产生幻读原因及解决方式
  10. 第三方定量定性质谱检测技术实验