中间代码生成(Intermediate Code Generation)

  • 申明语句的翻译
    • 类型表达式
    • 申明式语句翻译
  • 简单赋值语句的翻译
  • 数组引用的翻译
  • 控制流语句的翻译
    • 控制流语句及其SDT
    • 布尔表达式及其SDT
    • 控制流语句翻译的例子
    • 布尔表达式和控制流表达式的回填
  • switch语句的翻译
  • 过程调用语句的翻译

本文主要是对 哈工大编译原理课件 的学习和总结。

在语法制导翻译过程中,将生成中间代码的代码(抽象语法树的代码)嵌入到语义动作中,即可完成中间代码(抽象语法树)的生成。

经典的中间代码通常包括以下几种:

  • 树和有向无环图(DAG):是比较 high level 的表示形式。例如抽象语法树。
  • 三地址码(3-address code):是比较 low level 的表示形式,接近目标机器代码。
  • 控制流图(CFG):是更精细的三地址码,程序的图状表示,图中的每个节点是一个基本快(BB),基本块内的代码是三地址码。适合做程序分析。
  • 静态单赋值形式(SSA):更精细的CFG,同时包含控制流和数据流的信息。可以简化程序分析算法。

本文将分别介绍各种类型的语句的翻译。

申明语句的翻译

介绍申明语句的翻译前,需要先了解下类型表达式。

类型表达式

首先,基本类型是类型表达式。例如:

  • integer
  • real
  • char
  • boolean
  • type_error(出错类型)
  • void(无类型)

再者,将类型构造符 (type constructor) 作用于类型表达式可以构成新的类型表达式。例如:

  • 数组构造符array:若T是类型表达式,则array ( I, T )是类型表达式( I是一个整数)。

    类型 类型表达式
    int[3] array(3, int)
    int[2][3] array(2, array(3, int))
  • 指针构造符pointer:若T 是类型表达式,则 pointer ( T ) 是类型表达式,它表示一个指针类型。
  • 笛卡尔乘积构造符×:若T1 和T2 是类型表达式,则笛卡尔乘积 T1 × T2 是类型表达式。
  • 函数构造符→:若T1 、T2 、…、Tn 和R是类型表达式,则 T1×T2×…×Tn → R 是类型表达式。
  • 记录构造符record:若有标识符N1、N2 、…、Nn 与类型表达式T1、T2、…、Tn ,则 record ( (N1 × T1) × (N2 × T2)× …× ( Nn × Tn )) 是一个类型表达式。

例如,下面的C程序片段:

struct stype {char[8] name;int score;
};stype[50] table;
stype* p;
  • 和stype绑定的类型表达式:record((name×array(8, char)) × (score×integer))
  • 和table绑定的类型表达式:array (50, stype)
  • 和p绑定的类型表达式:pointer (stype)

申明式语句翻译

对于声明语句,语义分析的主要任务就是收集标识符的类型等属性信息,并为每一个名字分配一个相对地址:

  • 从类型表达式可以知道该类型在运行时刻所需的存储单元数量称为类型的宽度 (width)
  • 在编译时刻,可以使用类型的宽度为每一个名字分配一个相对地址

而名字的类型和相对地址信息保存在相应的符号表记录中。

下面看一个变量申明语句的SDT:

对于上述文法,可以计算出有相同左部产生式的可选集:

产生式 可选集(Select)
D→Tid;DD\rightarrow T\ id;DD→T id;D {↑,int,real}\{\uparrow,int, real\}{↑,int,real}
D→ϵD\rightarrow \epsilonD→ϵ {$}
T→BCT\rightarrow BCT→BC {int,real}\{int, real\}{int,real}
T→↑T1T\rightarrow {\uparrow}\ T_1T→↑ T1​ {↑}\{\uparrow\}{↑}
B→intB\rightarrow intB→int {int}\{int\}{int}
B→realB\rightarrow realB→real {real}\{real\}{real}
C→ϵC\rightarrow \epsilonC→ϵ {id}\{id\}{id}
C→[num]C1C\rightarrow [num]C_1C→[num]C1​ { [ }

可见,具有相同左部产生式的可选集是正交的,因此该文法是LL(1)文法,可以采用自顶向下的文法进行分析。分析过程如下:


简单赋值语句的翻译

赋值语句翻译的主要任务是生成对表达式求值的三地址码。例如:

x = ( a + b ) * c ;// 翻译成三地址码
t1 = a + b
t2 = t1 * c
x  = t2

下面看一个简单赋值语句的翻译过程:

符号的属性为:

符号 综合属性
S code
E code
addr

这个文法是LR文法,可以采用自底向上的LR语法分析方法。语义动作中函数说明如下:

  • lookup(name):查询符号表返回 name 对应的记录
  • gen(code):增量地生成三地址指令 code
  • newtemp( ):生成一个新的临时变量t,返回 t 的地址

数组引用的翻译

将数组引用翻译成三地址码时要解决的主要问题是确定数组元素的存放地址,也就是数组元素的寻址。

  • 一维数组。假设每个数组元素的宽度是 w,则数组元素 a[i]a[i]a[i] 的相对地址是:base+i∗wbase+i*wbase+i∗w。其中,base是数组的基地址,i*w 是偏移地址
  • 二维数组。假设一行的宽度是 w1,同一行中每个数组元素的宽度是w2,则数组元素 a[i1][i2]a[i1][i2]a[i1][i2] 的相对地址是:base+i1∗w1+i2∗w2base+i_1 *w_1 +i_2 * w_2base+i1​∗w1​+i2​∗w2​
  • k维数组。数组元素a[i1][i2]...[ik]a[i_1 ] [i_2 ] ...[i_k ]a[i1​][i2​]...[ik​]的相对地址是:base+i1∗w1+i2∗w2+...+ik∗wkbase + i_1 * w 1 + i_2 * w_2 +...+ i_k *w_kbase+i1​∗w1+i2​∗w2​+...+ik​∗wk​

例如:ype(a)= array(3, array(5, array(8, int) ) ),一个整型变量占用4个字节。则:
addr(a[i1][i2][i3])=base+i1∗w1+i2∗w2+i3∗w3=base+i1∗160+i2∗32+i3∗4addr(a[i_1][i_2][i_3]) = base + i_1 * w_1 + i_2 * w_2 + i_3 * w_3 \\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ = base + i_1 * 160 + i_2 * 32 + i_3 *4 addr(a[i1​][i2​][i3​])=base+i1​∗w1​+i2​∗w2​+i3​∗w3​                           =base+i1​∗160+i2​∗32+i3​∗4

c=a[i1][i2][i3]c= a[i_1][i_2][i_3]c=a[i1​][i2​][i3​] 对应的三地址码为:

t1 = i1 * 160;
t2 = i2 * 32;
t3 = t1 + t2;
t4 = i3 * 40;
t5 = t3 + t4;
t6 = a[t5]
c = t

为数组L定义综合属性如下:

  • L.type:L生成的数组元素的类型
  • L.offset:指示一个临时变量,该临时变量用于累加公式中的 ij×wji_j × w_jij​×wj​ 项,从而计算数组引用的偏移量
  • L.array:数组名在符号表的入口地址

控制流语句的翻译

控制流语句及其SDT

控制流语句的基本文法:

P→SS→S1S2S→id=E;∣L=E;S→ifBthenS1∣ifBthenS1elseS2∣whileBdoS1\begin{aligned} & P \rightarrow S \\ & S \rightarrow S_1S_2 \\ & S \rightarrow id=E;\\ & \ \ \ \ \ \ \ \ \ \ \ |\ L=E; \\ & S \rightarrow if\ B\ then\ S_1 \\ & \ \ \ \ \ \ \ \ \ \ \ | \ if\ B\ then\ S_1\ else\ S_2 \\ & \ \ \ \ \ \ \ \ \ \ \ | \ while\ B\ do\ S_1 \end{aligned} ​P→SS→S1​S2​S→id=E;           ∣ L=E;S→if B then S1​           ∣ if B then S1​ else S2​           ∣ while B do S1​​

控制流语句的代码结构:

很容易得到控制流语句的SDT:

布尔表达式及其SDT

布尔表达式的基本文法:

B→BorB∣BandB∣notB∣(B)∣ErelopE∣true∣false\begin{aligned} & B \rightarrow B\ or\ B \\ & \ \ \ \ \ \ \ |\ B\ and\ B \\ & \ \ \ \ \ \ \ |\ not\ B \\ & \ \ \ \ \ \ \ |\ (B) \\ & \ \ \ \ \ \ \ |\ E\ relop\ E \\ & \ \ \ \ \ \ \ |\ true \\ & \ \ \ \ \ \ \ |\ false \\ \end{aligned} ​B→B or B       ∣ B and B       ∣ not B       ∣ (B)       ∣ E relop E       ∣ true       ∣ false​

其中,relop(关系运算符)分别为 <、 <=、 >、 >=、==、 ! = 。

在跳转代码中,逻辑运算符 &&、|| 和 ! 被翻译成跳转指令。运算符本身不出现在代码中,布尔表达式的值是通过代码序列中的位置来表示的。例如:

// C 源码
if (x<100 || x>200 && x!=y)x = 0;// 三地址代码:if x<100 goto L2;goto L3;
L3: if x>200 goto L4;goto L1;
L4: if x != y goto L2;goto L1;
L2: x = 0;
L1:

则比较容易得出布尔表达式的SDT:

控制流语句翻译的例子

任何SDT都可以这样实现:首先建立一棵语法分析树(比如使用LR语法分析构建),然后按照从左到右的深度优先顺序来执行这些动作。

布尔表达式和控制流表达式的回填

上面在生成跳转指令的时候,目标指令的标号还不确定,是通过将存放标号的地址作为继承属性传递到跳转指令生成的地方,这样做会增加一趟处理:将标号同具体的地址绑定起来。

这里介绍一种称为回填的技术来解决这个问题。它的基本思想为:生成一个跳转指令时,暂时不指定该跳转指令的目标标号。这样的指令都被放入由跳转指令组成的列表中。同一个列表中的所有跳转指令具有相同的目标标号。等到能够确定正确的目标标号时,才去填充这些指令的目标标号。

增加非终结符B的综合属性:

  • B.truelist:指向一个包含跳转指令的列表,这些指令最终获得的目标标号就是当B为真时控制流应该转向的指令的标号
  • B.falselist:指向一个包含跳转指令的列表,这些指令最终获得的目标标号就是当B为假时控制流应该转向的指令的标号

使用到的函数:

  • makelist( i ):创建一个只包含i的列表,i是跳转指令的标号,函数返回指向新创建的列表的指针
  • merge( p1 , p2 ):将 p1 和 p2 指向的列表进行合并,返回指向合并后的列表的指针
  • backpatch( p, i ):将 i 作为目标标号插入到 p 所指列表中的各指令中

布尔表达式的回填:

B→E1relopE2B \rightarrow E_1\ relop\ E_2B→E1​ relop E2​,对应的语义动作为:

{B.truelist = makelist(nextquad);B.falselist = makelist(nextquad+1);gen(‘if ’ E 1 .addr relop E 2 .addr ‘goto _’);gen(‘goto _’);
}

B→trueB \rightarrow trueB→true,对应的语义动作为:

{B.truelist = makelist(nextquad);gen(‘goto _’);
}

B→falseB \rightarrow falseB→false,对应的语义动作为:

{B.falselist = makelist(nextquad);gen(‘goto _’);
}

B→(B1)B \rightarrow (B_1)B→(B1​),对应的语义动作为:

{B.truelist = B1.truelist ;B.falselist = B1.falselist ;
}

B→notB1B \rightarrow not\ B_1B→not B1​,对应的语义动作为:

{B.truelist = B1.falselist ;B.falselist = B1.truelist ;
}

B→B1orB2B \rightarrow B_1 \ or\ B_2B→B1​ or B2​,需要转换为B→B1orMB2B \rightarrow B_1 \ or\ M\ B_2B→B1​ or M B2​。标记终结符M的任务是用于记录B2第一条指令的标号。类似地,B→B1andB2B \rightarrow B_1 \ and\ B_2B→B1​ and B2​,需要转换为B→B1andMB2B \rightarrow B_1 \ and\ M\ B_2B→B1​ and M B2​。


下面看一个例子:



注:1、假设指令从100号开始执行。2、生成的指令中剩余标号待B的真假出口确定即可回填。

控制流语句的回填:

为控制语句增添综合属性:

  • S.next1ist:指向一个包含跳转指令的列表,这些指令最终获得的目标标号就是按照运行顺序紧跟在S代码之后的指令的标号

S→ifBthenS1S\rightarrow if\ B\ then\ S_1S→if B then S1​ 语句和 S→ifBthenS1elseS2S\rightarrow if\ B\ then\ S_1\ else\ S_2S→if B then S1​ else S2​ 语句的SDT:


S→whileBdoS1S\rightarrow while\ B\ do\ S_1S→while B do S1​ 语句和 S→S1S2S\rightarrow S_1S_2S→S1​S2​ 语句的SDT:


例:

while a < b doif c < 5 thenwhile x > y do z = x + 1;elsex = y;

switch语句的翻译


过程调用语句的翻译

过程调用语句的文法:

S→callid(Elist)Elist→Elist,EElist→E\begin{aligned} & S\rightarrow call\ id\ (Elist) \\ & Elist\rightarrow Elist,E \\ & Elist\rightarrow E \end{aligned} ​S→call id (Elist)Elist→Elist,EElist→E​

过程调用语句的SDT:

用一个队列 q 存放 E1.addr、E2.addr、…、En.addr。

例:

// 源码
f ( b*c-1, x+y, x, y )// 对应的三地址代码
t1 = b*c;
t2 = t1 - 1;
t3 = x + y;
param t2
param t3
param x
param y
call f, 4

参考

  • 哈工大编译原理课件

中间代码生成(Intermediate Code Generation)相关推荐

  1. 代码生成(Code Generation) 表达式编译

    代码生成(Code Generation)技术广泛应用于现代的数据系统中.代码生成是将用户输入的表达式.查询.存储过程等现场编译成二进制代码再执行,相比解释执行的方式,运行效率要高得多.尤其是对于计算 ...

  2. JIT Code Generation代码生成

    JIT Code Generation代码生成 一.表达式编译 代码生成(Code Generation)技术广泛应用于现代的数据系统中.代码生成是将用户输入的表达式.查询.存储过程等现场编译成二进制 ...

  3. MLIR与Code Generation

    MLIR与Code Generation MLIR多级中间表示 MLIR 项目是一种构建可重用和可扩展编译器基础架构的新方法.MLIR 旨在解决软件碎片问题,改进异构硬件的编译,显着降低构建特定领域编 ...

  4. JAVA 中的代码生成包 CGLIB (Code Generation Library)

    JAVA 中的代码生成包 CGLIB (Code Generation Library) CGLIB 是一个功能强大,高性能的代码生成包.它为没有实现接口的类提供代理,为 JDK 的动态代理提供了很好 ...

  5. 【AI人工智能】用于代码生成的大型语言模型 Large Language Models for Code Generation

    目录 Large Language Models for Code Generation – Part 1用于代码生成的大型语言模型--第 1 部分 Introduction

  6. asm(Java字节码操控框架)和 CGlib(Code Generation Library)

    asm概述 asm 是一个 Java 字节码操控框架. 它能够以二进制形式修改已有类或者动态生成类.ASM 可以直接产生二进制 class 文件,也可以在类被加载入Java 虚拟机之前动态改变类行为. ...

  7. 文章翻译|TRANX:A Transition-based Neural Abstract Syntax Parser for Semantic Parsing and Code Generation

    Abstract 我们提出了TRANX,一个基于transition的神经语义解析器,将自然语言(NL)映射到formal meaning representation(MRs).TRANX对目标MR ...

  8. 论文阅读:Leveraging Code Generation to Improve Code Retrieval and Summarization via Dual Learning

    论文题目与链接: [2002.10198] Leveraging Code Generation to Improve Code Retrieval and Summarization via Dua ...

  9. Apache CXF Code Generation Maven2 Plugins plugin.xml

    文章目录 pom.xml build plugin config Apache CXF Code Generation Maven2 Plugins plugin.xml pom.xml build ...

最新文章

  1. linux系统怎样连手机,Linux下手机与蓝牙的连接配置
  2. 基于GTID模式MySQL主从复制
  3. Robot framework+python安装使用图解版
  4. 饿了么EMonitor演进史
  5. abaqus的python安装文件在哪_在abaqus2016中安装xlwt和xlrd库教程
  6. 数字(进制/二分/组合数学)
  7. C语言数组元素的查询
  8. 小目标神器!TPH-YOLOv5:将Transformer预测加载Yolov5!
  9. eclipse配置xml的自动提示
  10. 蓝屏代码查询器1.1.8
  11. 语音识别行业技术和市场横向对比
  12. Android点9图的使用与制作工具推荐
  13. 记录FinalShell退格键
  14. html自动适应屏幕分辨率,css如何设置适配不同分辨率屏幕?
  15. STM32CubeMX安装问题【尤其是Java环境没安装好的情况】
  16. Java 设计模式(十三):代理模式
  17. 软件智能:aaas系统对AI的诠释-AI的可能的三个取向和必然的一个成果(演绎逻辑-必然的推理-的两个独立性:推论和定论)
  18. 计算机校本培训心得,2016信息技术校本研修心得体会
  19. Oracle嵌套表简介
  20. 基于TCP/IP协议 区别 GET和POST

热门文章

  1. 村田适用于车载以太网BroadR-Reach的静噪元件(共模电感)
  2. 项目管理的重要性及其对企业和个人的意义
  3. python用turtle画彩虹_Python语言,绘制彩虹场景
  4. media queries 适配 iphone6s
  5. 利用IIS在WIN7家庭高级版上发布网页
  6. butter滤波器是iir吗_IIR数字滤波器的设计与滤波
  7. Hybrid接口的配置
  8. Android记事本NotePad应用功能拓展(六)
  9. 尚硅谷宋红康2021JUC
  10. 自主复习CISA方法