初识LLVM

LLVM简介

LLVM项目是一个模块化和可重用的编译器和工具链技术集合,最初作为伊利诺伊大学的研究项目,旨在提供现代的、基于SSA的编译策略,支持任意编程语言的静态和动态编译。如今,LLVM已发展为包含多个子项目的总项目,被广泛应用于商业和开源项目以及学术研究。LLVM的代码在“Apache 2.0许可证加LLVM例外”下发布。

主要子项目包括:

  • LLVM核心库:提供与源和目标无关的优化器和代码生成支持。
  • Clang:一个高效的C/C++/Objective-C编译器,支持快速编译和有用的错误提示。
  • LLDB:基于LLVM和Clang的本地调试器。
  • libc++和libc++ ABI:高性能的C++标准库实现。
  • compiler-rt:低级代码生成器支持库及动态测试工具的运行时库。
  • MLIR:用于构建可重用和可扩展编译器基础设施的新方法。
  • OpenMP:为Clang中的OpenMP实现提供运行时支持。
  • Polly:实现缓存局部性优化和自动并行化、矢量化。
  • libclc:实现OpenCL标准库。
  • Klee:使用定理证明器进行符号执行以发现程序中的错误。
  • LLD:一个新型高效的链接器。
  • BOLT:基于执行配置文件的链接后优化器。

LLVM环境搭建与源码编译

本文以Ubuntu 18.04系统为例,需要安装的软件依赖包为:

sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig

安装完成依赖包以后,可以去github下载llvm源码,本文下载的llvm项目源码版本为9.0.1:

git clone https://github.com/llvm/llvm-project.git

进入项目目录以后开始编译Debug版本,编译命令为:

sudo apt-get install -y ninja-build # 如果ninja没有安装
cmake -G Ninja  ../llvm

生成配置文件以后,使用Ninja编译,-j表示自己的cpu核数

ninja -j2  

与编译Debug版本不同,编译Release版本指定了一些编译选项,-DLLVM_INCLUDE_TESTS=OFF表示在构建过程中不包括测试代码,-DLLVM_ENABLE_PROJECTS="clang"包括clang子项目,表示在构建过程中也会构建Clang编译器。编译命令为:

cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DLLVM_INCLUDE_TESTS=OFF -DLLVM_ENABLE_PROJECTS="clang" ../llvm

此时可以使用编译的工具构建自己编写的程序,其功能如下:

  • 使用clang编译中间文件
 clang -emit-llvm -S hello.c -o hello.ll
  • lli工具能够执行中间文件hello.ll
 lli hello.ll
  • llvm-as: 汇编器,将人类可读的LLVM汇编代码转换为LLVM位代码。
 llvm-as hello.ll -o hello.bc
  • llc: LLVM后端编译器,将LLVM位代码翻译为本机代码的汇编文件。
llc hello.bc -o  hello.s
  • 最后将本机的汇编文件使用clang编译成可执行的二进制文件
clang hello.s -o hello

官方文档总结

LLVM(低级虚拟机)提供了一套强大的工具,用于编译、调试和优化代码。这些工具由LLVM库构建而成,形成了用户与LLVM交互的主要接口。以下是一些关键工具的介绍:

  1. bugpoint: 用于调试优化通道或代码生成后端,通过缩小测试用例,找出导致问题的最小通道和指令。
  2. llvm-ar: 归档程序,生成包含LLVM位代码文件的存档,并可选择带有索引以加速查找。
  3. llvm-as: 汇编器,将人类可读的LLVM汇编代码转换为LLVM位代码。
  4. llvm-dis: 反汇编器,将LLVM位代码转换为人类可读的LLVM汇编代码。
  5. llvm-link: 链接器,将多个LLVM模块链接成一个单一的程序。
  6. lli: LLVM解释器,可以直接执行LLVM位代码,对于支持的架构,还可以作为即时编译器,提升执行速度。
  7. llc: LLVM后端编译器,将LLVM位代码翻译为本机代码的汇编文件。
  8. opt: 读取LLVM位代码,应用一系列转换,并输出生成的位代码,还可以运行特定分析并打印结果。
    每个工具都有详细的帮助文档,可以通过 tool_name -help 命令获取更多信息。希望这篇介绍能帮助你快速了解和使用LLVM工具集。

LLVM中Pass介绍

首先明白什么是Pass,我们可以看下官方文档介绍:

LLVM Pass 框架是 LLVM 编译器系统的重要组成部分,负责执行编译器的各种转换和优化任务。Pass 是编译器中用于分析和转换代码的模块,通过继承 Pass 类并重写其虚方法来实现特定功能。不同类型的 Pass(如 ModulePass、FunctionPass、LoopPass 等)提供了关于其功能和如何与其他 Pass 结合使用的详细信息。虽然 LLVM 优化管道现在使用新的 Pass 管理器,但代码生成管道仍然使用旧的 Pass 管理器。Pass 框架能够根据 Pass 的类型和约束高效地调度它们运行。

其中常用的Pass为FunctionPass和ModulePass,下面将逐一介绍:

  • FunctionPass: FunctionPass 子类具有可预测的局部行为,系统可以对其进行预期。所有的 FunctionPass 都独立地在程序中的每个函数上执行,不受程序中其他函数的影响。FunctionPass 不要求以特定顺序执行,并且不会修改外部函数。
1. doInitialization(Module &) 方法
  • 功能:允许执行大部分 FunctionPass 不允许的操作,如添加和移除函数,获取函数指针等。用于执行简单的初始化操作,不依赖于正在处理的函数。
  • 调度:该方法的调用不会与其他 Pass 的执行重叠,因此应该非常快速。
  • 示例:例如,LowerAllocations Pass 使用 doInitialization 方法获取所需函数的引用,并添加必要的原型到模块中。
2. runOnFunction(Function &F) 方法
  • 功能:必须由子类实现,用于执行 Pass 的主要转换或分析工作。
  • 返回值:如果函数被修改,则返回 true;否则返回 false。
3. doFinalization(Module &) 方法
  • 功能:在 Pass 框架完成对程序中每个函数的 runOnFunction 调用后调用,用于进行最终的清理工作。

我们自定义的Pass位置:~/Code/llvm-project-9.0.1/llvm/lib/Transforms/Hello,此时我们可以使用Clion打开整个llvm项目。

opt -load '/home/linker/Code/llvm-project-9.0.1/llvm/cmake-build-debug/lib/LLvMHello.so' -hello hello.ll
  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值