|
| 1 | + |
| 2 | +* IR:中间语言。它独立于机器,复杂性介于源码与机器码之间 |
| 3 | +* ILGen:(Intermediate Code/Language Generator)中间代码生成 |
| 4 | +* 解释器通常不生成中间码,而是直接计算结果。编译器会生成中间码,例如Java字节码 |
| 5 | +* 中间代码的设计要求:与机器无关、利于优化、利于目标代码的生成 |
| 6 | +* 中间码的作用: |
| 7 | + <br/> |
| 8 | + ~  易移植:与机器无关,所以它作为中间语言可以为生成多种不同型号的目标机器码服务 |
| 9 | + <br/> |
| 10 | + ~  机器无关优化:对中间码进行机器无关优化,利于提高代码质量 |
| 11 | + <br/> |
| 12 | + ~  层次清晰:将AST映射成中间代码表示,再映射成目标代码的工作分层进行,使编译算法更加清晰 |
| 13 | +* 编译器所使用的IR可以有很多种形式。就其"形状"而言,可以分为: |
| 14 | + <br/> |
| 15 | + ~ 基于图 |
| 16 | + <br/> |
| 17 | + ~ 基于线性代码 |
| 18 | + <br/> |
| 19 | + ~ 基于图与线性代码混合 |
| 20 | +* 基于图的IR,常见的情况有: |
| 21 | + <br/> |
| 22 | + ~ 基于树 |
| 23 | + <br/> |
| 24 | + ~ 基于DAG(有向无环图) |
| 25 | + <br/> |
| 26 | + ~ 基于一般图 |
| 27 | +* 基于线性代码的IR,常见的情况有: |
| 28 | + <br/> |
| 29 | + ~ 三地址代码(四元组) |
| 30 | + <br/> |
| 31 | + ~ 二地址代码(三元组) |
| 32 | + <br/> |
| 33 | + ~ 零地址代码 |
| 34 | +* 基于图与线性代码混合的IR: |
| 35 | + <br/> |
| 36 | + ~ 最常见的情况是控制流图(CFG)用图表示 |
| 37 | + <br/> |
| 38 | + ~ 而CFG的每个节点是基本块,每个基本块里的代码是线性代码 |
| 39 | +* 三地址表达式:表达式中最多只能出现3个地址,因为cpu无法处理4个及其以上地址的操作。可以看成是抽象语法树的一种线性表示,有如下形式: |
| 40 | + ``` |
| 41 | + x:=y op z |
| 42 | + x:=op y |
| 43 | + x:=y |
| 44 | + goto L |
| 45 | + if x relop y goto L或if a goto L |
| 46 | + 传参、转子:param x、call p,n |
| 47 | + 返回语句:return y |
| 48 | + 索引赋值:x:=y[i]、x[i]:=y |
| 49 | + 地址和指针赋值:x:=&y、x:=*y、*x:=y |
| 50 | + ``` |
| 51 | +* 为什么选择三地址的中间形式: |
| 52 | + <br/> |
| 53 | + ~ 三地址代码是一种线性IR。由于输入源程序及输出目标程序都是线性的,因此,线性IR有着其他形式无法比拟的优势。 |
| 54 | + <br/> |
| 55 | + ~ 相对于其他表示形式而言,程序员对于线性表示形式通常会有一种莫名的亲切感,编译器设计者当然也不例外。早期编译器设计者往往都是汇编语言程序设计的高手,可以非常自然、流畅地阅读线性的三地址代码形式。同时,线性表示形式也会降低输入输出的实现难度。 |
| 56 | +* 中间码生成:按深度优先遍历AST,每个节点生成一个临时变量 |
| 57 | +* 左值和右值:等号左边和右边的区别。数字1只有右值,变量可以有左值和右值 |
| 58 | +* BinaryExpr:op左右都有值的称为BinaryExpr |
| 59 | +* UaryExpr:op只有右值,没有左值的称为UnaryExpr。例如一个表达式 |
| 60 | +* 任何一个非叶子节点都能生成一个左值和一个右值,右值是一个表达式例如100+200,左值是一个临时变量例如t1,完整:t1=100+200 |
| 61 | +* Duck Type:鸭子类型。非鸭子类型语言生成左值时需要判定左值类型是否支持生成左值 |
| 62 | +* 三地址代码实例1: |
| 63 | + 源码: |
| 64 | + ``` |
| 65 | + var a=1 |
| 66 | + var b=5 |
| 67 | + var c=(a+b)*5 |
| 68 | + ``` |
| 69 | + 转换为三地址码如下: |
| 70 | + ``` |
| 71 | + declare a |
| 72 | + declare b |
| 73 | + declare c |
| 74 | + a=1 |
| 75 | + b=5 |
| 76 | + t1=a+b |
| 77 | + t2=t1*5 |
| 78 | + c=t2 |
| 79 | + ``` |
| 80 | + 转换为OPCode: |
| 81 | + ``` |
| 82 | + alloc a |
| 83 | + set a #1 |
| 84 | + alloc b |
| 85 | + set b #5 |
| 86 | + alloc t1 |
| 87 | + .... |
| 88 | + ``` |
| 89 | + 分析:实例展示了从js源码到三地址码,再到机器码的过程,可以看到三地址码的每行代码永远只存在三个变量。以上只需要按深度优先遍历AST,每个节点生成一个临时变量即可生成。 |
| 90 | +* 三地址表达式实例2: |
| 91 | + 源码: |
| 92 | + ``` |
| 93 | + function f(n){ |
| 94 | + if(n==1 || n==2){ |
| 95 | + return n |
| 96 | + } |
| 97 | + return f(n-1)+f(n-2) |
| 98 | + } |
| 99 | + f(5) |
| 100 | + ``` |
| 101 | + 转换为三地址码如下: |
| 102 | + ``` |
| 103 | + declare f |
| 104 | + declare n |
| 105 | + t1=n==1 |
| 106 | + t2=n==2 |
| 107 | + t3=t1 or t2 |
| 108 | + branch t3==true |
| 109 | + goto (return n)行 or (return f(n-1)+f(n-2))行 //return怎么处理?? |
| 110 | + t4=n-1 |
| 111 | + t5=f(t4) //如何递归?? |
| 112 | + t6=n-2 |
| 113 | + t7=f(t6) //如何递归?? |
| 114 | + t8=t5+t7 |
| 115 | + call f //调用函数如何表示?? |
| 116 | + ``` |
| 117 | +* 文档: |
| 118 | + <br/> |
| 119 | +   中间码简介:https://www.jianshu.com/p/2862623af39e |
| 120 | + <br/> |
| 121 | +   中间码的形式:https://www.hashcoding.net/2015/12/10/%E5%85%AD%E3%80%81%E4%B8%AD%E9%97%B4%E4%BB%A3%E7%A0%81-IR/ |
| 122 | + <br/> |
| 123 | +   中间码的设计:https://book.51cto.com/art/201206/340208.htm |
| 124 | + <br/> |
| 125 | +   图解各种三地址码的特点1:https://blog.csdn.net/SHU15121856/article/details/104711426/ |
| 126 | + <br/> |
| 127 | +   图解各种三地址码的特点2:https://blog.csdn.net/raojun/article/details/103605349 |
0 commit comments