LLVM 之后端篇(4):理解指令选择的 dump 输出

Author: stormQ

Created: Thursday, 13. January 2022 10:23PM

Last Modified: Wednesday, 13. July 2022 07:21AM



摘要

本文基于release/13.x版本的 LLVM 源码,介绍了指令选择的整体阶段以及llc中常用 SelectionDAG 命令选项与各阶段之间的对应关系。除此之外,通过一个简单的示例展示了指令选择的 dump 输出及其含义。 从而,掌握指令选择的常见调试分析方法,并有助于进一步研究指令选择的内部原理。


指令选择的整体阶段

指令选择是将LLVM IR转换为代表目标指令的 SelectionDAG 节点——SDNode。它是由一些较小的阶段组成的,这些阶段如下图所示。

为了直观地展示llc中常用 SelectionDAG 命令选项与各阶段之间的对应关系,图中除了指令选择阶段(1)~(10)以外,还包括了指令调度阶段(11)

查看各阶段输出结果的常见方式有以下两种:

需要注意的是:

注:

  --filter-view-dags=<string>                                          - Only display the basic block whose name matches this for all view-*-dags options
  --view-block-freq-propagation-dags=<value>                           - Pop up a window to show a dag displaying how block frequencies propagation through the CFG.
  --view-dag-combine-lt-dags                                           - Pop up a window to show dags before the post legalize types dag combine pass
  --view-dag-combine1-dags                                             - Pop up a window to show dags before the first dag combine pass
  --view-dag-combine2-dags                                             - Pop up a window to show dags before the second dag combine pass
  --view-isel-dags                                                     - Pop up a window to show isel dags as they are selected
  --view-legalize-dags                                                 - Pop up a window to show dags before legalize
  --view-legalize-types-dags                                           - Pop up a window to show dags before legalize types
  --view-machine-block-freq-propagation-dags=<value>                   - Pop up a window to show a dag displaying how machine block frequencies propagate through the CFG.
  --view-misched-dags                                                  - Pop up a window to show MISched dags after they are processed
  --view-sched-dags                                                    - Pop up a window to show sched dags as they are processed
  --view-sunit-dags                                                    - Pop up a window to show SUnit dags after they are processed

查看指令选择的输出结果

step 1: 示例程序

test.ll:

define i32 @test(i32 %a, i32 %b) {
    %1 = add i32 %a, %b
    ret i32 %1
}

step 2: 查看指令选择的文本输出

执行如下命令,打印指令选择各阶段的文本输出结果到 Shell 窗口中:

$ llc -march=riscv32 -filetype=asm test.ll -debug-only=isel -o test.s

注: 如果要将结果输出到指定文件test.isel,则执行命令llc -march=riscv32 -filetype=asm test.ll -debug-only=isel -o test.s 2>test.isel

输出结果如下:

=== test
Initial selection DAG: %bb.0 'test:'
SelectionDAG has 9 nodes:
  t0: ch = EntryToken
      t2: i32,ch = CopyFromReg t0, Register:i32 %0
      t4: i32,ch = CopyFromReg t0, Register:i32 %1
    t5: i32 = add t2, t4
  t7: ch,glue = CopyToReg t0, Register:i32 $x10, t5
  t8: ch = RISCVISD::RET_FLAG t7, Register:i32 $x10, t7:1


Optimized lowered selection DAG: %bb.0 'test:'
SelectionDAG has 9 nodes:
  t0: ch = EntryToken
      t2: i32,ch = CopyFromReg t0, Register:i32 %0
      t4: i32,ch = CopyFromReg t0, Register:i32 %1
    t5: i32 = add t2, t4
  t7: ch,glue = CopyToReg t0, Register:i32 $x10, t5
  t8: ch = RISCVISD::RET_FLAG t7, Register:i32 $x10, t7:1


Type-legalized selection DAG: %bb.0 'test:'
SelectionDAG has 9 nodes:
  t0: ch = EntryToken
      t2: i32,ch = CopyFromReg t0, Register:i32 %0
      t4: i32,ch = CopyFromReg t0, Register:i32 %1
    t5: i32 = add t2, t4
  t7: ch,glue = CopyToReg t0, Register:i32 $x10, t5
  t8: ch = RISCVISD::RET_FLAG t7, Register:i32 $x10, t7:1


Legalized selection DAG: %bb.0 'test:'
SelectionDAG has 9 nodes:
  t0: ch = EntryToken
      t2: i32,ch = CopyFromReg t0, Register:i32 %0
      t4: i32,ch = CopyFromReg t0, Register:i32 %1
    t5: i32 = add t2, t4
  t7: ch,glue = CopyToReg t0, Register:i32 $x10, t5
  t8: ch = RISCVISD::RET_FLAG t7, Register:i32 $x10, t7:1


Optimized legalized selection DAG: %bb.0 'test:'
SelectionDAG has 9 nodes:
  t0: ch = EntryToken
      t2: i32,ch = CopyFromReg t0, Register:i32 %0
      t4: i32,ch = CopyFromReg t0, Register:i32 %1
    t5: i32 = add t2, t4
  t7: ch,glue = CopyToReg t0, Register:i32 $x10, t5
  t8: ch = RISCVISD::RET_FLAG t7, Register:i32 $x10, t7:1


===== Instruction selection begins: %bb.0 ''

ISEL: Starting selection on root node: t8: ch = RISCVISD::RET_FLAG t7, Register:i32 $x10, t7:1
ISEL: Starting pattern match
  Morphed node: t8: ch = PseudoRET Register:i32 $x10, t7, t7:1
ISEL: Match complete!

ISEL: Starting selection on root node: t7: ch,glue = CopyToReg t0, Register:i32 $x10, t5

ISEL: Starting selection on root node: t5: i32 = add t2, t4
ISEL: Starting pattern match
  Initial Opcode index to 16032
  Skipped scope entry (due to false predicate) at index 16038, continuing at 16121
  Skipped scope entry (due to false predicate) at index 16122, continuing at 16156
  Skipped scope entry (due to false predicate) at index 16157, continuing at 16191
  Skipped scope entry (due to false predicate) at index 16192, continuing at 16226
  Match failed at index 16036
  Continuing at 16227
  Skipped scope entry (due to false predicate) at index 16240, continuing at 16308
  Skipped scope entry (due to false predicate) at index 16309, continuing at 16338
  Skipped scope entry (due to false predicate) at index 16339, continuing at 16368
  Skipped scope entry (due to false predicate) at index 16369, continuing at 16398
  Match failed at index 16238
  Continuing at 16399
  Match failed at index 16402
  Continuing at 16444
  Continuing at 16445
  Skipped scope entry (due to false predicate) at index 16451, continuing at 16598
  Skipped scope entry (due to false predicate) at index 16599, continuing at 16624
  Match failed at index 16448
  Continuing at 16625
  Skipped scope entry (due to false predicate) at index 16635, continuing at 16752
  Skipped scope entry (due to false predicate) at index 16753, continuing at 16773
  Match failed at index 16633
  Continuing at 16774
  Match failed at index 16777
  Continuing at 17225
  Match failed at index 17232
  Continuing at 17671
  Match failed at index 17677
  Continuing at 17778
  Match failed at index 17779
  Continuing at 17791
  Morphed node: t5: i32 = ADD t2, t4
ISEL: Match complete!

ISEL: Starting selection on root node: t4: i32,ch = CopyFromReg t0, Register:i32 %1

ISEL: Starting selection on root node: t2: i32,ch = CopyFromReg t0, Register:i32 %0

ISEL: Starting selection on root node: t6: i32 = Register $x10

ISEL: Starting selection on root node: t3: i32 = Register %1

ISEL: Starting selection on root node: t1: i32 = Register %0

ISEL: Starting selection on root node: t0: ch = EntryToken

===== Instruction selection ends:
Selected selection DAG: %bb.0 'test:'
SelectionDAG has 9 nodes:
  t0: ch = EntryToken
      t2: i32,ch = CopyFromReg t0, Register:i32 %0
      t4: i32,ch = CopyFromReg t0, Register:i32 %1
    t5: i32 = ADD t2, t4
  t7: ch,glue = CopyToReg t0, Register:i32 $x10, t5
  t8: ch = PseudoRET Register:i32 $x10, t7, t7:1


Total amount of phi nodes to update: 0
*** MachineFunction at end of ISel ***
# Machine code for function test: IsSSA, TracksLiveness
Function Live Ins: $x10 in %0, $x11 in %1

bb.0 (%ir-block.0):
  liveins: $x10, $x11
  %1:gpr = COPY $x11
  %0:gpr = COPY $x10
  %2:gpr = ADD %0:gpr, %1:gpr
  $x10 = COPY %2:gpr
  PseudoRET implicit $x10

# End machine code for function test.

step 3: 查看指令选择的可视化输出

1) 准备可视化数据

执行如下命令,打印阶段(10)——Instruction Selection的可视化数据(以.dot结尾的文件):

$ llc -march=riscv32 -filetype=asm -view-sched-dags test.ll -o test.s

输出结果如下:

Writing '/tmp/dag.test-e90982.dot'...  done. 
Trying 'xdg-open' program... Remember to erase graph file: /tmp/dag.test-e90982.dot
省略 ...

从上面的结果可以看出,可视化数据保存在/tmp/dag.test-e90982.dot文件中。

2) 可视化输出结果

dot命令支持多种可视化格式,比如:.png.svg.pdf等,可以通过命令man dot查看可支持的格式。

$ dot -Tpng /tmp/dag.test-e90982.dot -o dag.test_e90982.png

$ dot -Tsvg /tmp/dag.test-e90982.dot -o dag.test_e90982.svg

注: 为了使用dot命令,需要执行sudo apt-get install graphviz命令安装graphviz


理解指令选择的文本输出

指令选择中各阶段的文本输出都是由llvm::SelectionDAG::dump()函数打印的。该函数的声明位于 llvm/include/llvm/CodeGen/SelectionDAG.h 文件,其定义位于 llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp 文件。

llvm::SelectionDAG是一个有向无环图(Directed-Acyclic-Graph,DAG),用于表示每个基本块。DAG 的节点为llvm::SDNode类(定义在 llvm/include/llvm/CodeGen/SelectionDAGNodes.h 文件中)的实例,每个节点对应一条指令或一个操作数。DAG 的边为llvm::SDValue类的实例,作为节点的操作数(即节点的每个操作数都是一条指向其它节点的边);每条边由一个<SDNode, unsigned>键值对构成,Key 表示所指向的节点(即定义操作数的节点,不妨称为Def-SDNode),Value 是一个索引(从 0 开始)表示使用Def-SDNode所定义的哪个值;每条边决定了两个节点在指令调度后必须满足何种(在基本块内的)先后出现顺序。

DAG 的节点既可以同时定义多个值,也可以同时包含多个操作数。DAG 节点所定义的每个值都关联一个MVT(Machine Value Type),用于表示该值的类型。常见的类型有如下三种(定义在 llvm/include/llvm/Support/MachineValueType.h 文件中):

DAG 的边用于表示两个节点之间的依赖关系:数据依赖或控制依赖。如果节点A依赖于节点B记作A->B(意味着节点B必须位于节点A的前面);那么边的含义可以分为如下三种:

接下来,以阶段(10)——Instruction Selection为例,说明指令选择的文本输出所表示的含义。

114 Selected selection DAG: %bb.0 'test:'
115 SelectionDAG has 9 nodes:
116   t0: ch = EntryToken
117       t2: i32,ch = CopyFromReg t0, Register:i32 %0
118       t4: i32,ch = CopyFromReg t0, Register:i32 %1
119     t5: i32 = ADD t2, t4
120   t7: ch,glue = CopyToReg t0, Register:i32 $x10, t5
121   t8: ch = PseudoRET Register:i32 $x10, t7, t7:1

理解指令选择的可视化输出

指令选择中各阶段的可视化输出都是由llvm::SelectionDAG::viewGraph()函数打印的。该函数的声明位于 llvm/include/llvm/CodeGen/SelectionDAG.h 文件,其定义位于 llvm/lib/CodeGen/SelectionDAG/SelectionDAGPrinter.cpp 文件。

接下来,以阶段(10)——Instruction Selection为例,说明指令选择的可视化输出所表示的含义。


References


下一篇:LLVM 之后端篇(5):理解 SelectionDAG 合法化

上一篇:LLVM 之后端篇(3):如何添加 MyRISCV 目标后端的第一条指令

首页