LLVM笔记(9) - 指令选择(一) 概述

本文最初是基于对新员工培训, 使其快速上手编译器后端代码而写的入门简介. 为方便阅读又根据模块细分为若干章, 内容以分析代码为主, 偶尔也会穿插一些理论扩展.

什么是指令选择

指令选择(instruction selection)是将中间语言转换成汇编或机器代码的过程. 如果仅为单一语言在单一目标上实现指令选择, 可以使用手工编码的方法. 否则通过使用自动代码生成器生成代码, 编译器开发人员只需负责修改不同目标的机器指令描述, 是更优选择.

指令选择需要考虑的问题

  1. 目标的寄存器, 寻址方式和指令体系结构
    目标的指令集与体系结构会影响指令选择的方式. e.g. 在X86上给一个寄存器赋值32bit立即数地址只需要mov一条指令, 但像ARM与RISCV等架构只支持12bit的立即数加法, 需要更加复杂的方式实现同样的移动语义. 根据codesize model的设置, RISCV可以使用lui + addi两条指令实现(分别移动高20bit与低12bit), 或将地址保存到当前指令地址附近然后用addi + lw两条指令实现(计算常量所在地址并访问该地址).
  2. 软件调用约定(calling convention)
    指令选择必须考虑兼顾目标的ABI定义.
  3. 中间语言的结构和特征
    本质上中间语言对生成的代码的正确性没有影响, 然而中间语言的结构影响对自动代码生成算法的设计.
  4. 转化为机器代码的方式
    略.

常见的指令选择实现

常见的指令选择实现可以参见经典书籍Survey on Instruction Selection(这块内容以后有空单独再介绍).

  1. 宏展开(macro expension)
  2. 树覆盖(tree covering)
  3. 有向图覆盖(DAG covering)

LLVM当前的指令选择实现

LLVM在O0编译时使用名为FastISel的指令选择PASS, 在O2编译时使用名为SelectionDAG的指令选择PASS, 同时当前社区还在推进名为GlobalISel的指令选择PASS(当前仅在AArch64上支持), 希望能替代SelectionDAG. 我们将首先介绍SelectionDAG(如未特殊说明, 下文介绍均默认为SelectionDAG), 然后会简要介绍FastISel与GlobalISel, 并比较三者特点, 讨论为什么要使用GlobalISel替换SelectionDAG.

什么是SelectionDAG

SelectionDAG是一种trees-on-DAGs的有向图覆盖实现, 编译器开发人员通过编写指令的树匹配(tree pattern), 通过tablgen翻译成完整的树匹配代码交由代码生成器(matcher generator)处理, 后者采用贪婪的DAG-to-DAG策略将中端IR覆写为机器指令描述.

SelectionDAG流程简介

SelectionDAG由若干个优化组合而成, 其具体流程图如下所示, 其中红色节点表示数据(输入的中间语言)的组成形式, 黑色节点表示处理模块.

我们会依据SelectionDAG的流程依次分析每个步骤的目标, 实现方式以及在支持一个新架构时需要修改的注意点, 这里先简要介绍各个模块的作用:

  1. 上文提到SelectionDAG是基于DAG covering的指令选择实现, 因此需要首先将输入程序流的表示方式从IR转换为DAG, 该过程又被称做lowering. lowering会将IR节点一一的翻译为DAG节点的同时也会处理调用约定, 使其遵守特定目标的ABI规范(这也是lowering名字的含义). 在lowering后程序流是以名为SDNode的节点组成的DAG.
  2. 根据特定目标所支持的指令集与寄存器结构, SelectionDAG将目标不支持的操作与数据类型转换为合法的操作与数据类型, 这一步被称作legalize. 另一方面对于其中产生的可以合并的操作被称作combine. 可以看到legalize与combine执行了多次, 且legalize每次执行的内容都不一样.
  3. 在combine和legalize之后是select步骤, 通过pattern match或手写代码的方式生成对应指令. 经过指令选择后的程序流是以Machine Instruction为节点组成的DAG.
  4. 在早期的SelectionDAG实现里还考虑的了调度的优化, 然而现在LLVM已经支持了PreRA与PostRA的调度, 在此处调度优化并不重要, 本文暂不涉及, 关于调度的实现将在以后调度器实现分析中介绍.
  5. 指令选择(步骤3)只是选出了对应的指令, 还需要将其重新覆写为基于SSA格式的Machine Instruction描述, 为每条指令分配虚拟寄存器, 连接PHI节点.
posted @ 2020-05-03 15:50  Five100Miles  阅读(8657)  评论(1编辑  收藏  举报