如何使用 LLVM 优化器 | 编译器
本文转载自《LLVM 之 IR 篇(3):如何使用 LLVM IR 优化器》,原文有删改。
摘要
本文基于 release/12.x
版本的 LLVM 源码,介绍了 LLVM IR 优化的基本概念以及 opt
工具的使用方法。从而,初步了解 LLVM IR 优化器以便更深入地研究相关内容。
LLVM IR 优化概述
根据优化发生的时期,LLVM IR 层面的优化可以分为如下两种:
- 编译期优化。
- 链接期优化(跨编译单元)。
根据不同的优化范围,LLVM IR 层面的优化可以分为如下三种:
- 过程内优化。每次仅对一个函数进行优化。
- 过程间优化。每次对一个编译单元进行优化。
- 链接期优化。通过
llvm-link
工具将多个编译单元合并成一个,从而每次可以对多个编译单元进行优化。
我们可以通过 opt
工具启用这些优化。它既可以根据不同的优化等级选项启用不同的优化流水线(即一系列按照特定顺序执行的优化),也可以单独启用指定的优化。
如何启用优化
启用不同优化等级的优化
命令格式(输出 LLVM IR 位码文件):
1 | opt -O<level> <bitcode-file or assembly-file> -o <bitcode-file> |
注:opt
工具的输入既可以是 LLVM IR 位码文件,也可以是 LLVM IR 汇编文件。
命令格式(输出 LLVM IR 汇编文件):
1 | opt -O<level> <bitcode-file or assembly-file> -S -o <assembly-file> |
需要注意的是, 无论启用的优化等级是什么,都不会对带 optnone
属性的函数生效。
在 LLVM 12.0.0 版本中,opt
工具支持的优化等级选项有:-O0
、-O1
、-O2
、-O3
、-Os
、-Oz
,如下表所示。
opt 支持的优化等级选项(在 LLVM 12.0.0 版本中) | 行为 |
---|---|
-O0 | 不优化 |
-O1 | 介于 -O0 和 -O2 之间的优化 |
-O2 | 启用大多数优化 |
-O3 | 与 -O2 类似,并启用试图使程序运行得更快的优化(代码大小可能会增加) |
-Os | 与 -O2 类似,并启用减少代码大小的优化 |
-Oz | 与 -O2、-Os 类似,并启用进一步减少代码大小的优化 |
示例:
1 | opt -O1 test_without_optnone.ll -o test_without_optnone_O1_opt.bc |
注: 通过手工删除后,test_without_optnone.ll 汇编文件中的函数都不带 optnone
属性。从而,可以验证 -O1
选项的优化效果。
启用基于传统 Pass 框架的优化
1) 启用 opt 工具自带的基于传统 Pass 框架的优化
命令格式:
1 | opt -<pass1-name> -<pass2-name> <bitcode-file or assembly-file> -o <bitcode-file> |
需要注意的是, opt
工具自带的基于传统 Pass 框架的优化,都不会对带 optnone
属性的函数生效。
示例:
1 | opt -mem2reg test_without_optnone.ll -o test_without_optnone_mem2reg_opt.bc |
2) 启用自定义的基于传统 Pass 框架的优化
命令格式(输出优化后的位码文件):
1 | opt -load <pass-shared-library> -<pass1-name> -<pass2-name> <bitcode-file or assembly-file> -o <bitcode-file> |
命令格式(不输出优化后的位码文件):
1 | opt -load <pass-shared-library> -<pass1-name> -<pass2-name> <bitcode-file or assembly-file> > /dev/null |
或
1 | opt -disable-output -load <pass-shared-library> -<pass1-name> -<pass2-name> <bitcode-file or assembly-file> |
注:对于不输出优化后的位码文件的情况,上述命令中添加 > /dev/null
重定向或者 -disable-output
选项都是为了避免以下内容的输出:
1 | WARNING: You're attempting to print out a bitcode file. |
需要注意的是, 自定义的基于传统 Pass 框架的优化,对带 optnone
属性的函数是生效的。
示例:
1 | opt -disable-output -load ~/git-projects/llvm-project/build_ninja/lib/LLVMFnArgCntPlugin.so -plugin.fnargcnt test_with_optnone.ll |
注:自定义的基于传统 Pass 框架的优化 plugin.fnargcnt
用于统计函数的参数个数。
输出结果如下:
1 | FnArgCntPass --- foo: 1 |
启用基于新 Pass 框架的优化
命令格式(不输出优化后的位码文件):
1 | opt -disable-output -passes=<pass1-name,pass2-name> <bitcode-file or assembly-file> |
需要注意的是, 基于新 Pass 框架的优化,无论是 opt
工具自带的还是自定义的,都不会对带 optnone
属性的函数生效。
示例:
1 | opt -disable-output -passes=fnargcnt test_without_optnone.ll |
注:自定义的基于新 Pass 框架的优化 fnargcnt
用于统计函数的参数个数。
输出结果如下:
1 | FnArgCntPass --- foo: 1 |
查看优化的统计信息
1) 使用 clang 驱动器时,查看优化的统计信息
命令格式(示例):
1 | clang -Xclang -print-stats -emit-llvm -O<level> <source-file> -c -o <bitcode-file> |
注:上述命令的输出结果中除了优化的统计信息之外,还有其他的统计信息。
示例:
1 | clang -Xclang -print-stats -emit-llvm -O1 test.c -c -o test_O1_clang.bc |
输出结果如下(部分):
1 | 省略 ... |
2) 使用 opt 工具时,查看优化的统计信息
命令格式(示例):
1 | opt -stats -<pass-name> <bitcode-file or assembly-file> -o <bitcode-file> |
示例:
1 | opt -stats -mem2reg test_without_optnone.ll -o test_without_optnone_mem2reg_opt.bc |
输出结果如下:
1 | ===-------------------------------------------------------------------------=== |
查看实际启用的优化及依赖关系
命令格式(示例):
1 | opt -debug-pass=Structure -<pass-name> <bitcode-file or assembly-file> -o <bitcode-file> |
示例:
1 | opt -debug-pass=Structure -mem2reg test_without_optnone.ll -o test_without_optnone_O1_opt.bc |
输出结果如下:
1 | Pass Arguments: -targetlibinfo -tti -targetpassconfig -assumption-cache-tracker -domtree -mem2reg -verify -write-bitcode |
注:
- 实际启用的优化在
Pass Arguments
中列出。比如:targetlibinfo
、tti
、domtree
、mem2reg
等。 - 在 FunctionPass Manager 中,优化的执行先后顺序依次为:Dominator Tree Construction、Promote Memory to Register。这表明,优化
mem2reg
依赖于domtree
。
查看优化的执行时间
1) 使用 clang 驱动器时,查看优化的执行时间
命令格式(示例):
1 | clang -Xclang -ftime-report -emit-llvm -O<level> <source-file> -c -o <bitcode-file> |
注:上述命令的输出结果中除了优化的执行时间之外,还有其他的执行时间。
示例:
1 | clang -Xclang -ftime-report -emit-llvm -O1 test.c -c -o test_O1_clang.bc |
输出结果如下(部分):
1 | ===-------------------------------------------------------------------------=== |
2) 使用 opt 工具时,查看优化的执行时间
命令格式(示例):
1 | opt -time-passes -O<level> <bitcode-file or assembly-file> -o <bitcode-file> |
示例:
1 | opt -time-passes -O1 test_without_optnone.ll -o test_without_optnone_O1_opt.bc |
输出结果如下(部分):
1 | ===-------------------------------------------------------------------------=== |
References
如何使用 LLVM 优化器 | 编译器