LLVM源代码指南

LLVM源代码指南

  • LLVM内核不包括前端,只是“中端”优化器,一堆后端,文档,还有大量辅助代码。像Clang这样的前端被放在单独的项目中。
  • 核心的LLVM表现在RAM中,并使用大量C++ API进行操作。这种表现方式是可以转储到可读文本并解析回内存,但这只是为了方便调试:在使用LLVM的正常编译期间,永远不会生成文本形式的IR。通常,前端通过调用LLVM API来构建IR,然后运行一些优化PASS,最后调用后端来生成汇编或机器代码。当LLVM代码被存储在磁盘上时(在使用Clang正常编译C/C++时甚至都不会存在这种情况),是以“bitcode”形式存放,是一种紧凑的二进程表示。
  • 主要的LLVM API文档由doxygen生成,可以在这里找到。除非您已经知道自己在做什么以及在寻找什么(带有目的性的找和看),否侧这些信息很难被利用。以下链接是开始学习LLVM API的教程。

代码根目录,包含以下:

  1. bindings允许从C++以外的编程语言使用LLVM API。存在比这更多的bindings,包括C(我们将稍后介绍)和Haskell(目录之外)。
  2. cmake:LLVM现在使用CMake而不是autoconf。
  3. docsReStructuredText格式。请参阅语言参考手册,它定义了每个LLVM指令的含义。tutorial子目录中的素材特别有趣,但不要在那里看,而是去这里。这是学习LLVM最好的方式。
  4. examples:这是教程配套的源代码。作为LLVM hacker,应该尽可能地从这里抠代码、CMakeLists.txt等。
  5. include:第一个子目录llvm-c包含了C的binding(作者曰:我从没使用过,但是看起来十分可靠)。重要的是,LLVM试图保持这些binding不变,而C++ API在不同版本中发生较大变化,尽管过去几年中变化的步伐已经放缓。第二个子目录llvm是个大头:它包含878个头文件,用于定义所有LLVM API。一般来说,使用这些文件的doxygen版本而不是直接读取他们会更容易,但我经常最后使用这些文件来查找某些功能。
  6. lib包含了真正的好东西,我们之后会单独看一下。
  7. projects默认是不包含任何内容,但是可以在其中查看LLVM组件,例如compiler-rt(清理程序之类的运行时库),OpenMP support,以及位于单独repo中的LLVM C++库。
  8. resources一些Visual C++的东西,并不关心(用于Windows二进制文件的版本资源定义)。
  9. runtimes:外部项目的另一个占位符,在不久前加入。
  10. test:它也是一个大头,它包含了数以千计的LLVM单元测试,它们在构建检查目标时运行。其中大多数是包含LLVM IR文本版本的.ll文件。会测试比如说,一个优化pass是否能得到预期结果。我将在即将发布的博客中详细介绍LLVM的测试。
  11. tools:LLVM本身只是一个库的集合,没有任何特定的主要功能。tools目录的大多数子目录都包含一个链接LLVM库的可执行工具。例如,llvm-dis是从bitcode到文本汇编格式的反汇编程序。
  12. unittests:更多单元测试,也是在检查构建目标时运行。这些是使用Google Test框架直接调用API的C++文件,而不像“test”目录里的部分,通过运行汇编程序,反汇编程序或优化程序等内容来间接调用LLVM功能。
  13. utils:用于LLVM编码约定的emacs和vim模式;一个Valgrind suppression文件,以Valgrind监视所有子进程的方式在运行make check时消除误报;支持单元测试的lit和FileCheck工具;还有很多其他随机的东西。

以下是跳过的“lib”目录,它包含了所有的重要内容,现在看一下它的子目录:

  1. Analysis包含许多静态Analysis,可以在编译器教科书中阅读这些Analysis,例如别名分析和全局值编号。某些Analysis是基于LLVM pass,必须由pass manager运行。其他的是基于library,能够直接调用。Analysis中一个奇怪的成员是InstructionSimplify.cpp,这是一个转换器,而不是分析器;我相信有人可以发表评论来解释它在这里做了什么(见评论)。我将在后续帖子中深入研究这个目录。
  2. AsmParser:将文本IR解析道内存中。
  3. Bitcode:将IR序列化为紧凑格式并将其读回RAM。
  4. CodeGen:LLVM目标无关的代码生成器,基本上是LLVM后端适配的一个框架,也是后端可以使用的一堆库函数。这里有太多太多东西了(>100 KLOC)不幸作者也不完全了解。
  5. DebugInfo是一个用户维护LLVM指令和源代码位置之间映射的库。在2014年LLVM开发者大会的演讲中,这些slides中有很多有用的信息。
  6. ExecutionEngine:虽然LLVM通常被转换为汇编代码或机器码,但它可以使用解释器直接执行。作者最后一次使用它时,non-jitting解释器并不是十分有效,但是无论如何它比运行jitted代码慢地多。最新的JIT API
  7. Fuzzer:这是一个类似于AFL的coverage-guided fuzzer。它不是fuzz LLVM组件,而是作为LLVM功能对由LLVM编译得到的程序进行模糊测试。
  8. IR:一些与IR相关代码,没有其他明显的统一主题。有用于将IR转换为文本格式的代码,有用于升级由早期版本的LLVM创建的bitcode文件,有用于在创建IR节点时折叠常量等。
  9. IRReader, LibDriver, LineEditor:nobody care about these.
  10. Linker:一个LLVM模块(就像C/C++的编译单元),包含函数和变量。LLVM Linker将多个模块组合到一个更大的模块中。
  11. LTO:链接时优化(许多博客文章和博士论文的主题)允许汇编优化联系独立编译所创建的边界。LLVM可以无花销地进行链接时优化,链接器创建一个大模块,然后使用常规优化PASS来优化。这曾经是首选的方法,但它不能扩展到大型项目。目前采用的方法是ThinLTO,它以很小的代价获得很大的效益。
  12. MC:编译器通常发出汇编代码,让汇编程序处理创建机器代码。LLVM中的MC子系统切断了中间过程并直接生成机器代码。这可以加快编译速度,在LLVM用作JIT编译器时尤其有用。
  13. Object:处理目标文件格式的详细信息,例如ELF。
  14. ObjectYAML:ObjectYAML似乎支持将对象文件编码为YAML
  15. Option:命令行解析。
  16. Passes:PASS管理器的一部分,它调度和排序LLVM PASS,将其依赖关系和无效性考虑在内。
  17. ProfileData:读取和写入配置文件数据以支持配置文件引导的优化。
  18. Support:其他支持代码包括APInts(在LLVM中普遍使用的任意精度整数)等等。
  19. TableGen:一种工具(作者称其为古怪的瑞士军刀),它输入.td文件(其中LLVM中有200多个)包含结构化数据,并使用特定于域的后端发出由LLVM编译的C++代码。例如,TableGen被用来提取一些实现汇编器和反汇编器中单调的东西。
  20. Target:这里有后端中指定特定处理器的部分。有很多TableGen文件。
  21. Transforms:这是我最喜欢的目录,它是中端优化器所在的地方。IPO包含跨越函数边界的过程间优化,它们通常不会过于激进,因为它们必须查看大量代码。InstCombine是LLVM的窥视优化器的野兽。Instrumentation支持sanitizers。ObjCARC支持这一点。Scalar包含一堆教科书式的编译器各种优化器,我会尝试在某个时候写一篇关于这个目录内容的更详细的帖子。Utils是帮助代码。Vectorize是LLVM的自动矢量化器,近年来已成为许多工作的主题。

它不会改变IR本身。llvm::SumplifyInstruction的规则是它只能返回程序中的常量或现有值,这符合Analysis的要求。在每条指令上调用SimplifyInstruction的PASS是lib/Transforms/Utils/SimplifyInstructions.cpp里的转换器PASS。