中间代码生成(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/Android多点触摸协议
  2. [NOI2001]食物链
  3. Java opengl openal_项目里用到了openal,特分享一下openal全教程
  4. ORACLE客户端与服务器连接
  5. 2020年你应该学习 .Net Core
  6. 电信aep平台是什么意思_江苏天鼎证券:股票平台跳水是什么意思?股票为什么会跳水?...
  7. 和get redis_SpringBoot整合Redis,你get了吗?
  8. c#养老院老人信息管理系统源码 论文_辽宁吃的好的社区养老院位置,爱心养老院_抚顺市望花区社会养老中心...
  9. Hadoop集群安装部署_分布式集群安装_02
  10. mysql中merge的用法_mysql中merge表存儲引擎用法介紹
  11. cocos2dx 物理碰撞
  12. 如何安装Scrapy
  13. java项目-基于SSM实现在线考试及题库管理系统
  14. 摸爬滚打半年,我是如何从小白进阶到渗透测试工程师
  15. 【干货】微博短视频去水印下载使用介绍
  16. 数据仓库开发之路之三--时间维度的创建
  17. vs运行localhos转换为本地ip地址访问
  18. 腾讯游戏运营总监酒后吹批:运维工程师这些知识点都不会?赶紧找个地埋了吧!
  19. R语言 -- car::scatterplotmatrix散点图矩阵 参数详解
  20. Python代码解析数据

热门文章

  1. 安卓手机卡顿怎么解决_安卓手机卡是通病?大这样设置一招轻松解决
  2. 前端如何展示一个有上亿数据的树结构
  3. 桂电校易班工作站电脑系统(HP)重装
  4. 虚拟现实在医学领域应用
  5. 美女面试官问我链表的CURD,我彻底懵圈了……
  6. html5测试 iphone6sp,极限测试!iPhone6s Plus表现太夸张!
  7. 计算摄影 | 计算机如何学会欣赏照片的美感?
  8. 软件定义存储(SDS)的定义及其分类
  9. 微信模板推送踩过的坑
  10. android电视投影ipad,【沙发管家】苹果手机,,iPad连接安卓智能电视投屏方法