Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

为什么需要中间表示

复习

  • 语义分析总装:产出带类型标注的语法树
  • 抽象:加一层,把复杂细节藏起来
  • 编译器:前端、中端、后端的划分

TL;DR

  • 中间表示(IR)是介于源码与机器码之间的形式
  • 它让“m 种源语言 × n 种机器”不必两两适配
  • IR 与具体机器无关,便于统一做优化
  • 它是前端与后端之间的桥梁

正文

  语义分析之后,编译器有了一棵带类型的语法树。接下来是不是直接生成机器指令就好了?

  先别急。想一想这个现实问题:世界上有很多种编程语言,也有很多种机器架构。 如果每种语言都要为每种机器单独写一个编译器,那就是 m × n 个工作,多得吓人。

用一层“通用语”解耦

  解决思路还是那个熟悉的招数:加一层。

  如果所有语言先翻译成一种通用中间形式,所有机器再各自从这个通用形式生成机器码,那么工作量就变成了:

  • m 种语言,各写一个“翻译成 IR”的前端
  • n 种机器,各写一个“从 IR 生成代码”的后端

  总共 m + n,而不是 m × n。多了一层,就省掉了大量重复劳动。

  这层通用形式,就叫中间表示(IR,Intermediate Representation)。它像一位“世界语”翻译:各语言都先译成它,再由它译成各机器的话。

为什么优化也放在这里

  IR 还有一个更重要的作用:它非常适合做优化。

  因为在 IR 这一层:

  • 与具体机器无关,优化一次,对所有目标机器都适用
  • 它比源码更规整、更简单,分析起来容易
  • 它又比机器码保留了更多结构信息,不至于无从下手

  于是,编译器通常把各种优化都放在 IR 上做。前端负责“读懂并转成 IR”,中端在 IR 上优化,后端负责“把 IR 落成机器码”。 这条分工,正是我们在这一部分开头说的前端、中端、后端。

  那么,这棵带类型的语法树,具体是怎样变成 IR 的?下一章来看“降低”。

思考题 1

  中间表示怎样避免“每种源语言都要为每种机器写一个编译器”?

思考题 2

  为什么优化通常放在中间表示上做,而不是在源码或机器码上?

小结

知识点

  • IR 是介于源码与机器码之间的通用形式
  • 它把 m × n 的适配降为 m + n
  • IR 独立于机器,便于统一优化
  • 前端转 IR、中端优化、后端生成代码

参考资料

  1. Wikipedia(zh):中间表示:编译器内部使用的通用程序表示
  2. Wikipedia(zh):编译器:前端、中端与后端的划分

思考题答案(仅供参考)

思考题 1

  因为引入 IR 后,每种源语言只需写一个前端把它翻译成 IR,每种机器只需写一个后端从 IR 生成代码。这样工作量从 m × n 降为 m + n,避免了每种语言都要为每种机器单独适配。

思考题 2

  因为 IR 与具体机器无关,优化一次可适用于所有目标机器;它比源码规整、比机器码保留了更多结构,既便于分析又便于转换。因此把优化集中放在 IR 层最划算。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪