关于 IR 在程序分析和优化中应用的一些思考
作者 | 李彬
整理 | 编程语言 Lab
李彬,编程语言 Lab 程序分析应用专家,南京大学计算机系博士。研究领域是程序分析及验证。
本文以技术文章的方式回顾李老师在 SIG-程序分析 技术沙龙上的分享,回顾视频也已经上传 B 站,欢迎小伙伴们点开观看。
SIG-程序分析技术沙龙回顾|程序分析中的 IR 讨论_哔哩哔哩_bilibili编程语言社区微信公众号/ID:编程语言Lab/HW-PLLab小助手微信:pl_lab_001Gitee信息:https://gitee.com/HW-PLLab/communityhttps://www.bilibili.com/video/BV1sh411W7Uj
# Intermediate Representation #
根据 Wiki [1] 中的描述,Intermediate Representation 是编译器 / 虚拟机内部用于表示源代码的数据结构,被用来做进一步处理。例如下图中,编译器 Front End 读入源代码产生 IR;Middle End 将 IR 转换为运行效率更高的等价 IR;Back End 转换 IR 到 native code。
编译器内部使用多种 IR。《Engineering a Compiler》 [2] 将 IR 大体分为三类:
Structural IR:使用图结构表示 IR,一般使用该类 IR 进行源到源转换。编译器中这类 IR 有:Concrete Syntax Tree (CST),Abstract Syntax Tree (AST),Directed Acyclic Graph (DAG,用于表达式优化) 等。
Linear IR:类似 abstract machine 的汇编代码,编译器一般使用该类 IR 进行 lowering。编译器中这类 IR 有:three-address code,stack-machine code 等。
Hybrid IR:组合 Structural IR 和 Linear IR,该类 IR 尝试集合 Structural IR 和 Linear IR 的优点,避免它们的缺点。例如,常见的 CFG 中使用 low-level linear IR 表示基本块,使用图表示基本块之间的控制流关系。
当然,除了上述分类方式,也有其他的分类方式,例如 Chow Fred 在《The Challenge of Cross-language Interoperability》[3] 中将 IR 分为 Hierarchical IR 和 Flat IR。Hierarchical IR 类似上述分类中的 Structural IR 和 Hybrid IR,Flat IR 类似 Linear IR。Hierarchical IR 在形式上更接近典型的编程语言,一般被认为更高的抽象层次;Flat IR 通常被指定为更接近编译目标。
IR 在编译器中被设计用来进行进一步的处理,包括:转换、分析 和 优化。一个良好设计的 IR 应该可以被转换到多后端 (instruction set architectures),同时又能被不同的语言前端转换,这样可以简化编译器开发工作量。例如下图中,IR 将 20 个编译器(5 个语言 x 4 个后端)简化为只需要 5 个编译器前端 + 4 个编译器后端。
除了上面的作用外,IR 主要被分析工具和编译器用于分析和优化。下面我展开介绍一下 IR 在其中的应用。
# IR 用于程序分析和优化 #
# IR 用于代码安全规范检查分析
代码安全检查工具基于 IR 进行安全检查分析,发现代码中不符合代码规范的问题。一般各个语言在它的各个抽象层次的 IR 上都有相应的检查工具。以 C/C++ 为例,在 AST、AST/CFG、LLVM IR、混合 AST/CFG & LLVM IR 分别有如下检查工具:
Clang-format:基于 AST 的代码检查和格式工具
Clang static analyzer:基于 AST/CFG 的符号执行引擎
LLVM Pass Framework, Static Value-Flow Analysis Framework(SVF):基于 LLVM IR 的程序分析框架和工具
SVACE:混合 AST/CFG & LLVM IR 的静态分析工具
在哪些 IR 上进行哪些检查分析是设计安全检查工具时首先要考虑的问题。一般情况下,可以根据检查场景和 IR 的特点进行选择。
根据检查场景选择 IR
按照 fortify 的大类检查场景划分方式,不同的场景有如下 IR 选择:
值得注意的是,以笔者的经验,结构化规则场景一般会占据代码安全检查规范规则的一半,余下三类占另一半。
根据 IR 特点选择分析场景
如果需要更多 high-level 语义信息进行分析,选择 AST/CFG;否则,一般选择在更低层次的 IR 进行分析。
AST/CFG 会保留最多的语言特性 (语法糖) 和语义信息;low-level IR/CFG 会丢失部分语义信息。
AST/CFG 相对不容易进行分析,它的语法 / 语句复杂相比,例如 java bytecode(AST) 包含超过 200 种不同的操作;low-level IR/CFG 相对容易进行分析 (1) 语法 / 语句简单,例如 soot Jimple 仅包含 15 种不同的操作 (2) low-level IR 一般是 SSA 形式,SSA 的 def-use 可以简化和加速一些分析和优化。
有时候,工具中需要在 AST/CFG 和 low-level IR/CFG 上都进行分析。
例如,工业级检查工具 SVACE 混合 AST/CFG & LLVM IR/CFG 进行分析(三星主要的分析工具,用来检查 Android 和 Tizen 操作系统的自有代码。2015 至 2019 年间检查超过 300 billion 行代码 [4])。
如上图所示,最初 SVACE 基于 LLVM IR 进行分析,后续 SVACE 认为仅 LLVM IR 层次的分析不充分,又在 AST 上做了一些轻量级的 checkers。原因是当源代码转换到 LLVM IR 后,一些代码错误无法被检测出来 [5],例如:
array 的 index 表达式的误用 无法被分析出来
宏作为 array length 长度时的误用 无法被分析出来
# IR 用于编译器分析和优化
IR 的另一个重要应用是编译器中的分析和优化。编译器中大量的分析优化基于不同抽象层次的 IR。以 Clang/LLVM 为例,它的各抽象层次的 IR 如下图所示。
在 Clang/LLVM 中,一个 LLVM IR 明显是不够的。当前 LLVM codegen 会使用另外三个 IR:
SelectionDAG IR 用于 lowering 和指令选择,它正在慢慢被 GlobalISel 取代
Machine IR 用于寄存器分配、调度等,它接近目标机器指令
MC IR 用于汇编器优化,是集成汇编器(Integrated Assembler)中使用的 IR
Clang 将 C/C++ AST 编译为(也称为 lowering)LLVM IR。因为 LLVM IR 不适合使用语言特定语义(high-level 语义),无法生成具体准确位置的诊断信息,所以 Clang 基于 AST 构造了 CFG,基于 AST 的 CFG 进行数据流分析诊断报错。
尽管 Clang/LLVM 非常成功,但是仍然存在一些问题:
AST 和 LLVM IR 的抽象层次 gap 比较大,编译过程(lower 过程)会比较复杂,需要较高的工程能力
AST/CFG 数据流诊断路径和 LLVM codeGen 路径分叉了,在工程上不是最佳实践
因此,Chris Lattner 后来指出 "Clang should have a CIR! "(如下图所示),用来:
统一数据流诊断和编译路径
Progressive lowering,降低 CodeGen 的复杂度
在设计 Swift 时,Chris Lattner 总结了 Clang 的问题,提出了 SIL(Swift Intermediate Language)IR。
SIL 是介于 AST 和 LLVM IR 之间的 SSA 形式的 IR,具有 High-Level Type System,能够完全表示 Swift 的语义。并且,SIL 专为 CodeGen 和 Analysis 设计,可以:
弥合 Swift 源代码和 LLVM IR 之间的抽象差距
进行 High-level 上的分析 / 优化,包括数据流分析诊断报错、去虚拟化(devirtualization)、引用计数优化等
关于 SIL 的详细内容见官方资料 [6],这里就不详细介绍了。
不仅 Swift,现代高级语言基本在 AST 和 LLVM IR 之间提出了自己的 High-level IR,如下图所示。
High-level IR 可以用于:
Language specific optimizations
数据流分析诊断报错
降低 CodeGen 的复杂度(Progressive lowering)
# 新趋势 #
本节介绍 IR 在两个语言相关的新趋势中的重要应用,第一个是 DSL,第二个是多平台后端。
# IR 在 DSL 中的应用
随着摩尔定律的逐渐失效以及深度学习等特定领域的迫切需求,Domian Specific Architecture(DSA)和 Domian Specific Language(DSL)逐渐兴起并成为重要的趋势。
近两年可以看到大佬以及大佬关注的大佬 [7][8] 都在讲,DSL 的时代来临了。
Turing Lecture, Hennessy, Patterson; June 2018 / CACM Feb 2019
HW / SW co-design is the best way to expose parallelism of silicon... and utilize it, by Hennessy, Patterson; June 2018 / CACM Feb 2019
然而,DSL 的设计和实现非常困难。不考虑设计,仅实现 DSL 就需要考虑:
type inference/type checking
diagnostics/location tracking
dsl-specific analysis/optimizations/translation
middle level analysis/optimizations/translation
low level analysis/optimizations/translation
IR Gen/lowering
...
即使利用 LLVM 大杀器,DSL 的实现成本仍然很高:
仍然需要在 AST 上进行 DSL-Specific 的分析和优化
DSL AST 和 LLVM IR 的 gap 大,导致 IR Gen 实现复杂
目前业界实现 DSL 主要有两个方案:MLIR 和 eDSL。
## MLIR
MLIR(Multi-Level IR)是 LLVM 项目的一部分,主要解决 LLVM 实现 DSL 不够好的问题。MLIR 是一个构建多层次 IR 的可扩展框架:
支持多层次 IR
通过自定义类型系统和操作支持不同的抽象层次
每层 IR 都有一致的 IR 结构:SSA 和 Module/Function/Block/Operation
提供完善的基础设施
Diagnostics、 pass infrastructure、testing tools 等
各种 codeGen/lowering 策略
通过 MLIR,DSL 实现者可以:
使用更高抽象层次的 IR 对 DSL 进行抽象,从而保留更多的 high-level 信息
利用类似 LLVM 一样开箱即用的基础设施进行抽象层次 IR 上的分析和优化
渐进式 lowering,降低 CodeGen 的复杂度
下图是使用 MLIR 实现 DSL 的大概流程。需要特别指出,MLIR 不仅可以降低 DSL 的实现难度,也能够降低 GPL(general purpose languages)的实现难度。
目前,MLIR 的应用领域包括:
通用编程语言:Flang IR(Fortran)
机器学习:TensorFlow/ONNX/XLA/….
硬件设计:CIRCT
量子编程:Qcor
Computational Chemistry:COMET
......
MLIR 还在快速发展中,未来一定有更广阔的应用前景。
## eDSL
eDSL 是另一个实现 DSL 的方案。
eDSL 即 embedded DSL,又称内部 DSL,或嵌入式 DSL,它将一种现有通用编程语言 (GPL) 作为宿主语言,利用其基础设施 (语法、编译器、工具等) 建立专门面向特定领域的语义。关于如何使用 eDSL 实现 DSL 以及它的优缺点可以详见徐潇专家之前的文章。
# IR 在多平台后端中的应用
语言支持多平台(即 web、desktop、mobile 等平台)后端已经成为另一个趋势。新语言支持多平台后端的原因:
一次编码,多设备、多平台运行
编译到不同的后端,适应不同场景
如下图所示,新语言 Kotlin,Flutter/Dart 都支持多平台后端。
Introduction to Kotlin Multiplatform, by https://touchlab.co/kotlin-multiplatform/
Flutter cross-platform promises: mobile, web, and desktop, by https://medium.com/swlh/flutter-2020-state-of-cross-platform-814f1d8ff16
然而,多平台后端面临平台后端无关的分析和优化每个平台后端都要实现一遍的问题,如下图所示:
为了解决这个问题,新语言一般会将提出平台后端无关的 IR,在这个 IR 上进行平台后端无关的分析和优化。例如,Kotlin 提出了 unified IR,用于:
多平台后端共享统一处理,只实现一次优化
平台无关编译器插件
# Kotlin 中的 IR #
最后,本文希望从 Kotlin 完整的编译流程看 IR 的重要性,了解哪些 IR 进行哪些分析 和优化。
Kotlin 1.4 版本因为引入新的 IR, 所以编译流程发生了较大变化。下面会分别介绍 Kotlin 1.0 和 Kotlin 1.4 的编译流程。
需要说明的是,因为目前关于 Kotlin 的内部编译流程的资料较少,本文介绍的编译流程为笔者根据对 Kotlin 代码的理解给出的编译流程,难免有错误之处,欢迎指正。
# Kotlin 1.0 编译流程
Kotlin 1.0 编译流程如下图所示:
Kotlin 1.0 涉及到的 IR 有:
PSI (Program Structure Interface):即 Kotlin 的 CST(Concret Syntax Tree)。PSI 一般也会用在 IDE 中进行语法高量,格式化等;一些 Kotlin CodeStyle 检查器也会使用它。
BindingContext:记录 PSI 元素的类型等信息
Pseudocode Instruction:SSA 形式的 IR。Kotlin 1.0 会基于它构造 CFG,然后构造数据流框架进行数据流分析诊断报错,包括初始化、未使用表达式等检查。
JVM ByteCode:Mobile 平台的 IR。
JS:Web 平台的 IR。
LLVM IR:Native 后端 IR。
# Kotlin 1.4 编译流程
Kotlin 1.4 引入了两个新的 IR:FIR (Frontend IR) 和 IR (unified IR)。FIR 是前端 IR,IR 是统一后端 IR。工程实现上,Kotlin1.4 有两个编译流程:过渡流程 和 最终流程。
过渡流程
Kotlin 1.4 PSI -> IR(过渡流程)引入了统一后端 IR (unified IR):
用于序列化 Kotlin 代码成为 Klib,反序列化 Klib 成为 unified IR
用在平台无关编译器插件中
进行各种 Lowerings 优化,包括 TailrecLowering、EnumWhenLowering、ArrayConstructorLowering 等
最终流程
Kotlin 1.4 PSI - FIR -> IR(最终流程)在统一后端 IR (unified IR) 基础上,引入了 FIR (Frontend IR),同时消除了 Pseudocode Instruction 流程。FIR 的引入使得 Kotlin 前端编译速度提升 3-4 倍。
# 总结 #
IR 是程序分析和优化的基础,IR 的选择会对分析工具和编译器产生重大影响。现代语言普遍在 AST 和 LLVM IR 之间设计一个 High-level IR,用于 Language specific 的优化,数据流分析诊断和渐进式的 CodeGen。在 DSL 和多平台后端趋势下,IR 仍然扮演至关重要的角色。
参考
[1] https://en.wikipedia.org/wiki/Intermediate_representation
[2] Cooper, Keith, and Linda Torczon. Engineering a compiler. Elsevier, 2011.
[3] Chow F. Intermediate Representation: The increasing significance of intermediate representations in compilers[J]. Queue, 2013, 11(10). https://queue.acm.org/detail.cfm?id=2544374
[4] https://www.ispras.ru/en/technologies/svace/
[5] Using_Svace_static_analysis_tool_in_Samsung_environments_(Youil_Kim,ISPRASOPEN-2019) https://0x1.tv/Using_Svace_static_analysis_tool_in_Samsung_environments(Youil_Kim,_ISPRASOPEN-2019)
[6] https://github.com/apple/swift/blob/main/docs/SIL.rst
[7] Chris Lattner. The Golden Age of Compilers- in an era of Hardware/Software co-design. ASPLOS 2021 keynote. https://youtu.be/4HgShra-KnY
[8] 从芯片定制到语言定制: 程序设计语言的系统化定制及其支撑环境
关于 IR 在程序分析和优化中应用的一些思考相关推荐
- ROS-CAN通信解析程序分析(ROS中进行CAN通信)
CANALYST-II的linux版本通信解析程序 我们解析程序的先后顺便为: open,打开can卡: initcan,对can卡进行初始化: start,启动can通道: 就可以接收recive和 ...
- 程序分析与优化 - 6 循环优化
- 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )
文章目录 前言 一.DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 二./bin/dexopt 源码分析 前言 上一篇博客 [Android 逆向]整体加固脱壳 ...
- 《流量的秘密: Google Analytics网站分析与优化技巧(第3版)》一2.2 网站分析中的cookie...
本节书摘来自异步社区<流量的秘密: Google Analytics网站分析与优化技巧(第3版)>一书中的第2章,第2.2节,作者[英]Brian Clifton,更多章节内容可以访问云栖 ...
- 【中亦安图】清算/报表/日终跑批程序之性能优化案例(5)
第一章 技术人生系列 · 我和数据中心的故事(第五期)-清算/报表/日终跑批程序之性能优化案例(一) 中亦安图 | 2016-02-18 21:40 前言 不知不觉,技术人生系列·我和数据中心的故事来 ...
- 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmContinueOptimizati() 函数分析 )
文章目录 前言 一.DexPrepare.cpp 中 dvmContinueOptimizati() 方法分析 前言 上一篇博客 [Android 逆向]整体加固脱壳 ( DEX 优化流程分析 | D ...
- 日志分析系统分类有哪些_SEO优化中网站日志起到的重要性作用
在网站优化中,有一点是优化者避免不了的--网站日志的分析.网站日志分析是一个成功的SEO大师的日常优化工作的一部分,通过网站数据的分析,可以更清晰地知道网站的现状,可以挖掘网站潜在的问题,做好优化改善 ...
- LEAP能源供应转换、能源需求及碳排放预测中的基础数据搜集及处理、能源平衡表核算、模型框架构建、模型操作、情景设计、结果分析、优化、预测结果不确定性分析
采用部门分析法建立的LEAP(Long Range Energy Alternatives Planning System/ Low emission analysis platform,长期能源可替 ...
- python文本字符分析、编写程序接收字符串_python 文本分析Python编程中字符串和列表的基本知识讲解...
Python 字符串 字符串是 Python 中最常用的数据类型.我们可以使用引号来创建字符串. 创建字符串很简单,只要为变量分配一个值即可.例如: var1 = 'Hello World!' var ...
最新文章
- php五只猴子分椰子_tubes五只雪茄_phillies雪茄五只装
- 链接2: excel中的截取文子串的功能------left,mid以及right函数使用
- Tokyo Tyrant的下工具的使用
- pandas 读csv 报错 UnicodeDecodeError: 'utf-8' codec can't decode byte 0xca in position 0: invalid cont
- MobileSpace-关于我的激情的故事
- stl源码分析de练习
- python的科学计算库总结
- Pytorch Feature loss与Perceptual Loss的实现
- sklearn datasets 库使用说明
- 量子计算机前置同态加密技术,什么是具有抗量子计算机攻击潜质的全同态加密技术?...
- java 数据 excel模板_java导出excel模板数据
- 树上战争 HDU - 2545
- No tracked branch configured for branch master or the branch doesn‘t exist. To make your branch trac
- 基于微信小程序的实验室管理系统 文档+云开发项目源码及云数据库+部署说明
- 【无标题】C基础 2
- 人到底是为了什么活着?
- 电脑端(PC)按键精灵2023——入门小白 详细 教程
- 串扰——Crosstalk
- C++反射:全方位解读Lura库的前世今生!
- java白皮书是什么_ABEL 更新白皮书,对我们意味着什么?
热门文章
- 亚马逊邮件关联 关联原因?邮件
- 服务器iis短文件名漏洞,IIS短文件名漏洞分析及一个实例
- 做php的灯就灭,121128 还原 我是做PHP的,女嘉宾把灯全灭了 真相
- 轮播图代码,带定时器和小圆圈(易懂)
- 文本聚类算法Java实现
- css3中单位px,em,rem,vh,vw,vmin,vmax的区别及浏览器支持情况
- java autoconf_Centos7安装autoconf
- 15款js编辑器大全
- 团队管理29--沟通理解层次图
- oracle 查找序列号,Oracle 查找丢失数据序列号