0%

llvm学习(1)

LLVM Pass学习(1)

因为不想再在这种题上爆零,所以开了这个系列😭

LLVM Pass概述

LLVM Pass 是一个在 LLVM 编译框架中执行的独立的代码变换或优化步骤。LLVM 本身是一个开源的编译器框架,广泛用于生成机器代码、优化代码和进行程序分析。Pass 是 LLVM 中处理中间表示(IR, Intermediate Representation)的基本单位。

具体概念解释:

  1. 中间表示(IR)
    LLVM 使用一种称为中间表示(IR)的低级代码格式,介于源代码和机器代码之间。IR 更加抽象,不依赖于任何特定的硬件架构,便于进行优化和转换。

  2. Pass:在 LLVM 中,Pass 是对 IR 进行某种操作的单元。每个 Pass 会执行特定的任务,如优化、分析或者代码转换。Pass 会遍历 IR,并对其进行修改或分析,生成更有效的代码。

    Pass 可以分为两大类:

    • 分析 Pass:这类 Pass 只对程序进行分析,不会修改代码。例如,分析程序的控制流、数据流、依赖关系等。

    • 变换 Pass:这类 Pass 会对代码进行修改,通常用于优化。比如,消除无用代码(Dead Code Elimination)、循环展开(Loop Unrolling)等。

  3. 优化
    LLVM Pass 的重要功能之一是优化,优化可以帮助生成运行更高效的代码。优化的类型有很多,包括:

    • 常量传播(Constant Propagation):用已知的常量值替换表达式。
    • 循环优化:对循环进行优化,如循环合并、循环展开等。
    • 死代码消除(Dead Code Elimination):删除不影响程序结果的代码。
    • 内联(Inlining):将函数调用替换为函数体,以减少函数调用的开销。
  4. Pass 管理器:在 LLVM 中,多个 Pass 会被组合在一起,形成一个 Pass 管理器(Pass Manager)。Pass 管理器负责管理和执行所有的 Pass,并确保它们按照正确的顺序执行。Pass 可以是一个独立的步骤,也可以按需组合执行。

  5. 优化级别
    LLVM 提供不同的优化级别(如 -O1-O2-O3)来控制 Pass 的应用程度。不同的级别表示不同的优化强度,高级别可能会使用更复杂的优化,但也可能会增加编译时间。

人话:

假设有一段程序,它做了很多重复的计算。如果使用 LLVM Pass 来优化代码,Pass 可能会发现这些计算是冗余的(例如,两个相同的加法操作),然后将其优化成更简单的代码,从而加速程序的执行。

总结来说,LLVM Pass 是一种对代码进行优化和变换的机制,通过多个独立的 Pass 对中间表示进行操作,最终帮助生成更加高效的机器代码。

小试牛刀

环境为Ubuntu20.04,llvm版本为llvm-9

首先准备一段官方资料给的测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <llvm/IR/Function.h>
#include <llvm/Pass.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>

using namespace llvm;

namespace { //声明匿名空间,被声明的内容仅在文件内部可见
struct Hello : public FunctionPass {
static char ID;
Hello() : FunctionPass(ID) {}
bool runOnFunction(Function &F) override {//重写runOnFunction,使得每次遍历到一个函数的时候就输出函数名
errs() << "Hello: ";
errs().write_escaped(F.getName()) << '\n';
return false;
}
};
}

char Hello::ID = 0;

// Register for opt
static RegisterPass<Hello> X("hello", "Hello World Pass");//注册类Hello,第一个参数是命令行参数,第二个参数是名字

// Register for clang
static RegisterStandardPasses Y(PassManagerBuilder::EP_EarlyAsPossible,
[](const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) {
PM.add(new Hello());
});

其中,Hello模块的功能为输出程序所有的函数名接着将其编译为so文件

1
clang `llvm-config --cxxflags` -Wl,-znodelete -fno-rtti -fPIC -shared myFirstLLVMpass.cpp -o LLVMFirst.so `llvm-config --ldflags`

现在再随便准备一个C语言程序,将其编译为.ll文件

1
clang -emit-llvm -S test.c -o test.ll

最后使用opt加载so文件,测试下hello模块

1
opt -load ./LLVMFirst.so -hello test.ll

upload successful