├── .gitignore
├── .idea
├── codeStyles
│ └── Project.xml
├── implement_llvm.iml
├── misc.xml
├── modules.xml
├── vcs.xml
└── workspace.xml
├── CMakeLists.txt
├── README.md
├── doc
├── 2004-01-30-CGO-LLVM.pdf
├── 2008-10-04-ACAT-LLVM-Intro.pdf
├── Intro to LLVM.md
├── Kaleidoscope Tutorial.md
├── LLVM Coding Standards.md
├── LLVM Language Reference Manual.md
├── LLVM Programmer’s Manual.md
├── LLVM_FrozenGene.pdf
├── SSA.md
├── The LLVM Target-Independent Code Generator.md
├── Writing an LLVM Pass.md
└── image
│ ├── Intro to LLVM
│ ├── InstallTime.png
│ ├── LLVMCompiler1.png
│ ├── LTO.png
│ ├── PassLinkage.png
│ ├── RetargetableCompiler.png
│ ├── SimpleCompiler.png
│ └── X86Target.png
│ └── SSA
│ ├── SSA_example1.1.png
│ ├── SSA_example1.2.png
│ └── SSA_example1.3.png
├── lib
├── CMakeLists.txt
├── Kaleidoscope_tutorial
│ ├── CMakeLists.txt
│ ├── include
│ │ └── KaleidoscopeJIT.h
│ └── src
│ │ ├── BuildingAJIT
│ │ ├── CMakeLists.txt
│ │ └── Chapter01
│ │ │ ├── CMakeLists.txt
│ │ │ ├── KaleidoscopeJIT.h
│ │ │ └── toy.cpp
│ │ ├── Chapter02
│ │ ├── CMakeLists.txt
│ │ └── toy.cpp
│ │ ├── Chapter03
│ │ ├── CMakeLists.txt
│ │ └── toy.cpp
│ │ ├── Chapter04
│ │ ├── CMakeLists.txt
│ │ └── toy.cpp
│ │ ├── Chapter05
│ │ ├── CMakeLists.txt
│ │ └── toy.cpp
│ │ ├── Chapter06
│ │ ├── CMakeLists.txt
│ │ └── toy.cpp
│ │ └── Chapter07
│ │ ├── CMakeLists.txt
│ │ └── toy.cpp
├── src_clang
│ └── CMakeLists.txt
└── src_llvm
│ └── CMakeLists.txt
└── main.cpp
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Compiled Object files
5 | *.slo
6 | *.lo
7 | *.o
8 | *.obj
9 |
10 | # Precompiled Headers
11 | *.gch
12 | *.pch
13 |
14 | # Compiled Dynamic libraries
15 | *.so
16 | *.dylib
17 | *.dll
18 |
19 | # Fortran module files
20 | *.mod
21 | *.smod
22 |
23 | .idea/
24 | .vscode/
25 | build/
26 | # Compiled Static libraries
27 | *.lai
28 | *.la
29 | *.a
30 | *.lib
31 | # *.txt
32 |
33 | # Executables
34 | *.exe
35 | *.out
36 | *.app
37 |
38 | cmake-build-debug/
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/.idea/implement_llvm.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 | project(implement_llvm)
3 | set(CMAKE_CXX_STANDARD 11)
4 |
5 | set(LLVM_DIR /media/cyoung/000E88CC0009670E/llvm/build_install/lib/cmake/llvm)
6 | #set(LLVM_DIR /home/cyoung/llvm/build_install/lib/cmake/llvm)
7 |
8 | #添加cmake指令
9 | set (CMAKE_MODULE_PATH ${LLVM_DIR})
10 | include(${LLVM_DIR}/AddLLVM.cmake)
11 |
12 |
13 |
14 |
15 | find_package(LLVM REQUIRED CONFIG)
16 | message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
17 | message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
18 | include_directories(${LLVM_INCLUDE_DIRS})
19 | add_definitions(${LLVM_DEFINITIONS})
20 |
21 |
22 |
23 | add_subdirectory(lib)
24 |
25 |
26 | # Find the libraries that correspond to the LLVM components
27 | # that we wish to use
28 | llvm_map_components_to_libnames(llvm_libs support core irreader)
29 |
30 | # Link against LLVM libraries
31 | #target_link_libraries(implement_llvm ${llvm_libs})
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # implement_llvm
2 |
3 | 环境:ubuntu16.04 , Jetbrain Clion IDE
4 |
5 | 网上的入门教程很多,[@snsn1984](https://blog.csdn.net/snsn1984/article/details/81283070),但是没有一个手把手上手实践的教程,最近几天倒腾llvm,创建了第一个实践项目,下面就将把这个过程梳理下来,供小白参考
6 |
7 | ## 1.下载源码,编译
8 |
9 | [llvm官方文档](http://llvm.org/docs/GettingStarted.html) [clang官方文档](http://clang.llvm.org/get_started.html),比较详细,但是太冗长,下边给出简单方法:
10 |
11 | ### git clone源码:
12 |
13 | ```shell
14 | cd youLLVM_Path/
15 | #llvm源码
16 | git clone https://git.llvm.org/git/llvm.git/
17 | cd llvm/tools
18 | #clang源码,放在llvm/tools下
19 | git clone https://git.llvm.org/git/clang.git/
20 | cd ../projects
21 | #compiler-rt源码,放在llvm/projects下
22 | git clone https://git.llvm.org/git/compiler-rt.git/
23 | git clone https://git.llvm.org/git/openmp.git/
24 | ```
25 |
26 | ### 编译源码
27 |
28 | ```shell
29 | cd ..
30 | #来到llvm root目录下
31 | mkdir build
32 | cd build
33 | cmake -G "Unix Makefiles" ..
34 | make -j4
35 | ```
36 |
37 | 现在源码llvm已经来到8.0.0版本,以上编译默认debug模式,编译完成需要50G存储空间,所以编译之前需要保证磁盘空间充足,加上后面安装需要的35G,所以需要为llvm保留100G左右的磁盘空间
38 |
39 | ## 2.安装
40 |
41 | 由于之前安装tvm时的需要,系统本身已经安装了llvm6.0,[安装方式](https://apt.llvm.org/):
42 |
43 | ```shell
44 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
45 | # Fingerprint: 6084 F3CF 814B 57C1 CF12 EFD5 15CF 4D18 AF4F 7421
46 | sudo apt-get install clang-6.0 lldb-6.0 lld-6.0
47 | ```
48 |
49 | 如果想将以上编译结果直接安装到系统中,很简单,但是不建议,因为消耗的系统空间太大
50 |
51 | ```shell
52 | #不建议!!!
53 | # build 目录下:
54 | sudo make install
55 | ```
56 |
57 | 于是乎,想将源码安装在指定目录,为这一步,一个小白倒腾了整整一天,最后还是在[官网](http://llvm.org/docs/CMake.html)找到了答案:
58 |
59 | 现在假设安装在llvm root目录下的build_install下:
60 |
61 | ```shell
62 | cd llvmRootPath/
63 | #创建安装目录
64 | mkdir build_install
65 | cd build
66 | #安装到指定目录
67 | cmake -DCMAKE_INSTALL_PREFIX=/llvmRootPath/build_install -P cmake_install.cmake
68 | ```
69 |
70 | 此时已经将llvm安装到build_install下
71 |
72 | ## 3.创建第一个llvm项目
73 |
74 | 打开clion,新建项目,项目名: implement_llvm
75 |
76 | 如何在CMakeList.txt中引用项目,答案还是在[官网](http://llvm.org/docs/CMake.html#embedding-llvm-in-your-project)
77 |
78 | ```cmake
79 | cmake_minimum_required(VERSION 3.4.3)
80 | project(SimpleProject)
81 |
82 | find_package(LLVM REQUIRED CONFIG)
83 |
84 | message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
85 | message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
86 |
87 | # Set your project compile flags.
88 | # E.g. if using the C++ header files
89 | # you will need to enable C++11 support
90 | # for your compiler.
91 |
92 | include_directories(${LLVM_INCLUDE_DIRS})
93 | add_definitions(${LLVM_DEFINITIONS})
94 |
95 | # Now build our tools
96 | add_executable(simple-tool tool.cpp)
97 |
98 | # Find the libraries that correspond to the LLVM components
99 | # that we wish to use
100 | llvm_map_components_to_libnames(llvm_libs support core irreader)
101 |
102 | # Link against LLVM libraries
103 | target_link_libraries(simple-tool ${llvm_libs})
104 |
105 | ```
106 |
107 | 但是,重点来了,由于之前系统安装了llvm6.0,cmake会优先在系统中找到llvm6.0,如何调用以上源码安装库,一个小白又倒腾了半天
108 |
109 | 官网一句话:The `find_package(...)` directive when used in CONFIG mode (as in the above example) will look for the `LLVMConfig.cmake` file in various locations (see cmake manual for details),It creates a `LLVM_DIR` cache entry to save the directory where `LLVMConfig.cmake` is found
110 |
111 | 大意是说:find_package采用config模式,会根据LLVM_DIR缓存条目(cache entry)来寻找`LLVMConfig.cmake` ,这个缓存条目是个什么玩意儿???懵逼
112 |
113 | 将环境变量中加入`export LLVM_DIR=/llvmRootPath/build_install`,等等一系列措施,最后找到了方法
114 |
115 | 在CMakeList.txt里加上一句话
116 |
117 | ```cmake
118 | cmake_minimum_required(VERSION 3.4.3)
119 | project(SimpleProject)
120 |
121 | #加上一句,这是config模式来寻找LLVMConfig.cmake的路径,添加即可
122 | set(LLVM_DIR /llvmRootPath/build_install/lib/cmake/llvm)
123 |
124 | find_package(LLVM REQUIRED CONFIG)
125 | #-----后面省略-----
126 | ```
127 |
128 | 于是乎,cmake就可以快乐的找到llvm8.0.0了,第一个项目创建完成
129 |
130 | 踩坑到此为止,后面利用此项目来学习llvm.欢迎交流.
131 |
132 | ## 4.[llvm tutotial](https://llvm.org/docs/tutorial/index.html#kaleidoscope-implementing-a-language-with-llvm)
133 |
134 | 利用google翻译的[中文文档](https://github.com/Cyoung7/implement_llvm/blob/master/doc/Kaleidoscope%20Tutorial.md)(待校对)
135 |
136 | ## 5.[LLVM Programmer’s Manual](https://llvm.org/docs/ProgrammersManual.html)
137 |
138 | llvm编程手册,利用google翻译的[中文文档](https://github.com/Cyoung7/implement_llvm/blob/master/doc/LLVM%20Programmer%E2%80%99s%20Manual.md)(待校对)
139 |
140 | ## 6.[LLVM Language Reference Manual](https://llvm.org/docs/LangRef.html)
141 |
142 | llvm IR手册,利用google翻译的[中文文档](https://github.com/Cyoung7/implement_llvm/blob/master/doc/LLVM%20Language%20Reference%20Manual.md)(待校对)
143 |
144 |
--------------------------------------------------------------------------------
/doc/2004-01-30-CGO-LLVM.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cyoung7/implement_llvm/504660969f6366f453a655a27ec337d6c3f739f0/doc/2004-01-30-CGO-LLVM.pdf
--------------------------------------------------------------------------------
/doc/2008-10-04-ACAT-LLVM-Intro.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cyoung7/implement_llvm/504660969f6366f453a655a27ec337d6c3f739f0/doc/2008-10-04-ACAT-LLVM-Intro.pdf
--------------------------------------------------------------------------------
/doc/Intro to LLVM.md:
--------------------------------------------------------------------------------
1 | # llvm简介
2 |
3 | [原文链接](http://www.aosabook.org/en/llvm.html)
4 |
5 | [TOC]
6 |
7 | 本章讨论了一些在LLVM成型的设计决策,这是一个包含和开发一组紧密结合的低级工具链组件(例如,汇编器,编译器,调试器等)的伞状项目,它被设计成需要在类Unix系统上兼容已存在的工具。 “LLVM”这个名字曾经是一个缩写词,但现在只是伞形项目的一个品牌。 虽然LLVM提供了一些独特的功能,并且以其一些出色的工具而闻名(例如,Clang编译器 ,C / C ++ / Objective-C编译器,它提供了许多优于GCC编译器的优点),但是LLVM与其他编译器不同的主要是它的内部架构。
8 |
9 | 从2000年12月开始,LLVM被设计为一组具有良好定义接口的可重用库。 当时,开源编程语言实现被设计为通常具有单独可执行文件的专用工具。 例如,从静态编译器(例如,GCC)重用解析器进行静态分析或重构是非常困难的。 虽然脚本语言通常提供了将运行时和解释器嵌入到更大的应用程序中的方法,但是这个运行时是一个单个整体代码块,他可能被包含在内或排除在外。 没有办法重复使用片段,并且很少在语言实现项目中共享。
10 |
11 | 除了编译器本身的组成之外,流行语言实现的社区通常是强烈的两极化:一个实现通常提供传统的静态编译器,如GCC,Free Pascal和FreeBASIC, 或者它提供了一个解释器形式的运行时编译器或即时(JIT)编译器。 看到支持两者的语言实现非常罕见,即使他们这样做,通常也很少实现代码共享。
12 |
13 | 在过去的十年中,LLVM已经彻底改变了这种状况。 LLVM现在被用作实现各种静态和运行时编译语言的通用基础结构(例如,由GCC,Java,.NET,Python,Ruby,Scheme,Haskell,D支持的语言系列,以及无数的较小的语言已知语言)。 它还取代了各种各样的专用编译器,例如Apple的OpenGL堆栈中的运行时专用引擎和Adobe After Effects产品中的图像处理库。 最后,LLVM还被用于创建各种各样的新产品,其中最着名的可能是OpenCL GPU编程语言和运行时。
14 |
15 | ## 1.经典编译器设计
16 |
17 | 传统静态编译器(如大多数C编译器)最流行的设计是三阶段设计,其主要组件是前端,优化器和后端。 前端解析源代码,检查它是否有错误,并构建一个特定于语言的抽象语法树(AST)来表示输入代码。 AST可选地转换为新的表示以进行优化,优化器和后端在代码上运行。
18 |
19 | 
20 |
21 | 三阶段编译器的三个主要组件
22 |
23 | 优化器负责进行各种各样的转换以尝试改进代码的运行时间,例如消除冗余计算,并且通常或多或少地独立于语言和目标。 然后,后端(也称为代码生成器)将代码映射到目标指令集。 除了生成正确的代码外,它还负责生成充分利用硬件支持的体系结构的优秀代码。 编译器后端的公共部分包括指令选择,寄存器分配和指令调度。
24 |
25 | 该模型同样适用于解释器和JIT编译器。 Java虚拟机(JVM)也是此模型的一个实现,它使用Java字节码作为前端和优化器之间的接口。
26 |
27 | ### 1.1 这种设计的含义
28 |
29 | 当编译器决定支持多种源语言或目标体系结构时,这种经典设计的最重要的优势就来了。 如果编译器在其优化器中使用公共代码表示,则可以为任何可以编译到它的语言编写前端,并且可以为可以从中编译的任何目标编写后端,如下图所示:
30 |
31 | 
32 |
33 | 使用此设计,移植编译器以支持新的源语言(例如,Algol或BASIC)只需要实现新的前端,但现有的优化器和后端可以重用。 如果这些部分没有分开,那么实现新的源语言需要从头开始,因此支持`N`目标和`M`源语言需要N * M个编译器。
34 |
35 | 三阶段设计的另一个优点(直接来自可重定向性)是编译器服务于更广泛的程序员集合,而不是仅支持一种源语言和一种目标。 对于一个开源项目,这意味着可以从中抽取更大一群潜在的社区贡献者,这自然会导致编译器的更多增强和改进。 这就是为什么服务于许多社区的开源编译器(如GCC)倾向于生成比FreePASCAL等较窄的编译器更好的优化机器代码。 专有编译器的情况并非如此,其质量与项目的预算直接相关。 例如,英特尔ICC编译器因其生成的代码质量而广为人知,即使它服务于狭隘的受众。
36 |
37 | 三阶段设计的最后一个主要优势是实现前端所需的技能与优化器和后端所需的技能不同。 将这些分开使得“前端人员”更容易增强和维护他们的编译器部分。 虽然这是一个社会问题,而不是技术问题,但它在实践中很重要,特别是对于开源项目就是为了减少程序员的障碍。
38 |
39 | ### 1.2现有语言实现
40 |
41 | 虽然三阶段设计的好处在编译器教科书中引人注目并且有充分的文档记录,但实际上它几乎从未完全实现。 查看开源语言实现(当LLVM启动时),您会发现Perl,Python,Ruby和Java的实现不共享任何代码。 此外,像格拉斯哥Haskell编译器(GHC)和FreeBASIC这样的项目可以重新定位到多个不同的CPU,但是它们的实现非常特定于它们支持的一种源语言。 还部署了各种各样的专用编译器技术来实现JIT编译器,用于图像处理,正则表达式,图形卡驱动程序以及需要CPU密集型工作的其他子域.
42 |
43 | 还有,这个模型有三个主要的成功案例,第一个是Java和.NET虚拟机。 这些系统提供JIT编译器,运行时支持和定义良好的字节码格式。 这意味着任何可以编译为字节码格式的语言(数十种语言)可以再次利用优化器和JIT以及运行时。 需要权衡的是在运行时选择方面提供的实现灵活性很小:它们都强制采用JIT编译,垃圾回收以及使用非常特定的对象模型。 当遇到编译与该模型不匹配的语言(例如C)(例如,使用LLJVM项目)时,这会导致性能欠佳。
44 |
45 | 第二个成功案例可能是最不幸的,也是最常用的重用编译器技术的方法:将输入源转换为C代码(或其他语言)并通过现有的C编译器发送。 这允许重用优化器和代码生成器,提供良好的灵活性,控制运行时,并且前端实现者很容易理解,实现和维护。 不幸的是,这样做会妨碍异常处理的有效实现,提供糟糕的调试体验,减慢编译速度,并且对于需要保证尾调用(tail call)的语言(或C不支持的其他功能)可能会出现问题。
46 |
47 | 该模型的最终成功实施是GCC 。 GCC支持许多前端和后端,并且拥有活跃且广泛的贡献者社区。 GCC作为一个C编译器有着悠久的历史,它支持多个目标,并且支持其他几种语言。 随着岁月的流逝,GCC社区正在慢慢发展清洁设计。 从GCC 4.4开始,它有一个新的优化器表示(称为“GIMPLE元组”),它比前面更接近于与前端表示分离。 此外,它的Fortran和Ada前端使用干净的AST。
48 |
49 | 虽然非常成功,但这三种方法对它们的用途有很大限制,因为它们被设计为单独的应用程序。 比如,将GCC嵌入到其他应用程序中,将GCC用作运行时/ JIT编译器,或者在不引入编译器大部分的情况下提取和重用GCC片段是不现实的。 想要使用GCC的C ++前端进行文档生成,代码索引,重构和静态分析工具的人不得不将GCC用作以XML形式发送信息的独立应用程序,或编写插件以将外部代码注入GCC流程。
50 |
51 | GCC片段不能作为库重用的原因有很多,包括全局变量的泛滥使用,弱不变的常量,数据结构的设计不良,庞大的代码库,以及使用宏阻止代码库被编译以支持更多的前端/目标对。 然而,最难解决的问题是其早期设计和时代所固有的架构问题。 具体来说,GCC会遇到分层问题和抽象漏洞:后端遍历前端AST以生成调试信息,前端生成后端数据结构,整个编译器依赖于命令行接口设置的全局数据结构。
52 |
53 | ### 1.3 LLVM的代码中间表示 LLVM IR
54 |
55 | 有了历史背景和错误经验,让我们深入研究LLVM:其设计中最重要的一项便是LLVM中间表示(IR),它是用于表示编译器中的代码格式。 LLVM IR旨在托管您在编译器的优化器部分中找到的中级分析和转换。它的设计考虑了许多具体目标,包括支持轻量级运行时优化,跨函数/过程间优化,整个程序分析和积极的重组转换等。但最重要的是,它本身被定义为具有明确语义的第一类语言。 为了具体的展现,这是一个简单的`.ll`文件示例:
56 |
57 | ```
58 | define i32 @add1(i32 %a, i32 %b) {
59 | entry:
60 | %tmp1 = add i32 %a, %b
61 | ret i32 %tmp1
62 | }
63 |
64 | define i32 @add2(i32 %a, i32 %b) {
65 | entry:
66 | %tmp1 = icmp eq i32 %a, 0
67 | br i1 %tmp1, label %done, label %recurse
68 |
69 | recurse:
70 | %tmp2 = sub i32 %a, 1
71 | %tmp3 = add i32 %b, 1
72 | %tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3)
73 | ret i32 %tmp4
74 |
75 | done:
76 | ret i32 %b
77 | }
78 | ```
79 |
80 | 此LLVM IR对应于此C代码,它提供了两种不同的方法来添加整数:
81 |
82 | ```c
83 | unsigned add1(unsigned a, unsigned b) {
84 | return a+b;
85 | }
86 |
87 | // Perhaps not the most efficient way to add two numbers.
88 | unsigned add2(unsigned a, unsigned b) {
89 | if (a == 0) return b;
90 | return add2(a-1, b+1);
91 | }
92 | ```
93 |
94 | 从这个例子中可以看出,LLVM IR是一种类似RISC的低级虚拟指令集。 与真正的RISC指令集一样,它支持简单指令的线性序列,如加,减,比较和分支。 这些指令采用三种地址形式,这意味着它们需要一些输入并在不同的寄存器中产生结果。 LLVM IR支持标签,通常看起来像一种奇怪的汇编语言形式.
95 |
96 | 与大多数RISC指令集不同,LLVM是强类型的简单类型系统(例如, `i32`是`32位整数`, `i32**`是指向`32位整数指针`),并且机器的一些细节被抽象掉。 例如,调用约定通过`call`和`ret`指令以及显式参数进行抽象。 与机器代码的另一个显着区别是LLVM IR不使用一组固定的命名寄存器,它使用一组以`%`字符命名的无限临时值。
97 |
98 | 除了作为一种语言实现之外,LLVM IR实际上以三种同构形式的定义:上面的文本格式,还有由优化本身检查和修改的内存中的数据结构,以及高效且密集的磁盘二进制“bitcode”格式。 LLVM项目还提供了将磁盘格式从文本转换为二进制的工具: `llvm-as`将文本`.ll`文件组装成包含bitcode goop的`.bc`文件, `llvm-dis`将`.bc`文件转换为`.ll`文件。
99 |
100 | 编译器的中间表示很有意思,因为它可以是编译器优化器的“完美世界”:与编译器的前端和后端不同,优化器不受特定源语言或特定目标机器的约束。 另一方面,它必须有很好地服务:它必须被设计成易于前端使用,生成针对目标硬件充分优化的代码。
101 |
102 | #### 1.3.1 编写LLVM IR优化
103 |
104 | 为了对优化如何工作提供一些直观感受,下面通过一些例子。 有许多不同类型的编译器优化,因此很难提供能够解决任意问题的方法。 也就是说,大多数优化都遵循一个简单的三部分结构:
105 |
106 | - 寻找要转变的模式。
107 | - 验证匹配实例的转换是否安全/正确。
108 | - 进行转换,更新代码。
109 |
110 | 最简单的优化是对算术标识的模式匹配,例如:对于任何整数`X` , `X-X`为0, `X-0`为`X` , `(X*2)-X`为`X` 第一个问题是LLVM IR中的这些问题。 一些例子是:
111 |
112 | ```
113 | ⋮ ⋮ ⋮
114 | %example1 = sub i32 %a, %a
115 | ⋮ ⋮ ⋮
116 | %example2 = sub i32 %b, 0
117 | ⋮ ⋮ ⋮
118 | %tmp = mul i32 %c, 2
119 | %example3 = sub i32 %tmp, %c
120 | ⋮ ⋮ ⋮
121 | ```
122 |
123 | 对于这些“洞察”转换,LLVM提供了一个指令简化接口,通过各种其他更高级别的转换用作实用程序。 这些特定的转换在`SimplifySubInst`函数中,如下所示:
124 |
125 | ```c
126 | // X - 0 -> X
127 | if (match(Op1, m_Zero()))
128 | return Op0;
129 |
130 | // X - X -> 0
131 | if (Op0 == Op1)
132 | return Constant::getNullValue(Op0->getType());
133 |
134 | // (X*2) - X -> X
135 | if (match(Op0, m_Mul(m_Specific(Op1), m_ConstantInt<2>())))
136 | return Op1;
137 |
138 | …
139 |
140 | return 0; // Nothing matched, return null to indicate no transformation.
141 | ```
142 |
143 | 在此代码中,`Op0`和`Op1`绑定到整数减法指令的左右操作数(重要的是,这些标识不一定适用于IEEE浮点数!)。 LLVM是用C ++实现的,它的模式匹配功能并不为人所熟知(与Objective Caml等功能语言相比),但它提供了一个非常通用的模板系统,允许我们实现类似的东西。 `match`函数和`m_`函数允许我们对LLVM IR代码执行声明性模式匹配操作。 例如,如果乘法的左侧与`Op1`相同,则`m_Specific`谓词仅匹配。
144 |
145 | 总之,这三种情况都是模式匹配的,如果可以,函数返回替换,如果不可以替换,则返回空指针。 此函数的调用者( `SimplifyInstruction` )是一个调度程序,它对指令操作码进行切换,并调度到每操作码辅助函数。 它是从各种优化中调用的。 一个简单的驱动程序如下所示:
146 |
147 | ```c++
148 | for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I)
149 | if (Value *V = SimplifyInstruction(I))
150 | I->replaceAllUsesWith(V);
151 | ```
152 |
153 | 这段代码简单地循环遍历块中的每条指令,检查它们是否有任何简化。 如果可以(因为`SimplifyInstruction`返回非null),它会使用`replaceAllUsesWith`方法使用更简单的形式更新代码中的任何内容。
154 |
155 | ### 1.4 LLVM实现三阶段设计
156 |
157 | 在基于LLVM的编译器中,前端负责解析,验证和诊断输入代码中的错误,然后将解析的代码转换为LLVM IR(通常但不总是通过构建AST然后将AST转换为LLVM IR)。 该IR可选地通过一系列改进代码的分析和优化过程提供,然后被发送到代码生成器以生成本机机器代码, 如图所示。 这是三阶段设计的一个非常简单的实现,但是这个简单的描述掩盖了LLVM架构从LLVM IR派生的一些功能和灵活性。
158 |
159 | 
160 |
161 |
162 |
163 | #### 1.4.1 LLVM IR是完整的代码表示
164 |
165 | 特别是,LLVM IR既是明确指定的,也是优化器的*唯一*接口。 这个属性意味着为LLVM编写前端所需要知道的就是LLVM IR是什么,它是如何工作的,以及它所期望哪些不需要改变。 由于LLVM IR具有第一类的文本形式,因此构建时将LLVM IR作为文本输出的前端也是合理的,然后使用Unix管道通过您选择的优化器序列和代码生成器来发送它。
166 |
167 | 这可能是令人惊讶的,但这实际上是LLVM的一个非常新颖的特性,也是其在各种不同应用中取得成功的主要原因之一。 即使是广泛成功且相对精心设计的GCC编译器也没有这个属性:它的GIMPLE中级表示不是一个独立的表示。 举个简单的例子,当GCC代码生成器发出DWARF调试信息时,它会返回并遍历源级"树"形式。GIMPLE本身对代码中的操作使用“元组”表示,但(至少从GCC 4.5开始)仍将操作数表示为返回源级树形式的引用。
168 |
169 | 这意味着前端作者需要知道并生成GCC的树数据结构以及GIMPLE来编写GCC前端。 GCC后端有类似的问题,因此他们还需要了解RTL后端的工作原理。 最后,GCC没有办法转储“代表我代码的所有内容”,或者以文本形式读取和编写GIMPLE(以及构成代码表示的相关数据结构)的方法。 结果是,用GCC进行实验相对困难,因此它的前端相对较少。
170 |
171 | #### 1.4.2 LLVM是一个库集合
172 |
173 | 在LLVM IR的设计之后,LLVM的下一个最重要的方面是把它设计为一组库,而不是像GCC这样的单独命令行编译器或像JVM或.NET虚拟机那样的不透明虚拟机。 LLVM是一种基础结构,是一组有用的编译器技术,可以解决特定问题(例如构建C编译器或特殊效果管道中的优化器)。 虽然它LLVM最强大的功能之一,但也是人们最不了解的设计点之一。
174 |
175 | 让我们看看优化器设计的一个例子:它读取LLVM IR,稍微处理它,然后发出LLVM IR,希望能更快地执行。 在LLVM中(与许多其他编译器一样),优化器被组织为不同优化传递的管道,每个优化在输入上传递运行并且可以执行某些优化操作。 传递的常见示例是内联器(将函数体替换为调用站点),表达式重新关联,循环不变代码运动等。根据优化级别,运行不同的传递:例如在`-O0`(无优化) Clang编译器不运行任何传递,在`-O3`它在其优化器中运行总共67次传递(从LLVM 2.8开始)。
176 |
177 | 每个LLVM传递都被编写为C ++类,它从`Pass`类派生(间接)。 大多数传递都是用单个`.cpp`文件编写的,而`Pass`类的子类是在匿名命名空间中定义的(这使得它对定义文件完全私有)。 为了使传递有用,文件外部的代码必须能够获取它,因此从文件中导出单个函数(创建传递)。 这是一个略微简化的例子,用于使事情具体化。
178 |
179 | ```c++
180 | namespace {
181 | class Hello : public FunctionPass {
182 | public:
183 | // Print out the names of functions in the LLVM IR being optimized.
184 | virtual bool runOnFunction(Function &F) {
185 | cerr << "Hello: " << F.getName() << "\n";
186 | return false;
187 | }
188 | };
189 | }
190 |
191 | FunctionPass *createHelloPass() { return new Hello(); }
192 | ```
193 |
194 | 如上所述,LLVM优化器提供了许多不同的传递,每个传递都以类似的方式编写。 这些传递被编译成一个或多个`.o`文件,然后将这些文件构建到一系列静态文件(Unix系统上的`.a`文件)中。 这些库提供了各种各样的分析和转换功能,并且通道尽可能松散地耦合:如果它们依赖于其他分析来完成它们的工作,它们应该独立存在,或者在其他通道中明确声明它们的依赖关系。 当给出一系列要运行的传递时,LLVM PassManager使用显式依赖关系信息来满足这些依赖关系并优化传递的执行。
195 |
196 | 库和抽象功能很棒,但它们实际上并不能解决问题。 当有人想要构建一个可以从编译器技术中受益的新工具时,有趣的是,可能是用于图像处理语言的JIT编译器。 此JIT编译器的实现者在设计时考虑了一组约束:例如,图像处理语言可能对编译时延迟高度敏感,并且具有一些惯用语言属性,这些属性出于性能原因进行优化非常重要。
197 |
198 | LLVM优化器的基于库的设计允许我们的实现者选择执行传递的顺序,以及哪些对图像处理域有意义:如果所有内容都被定义为单个大函数,则它不会感觉浪费时间内联。 如果指针很少,别名分析和内存优化就不值得烦恼了。 然而,尽管我们尽最大努力,但LLVM并没有神奇地解决所有优化问题! 由于传递子系统是模块化的,并且PassManager本身对传递的内部不了解,因此实现者可以自由地实现他们自己的语言特定传递,以弥补LLVM优化器中的缺陷或明确的语言特定优化机会。 下图显示了我们假设的XYZ图像处理系统的一个简单示例:
199 |
200 | 
201 |
202 | 使用LLVM的假设XYZ系统
203 |
204 | 一旦选择了一组优化(并且对代码生成器做出了类似的决定),图像处理编译器就被构建到可执行或动态库中。 由于对LLVM优化传递的唯一引用是每个`.o`文件中定义的简单`create`函数,并且由于优化器位于`.a`静态文件中,因此只有*实际使用*的优化传递链接到最终应用程序,而不是整个LLVM优化器。 在上面的示例中,由于存在对PassA和PassB的引用,它们将被链接。由于PassB使用PassD进行一些分析,因此PassD被链接。但是,因为PassC(以及许多其他优化)未被使用,它的代码没有链接到图像处理应用程序。
205 |
206 | 这就是基于库设计的LLVM的强大功能。 这种简单的设计方法允许LLVM提供大量功能,其中一些功能可能仅对特定受众有用,而不会损害只想做简单事情的库的客户端。 相比之下,传统的编译器优化器是作为紧密互连的大量代码构建的,这对于子集,推理和加速来说要困难得多。 使用LLVM,您可以了解各个优化器,而无需了解整个系统如何组合在一起。
207 |
208 | 这种基于库的设计也是为什么很多人误解LLVM的原因:LLVM库有很多功能,但它们实际上*并*没有自己*做*任何事情。 由库的客户端(例如,Clang C编译器)的设计者来决定如何最好地使用这些部件。 这种仔细的分层,分解和关注子集的能力也是LLVM优化器可广泛用于不同环境,不同应用的原因。 此外,仅仅因为LLVM提供JIT编译功能,并不意味着每个客户端都使用它。
209 |
210 | ### 1.5 可重定向LLVM代码生成器的设计
211 |
212 | LLVM代码生成器负责将LLVM IR转换为目标特定的机器代码。 一方面,代码生成器的任务是为任何给定目标生成最佳机器代码。 理想情况下,每个代码生成器应该是针对特定目标硬件的完全独立实现,但另一方面,每个目标的代码生成器需要解决非常类似的问题。 例如,每个目标需要为寄存器分配值,尽管每个目标具有不同的寄存器文件,但应尽可能共享所使用的算法。
213 |
214 | 与优化器中的方法类似,LLVM的代码生成器将代码生成问题分解为单独的传递 - 指令选择,寄存器分配,调度,代码布局优化和汇编发射 - 并提供许多默认运行的内置传递。 然后,目标硬件作者有机会在默认传递中进行选择,覆盖默认值并根据需要实现完全自定义的特定于目标的传递。 例如,x86后端使用寄存器压力降低调度程序,因为它只有很少的寄存器,但PowerPC后端使用延迟优化调度程序,因为它有很多寄存器。 x86后端使用自定义传递来处理x87浮点堆栈,ARM后端使用自定义传递将常量池岛放置在需要的函数内。 这种灵活性允许目标作者生成高效的代码,而无需从头开始为其目标硬件编写整个代码生成器。
215 |
216 | #### 1.5.1 LLVM目标描述文件
217 |
218 | “混合和匹配”的方法允许目标硬件作者选择对其体系结构有意义的内容,并允许跨不同目标重用大量代码。 这带来了另一个挑战:每个共享组件都需要能够以通用方式推断目标特定属性。 例如,共享寄存器分配器需要知道每个目标硬件的寄存器文件以及指令与其寄存器操作数之间存在的约束。 LLVM的解决方案是为每个目标提供由tblgen工具处理的声明性特定语言(一组`.td`文件)的目标描述。 x86目标的(简化)构建过程[如图11.5](https://translate.googleusercontent.com/translate_c?act=url&depth=1&hl=zh-CN&ie=UTF8&prev=_t&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=http://www.aosabook.org/en/llvm.html&xid=17259,1500000,15700023,15700124,15700149,15700186,15700191,15700201&usg=ALkJrhinVvXb6fPVCPRe2wOKN2jF14NpYQ#fig.llvm.x86)所示。
219 |
220 | 
221 |
222 | `.td`文件支持的不同子系统允许目标作者构建其目标的不同部分。 例如,x86后端定义了一个寄存器类,它包含所有名为`GR32`的32位寄存器(在`.td`文件中,目标特定定义都是大写),如下所示:
223 |
224 | ```
225 | def GR32 : RegisterClass<[i32], 32,
226 | [EAX, ECX, EDX, ESI, EDI, EBX, EBP, ESP,
227 | R8D, R9D, R10D, R11D, R14D, R15D, R12D, R13D]> { … }
228 | ```
229 |
230 | 这个定义是说这个类中的寄存器可以保存32位整数值(“i32”),更喜欢32位对齐,具有指定的16个寄存器(在`.td`文件的其他地方定义)并有更多信息指定首选分配顺序和其他东西。 给定此定义,特定指令可以引用它,将其用作操作数。 例如,“完整32位寄存器”指令定义为:
231 |
232 | ```
233 | let Constraints = "$src = $dst" in
234 | def NOT32r : I<0xF7, MRM2r,
235 | (outs GR32:$dst), (ins GR32:$src),
236 | "not{l}\t$dst",
237 | [(set GR32:$dst, (not GR32:$src))]>;
238 | ```
239 |
240 | 这个定义说NOT32r是一个指令(它使用`I` tblgen类),指定编码信息( `0xF7, MRM2r` ),指定它定义一个“输出”32位寄存器`$dst`并具有一个32位寄存器“输入”名为`$src` (上面定义的`GR32`寄存器类定义哪些寄存器对操作数有效),指定指令的汇编语法(使用`{}`语法处理AT&T和Intel语法),指定指令的效果并提供它应该在最后一行匹配的模式。 第一行的“let”约束告诉寄存器分配器必须将输入和输出寄存器分配给同一物理寄存器。
241 |
242 | 这个定义是对指令的非常密集的描述,并且通用的LLVM代码可以使用从它派生的信息(通过`tblgen`工具)做很多`tblgen` 。 这个定义足以使指令选择通过编译器的输入IR代码上的模式匹配来形成该指令。 它还告诉寄存器分配器如何处理它,足以对指令进行编码和解码以加工代码字节,并且足以以文本形式解析和打印指令。 这些功能允许x86目标支持生成独立的x86汇编程序(它是“gas”GNU汇编程序的直接替代品)和目标描述中的反汇编程序以及处理JIT指令的编码。
243 |
244 | 除了提供有用的功能之外,具有由相同“真实”生成的多条信息也有其他原因。 这种方法使得汇编器和反汇编器在汇编语法或二进制编码中彼此不一致时几乎是不可行的。 它还使目标描述易于测试:指令编码可以进行单元测试,而不必涉及整个代码生成器。
245 |
246 | 虽然我们的目标是以一个很好的声明形式将尽可能多的目标信息放到`.td`文件中,但我们仍然没有解决所有问题。 相反,我们要求目标作者为各种支持例程编写一些C ++代码,并实现他们可能需要的任何目标特定传递(如`X86FloatingPoint.cpp` ,它处理x87浮点堆栈)。随着LLVM继续增长新目标,增加可以在`.td`文件中表达的目标数量变得越来越重要,并且我们继续增加`.td`文件的表达力来处理这个问题。一个很大的好处是随着时间的推移,它可以越来越容易地在LLVM中编写目标。
247 |
248 | ### 1.6 模块化设计提供的有用功能
249 |
250 | 除了优雅的设计外,模块化还为LLVM库的客户提供了一些有趣的功能。这些功能源于LLVM提供,但让客户决定如何使用它的大多数*策略*。
251 |
252 | #### 1.6.1 选择每个阶段运行的时间和地点
253 |
254 | 如前所述,LLVM IR可以有效地(反)序列化为/LLVM bitcode的二进制格式。由于LLVM IR是自包含的,并且序列化是一个无损过程,我们可以进行部分编译,将进度保存到磁盘,然后在将来的某个时间点继续工作。此特系能提供了许多有趣的功能,包括对链接时和安装时优化的支持,这两种功能都会从“编译时”延迟代码生成。
255 |
256 | 链接时优化(LTO)解决了编译器传统上一次只看到一个转换单元(例如,`.c`具有其所有头的文件)的问题,因此不能跨文件边界进行优化(如内联)。Clang等LLVM编译器使用`-flto`或`-O4`命令行选项支持此功能。此选项指示编译器向`.o`文件发出LLVM bitcode,而不是写出本机对象文件,并将代码生成延迟到链接时间,如图所示。
257 |
258 | 
259 |
260 | 细节因您所使用的操作系统而异,但重要的是链接器检测到它具有LLVM bitcode `.o`文件而不是本机对象文件。当它看到这一点时,它会将所有bitcode文件读入内存,将它们链接在一起,然后在聚合上运行LLVM优化器。由于优化器现在可以看到代码的更大部分,它可以内联,传播常量,执行更积极的死代码消除,以及更多跨文件边界。虽然许多现代编译器支持LTO,但大多数(例如,GCC,Open64,英特尔编译器等)都是通过昂贵且缓慢的序列化过程来实现的。在LLVM中,LTO自然地脱离了系统的设计,并且适用于不同的源语言(与许多其他编译器不同),因为IR是真正的中间源语言。
261 |
262 | 安装时优化是延迟代码生成的想法,甚至比链接时间晚,一直到安装时间,下图所示。安装时间是一个非常有趣的时间(如果软件在一个盒子中发货,下载,上传到移动设备等),因为这是当你找到你所针对的设备的细节时。例如,在x86系列中,存在各种各样的芯片和特性。通过延迟指令选择,调度和代码生成的其他方面,您可以为应用程序最终运行的特定硬件选择最佳答案。
263 |
264 | 
265 |
266 | #### 1.6.2 单元测试优化器
267 |
268 | 编译器非常复杂,质量很重要,因此测试至关重要。例如,在修复导致优化器崩溃的错误之后,应添加回归测试以确保它不会再次发生。测试这种方法的传统方法是编写一个`.c`通过编译器运行的文件(例如),并使用一个测试工具来验证编译器是否崩溃。例如,这是GCC测试套件使用的方法。
269 |
270 | 这种方法的问题在于编译器由许多不同的子系统组成,甚至包括优化器中的许多不同的传递,所有这些传递都有可能在到达之前有问题的代码时改变输入代码的表达意思。如果前端或早期优化器发生了某些变化,测试用例很容易无法测试它应该测试的内容。
271 |
272 | 通过使用LLVM IR的文本形式和模块化优化器,LLVM测试套件具有高度集中的回归测试,可以从磁盘加载LLVM IR,通过一次优化传递运行它,并验证预期的行为。除了崩溃之外,更复杂的行为测试想要验证实际执行了优化。这是一个简单的测试用例,它检查常量传播过程是否与添加指令一起使用:
273 |
274 | ```
275 | ; RUN: opt < %s -constprop -S | FileCheck %s
276 | define i32 @test() {
277 | %A = add i32 4, 5
278 | ret i32 %A
279 | ; CHECK: @test()
280 | ; CHECK: ret i32 9
281 | }
282 | ```
283 |
284 | 该`RUN`行指定要执行的命令:在这种情况下,`opt`和`FileCheck`命令行工具。该`opt`程序是LLVM传递管理器的简单包装器,它连接所有标准传递(并且可以动态加载包含其他传递的插件)并将它们公开到命令行。该`FileCheck`工具验证其标准输入是否与一系列`CHECK`指令匹配。在这种情况下,这个简单的测试是验证`constprop`传球将`add`4和5 折叠成9。
285 |
286 | 虽然这看起来像是一个非常简单的例子,但通过编写.c文件来测试很难:前端经常在解析时进行常量折叠,因此编写下游到常量的代码是非常困难和脆弱的折叠优化通过。因为我们可以将LLVM IR作为文本加载并通过我们感兴趣的特定优化传递发送它,然后将结果转储为另一个文本文件,对于回归和功能测试来说,确切地测试我们想要的内容真的很简单。
287 |
288 | #### 1.6.3 使用BugPoint自动减少测试用例
289 |
290 | 当在LLVM库的编译器或其他客户端中发现错误时,修复它的第一步是获得一个再现问题的测试用例。一旦有了测试用例,最好将其最小化为再现问题的最小示例,并将其缩小到发生问题的LLVM部分,例如故障时的优化传递。虽然您最终会学习如何执行此操作,但是对于编译器生成错误代码但不会崩溃的情况,此过程非常繁琐,手动且特别痛苦。
291 |
292 | LLVM BugPoint工具使用LLVM 的IR序列化和模块化设计来自动执行此过程。例如,给定一个输入`.ll`或`.bc`文件以及导致优化器崩溃的优化传递列表,BugPoint会将输入减少到一个小测试用例并确定哪个优化器出错。然后它输出简化的测试用例和`opt`用于重现失败的命令。它通过使用类似于“delta debugging”的技术来减少输入和优化器传递列表。因为它知道LLVM IR的结构,所以与标准的“delta”命令行工具不同,BugPoint不会浪费时间生成无效的IR来输入优化器。
293 |
294 | 在更复杂的错误编译情况下,您可以指定输入,代码生成器信息,传递给可执行文件的命令行以及参考输出。 BugPoint将首先确定问题是由优化器还是代码生成器引起的,然后将测试用例重复分为两部分:一部分被发送到“已知良好”组件,另一部分被发送到“已知错误” “ 零件。通过迭代地将越来越多的代码移出已发送到已知错误代码生成器的分区,它减少了测试用例。
295 |
296 | BugPoint是一个非常简单的工具,在LLVM的整个生命周期中节省了无数小时的测试用例。没有其他开源编译器具有类似功能强大的工具,因为它依赖于明确定义的中间表示。也就是说,BugPoint并不完美,并且会从重写中获益。它可以追溯到2002年,并且通常只有当某人有一个非常棘手的错误来追踪现有工具处理得不好时才会改进。它随着时间的推移而增长,在没有一致设计或所有者的情况下积累新功能(例如JIT调试)。
297 |
298 | ### 1.7 回顾和未来的方向
299 |
300 | LLVM的模块化最初并非旨在直接实现此处描述的任何目标。这是一种自卫机制:很明显,我们不会在第一次尝试时把一切都弄好。例如,存在模块化传递管道,以便更容易隔离传递,以便在被更好的实现替换后可以丢弃它们。
301 |
302 | LLVM保持灵活的另一个主要方面(以及与库使用者有争议的话题)是我们愿意重新考虑先前的决策并对API进行广泛的更改而不必担心向后兼容性。例如,对LLVM IR本身的侵入性更改需要更新所有优化过程并导致对C ++ API的大量改变。我们已经多次这样做了,尽管它会给客户带来痛苦,但保持快速前进是正确的。为了使外部客户端的生活更轻松(并支持其他语言的绑定),我们为许多流行的API(旨在非常稳定)提供C包装器,新版本的LLVM旨在继续读取旧的`.ll`和`.bc`文件。
303 |
304 | 展望未来,我们希望继续使LLVM更加模块化,更易于子集化。例如,代码生成器仍然过于单一:目前无法根据功能对LLVM进行子集化。例如,如果您想使用JIT,但不需要内联汇编,异常处理或调试信息生成,则应该可以构建代码生成器而无需链接以支持这些功能。我们还不断提高优化器和代码生成器生成的代码质量,添加IR功能以更好地支持新语言和目标构造,并为在LLVM中执行高级语言特定优化添加更好的支持。
305 |
306 | LLVM项目以多种方式不断发展和完善。看到LLVM在其他项目中使用的不同方式的数量以及它如何在设计师从未想过的令人惊讶的新环境中不断出现,真是令人兴奋。新的LLDB调试器就是一个很好的例子:它使用Clang的C / C ++ / Objective-C解析器来解析表达式,使用LLVM JIT将它们转换为目标代码,使用LLVM反汇编程序,并使用LLVM目标来处理调用约定等等。能够重用这些现有代码允许开发调试器的人专注于编写调试器逻辑,而不是重新实现另一个(边缘正确的)C ++解析器。
307 |
308 | 尽管迄今为止取得了成功,但仍有许多工作要做,以及随着年龄的增长LLVM将变得不那么灵活和更加钙化的风险始终存在。虽然这个问题没有神奇的答案,但我希望继续接触新的问题领域,重新评估以前的决策,重新设计和丢弃代码的意愿将有所帮助。毕竟,目标不是完美,而是随着时间的推移不断变得更好。
--------------------------------------------------------------------------------
/doc/LLVM Coding Standards.md:
--------------------------------------------------------------------------------
1 | # LLVM Coding Standards
2 |
3 | [原文链接](http://llvm.org/docs/CodingStandards.html)
4 |
5 | [TOC]
6 |
7 | ## Introduction
8 |
9 | ## Languages, Libraries, and Standards
10 |
11 | ## Mechanical Source Issues
12 |
13 | ## Style Issues
14 |
15 | ## See Also
16 |
17 |
--------------------------------------------------------------------------------
/doc/LLVM_FrozenGene.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cyoung7/implement_llvm/504660969f6366f453a655a27ec337d6c3f739f0/doc/LLVM_FrozenGene.pdf
--------------------------------------------------------------------------------
/doc/SSA.md:
--------------------------------------------------------------------------------
1 | # Static single assignment form
2 | *静态单一赋值形式*
3 | [链接](https://en.wikipedia.org/wiki/Static_single_assignment_form)
4 |
5 | 在编译器设计中,静态单一赋值形式(通常缩写为SSA形式或简称为SSA)是中间表示(IR)的属性,它要求每个变量只分配一次,并且每个变量在使用之前定义。原始IR中的存在变量以版本划分,新变量通常由原始名称在文本中用下标表示,以便每个定义都有自己的版本。在SSA形式中,use-def链是显式的,每个包含一个元素。
6 |
7 | SSA由Barry K. Rosen,Mark N. Wegman和F. Kenneth Zadeck于1988年提出。[1] Ron Cytron,Jeanne Ferrante和IBM的前三位研究人员开发了一种算法,可以有效地计算SSA形式。[2]
8 |
9 | 可以期望在Fortran或C的编译器中找到SSA,而在函数式语言编译器中,例如Scheme,ML和Haskell的编译器,通常使用连续传递样式(CPS)。 SSA在形式上等同于除了非本地控制流之外的良好行为的CPS子集,当CPS用作中间表示时不会发生这种情况。因此,以一个方式制定的优化和转换立即适用于另一个。
10 |
11 | ## Benefits
12 |
13 | SSA的主要用途来自于它如何通过简化变量的属性来同时简化和改进各种编译器优化的结果。 例如,考虑这段代码:
14 |
15 | ```
16 | y := 1
17 | y := 2
18 | x := y
19 | ```
20 |
21 | 人类可以看到第一个赋值是不必要的,并且在第三行中使用的y的值来自y的第二个赋值。 程序必须执行到达定义分析以确定这一点。 但如果该程序采用SSA形式,则这些都是即时的:
22 |
23 | ```
24 | y1 := 1
25 | y2 := 2
26 | x1 := y2
27 | ```
28 |
29 | 通过使用SSA启用或强大增强的编译器优化算法包括:
30 |
31 | - 常量折叠
32 | - value范围传播[3]
33 | - 稀疏条件常数传播
34 | - 无用代码消除
35 | - 全局变量编号
36 | - 部分冗余消除
37 | - 力量减少
38 | - 注册分配
39 |
40 | ## Converting to SSA
41 |
42 | 将普通代码转换为SSA形式主要是用新变量替换每个赋值的目标,并将变量的每个用法替换为到达该点的变量的“版本”。 例如,请考虑以下控制流程图:
43 |
44 | 
45 |
46 | 更改“x <-- x - 3”左侧的名称,并将x的以下用法更改为该新名称将使程序保持不变。 这可以在SSA中通过创建两个新变量来利用:x1和x2,每个变量只分配一次。 同样,为所有其他变量提供可区分的下标产生:
47 |
48 | 
49 |
50 | 很清楚每个用途所指的定义,除了一种情况:底部块中y的两个使用都可以指y1或y2,这取决于控制流采用的路径。
51 |
52 | 要解决此问题,会在最后一个块中插入一个特殊语句,称为Φ(Phi)函数。 该语句将通过“选择”y1或y2生成y的新定义,称为y3,具体取决于过去的控制流程。
53 |
54 | 
55 |
56 | 现在,最后一个块可以简单地使用y3,并且将以任一方式获得正确的值。不需要x的Φ函数:只有x的一个版本,即x2到达这个位置,所以没有问题(换句话说,Φ(x2,x2)= x2)。
57 |
58 | 给定一个任意的控制流图,很难说出插入Φ函数的位置以及哪些变量。这个一般性问题有一个有效的解决方案,可以使用一个称为优势边界的概念来计算(见下文)。
59 |
60 | Φ功能未在大多数机器上实现为机器操作。编译器可以简单地通过在存储器(或相同的寄存器)中使用与产生Φ功能的输入的任何操作的目的地相同的位置来实现Φ功能。然而,当同时操作推测性地产生对Φ功能的输入时,这种方法不起作用,如在宽问题机器上可能发生的那样。通常,广泛发布机器具有在这种情况下由编译器用于实现Φ功能的选择指令。
61 |
62 | 根据Kenny Zadeck [4]的说法,Φ函数最初被称为伪函数,而SSA是在20世纪80年代在IBM Research开发的。 Φ功能的正式名称仅在作品首次发表在学术论文中时才被采用。
63 |
64 | ### Computing minimal SSA using dominance frontiers(边界计算)
65 |
66 | 首先,我们需要一个dominator的概念:我们说如果不通过A第一个就不可能到达B,则节点A严格控制控制流图中的节点B.这很有用,因为如果我们到达B,我们就知道A中的任何代码都已运行。如果A严格支配B或A = B,我们说A支配B(B由A支配)。
67 |
68 | 现在我们可以定义支配边界:如果A不严格支配B,则节点B处于节点A的支配边界,但确实支配B的一些直接前任。(可能节点A是B的直接前身。然后,因为任何节点都支配自身而节点A占主导地位,节点B处于节点A的支配边界。)从A的角度来看,这些节点是其他控制路径(不通过A)最早出现的节点。
69 |
70 | 优势边界捕获我们需要Φ函数的精确位置:如果节点A定义了某个变量,那么该定义和该定义单独(或重新定义)将到达每个节点A占主导地位。只有当我们离开这些节点并进入支配边界时,我们必须考虑引入同一变量的其他定义的其他流量。此外,控制流程图中不需要其他Φ功能来处理A的定义,我们可以做到这一点。
71 |
72 | 计算优势边界集[5]的一种算法是:
73 |
74 | ```
75 | for each node b
76 | if the number of immediate predecessors of b ≥ 2
77 | for each p in immediate predecessors of b
78 | runner := p
79 | while runner ≠ idom(b)
80 | add b to runner’s dominance frontier set
81 | runner := idom(runner)
82 | ```
83 |
84 | 注意:在上面的代码中,节点n的前一个前导是控制转移到节点n的任何节点,而idom(b)是立即支配节点b(单个集合)的节点。
85 |
86 | 有一种有效的算法可以找到每个节点的优势边界。 该算法最初在Cytron等人中描述。 同样有用的是Andrew Appel的书“Java中的现代编译器实现”第19章(剑桥大学出版社,2002年)。 有关详细信息,请参阅该文章。
87 |
88 | Rice大学的Keith D. Cooper,Timothy J. Harvey和Ken Kennedy在他们的题为“简单,快速优势算法”的论文中描述了一种算法。[5] 该算法使用精心设计的数据结构来提高性能。
89 |
90 | ## Variations that reduce the number of Φ functions
91 |
92 | “最小”SSA插入所需的最少数量的Φ函数,以确保每个名称仅被赋值一次,并且原始程序中每个名称的引用(使用)仍然可以引用唯一名称。 (需要后一个要求以确保编译器可以在每个操作中记下每个操作数的名称。)
93 |
94 | 然而,其中一些Φ功能可能已经死亡。 因此,最小SSA不一定产生特定过程所需的最少数量的Φ功能。 对于某些类型的分析,这些Φ功能是多余的,可能导致分析效率降低。
95 |
96 | ### Pruned SSA
97 |
98 | 修剪的SSA形式基于简单的观察:仅在Φ函数之后“活”的变量需要Φ函数。 (这里,“实时”表示该值沿着从所讨论的Φ函数开始的某个路径使用。)如果变量不是实时的,则不能使用Φ函数的结果,并且Φ函数的赋值为死。
99 |
100 | 修剪的SSA形式的构造使用Φ函数插入阶段中的实时变量信息来确定是否需要给定的Φ函数。如果原始变量名称不在Φ功能插入点处,则不插入Φ功能。
101 |
102 | 另一种可能性是将修剪视为死代码消除问题。然后,只有当输入程序中的任何用途被重写到它时,或者如果它将用作另一个Φ函数中的参数时,Φ函数才是有效的。输入SSA表单时,每次使用都会被重写为最接近它的定义。然后,Φ函数将被认为是实时的,只要它是支配至少一个用途的最近定义,或至少一个活Φ的参数。
103 |
104 | ### Semi-pruned SSA
105 |
106 | 半修剪的SSA形式[6]试图减少Φ函数的数量而不会产生计算实时变量信息的相对高的成本。 它基于以下观察:如果变量在进入基本块时永远不会存在,则它永远不需要Φ函数。 在SSA构造期间,省略任何“块局部”变量的Φ函数。
107 |
108 | 计算块局部变量集是一种比完全实时变量分析更简单,更快速的过程,使得半修剪SSA形式比修剪SSA形式更有效。 另一方面,半修剪的SSA形式将包含更多的Φ函数。
109 |
110 | ## Converting out of SSA form
111 |
112 | SSA格式通常不用于直接执行(尽管可以解释SSA [7]),并且它经常在“另一个IR之上”使用它与之保持直接对应。这可以通过将SSA“构造”为一组函数来实现,这些函数在现有IR的部分(基本块,指令,操作数等)与其SSA对应物之间进行映射。当不再需要SSA格式时,可以丢弃这些映射函数,只留下现在优化的IR。
113 |
114 | 在SSA表单上执行优化通常会导致纠缠的SSA-Web,这意味着存在Φ指令,其操作数并非都具有相同的根操作数。在这种情况下,使用颜色输出算法来解决SSA问题。朴素算法沿着每个前任路径引入一个副本,这导致不同根符号的源被放入Φ而不是Φ的目的地。有多种算法可以用较少的副本从SSA中出来,大多数使用干扰图或一些近似它来进行复制合并。
115 |
116 | ## Extensions
117 |
118 | SSA格式的扩展可以分为两类。
119 |
120 | 重命名方案扩展会更改重命名标准。 回想一下,SSA表单在为每个变量赋值时重命名。 替代方案包括静态单一使用形式(在使用时在每个语句中重命名每个变量)和静态单一信息形式(在赋值时重命名每个变量,并在后支配边界处重命名)。
121 |
122 | 特定于功能的扩展保留变量的单一赋值属性,但包含新的语义以模拟其他功能。 一些特定于功能的扩展模拟高级编程语言功能,如数组,对象和别名指针。 其他特定于功能的扩展模拟了低级架构功能,如推测和预测。
--------------------------------------------------------------------------------
/doc/The LLVM Target-Independent Code Generator.md:
--------------------------------------------------------------------------------
1 | # The LLVM Target-Independent Code Generator
2 |
3 | [原文链接](https://llvm.org/docs/CodeGenerator.html)
4 |
5 | [TOC]
6 |
7 | ## Introduction
8 |
9 | ## Target description classes
10 |
11 | ## Machine code description classes
12 |
13 | ## The “MC” Layer
14 |
15 | ## Target-independent code generation algorithms
16 |
17 | ## Implementing a Native Assembler
18 |
19 | ## Target-specific Implementation Notes
20 |
21 |
--------------------------------------------------------------------------------
/doc/Writing an LLVM Pass.md:
--------------------------------------------------------------------------------
1 | # Writing an LLVM Pass
2 |
3 | Information on how to write LLVM transformations and analyses.(LLVM的转换和分析)
4 |
5 | ./gh-md-toc /media/cyoung/000E88CC0009670E/CLionProjects/implement_llvm/doc/Writing\ an\ LLVM\ Pass.md
6 |
7 | [原文链接](http://llvm.org/docs/WritingAnLLVMPass.html)
8 |
9 | [TOC]
10 |
11 | * [Writing an LLVM Pass](#writing-an-llvm-pass)
12 | * [Introduction — What is a pass?](#introduction--what-is-a-pass)
13 | * [Quick Start — Writing hello world](#quick-start--writing-hello-world)
14 | * [Setting up the build environment](#setting-up-the-build-environment)
15 | * [Basic code required](#basic-code-required)
16 | * [Running a pass with opt](#running-a-pass-with-opt)
17 | * [Pass classes and requirements](#pass-classes-and-requirements)
18 | * [The ImmutablePass class](#the-immutablepass-class)
19 | * [The ModulePass class](#the-modulepass-class)
20 | * [The runOnModule method](#the-runonmodule-method)
21 | * [The CallGraphSCCPass class](#the-callgraphsccpass-class)
22 | * [The doInitialization(CallGraph &) method](#the-doinitializationcallgraph--method)
23 | * [The runOnSCC method](#the-runonscc-method)
24 | * [The doFinalization(CallGraph &) method](#the-dofinalizationcallgraph--method)
25 | * [The FunctionPass class](#the-functionpass-class)
26 | * [The doInitialization(Module &) method](#the-doinitializationmodule--method)
27 | * [The runOnFunction method](#the-runonfunction-method)
28 | * [The doFinalization(Module &) method](#the-dofinalizationmodule--method)
29 | * [The LoopPass class](#the-looppass-class)
30 | * [The doInitialization(Loop *, LPPassManager &) method](#the-doinitializationloop--lppassmanager--method)
31 | * [The runOnLoop method](#the-runonloop-method)
32 | * [The doFinalization() method](#the-dofinalization-method)
33 | * [The RegionPass class](#the-regionpass-class)
34 | * [The doInitialization(Region *, RGPassManager &) method](#the-doinitializationregion--rgpassmanager--method)
35 | * [The runOnRegion method](#the-runonregion-method)
36 | * [The doFinalization() method](#the-dofinalization-method-1)
37 | * [The BasicBlockPass class](#the-basicblockpass-class)
38 | * [The doInitialization(Function &) method](#the-doinitializationfunction--method)
39 | * [The runOnBasicBlock method](#the-runonbasicblock-method)
40 | * [The doFinalization(Function &) method](#the-dofinalizationfunction--method)
41 | * [The MachineFunctionPass class](#the-machinefunctionpass-class)
42 | * [The runOnMachineFunction(MachineFunction &MF) method](#the-runonmachinefunctionmachinefunction-mf-method)
43 | * [Pass registration](#pass-registration)
44 | * [The print method](#the-print-method)
45 | * [Specifying interactions between passes](#specifying-interactions-between-passes)
46 | * [The getAnalysisUsage metho](#the-getanalysisusage-metho)
47 | * [The AnalysisUsage::addRequired<> and AnalysisUsage::addRequiredTransitive<> methods](#the-analysisusageaddrequired-and-analysisusageaddrequiredtransitive-methods)
48 | * [The AnalysisUsage::addPreserved<> method](#the-analysisusageaddpreserved-method)
49 | * [Example implementations of getAnalysisUsage](#example-implementations-of-getanalysisusage)
50 | * [The getAnalysis<> and getAnalysisIfAvailable<> methods](#the-getanalysis-and-getanalysisifavailable-methods)
51 | * [Implementing Analysis Groups](#implementing-analysis-groups)
52 | * [Analysis Group Concepts](#analysis-group-concepts)
53 | * [Using RegisterAnalysisGroup](#using-registeranalysisgroup)
54 | * [Pass Statistics](#pass-statistics)
55 | * [What PassManager does](#what-passmanager-does)
56 | * [The releaseMemory method](#the-releasememory-method)
57 | * [Registering dynamically loaded passes](#registering-dynamically-loaded-passes)
58 | * [Using existing registries](#using-existing-registries)
59 | * [Creating new registries](#creating-new-registries)
60 | * [Using GDB with dynamically loaded passes](#using-gdb-with-dynamically-loaded-passes)
61 | * [Setting a breakpoint in your pass](#setting-a-breakpoint-in-your-pass)
62 | * [Miscellaneous Problems](#miscellaneous-problems)
63 | * [Future extensions planned](#future-extensions-planned)
64 | * [Multithreaded LLVM](#multithreaded-llvm)
65 |
66 | ## Introduction — What is a pass?
67 |
68 | LLVM Pass框架是LLVM系统的重要组成部分,因为LLVM传递(transformations)存在着编译器大多数有趣部分。通过执行构成编译器的转换和优化,它们构建这些转换所使用到的分析结果,基于以上,它们是编译器代码的结构化技术。
69 |
70 | 所有LLVMpasses都是[Pass](http://llvm.org/doxygen/classllvm_1_1Pass.html)类的子类,它通过重写从Pass继承的虚方法来实现功能。根据pass的工作方式,您应该继承[ModulePass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-modulepass) , [CallGraphSCCPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-callgraphsccpass), [FunctionPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-functionpass) 或[LoopPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-looppass),或[RegionPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-regionpass)或 [BasicBlockPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-basicblockpass) 类,这样可以为系统提供有关pass操作的更多信息,以及如何与其他pass结合使用。 LLVM Pass Framework的一个主要特性是对您的pass有所约束(由它们派生的类指示)来让pass以高效的方式运行。
71 |
72 | 我们首先向您展示如何构建Pass,从设置代码到编译,加载和执行它。基础知识完成后,将讨论更多高级功能。
73 |
74 | ## Quick Start — Writing hello world
75 |
76 | 在这里,我们描述如何编写passes的“hello world”。 “Hello”pass旨在简单地打印出正在编译的程序中存在的非外部函数的名称。 它根本不修改程序,它只是检查它。 此pass的源代码和文件位于`lib/Transforms/Hello`目录中的LLVM源代码树中。
77 |
78 | ### Setting up the build environment
79 |
80 | 首先,配置和构建LLVM。 接下来,您需要在LLVM源代码库中的某个位置创建一个新目录。 对于这个例子,我们假设您创建了`lib/Transforms/Hello`。 最后,您必须设置一个构建脚本,该脚本将编译新pass的源代码。 为此,请将以下内容复制到CMakeLists.txt中:
81 |
82 | ```cmake
83 | add_llvm_loadable_module( LLVMHello
84 | Hello.cpp
85 | PLUGIN_TOOL
86 | opt
87 | )
88 | ```
89 |
90 | 和以下一行 `lib/Transforms/CMakeLists.txt`:
91 |
92 | ```cmake
93 | add_subdirectory(Hello)
94 | ```
95 |
96 | (请注意,已经有一个名为Hello的目录,带有示例“Hello”pass;您可以使用它 - 在这种情况下,您不需要修改任何 `CMakeLists.txt`文件 - 或者,如果您想从头开始创建所有内容 ,使用另一个名字。)
97 |
98 | 此构建脚本指定编译当前目录中的Hello.cpp文件并将其链接到共享对象`$(LEVEL)/lib/LLVMHello.so`,该工具可由opt工具通过其-load选项动态加载。 如果您的操作系统使用.so之外的后缀(例如Windows或Mac OS X),则将使用相应的扩展名。
99 |
100 | 现在我们已经设置了构建脚本,我们只需要为pass本身编写代码。
101 |
102 | ### Basic code required
103 |
104 | 现在我们有了编译新pass的方法,我们只需要编写它。 从以下开始:
105 |
106 | ```c++
107 | #include "llvm/Pass.h"
108 | #include "llvm/IR/Function.h"
109 | #include "llvm/Support/raw_ostream.h"
110 | ```
111 |
112 | 这是必需的,因为我们正在编写Pass,我们正在使用 [Function](http://llvm.org/doxygen/classllvm_1_1Function.html)s,我们将进行一些打印。
113 |
114 | 接下来我们有:
115 |
116 | ```c++
117 | using namespace llvm;
118 | ```
119 |
120 | ...这是必需的,因为包含文件中的函数存在于llvm命名空间中。
121 |
122 | 接下来我们有:
123 |
124 | ```c++
125 | namespace {
126 | ```
127 |
128 | ...开始一个匿名命名空间。 匿名命名空间是C++的“静态”关键字是C(在全局范围内)。 它使在匿名命名空间内声明的内容仅对当前文件可见。 如果您不熟悉它们,请参阅一本体面的C++书籍以获取更多信息。
129 |
130 | 接下来,我们声明我们的传递本身:
131 |
132 | ```c++
133 | struct Hello : public FunctionPass {
134 | ```
135 |
136 | 这声明了一个“Hello”类,它是[FunctionPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-functionpass)的子类。 稍后将详细描述不同的内置pass子类,但是现在,知道FunctionPass一次对一个函数进行操作。
137 |
138 | ```c++
139 | static char ID;
140 | Hello() : FunctionPass(ID) {}
141 | ```
142 |
143 | 这声明了LLVM用于标识pass的传递标识符。 这允许LLVM避免使用昂贵的C++运行时信息。
144 |
145 | ```c++
146 | bool runOnFunction(Function &F) override {
147 | errs() << "Hello: ";
148 | errs().write_escaped(F.getName()) << '\n';
149 | return false;
150 | }
151 | }; // end of struct Hello
152 | } // end of anonymous namespace
153 | ```
154 |
155 | 我们声明了一个 [runOnFunction](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-runonfunction)方法,它覆盖了从 [FunctionPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-functionpass)继承的抽象虚方法。 这是我们应该做的事情,所以我们只用每个函数的名称打印出我们的消息。
156 |
157 | ```c++
158 | char Hello::ID = 0;
159 | ```
160 |
161 | 我们在这里初始化通行证ID。 LLVM使用ID的地址来标识传递,因此初始化值并不重要。
162 |
163 | ```c++
164 | static RegisterPass X("hello", "Hello World Pass",
165 | false /* Only looks at CFG */,
166 | false /* Analysis Pass */);
167 | ```
168 |
169 | 最后,我们e [register our class](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-registration) Hello,给它一个命令行参数“hello”,并命名为“Hello World Pass”。 最后两个参数描述了它的行为:如果传递遍历CFG而不修改它,那么第三个参数设置为true; 如果pass是分析遍,例如dominator tree pass,则提供true作为第四个参数。
170 |
171 | 整体而言,.cpp文件如下所示:
172 |
173 | ```c++
174 | #include "llvm/Pass.h"
175 | #include "llvm/IR/Function.h"
176 | #include "llvm/Support/raw_ostream.h"
177 |
178 | using namespace llvm;
179 |
180 | namespace {
181 | struct Hello : public FunctionPass {
182 | static char ID;
183 | Hello() : FunctionPass(ID) {}
184 |
185 | bool runOnFunction(Function &F) override {
186 | errs() << "Hello: ";
187 | errs().write_escaped(F.getName()) << '\n';
188 | return false;
189 | }
190 | }; // end of struct Hello
191 | } // end of anonymous namespace
192 |
193 | char Hello::ID = 0;
194 | static RegisterPass X("hello", "Hello World Pass",
195 | false /* Only looks at CFG */,
196 | false /* Analysis Pass */);
197 | ```
198 |
199 | 现在它们一起使用,从构建目录的顶层使用简单的“gmake”命令编译该文件,您应该获得一个新文件“lib / LLVMHello.so”。 请注意,此文件中的所有内容都包含在匿名命名空间中 - 这反映了这样一个事实:pass是自包含的单元,不需要外部接口(尽管它们可以使用它们)。
200 |
201 | ### Running a pass with *opt*
202 |
203 | 现在您有了一个全新的闪亮共享对象文件,我们可以使用opt命令通过您的传递来运行LLVM程序。 因为您使用RegisterPass注册了您的通行证,所以一旦加载,您就可以使用opt工具来访问它。
204 |
205 | 要对其进行测试,请按照 [Getting Started with the LLVM System](https://llvm.org/docs/GettingStarted.html)末尾的示例将“Hello World”编译为LLVM。 我们现在可以通过这样的转换运行程序的bitcode文件(hello.bc)(或者当然,任何bitcode文件都可以):
206 |
207 | ```
208 | $ opt -load lib/LLVMHello.so -hello < hello.bc > /dev/null
209 | Hello: __main
210 | Hello: puts
211 | Hello: main
212 | ```
213 |
214 | [`-load`](https://llvm.org/docs/CommandGuide/lli.html#cmdoption-load)选项指定opt应该将您的传递加载为共享对象,这使得“-hello”成为有效的命令行参数(这是您需要 [register your pass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-registration))的一个原因)。 因为Hello pass不以任何有趣的方式修改程序,所以我们只丢弃opt的结果(将它发送到 /dev/null)。
215 |
216 | 要查看您注册的其他字符串发生了什么,请尝试使用[`-help`](https://llvm.org/docs/CommandGuide/FileCheck.html#cmdoption-help)选项运行opt:
217 |
218 | ```
219 | $ opt -load lib/LLVMHello.so -help
220 | OVERVIEW: llvm .bc -> .bc modular optimizer and analysis printer
221 |
222 | USAGE: opt [subcommand] [options]
223 |
224 | OPTIONS:
225 | Optimizations available:
226 | ...
227 | -guard-widening - Widen guards
228 | -gvn - Global Value Numbering
229 | -gvn-hoist - Early GVN Hoisting of Expressions
230 | -hello - Hello World Pass
231 | -indvars - Induction Variable Simplification
232 | -inferattrs - Infer set function attributes
233 | ...
234 | ```
235 |
236 | 传递名称将作为传递的信息字符串添加,为opt用户提供一些文档。 既然你有一个工作通行证,你就可以继续前进,让它做你想要的酷转换。 一旦你完成所有的工作和测试,找出你的传球速度可能会很有用。 [PassManager](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-passmanager)提供了一个很好的命令行选项(--time-passes),允许您获取有关传递执行时间的信息以及您排队的其他传递。 例如:
237 |
238 | ```shell
239 | $ opt -load lib/LLVMHello.so -hello -time-passes < hello.bc > /dev/null
240 | Hello: __main
241 | Hello: puts
242 | Hello: main
243 | ===-------------------------------------------------------------------------===
244 | ... Pass execution timing report ...
245 | ===-------------------------------------------------------------------------===
246 | Total Execution Time: 0.0007 seconds (0.0005 wall clock)
247 |
248 | ---User Time--- --User+System-- ---Wall Time--- --- Name ---
249 | 0.0004 ( 55.3%) 0.0004 ( 55.3%) 0.0004 ( 75.7%) Bitcode Writer
250 | 0.0003 ( 44.7%) 0.0003 ( 44.7%) 0.0001 ( 13.6%) Hello World Pass
251 | 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0001 ( 10.7%) Module Verifier
252 | 0.0007 (100.0%) 0.0007 (100.0%) 0.0005 (100.0%) Total
253 | ```
254 |
255 | 如您所见,我们上面的实现非常快。 opt工具会自动插入列出的其他passes,以验证passes发出的LLVM是否仍然有效并且格式良好的LLVM(尚未以某种方式被破坏)。
256 |
257 | 现在你已经看到了传递背后的机制的基础知识,我们可以讨论它们如何工作以及如何使用它们的更多细节。
258 |
259 | ## Pass classes and requirements
260 |
261 | 在设计新传递时,您应该做的第一件事就是确定应该为传递子类化的类。 [Hello World](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-basiccode)示例使用 [FunctionPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-functionpass)类进行实现,但我们没有讨论为什么或何时发生这种情况。 在这里,我们讨论可用的类,从最一般到最具体。
262 |
263 | 为Pass选择超类时,您应该选择最具体的类,同时仍然能够满足列出的要求。 这为LLVM Pass Infrastructure提供了优化passes运行所需的信息,因此生成的编译器不会非常慢。
264 |
265 | ### The ImmutablePass class
266 |
267 | 最简单和无聊的传递类型是“[ImmutablePass](http://llvm.org/doxygen/classllvm_1_1ImmutablePass.html)”类。 此传递类型用于不必运行的传递,不更改状态,永远不需要更新。 这不是正常类型的转换或分析,但可以提供有关当前编译器配置的信息。
268 |
269 | 虽然很少使用此传递类,但提供有关正在编译的当前目标机器的信息以及可能影响各种转换的其他静态信息非常重要。
270 |
271 | ImmutablePasses永远不会使其他转换无效,永远不会失效,永远不会“运行”。
272 |
273 | ### The ModulePass class
274 |
275 | [ModulePass](http://llvm.org/doxygen/classllvm_1_1ModulePass.html)类是您可以使用的所有超类中最常用的类。 从ModulePass派生表明您的传递使用整个程序作为一个单元,以无法预测的顺序引用函数体,或添加和删除函数。 因为对ModulePass子类的行为一无所知,所以不能对它们的执行进行优化。
276 |
277 | 模块传递可以使用函数级别传递(例如,支配者)使用`getAnalysis`接口`getAnalysis (llvm :: Function *)`来提供检索分析结果的函数,如果函数传递不需要任何模块或不可变传递。 注意,这只能对分析运行的函数进行,例如, 在统治者的情况下,你应该只要求DominatorTree进行函数定义,而不是声明。
278 |
279 | 要编写正确的ModulePass子类,请从ModulePass派生并使用以下签名重载runOnModule方法:
280 |
281 | #### The runOnModule method
282 |
283 | ```c++
284 | virtual bool runOnModule(Module &M) = 0;
285 | ```
286 |
287 | runOnModule方法执行pass的有趣工作。 如果模块被转换修改,它应该返回true,否则返回false。
288 |
289 | ### The CallGraphSCCPass class
290 |
291 | [CallGraphSCCPass](http://llvm.org/doxygen/classllvm_1_1CallGraphSCCPass.html)用于需要在调用图上自动遍历程序的传递(在调用者之前调用callees)。从CallGraphSCCPass派生提供了一些构建和遍历CallGraph的机制,但也允许系统优化CallGraphSCCPasses的执行。如果您的通行证符合下面列出的要求,并且不符合 [FunctionPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-functionpass) 或 [BasicBlockPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-basicblockpass),的要求,则应从CallGraphSCCPass派生。
292 |
293 | TODO:简要解释一下SCC,Tarjan的算法和B-U的含义。
294 |
295 | 为了明确,CallGraphSCCPass子类是:
296 |
297 | - ...不允许检查或修改除当前SCC以及SCC的直接呼叫者和直接被叫者之外的任何功能。
298 | - ...需要保留当前的CallGraph对象,更新它以反映对程序所做的任何更改。
299 | - ...不允许在当前模块中添加或删除SCC,但它们可能会更改SCC的内容。
300 | - ...允许在当前模块中添加或删除全局变量。
301 | - ...允许在runOnSCC(包括全局数据)的调用中维护状态。
302 |
303 | 在某些情况下,实现CallGraphSCCPass有点棘手,因为它必须处理具有多个节点的SCC。如果修改程序,下面描述的所有虚拟方法都应返回true,否则返回false。
304 |
305 | #### The doInitialization(CallGraph &) method
306 |
307 | ```
308 | virtual bool doInitialization(CallGraph &CG);
309 | ```
310 |
311 | `doInitialization`方法允许执行CallGraphSCCPas不允许执行的大部分操作。 它们可以添加和删除函数,获取函数指针等.doInitialization方法旨在执行不依赖于正在处理的SCC的简单初始化类型的东西。 doInitialization方法调用未安排与任何其他传递执行重叠(因此它应该非常快)。
312 |
313 | #### The runOnSCC method
314 |
315 | ```
316 | virtual bool runOnSCC(CallGraphSCC &SCC) = 0;
317 | ```
318 |
319 | runOnSCC方法执行pass的有趣工作,如果模块被转换修改则返回true,否则返回false。
320 |
321 | #### The doFinalization(CallGraph &) method
322 |
323 | ```
324 | virtual bool doFinalization(CallGraph &CG);
325 | ```
326 |
327 | doFinalization方法是一种不常用的方法,当传递框架为正在编译的程序中的每个SCC调用runOnSCC时调用该方法。
328 |
329 | ### The FunctionPass class
330 |
331 | 与ModulePass子类相比, [FunctionPass](http://llvm.org/doxygen/classllvm_1_1Pass.html) 子类确实具有系统可以预期的可预测的本地行为。 所有FunctionPass都独立于程序中的所有其他功能执行程序中的每个功能。 FunctionPasses不要求它们按特定顺序执行,FunctionPasses不要修改外部函数。
332 |
333 | 显而易见,不允许FunctionPass子类:
334 |
335 | - 检查或修改当前正在处理的功能以外的功能。
336 | - 在当前模块中添加或删除功能。
337 | - 在当前模块中添加或删除全局变量。
338 | - 跨[runOnFunction](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-runonfunction)调用(包括全局数据)维护状态。
339 |
340 | 实现FunctionPass通常很简单(例如,请参阅Hello World pass)。 FunctionPasses可能会使三个虚拟方法超载以完成其工作。 如果修改程序,所有这些方法都应返回true,否则返回false。
341 |
342 | #### The doInitialization(Module &) method
343 |
344 | ```c++
345 | virtual bool doInitialization(Module &M);
346 | ```
347 |
348 | doInitialization方法允许执行不允许FunctionPasses执行的大部分操作。 它们可以添加和删除函数,获取函数指针等.doInitialization方法旨在执行不依赖于正在处理的函数的简单初始化类型的东西。 doInitialization方法调用未安排与任何其他传递执行重叠(因此它应该非常快)。
349 |
350 | 如何使用此方法的一个很好的例子是 [LowerAllocations](http://llvm.org/doxygen/LowerAllocations_8cpp-source.html)传递。 此过程将malloc和free指令转换为依赖于平台的malloc()和free()函数调用。 它使用doInitialization方法获取对其所需的malloc和free函数的引用,如有必要,可将原型添加到模块中。
351 |
352 | #### The runOnFunction method
353 |
354 | ```c++
355 | virtual bool runOnFunction(Function &F) = 0;
356 | ```
357 |
358 | runOnFunction方法必须由您的子类实现,以执行传递的转换或分析工作。 像往常一样,如果修改了函数,则应返回true值。
359 |
360 | #### The doFinalization(Module &) method
361 |
362 | ```
363 | virtual bool doFinalization(Module &M);
364 | ```
365 |
366 | doFinalization方法是一种不经常使用的方法,当传递框架为正在编译的程序中的每个函数调用[runOnFunction](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-runonfunction)时调用该方法。
367 |
368 | ### The LoopPass class
369 |
370 | 所有LoopPass都在函数中的每个循环上执行,与函数中的所有其他循环无关。 LoopPass以循环嵌套顺序处理循环,以便最后处理最外层循环。
371 |
372 | 允许LoopPass子类使用LPPassManager接口更新循环嵌套。 实现循环传递通常很简单。 LoopPasses可能会使三个虚拟方法超载以完成其工作。 如果修改程序,所有这些方法都应该返回true,否则应该返回false。
373 |
374 | LoopPass子类旨在作为主循环传递管道的一部分运行,需要保留其他循环在其管道所需的所有相同的函数分析。 为了简化这一过程,LoopUtils.h提供了一个getLoopAnalysisUsage函数。 它可以在子类的getAnalysisUsage覆盖中调用,以获得一致和正确的行为。 类似地,INITIALIZE_PASS_DEPENDENCY(LoopPass)将初始化这组功能分析。
375 |
376 | #### The doInitialization(Loop *, LPPassManager &) method
377 |
378 | ```c++
379 | virtual bool doInitialization(Loop *, LPPassManager &LPM);
380 | ```
381 |
382 | doInitialization方法旨在执行不依赖于正在处理的函数的简单初始化类型的东西。 doInitialization方法调用未安排与任何其他传递执行重叠(因此它应该非常快)。 LPPassManager接口应用于访问功能或模块级别的分析信息。
383 |
384 | #### The runOnLoop method
385 |
386 | ```c++
387 | virtual bool runOnLoop(Loop *, LPPassManager &LPM) = 0;
388 | ```
389 |
390 | 您的子类必须实现runOnLoop方法才能执行传递的转换或分析工作。 像往常一样,如果修改了函数,则应返回true值。 LPPassManager接口应该用于更新循环嵌套。
391 |
392 | #### The doFinalization() method
393 |
394 | ```c++
395 | virtual bool doFinalization();
396 | ```
397 |
398 | doFinalization方法是一种不经常使用的方法,当传递框架为正在编译的程序中的每个循环调用runOnLoop时调用该方法。
399 |
400 | ### The RegionPass class
401 |
402 | RegionPass类似于 [LoopPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-looppass),但在函数中的每个单个条目单个退出区域上执行。 RegionPass以嵌套顺序处理区域,以便最后处理最外层区域。
403 |
404 | 允许RegionPass子类使用RGPassManager接口更新区域树。 您可以重载RegionPass的三个虚方法来实现您自己的区域传递。 如果修改程序,所有这些方法都应该返回true,否则应该返回false。
405 |
406 | #### The doInitialization(Region *, RGPassManager &) method
407 |
408 | ```c++
409 | virtual bool doInitialization(Region *, RGPassManager &RGM);
410 | ```
411 |
412 | doInitialization方法旨在执行不依赖于正在处理的函数的简单初始化类型的东西。 doInitialization方法调用未安排与任何其他传递执行重叠(因此它应该非常快)。 RPPassManager接口应用于访问功能或模块级别分析信息。
413 |
414 | #### The runOnRegion method
415 |
416 | ```c++
417 | virtual bool runOnRegion(Region *, RGPassManager &RGM) = 0;
418 | ```
419 |
420 | runOnRegion方法必须由您的子类实现,以执行传递的转换或分析工作。 像往常一样,如果修改了区域,则应返回真值。 应使用RGPassManager接口更新区域树。
421 |
422 | #### The doFinalization() method
423 |
424 | ```c++
425 | virtual bool doFinalization();
426 | ```
427 |
428 | doFinalization方法是一种不常用的方法,当pass框架为正在编译的程序中的每个区域调用runOnRegion时调用该方法。
429 |
430 | ### The BasicBlockPass class
431 |
432 | `BasicBlockPasses` 就像[FunctionPass’s](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-functionpass)一样,除了它们必须一次限制它们的检查和修改范围到一个基本块。 因此,他们不允许执行以下任何操作:
433 |
434 | - 修改或检查当前基本块之外的任何基本块。
435 | - 在 [runOnBasicBlock](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-runonbasicblock).的调用中维护状态。
436 | - 修改控制流图(通过更改终止符指令)
437 | - 任何禁止使用 [FunctionPasses](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-functionpass)的东西。
438 |
439 | BasicBlockPasses对传统的本地和“窥视孔”优化非常有用。 它们可能会覆盖[FunctionPass’s](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-functionpass) 所具有的相同的[doInitialization(Module &)](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-doinitialization-mod)和 [doFinalization(Module &)](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-dofinalization-mod) 方法,但也可以实现以下虚拟方法:
440 |
441 | #### The doInitialization(Function &) method
442 |
443 | ```c++
444 | virtual bool doInitialization(Function &F);
445 | ```
446 |
447 | `doInitialization` 方法允许执行`BasicBlockPass`es不允许执行的大部分操作,但FunctionPasses可以执行。 doInitialization方法旨在执行简单的初始化,该初始化不依赖于正在处理的BasicBlock。 doInitialization方法调用未安排与任何其他传递执行重叠(因此它应该非常快)。
448 |
449 | #### The runOnBasicBlock method
450 |
451 | ```c++
452 | virtual bool runOnBasicBlock(BasicBlock &BB) = 0;
453 | ```
454 |
455 | 重写此函数以执行BasicBlockPass的工作。 此功能不允许检查或修改参数以外的基本块,也不允许修改CFG。 如果修改了基本块,则必须返回true值。
456 |
457 | #### The doFinalization(Function &) method
458 |
459 | ```c++
460 | virtual bool doFinalization(Function &F);
461 | ```
462 |
463 | doFinalization方法是一种不经常使用的方法,当传递框架为正在编译的程序中的每个BasicBlock调用[runOnBasicBlock](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-runonbasicblock)时调用该方法。 这可用于执行按功能完成。
464 |
465 | ### The MachineFunctionPass class
466 |
467 | `MachineFunctionPass`是LLVM代码生成器的一部分,它在程序中每个LLVM函数的机器相关表示上执行。
468 |
469 | 代码生成器传递由TargetMachine :: addPassesToEmitFile和类似例程专门注册和初始化,因此通常不能从opt或bugpoint命令运行它们。
470 |
471 | MachineFunctionPass也是一个FunctionPass,因此适用于FunctionPass的所有限制也适用于它。 MachineFunctionPasses还有其他限制。 特别是,不允许MachineFunctionPasses执行以下任何操作:
472 |
473 | - 修改或创建任何LLVM IR指令,BasicBlocks,Arguments,Functions,GlobalVariables,GlobalAliases或Modules。
474 | - 修改当前正在处理的MachineFunction以外的MachineFunction。
475 | - 跨 [runOnMachineFunction](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-runonmachinefunction) (包括全局数据)的调用维护状态。
476 |
477 | #### The runOnMachineFunction(MachineFunction &MF) method
478 |
479 | ```c++
480 | virtual bool runOnMachineFunction(MachineFunction &MF) = 0;
481 | ```
482 |
483 | runOnMachineFunction可以被认为是MachineFunctionPass的主要入口点; 也就是说,您应该覆盖此方法以执行MachineFunctionPass的工作。
484 |
485 | 在Module中的每个MachineFunction上调用runOnMachineFunction方法,以便MachineFunctionPass可以对函数的机器相关表示执行优化。 如果您想获得正在使用的MachineFunction的LLVM函数,请使用MachineFunction的getFunction()访问器方法 - 但请记住,您不能从MachineFunctionPass修改LLVM函数或其内容。
486 |
487 | ### Pass registration
488 |
489 | 在Hello World示例传递中,我们说明了传递注册的工作原理,并讨论了使用它的一些原因以及它的用途。 在这里,我们讨论如何以及为什么通过注册。
490 |
491 | 如上所述,通过RegisterPass模板注册。 template参数是要在命令行上使用的传递的名称,用于指定应将传递添加到程序中(例如,使用opt或bugpoint)。 第一个参数是传递的名称,它将用于程序的 [`-help`](https://llvm.org/docs/CommandGuide/FileCheck.html#cmdoption-help)输出,以及-debug-pass选项生成的调试输出。
492 |
493 | 如果您希望传递易于转储,则应实现虚拟打印方法:
494 |
495 | #### The print method
496 |
497 | ```c++
498 | virtual void print(llvm::raw_ostream &O, const Module *M) const;
499 | ```
500 |
501 | 打印方法必须通过“分析”来实现,以便打印分析结果的人类可读版本。 这对于调试分析本身以及其他人来确定分析如何工作非常有用。 使用opt -analyze参数来调用此方法。
502 |
503 | llvm :: raw_ostream参数指定要在其上写入结果的流,Module参数提供指向已分析程序的顶级模块的指针。 但请注意,在某些情况下(例如从调试器调用Pass :: dump()),此指针可能为NULL,因此它只应用于增强调试输出,不应该依赖它。
504 |
505 | ### Specifying interactions between passes
506 |
507 | PassManager的主要职责之一是确保传递正确地相互交互。 因为PassManager试图 [optimize the execution of passes](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-passmanager),所以它必须知道传递如何相互交互以及各种传递之间存在哪些依赖关系。 为了跟踪这一点,每次传递都可以声明在当前传递之前需要执行的传递集,以及当前传递失效的传递。
508 |
509 | 通常,此功能用于要求在运行传递之前计算分析结果。 运行任意转换过程可以使计算的分析结果无效,这是失效集指定的。 如果传递未实现 [getAnalysisUsage](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-getanalysisusage)方法,则默认为没有任何先决条件传递,并使所有其他传递无效。
510 |
511 | #### The getAnalysisUsage metho
512 |
513 | ```c++
514 | virtual void getAnalysisUsage(AnalysisUsage &Info) const;
515 | ```
516 |
517 | 通过实现getAnalysisUsage方法,可以为转换指定必需和无效的集合。 实现应该在 [AnalysisUsage](http://llvm.org/doxygen/classllvm_1_1AnalysisUsage.html)对象中填写有关哪些传递是必需的而不是无效的信息。 要执行此操作,传递可以在AnalysisUsage对象上调用以下任何方法:
518 |
519 | #### The AnalysisUsage::addRequired<> and AnalysisUsage::addRequiredTransitive<> methods
520 |
521 | 如果你的传球需要执行前一个传球(例如分析),它可以使用这些方法之一安排在传球之前运行。 LLVM有许多不同类型的分析和传递,可以满足从DominatorSet到BreakCriticalEdges的范围。 例如,要求BreakCriticalEdges保证在运行pass时CFG中没有关键边缘。
522 |
523 | 一些分析[chain](https://llvm.org/docs/AliasAnalysis.html#aliasanalysis-chaining) 到其他分析来完成它们的工作。 例如,需要`AliasAnalysis `实现链接到其他别名分析过程。 在分析链的情况下,应使用addRequiredTransitive方法而不是addRequired方法。 这通知PassManager,只要需要通过,传递所需的传递应该是活动的。
524 |
525 | #### The AnalysisUsage::addPreserved<> method
526 |
527 | PassManager的一项工作是优化分析的运行方式和时间。特别是,它试图避免重新计算数据,除非它需要。出于这个原因,允许传递声明它们保留(即,它们不会使现有分析无效),如果它可用的话。例如,简单的常量折叠传递不会修改CFG,因此它不可能影响支配者分析的结果。默认情况下,假定所有传递都使所有其他传递无效。
528 |
529 | AnalysisUsage类提供了几种在与addPreserved相关的特定情况下有用的方法。特别是,可以调用setPreservesAll方法来指示传递根本不修改LLVM程序(对于分析也是如此),并且setPreservesCFG方法可以由更改程序中的指令但不修改CFG或终止符指令(请注意,此属性是为[BasicBlockPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-basicblockpass)隐式设置的)。
530 |
531 | addPreserved对于像BreakCriticalEdges这样的转换特别有用。这个过程知道如何更新一小组循环和支配者相关的分析(如果它们存在),所以它可以保留它们,尽管它会破坏CFG。
532 |
533 | #### Example implementations of getAnalysisUsage
534 |
535 | ```c++
536 | // This example modifies the program, but does not modify the CFG
537 | void LICM::getAnalysisUsage(AnalysisUsage &AU) const {
538 | AU.setPreservesCFG();
539 | AU.addRequired();
540 | }
541 | ```
542 |
543 | #### The getAnalysis<> and getAnalysisIfAvailable<> methods
544 |
545 | Pass :: getAnalysis <>方法由您的类自动继承,使您可以访问使用 [getAnalysisUsage](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-getanalysisusage)方法声明的通道。 它需要一个模板参数来指定所需的传递类,并返回对该传递的引用。 例如:
546 |
547 | class you want, and returns a reference to that pass. For example:
548 |
549 | ```c++
550 | bool LICM::runOnFunction(Function &F) {
551 | LoopInfo &LI = getAnalysis().getLoopInfo();
552 | //...
553 | }
554 | ```
555 |
556 | 此方法调用返回对所需传递的引用。 如果您尝试获取未在[getAnalysisUsage](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-getanalysisusage)实现中声明的分析,则可能会导致运行时断言失败。 此方法可以由run *方法实现调用,也可以由run *方法调用的任何其他本地方法调用。
557 |
558 | 模块级别传递可以使用此接口使用功能级别分析信息。 例如:
559 |
560 | ```c++
561 | bool ModuleLevelPass::runOnModule(Module &M) {
562 | //...
563 | DominatorTree &DT = getAnalysis(Func);
564 | //...
565 | }
566 | ```
567 |
568 | 在上面的示例中,在返回对所需传递的引用之前,传递管理器调用runOnFunction for DominatorTree。
569 |
570 | 如果您的传递能够更新分析(如上所述),则可以使用getAnalysisIfAvailable方法,该方法返回指向分析的指针(如果它处于活动状态)。 例如:
571 |
572 | ```
573 | if (DominatorSet *DS = getAnalysisIfAvailable()) {
574 | // A DominatorSet is active. This code will update it.
575 | }
576 | ```
577 |
578 | ### Implementing Analysis Groups
579 |
580 | 现在我们已经了解了如何定义传球,如何使用它们以及如何从其他传球中获得传球的基础知识,现在是时候让它变得更有趣了。到目前为止,我们看到的所有传递关系都非常简单:一次传递依赖于另一个特定传递,在它可以运行之前运行。对于许多应用来说,这很好,对于其他应用,需要更大的灵活性。
581 |
582 | 特别是,定义了一些分析,使得分析结果只有一个简单的界面,但有多种计算方法。例如,考虑别名分析。最琐碎的别名分析为任何别名查询返回“may alias”。最复杂的分析是流量敏感的,上下文敏感的过程间分析,可能需要花费大量的时间来执行(显然,这两个极端之间有很大空间用于其他实现)。为了干净地支持这种情况,LLVM Pass Infrastructure支持分析组的概念。
583 |
584 | #### Analysis Group Concepts
585 |
586 | Analysis Group是一个简单的界面,可以通过多个不同的传递来实现。分析组可以像传球一样被赋予人类可读的名称,但与传递不同,它们不需要从Pass类派生。分析组可以具有一个或多个实现,其中之一是“默认”实现。
587 |
588 | 客户端传递使用分析组,就像其他传递一样:AnalysisUsage :: addRequired()和Pass :: getAnalysis()方法。为了解决此要求, [PassManager](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-passmanager) 会扫描可用的传递以查看是否有任何分析组的实现可用。如果没有,则为要使用的传递创建默认实现。 [interaction between passes](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-interaction) 的所有标准规则仍然适用。
589 |
590 | 虽然 [Pass Registration](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-registration)对于正常传递是可选的,但是必须注册所有分析组实现,并且必须使用 [INITIALIZE_AG_PASS](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-registeranalysisgroup)模板来加入实现池。此外,必须使用 [RegisterAnalysisGroup](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-registeranalysisgroup)注册接口的默认实现。
591 |
592 | 作为分析组的具体实例,请考虑 [AliasAnalysis](http://llvm.org/doxygen/classllvm_1_1AliasAnalysis.html)分析组。别名分析界面( [basicaa](http://llvm.org/doxygen/structBasicAliasAnalysis.html) pass)的默认实现只做了一些简单的检查,不需要进行大量的计算分析(例如:两个不同的全局变量永远不能互为别名等)。使用 [AliasAnalysis](http://llvm.org/doxygen/classllvm_1_1AliasAnalysis.html)接口的传递(例如 [gvn](http://llvm.org/doxygen/classllvm_1_1GVN.html))不关心实际提供别名分析的实现,它们只使用指定的接口。
593 |
594 | 从用户的角度来看,命令就像正常一样工作。发出命令opt -gvn ...将导致basicaa类被实例化并添加到传递序列中。发出命令opt -somefancyaa -gvn ...将导致gvn传递使用somefancyaa别名分析(实际上并不存在,它只是一个假设的例子)。
595 |
596 | #### Using RegisterAnalysisGroup
597 |
598 | RegisterAnalysisGroup模板用于注册分析组本身,而INITIALIZE_AG_PASS用于将传递实现添加到分析组。 首先,应该注册一个分析组,并为其提供一个人类可读的名称。 与传递注册不同,没有为分析组接口本身指定命令行参数,因为它是“抽象的”:
599 |
600 | ```C++
601 | static RegisterAnalysisGroup A("Alias Analysis");
602 | ```
603 |
604 | 注册分析后,pass可以使用以下代码声明它们是接口的有效实现:
605 |
606 | ```c++
607 | namespace {
608 | // Declare that we implement the AliasAnalysis interface
609 | INITIALIZE_AG_PASS(FancyAA, AliasAnalysis , "somefancyaa",
610 | "A more complex alias analysis implementation",
611 | false, // Is CFG Only?
612 | true, // Is Analysis?
613 | false); // Is default Analysis Group implementation?
614 | }
615 | ```
616 |
617 | 这只显示了一个类FancyAA,它使用INITIALIZE_AG_PASS宏来注册和“加入”AliasAnalysis分析组。 分析组的每个实现都应该使用此宏加入。
618 |
619 | ```c++
620 | namespace {
621 | // Declare that we implement the AliasAnalysis interface
622 | INITIALIZE_AG_PASS(BasicAA, AliasAnalysis, "basicaa",
623 | "Basic Alias Analysis (default AA impl)",
624 | false, // Is CFG Only?
625 | true, // Is Analysis?
626 | true); // Is default Analysis Group implementation?
627 | }
628 | ```
629 |
630 | 这里我们展示如何指定默认实现(使用INITIALIZE_AG_PASS模板的最后一个参数)。 对于要使用的分析组,必须始终只有一个默认实现可用。 只有默认实现可以从ImmutablePass派生。 这里我们声明BasicAliasAnalysis传递是接口的默认实现。
631 |
632 | ## Pass Statistics
633 |
634 | [Statistic](http://llvm.org/doxygen/Statistic_8h_source.html)类旨在通过传递方式公开各种成功指标。 在命令行上启用[`-stats`](https://llvm.org/docs/CommandGuide/lli.html#cmdoption-stats)命令行选项时,将在运行结束时打印这些统计信息。 有关详细信息,请参阅“程序员手册”中的“统计”部分。
635 |
636 | ### What PassManager does
637 |
638 | [PassManager](http://llvm.org/doxygen/PassManager_8h_source.html) 类获取传递列表,确保正确设置其[prerequisites](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-interaction) ,然后调度传递以高效运行。所有运行传递的LLVM工具都使用PassManager来执行这些传递。
639 |
640 | PassManager做了两件主要的事情来尝试减少一系列传递的执行时间:
641 |
642 | - 分享分析结果。 PassManager尝试尽可能避免重新计算分析结果。这意味着要跟踪哪些分析已经可用,哪些分析失效,以及需要为通过运行哪些分析。工作的一个重要部分是PassManager跟踪所有分析结果的确切生命周期,允许它在不再需要时释放分配给保存分析结果的内存 [free memory](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-releasememory)。
643 |
644 | - 管道程序执行传递。 PassManager尝试通过将传递流水线化,从而在一系列传递中获得更好的缓存和内存使用行为。这意味着,给定一系列连续的[FunctionPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-functionpass),它将执行第一个函数上的所有[FunctionPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-functionpass),然后执行第二个函数上的所有[FunctionPass](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-functionpass)等等,直到整个程序运行完遍。
645 |
646 | 这改进了编译器的缓存行为,因为它一次只触及单个函数的LLVM程序表示,而不是遍历整个程序。它减少了编译器的内存消耗,因为,例如,一次只需要计算一个 [DominatorSet](http://llvm.org/doxygen/classllvm_1_1DominatorSet.html) 。这也使得将来可以实现一些 [interesting enhancements](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-smp) 功能。
647 |
648 | PassManager的有效性直接受到它所调度的传递行为的信息量的影响。例如,面对未实现的[getAnalysisUsage](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-getanalysisusage)方法,“保留”集合是故意保守的。如果不实施它将不会允许任何分析结果贯穿执行传递。
649 |
650 | PassManager类公开了一个--debug-pass命令行选项,这些选项对于调试传递执行,查看工作原理以及诊断何时应该保留比当前更多的分析非常有用。 (要获取有关--debug-pass选项的所有变体的信息,只需键入“opt -help-hidden”)。
651 |
652 | 例如,通过使用-debug-pass = Structure选项,我们可以看到 [Hello World](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-basiccode)传递如何与其他传递进行交互。让我们尝试使用gvn和licm传递:
653 |
654 | ```
655 | $ opt -load lib/LLVMHello.so -gvn -licm --debug-pass=Structure < hello.bc > /dev/null
656 | ModulePass Manager
657 | FunctionPass Manager
658 | Dominator Tree Construction
659 | Basic Alias Analysis (stateless AA impl)
660 | Function Alias Analysis Results
661 | Memory Dependence Analysis
662 | Global Value Numbering
663 | Natural Loop Information
664 | Canonicalize natural loops
665 | Loop-Closed SSA Form Pass
666 | Basic Alias Analysis (stateless AA impl)
667 | Function Alias Analysis Results
668 | Scalar Evolution Analysis
669 | Loop Pass Manager
670 | Loop Invariant Code Motion
671 | Module Verifier
672 | Bitcode Writer
673 | ```
674 |
675 | 此输出显示构建通道的时间。 在这里,我们看到GVN使用支配树信息来完成它的工作。 LICM传递使用自然循环信息,它也使用支配树。
676 |
677 | 在LICM传递之后,模块验证程序运行(由opt工具自动添加),它使用支配树来检查生成的LLVM代码是否格式正确。 注意,支配树计算一次,并由三遍共享。
678 |
679 | 让我们看看当我们在两次传递之间运行 [Hello World](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-basiccode)传递时这会如何变化:
680 |
681 | ```
682 | $ opt -load lib/LLVMHello.so -gvn -hello -licm --debug-pass=Structure < hello.bc > /dev/null
683 | ModulePass Manager
684 | FunctionPass Manager
685 | Dominator Tree Construction
686 | Basic Alias Analysis (stateless AA impl)
687 | Function Alias Analysis Results
688 | Memory Dependence Analysis
689 | Global Value Numbering
690 | Hello World Pass
691 | Dominator Tree Construction
692 | Natural Loop Information
693 | Canonicalize natural loops
694 | Loop-Closed SSA Form Pass
695 | Basic Alias Analysis (stateless AA impl)
696 | Function Alias Analysis Results
697 | Scalar Evolution Analysis
698 | Loop Pass Manager
699 | Loop Invariant Code Motion
700 | Module Verifier
701 | Bitcode Writer
702 | Hello: __main
703 | Hello: puts
704 | Hello: main
705 | ```
706 |
707 | 在这里,我们看到 [Hello World](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-basiccode)传递已经杀死了Dominator Tree传递,即使它根本不修改代码! 要解决此问题,我们需要在pass中添加以下 [getAnalysisUsage](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-getanalysisusage)方法:
708 |
709 | ```
710 | // We don't modify the program, so we preserve all analyses
711 | void getAnalysisUsage(AnalysisUsage &AU) const override {
712 | AU.setPreservesAll();
713 | }
714 | ```
715 |
716 | 现在当我们运行pass时,我们得到这个输出:
717 |
718 | ```
719 | $ opt -load lib/LLVMHello.so -gvn -hello -licm --debug-pass=Structure < hello.bc > /dev/null
720 | Pass Arguments: -gvn -hello -licm
721 | ModulePass Manager
722 | FunctionPass Manager
723 | Dominator Tree Construction
724 | Basic Alias Analysis (stateless AA impl)
725 | Function Alias Analysis Results
726 | Memory Dependence Analysis
727 | Global Value Numbering
728 | Hello World Pass
729 | Natural Loop Information
730 | Canonicalize natural loops
731 | Loop-Closed SSA Form Pass
732 | Basic Alias Analysis (stateless AA impl)
733 | Function Alias Analysis Results
734 | Scalar Evolution Analysis
735 | Loop Pass Manager
736 | Loop Invariant Code Motion
737 | Module Verifier
738 | Bitcode Writer
739 | Hello: __main
740 | Hello: puts
741 | Hello: main
742 | ```
743 |
744 | 这表明我们不会意外地使支配者信息无效,因此不必计算两次。
745 |
746 | #### The releaseMemory method
747 |
748 | ```c++
749 | virtual void releaseMemory();
750 | ```
751 |
752 | PassManager会自动确定何时计算分析结果,以及保留它们的时间。 因为传递对象本身的生命周期实际上是编译过程的整个持续时间,所以当它们不再有用时,我们需要一些方法来释放分析结果。 releaseMemory虚拟方法是执行此操作的方法。
753 |
754 | 如果您正在编写分析或任何其他保留大量状态的传递(供另一个“需要”传递并使用[getAnalysis](https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-getanalysis) 方法的pass),您应该实现releaseMemory,以便释放分配的内存来维护 内部状态。 在传递中下一次调用run *之前,在类的run *方法之后调用此方法。
755 |
756 | ## Registering dynamically loaded passes
757 |
758 | 在使用LLVM构建生产质量工具时,大小很重要,既用于分发,也用于在目标系统上运行时调整驻留代码大小。因此,期望选择性地使用一些通道,同时省略其他通道并保持稍后改变配置的灵活性。您希望能够完成所有这些,并向用户提供反馈。这是通行证登记发挥作用的地方。
759 |
760 | 传递注册的基本机制是MachinePassRegistry类和MachinePassRegistryNode的子类。
761 |
762 | MachinePassRegistry的实例用于维护MachinePassRegistryNode对象的列表。此实例维护列表并向命令行界面传达添加和删除。
763 |
764 | MachinePassRegistryNode子类的实例用于维护有关特定传递的信息。此信息包括命令行名称,命令帮助字符串以及用于创建传递实例的函数的地址。其中一个实例的全局静态构造函数向对应的MachinePassRegistry注册,静态析构函数取消注册。因此,在工具中静态链接的传递将在启动时注册。动态加载的传递将在加载时注册,并在卸载时注销。
765 |
766 | ### Using existing registries
767 |
768 | 有预定义的注册表来跟踪指令调度(RegisterScheduler)和寄存器分配(RegisterRegAlloc)机器通过。 这里我们将描述如何注册寄存器分配器机器通过。
769 |
770 | 实现你的寄存器分配器机器通行证。 在您的register allocator .cpp文件中添加以下include:
771 |
772 | ```
773 | #include "llvm/CodeGen/RegAllocRegistry.h"
774 | ```
775 |
776 | 同样在您的register allocator .cpp文件中,以下列形式定义创建者函数:
777 |
778 | ```
779 | FunctionPass *createMyRegisterAllocator() {
780 | return new MyRegisterAllocator();
781 | }
782 | ```
783 |
784 | 请注意,此函数的签名应与RegisterRegAlloc :: FunctionPassCtor的类型匹配。 在同一个文件中添加“安装”声明,格式如下:
785 |
786 | ```
787 | static RegisterRegAlloc myRegAlloc("myregalloc",
788 | "my register allocator help string",
789 | createMyRegisterAllocator);
790 | ```
791 |
792 | 请注意,帮助字符串之前的两个空格在 [`-help`](https://llvm.org/docs/CommandGuide/FileCheck.html#cmdoption-help)查询上产生了整洁的结果。
793 |
794 | ```
795 | $ llc -help
796 | ...
797 | -regalloc - Register allocator to use (default=linearscan)
798 | =linearscan - linear scan register allocator
799 | =local - local register allocator
800 | =simple - simple register allocator
801 | =myregalloc - my register allocator help string
802 | ...
803 | ```
804 |
805 | 就是这样。 用户现在可以自由使用-regalloc = myregalloc作为选项。 除了使用RegisterScheduler类之外,注册指令调度程序是类似的。 请注意,RegisterScheduler :: FunctionPassCtor与RegisterRegAlloc :: FunctionPassCtor明显不同。
806 |
807 | 要强制将寄存器分配器加载/链接到llc / lli工具,请将创建者函数的全局声明添加到Passes.h,并将“伪”调用行添加到llvm / Codegen / LinkAllCodegenComponents.h。
808 |
809 | ### Creating new registries
810 |
811 | 最简单的入门方法是克隆一个现有的注册表; 我们推荐llvm / CodeGen / RegAllocRegistry.h。 要修改的关键是类名和FunctionPassCtor类型。
812 |
813 | 然后你需要声明注册表。 示例:如果您的pass注册表是RegisterMyPasses,则定义:
814 |
815 | ```c++
816 | MachinePassRegistry RegisterMyPasses::Registry;
817 | ```
818 |
819 | 最后,为你的传递声明命令行选项。 例:
820 |
821 | ```
822 | cl::opt >
824 | MyPassOpt("mypass",
825 | cl::init(&createDefaultMyPass),
826 | cl::desc("my pass option help"));
827 | ```
828 |
829 | 这里的命令选项是“mypass”,createDefaultMyPass是默认创建者。
830 |
831 | ### Using GDB with dynamically loaded passes
832 |
833 | 不幸的是,使用带有动态加载传递的GDB并不像应该的那样容易。 首先,您不能在尚未加载的共享对象中设置断点,其次在共享对象中存在内联函数的问题。 以下是使用GDB调试传递的一些建议。
834 |
835 | 为了便于讨论,我将假设您正在调试由opt调用的转换,尽管此处描述的内容不依赖于此。
836 |
837 | #### Setting a breakpoint in your pass
838 |
839 | 你要做的第一件事就是在opt过程中启动gdb:
840 |
841 | ```
842 | gdb opt
843 | GNU gdb 5.0
844 | Copyright 2000 Free Software Foundation, Inc.
845 | GDB is free software, covered by the GNU General Public License, and you are
846 | welcome to change it and/or distribute copies of it under certain conditions.
847 | Type "show copying" to see the conditions.
848 | There is absolutely no warranty for GDB. Type "show warranty" for details.
849 | This GDB was configured as "sparc-sun-solaris2.6"...
850 | (gdb)
851 | ```
852 |
853 | 请注意,opt中包含大量调试信息,因此加载需要时间。 耐心点。 由于我们无法在传递中设置断点(共享对象直到运行时才加载),我们必须执行该过程,并在它调用我们的传递之前停止它,但是在它加载了共享对象之后。 最简单的方法是在PassManager :: run中设置断点,然后使用您想要的参数运行该进程:
854 |
855 | ```
856 | $ (gdb) break llvm::PassManager::run
857 | Breakpoint 1 at 0x2413bc: file Pass.cpp, line 70.
858 | (gdb) run test.bc -load $(LLVMTOP)/llvm/Debug+Asserts/lib/[libname].so -[passoption]
859 | Starting program: opt test.bc -load $(LLVMTOP)/llvm/Debug+Asserts/lib/[libname].so -[passoption]
860 | Breakpoint 1, PassManager::run (this=0xffbef174, M=@0x70b298) at Pass.cpp:70
861 | 70 bool PassManager::run(Module &M) { return PM->run(M); }
862 | (gdb)
863 | ```
864 |
865 | 一旦opt在PassManager :: run方法中停止,您现在可以在传递中自由设置断点,以便您可以跟踪执行或执行其他标准调试。
866 |
867 | #### Miscellaneous Problems
868 |
869 | 一旦掌握了基础知识,GDB就会遇到一些问题,一些是解决方案,一些是没有解决方案。
870 |
871 | 内联函数具有伪造的堆栈信息。通常,GDB在获取堆栈跟踪和单步执行内联函数方面做得非常好。但是,当动态加载传递时,它会以某种方式完全失去此功能。我所知道的唯一解决方案是取消内联函数(将其从类的主体移动到.cpp文件)。
872 | 重新启动程序会破坏断点。按照上述信息后,您已成功获得通行证中的一些断点。接下来你知道,你重新启动程序(即你再次键入“run”),然后开始得到关于断点不可设置的错误。我发现“修复”此问题的唯一方法是删除已在传递中设置的断点,运行程序,并在PassManager :: run中执行停止后重新设置断点。
873 | 希望这些技巧可以帮助解决常见的案例调试问题。如果您想提供自己的一些提示,请联系 [Chris](mailto:sabre%40nondot.org)。
874 |
875 | ### Future extensions planned
876 |
877 | 尽管LLVM Pass基础设施非常强大,并且做了一些漂亮的东西,但我们希望将来添加一些东西。 这是我们要去的地方:
878 |
879 | #### Multithreaded LLVM
880 |
881 | 多CPU机器变得越来越普遍,编译永远不够快:显然我们应该允许多线程编译器。 由于为上面的传递定义了语义(特别是它们不能在其run *方法的调用中维护状态),实现多线程编译器的一种很好的干净方法是让PassManager类创建每个传递对象的多个实例,并允许 单独的实例同时攻击程序的不同部分。
882 |
883 | 此实现将阻止每个传递必须实现多线程构造,只需要LLVM核心在几个位置(对于全局资源)具有锁定。 虽然这是一个简单的扩展,但我们根本没有时间(或多处理器机器,因此有理由)来实现它。 尽管如此,我们仍然保持LLVM准备好SMP,你也应该这样做。
--------------------------------------------------------------------------------
/doc/image/Intro to LLVM/InstallTime.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cyoung7/implement_llvm/504660969f6366f453a655a27ec337d6c3f739f0/doc/image/Intro to LLVM/InstallTime.png
--------------------------------------------------------------------------------
/doc/image/Intro to LLVM/LLVMCompiler1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cyoung7/implement_llvm/504660969f6366f453a655a27ec337d6c3f739f0/doc/image/Intro to LLVM/LLVMCompiler1.png
--------------------------------------------------------------------------------
/doc/image/Intro to LLVM/LTO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cyoung7/implement_llvm/504660969f6366f453a655a27ec337d6c3f739f0/doc/image/Intro to LLVM/LTO.png
--------------------------------------------------------------------------------
/doc/image/Intro to LLVM/PassLinkage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cyoung7/implement_llvm/504660969f6366f453a655a27ec337d6c3f739f0/doc/image/Intro to LLVM/PassLinkage.png
--------------------------------------------------------------------------------
/doc/image/Intro to LLVM/RetargetableCompiler.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cyoung7/implement_llvm/504660969f6366f453a655a27ec337d6c3f739f0/doc/image/Intro to LLVM/RetargetableCompiler.png
--------------------------------------------------------------------------------
/doc/image/Intro to LLVM/SimpleCompiler.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cyoung7/implement_llvm/504660969f6366f453a655a27ec337d6c3f739f0/doc/image/Intro to LLVM/SimpleCompiler.png
--------------------------------------------------------------------------------
/doc/image/Intro to LLVM/X86Target.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cyoung7/implement_llvm/504660969f6366f453a655a27ec337d6c3f739f0/doc/image/Intro to LLVM/X86Target.png
--------------------------------------------------------------------------------
/doc/image/SSA/SSA_example1.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cyoung7/implement_llvm/504660969f6366f453a655a27ec337d6c3f739f0/doc/image/SSA/SSA_example1.1.png
--------------------------------------------------------------------------------
/doc/image/SSA/SSA_example1.2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cyoung7/implement_llvm/504660969f6366f453a655a27ec337d6c3f739f0/doc/image/SSA/SSA_example1.2.png
--------------------------------------------------------------------------------
/doc/image/SSA/SSA_example1.3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cyoung7/implement_llvm/504660969f6366f453a655a27ec337d6c3f739f0/doc/image/SSA/SSA_example1.3.png
--------------------------------------------------------------------------------
/lib/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(Kaleidoscope_tutorial)
--------------------------------------------------------------------------------
/lib/Kaleidoscope_tutorial/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_custom_target(llvm_tutorial)
2 | set_target_properties(llvm_tutorial PROPERTIES FOLDER Examples)
3 |
4 |
5 | macro(add_kaleidoscope_chapter name)
6 | add_dependencies(llvm_tutorial ${name})
7 | add_llvm_example(${name} ${ARGN})
8 | endmacro(add_kaleidoscope_chapter name)
9 |
10 | add_subdirectory(src/BuildingAJIT)
11 | add_subdirectory(src/Chapter02)
12 | add_subdirectory(src/Chapter03)
13 | add_subdirectory(src/Chapter04)
14 | add_subdirectory(src/Chapter05)
15 | add_subdirectory(src/Chapter06)
16 | add_subdirectory(src/Chapter07)
--------------------------------------------------------------------------------
/lib/Kaleidoscope_tutorial/include/KaleidoscopeJIT.h:
--------------------------------------------------------------------------------
1 | #ifndef LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H
2 | #define LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H
3 |
4 | #include "llvm/ADT/STLExtras.h"
5 | #include "llvm/ADT/iterator_range.h"
6 | #include "llvm/ExecutionEngine/ExecutionEngine.h"
7 | #include "llvm/ExecutionEngine/JITSymbol.h"
8 | #include "llvm/ExecutionEngine/Orc/CompileUtils.h"
9 | #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
10 | #include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
11 | #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
12 | #include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
13 | #include "llvm/ExecutionEngine/SectionMemoryManager.h"
14 | #include "llvm/IR/DataLayout.h"
15 | #include "llvm/IR/Mangler.h"
16 | #include "llvm/Support/DynamicLibrary.h"
17 | #include "llvm/Support/raw_ostream.h"
18 | #include "llvm/Target/TargetMachine.h"
19 | #include
20 | #include