中间代码生成(Intermediate Code Generation)

news2024/9/23 17:17:11

中间代码生成(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 → T   i d ; D D\rightarrow T\ id;D DT id;D { ↑ , i n t , r e a l } \{\uparrow,int, real\} {,int,real}
D → ϵ D\rightarrow \epsilon Dϵ{$}
T → B C T\rightarrow BC TBC { i n t , r e a l } \{int, real\} {int,real}
T → ↑   T 1 T\rightarrow {\uparrow}\ T_1 T T1 { ↑ } \{\uparrow\} {}
B → i n t B\rightarrow int Bint { i n t } \{int\} {int}
B → r e a l B\rightarrow real Breal { r e a l } \{real\} {real}
C → ϵ C\rightarrow \epsilon Cϵ { i d } \{id\} {id}
C → [ n u m ] C 1 C\rightarrow [num]C_1 C[num]C1{ [ }

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

简单赋值语句的翻译

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

x = ( a + b ) * c ;

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

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

符号的属性为:

符号综合属性
Scode
Ecode
addr

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

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

数组引用的翻译

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

  • 一维数组。假设每个数组元素的宽度是 w,则数组元素 a [ i ] a[i] a[i] 的相对地址是: b a s e + i ∗ w base+i*w base+iw。其中,base是数组的基地址,i*w 是偏移地址
  • 二维数组。假设一行的宽度是 w1,同一行中每个数组元素的宽度是w2,则数组元素 a [ i 1 ] [ i 2 ] a[i1][i2] a[i1][i2] 的相对地址是: b a s e + i 1 ∗ w 1 + i 2 ∗ w 2 base+i_1 *w_1 +i_2 * w_2 base+i1w1+i2w2
  • k维数组。数组元素 a [ i 1 ] [ i 2 ] . . . [ i k ] a[i_1 ] [i_2 ] ...[i_k ] a[i1][i2]...[ik]的相对地址是: b a s e + i 1 ∗ w 1 + i 2 ∗ w 2 + . . . + i k ∗ w k base + i_1 * w 1 + i_2 * w_2 +...+ i_k *w_k base+i1w1+i2w2+...+ikwk

例如:ype(a)= array(3, array(5, array(8, int) ) ),一个整型变量占用4个字节。则:
a d d r ( a [ i 1 ] [ i 2 ] [ i 3 ] ) = b a s e + i 1 ∗ w 1 + i 2 ∗ w 2 + i 3 ∗ w 3                             = b a s e + i 1 ∗ 160 + i 2 ∗ 32 + i 3 ∗ 4 addr(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+i1w1+i2w2+i3w3                           =base+i1160+i232+i34

c = a [ i 1 ] [ i 2 ] [ i 3 ] 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:指示一个临时变量,该临时变量用于累加公式中的 i j × w j i_j × w_j ij×wj 项,从而计算数组引用的偏移量
  • L.array:数组名在符号表的入口地址

控制流语句的翻译

控制流语句及其SDT

控制流语句的基本文法:

P → S S → S 1 S 2 S → i d = E ;             ∣   L = E ; S → i f   B   t h e n   S 1             ∣   i f   B   t h e n   S 1   e l s e   S 2             ∣   w h i l e   B   d o   S 1 \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} PSSS1S2Sid=E;            L=E;Sif B then S1            if B then S1 else S2            while B do S1

控制流语句的代码结构:

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

布尔表达式及其SDT

布尔表达式的基本文法:

B → B   o r   B         ∣   B   a n d   B         ∣   n o t   B         ∣   ( B )         ∣   E   r e l o p   E         ∣   t r u e         ∣   f a l s e \begin{aligned} & B \rightarrow B\ or\ B \\ & \ \ \ \ \ \ \ |\ B\ and\ B \\ & \ \ \ \ \ \ \ |\ not\ B \\ & \ \ \ \ \ \ \ |\ (B) \\ & \ \ \ \ \ \ \ |\ E\ relop\ E \\ & \ \ \ \ \ \ \ |\ true \\ & \ \ \ \ \ \ \ |\ false \\ \end{aligned} BB 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 → E 1   r e l o p   E 2 B \rightarrow E_1\ relop\ E_2 BE1 relop E2,对应的语义动作为:

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

B → t r u e B \rightarrow true Btrue,对应的语义动作为:

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

B → f a l s e B \rightarrow false Bfalse,对应的语义动作为:

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

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

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

B → n o t   B 1 B \rightarrow not\ B_1 Bnot B1,对应的语义动作为:

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

B → B 1   o r   B 2 B \rightarrow B_1 \ or\ B_2 BB1 or B2,需要转换为 B → B 1   o r   M   B 2 B \rightarrow B_1 \ or\ M\ B_2 BB1 or M B2。标记终结符M的任务是用于记录B2第一条指令的标号。类似地, B → B 1   a n d   B 2 B \rightarrow B_1 \ and\ B_2 BB1 and B2,需要转换为 B → B 1   a n d   M   B 2 B \rightarrow B_1 \ and\ M\ B_2 BB1 and M B2

下面看一个例子:

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

控制流语句的回填:

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

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

S → i f   B   t h e n   S 1 S\rightarrow if\ B\ then\ S_1 Sif B then S1 语句和 S → i f   B   t h e n   S 1   e l s e   S 2 S\rightarrow if\ B\ then\ S_1\ else\ S_2 Sif B then S1 else S2 语句的SDT:

S → w h i l e   B   d o   S 1 S\rightarrow while\ B\ do\ S_1 Swhile B do S1 语句和 S → S 1 S 2 S\rightarrow S_1S_2 SS1S2 语句的SDT:

例:

while a < b do
  if c < 5 then
    while x > y do z = x + 1;
  else
    x = y;

switch语句的翻译

过程调用语句的翻译

过程调用语句的文法:

S → c a l l   i d   ( E l i s t ) E l i s t → E l i s t , E E l i s t → E \begin{aligned} & S\rightarrow call\ id\ (Elist) \\ & Elist\rightarrow Elist,E \\ & Elist\rightarrow E \end{aligned} Scall id (Elist)ElistElist,EElistE

过程调用语句的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

参考

  • 哈工大编译原理课件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/57290.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

游戏开发32课 typescript super

super 在类的方法中super就表示当前类的父类。 如果在子类中写了构造函数&#xff0c;在子类构造函数中必须对父类的构造函数进行调用。 例子 (function() { // 父类 class Animal { name: string; constructor(name: string) { this.na…

25. 答疑 - SAP OData 框架处理 Metadata 元数据请求的实现细节,前后端组件部署在同一台物理服务器

我的知识星球 里有一个朋友提出了 SAP OData 服务 metadata 缓存方面的疑问,本文就来详细说一说: jerry,啥时候有时间给介绍一下fiori的Metadata数据系统的处理机制吧。我现在在做的一个项目,用rap开发的。rap开发的service binding,在maintain service注册时,开始注册的…

简单的个人博客网站设计 静态HTML个人博客主页 DW个人网站模板下载 大学生简单个人网页作品代码 个人网页制作 学生个人网页设计作业

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

TEE安全系统SMC深入理解

1、TEE背景在文章开始之前提几个问题&#xff1a;Android手机中至少运行着几个操作系统OS&#xff1f;如何进入安全操作系统&#xff1f;异常等级和安全操作系统之间的关系&#xff1f;SMC调用的实质、约定及流程是什么&#xff1f;随着智能手机的普及&#xff0c;手机上数据的…

R语言中ARMA,ARIMA(Box-Jenkins),SARIMA和ARIMAX模型用于预测时间序列数据

原文链接:http://tecdat.cn/?p5919在本文中&#xff0c;我将介绍ARMA&#xff0c;ARIMA&#xff08;Box-Jenkins&#xff09;&#xff0c;SARIMA和ARIMAX模型如何用于预测时间序列数据&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。使用滞后算子计算滞后差分…

[附源码]计算机毕业设计JAVA校园网学生成绩查询系统

[附源码]计算机毕业设计JAVA校园网学生成绩查询系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM …

Android面试题——高级开发面试题二

一 面试题概述 回答自己理解的java虚拟机、gc机制Java多线程、线程池集合原理(hashmap,list)java虚引用封装、继承、多态的理解activity生命周期安卓activity和fragment数据传递Handler内存泄漏、内存溢出、内存抖动 原因及解决办法ANR原因以及解决办法性能优化、卡顿优化事件…

xshell与xftp

目录 1.什么是xshell 2.下载xshell与xftp 3.安装与操作xshell 4.什么是xftp 5.安装与操作xftp 6.xshell与xftp互联 1.什么是xshell Xshell是一个强大的安全终端模拟软件&#xff0c;它支持SSH1&#xff0c;SSH2&#xff0c; 以及Microsoft Windows平台的TELNET协议。. …

[Camunda BPMN进阶] 电商订单流程设计与调试

目录 摘要 基于BPMN的软件设计思想 电商订单流程业务场景 基本订单流程BPMN设计 1.最基本流程设计 2. 添加超时未付款自动取消功能 3. 添加15分钟付款提醒 4. 添加用户取消订单事件 进阶订单流程BPMN设计 1. 使用并行网关执行任务 2. 将具有相同事件分支的任务合并…

基于PHP+MySQL月子中心管理系统的设计与实现

月子中心管理系统是信息时代的产物,月子基本是每个适龄女子都会经历的一个特殊时期,尤其是在中国对月子的重视程度尤其的重要,只有让产妇和婴儿受到精心的照顾才能够让产妇更好的康复,才能够让婴儿更好的成长,所以越来越多的人关注到了月子期间的养护问题,为了能够让更多的月子…

安卓APP源码和报告——学生信息管理系统

学生信息管理系统APP演示视频《移动开发技术II》实践考核方案 适用网络工程&#xff08;网络软件开发&#xff09;2018级 一、考核内容&#xff1a; 环境配置及移动开发生命周期、控件的使用、用户界面设计、数据存储与访问、广播、服务、网络编程、蓝牙应用等知识点。 二、…

canal同步MySQL的binlog数据时踩了个大坑

背景 在同步MySQL数据到ES的场景中&#xff0c;选择了canal组件同步数据。 问题描述 在同步的时候发现canal-adapter中canal-adapter/conf/es7/product.yml 配置文件中sql 语句连表查询的时候会出现无法更新Elasticsearch 中数据的情况&#xff0c;而且日志没有提示异常&…

python使用opencv画圣诞树和画小星星函数

画星星函数&#xff1a; #img 图片 #x y 坐标 #size 大小 def darw_star(img,x,y,color,size40):poly_linenp.array([[x, y-size], [xint(size/4), y-int(size/4)], [xsize, y-int(size/4)],[xint(0.375*size),yint(size/4)],[xsize,ysize],\[x,yint(0.625*size)],[x-size,ysi…

分布式定时调度:xxl-job 万字详解

一.定时任务概述 1.定时任务认识 1.1.什么是定时任务 定时任务是按照指定时间周期运行任务。使用场景为在某个固定时间点执行&#xff0c;或者周期性的去执行某个任务&#xff0c;比如&#xff1a;每天晚上24点做数据汇总&#xff0c;定时发送短信等。 1.2.常见定时任务方案…

基于web的家电维修系统/家电维修管理系统

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的系统管理应运而生&#xff0c;各行各业相继进入信息管理时代&a…

生物素标记甾体化合物/多杀菌素探针分子/壳聚糖/聚乙二醇偶连基团为华生物提供

生物素标记的甾体化合物探针将D-生物素酰氯与胺反应,合成了新型生物素探针标记的甾体衍生物,经检测这些衍生物对某些肿瘤细胞株有较好的抗肿瘤活性,且对正常细胞株(HEK293T)没有明显的毒害作用。 生物素标记的多杀菌素探针分子&#xff0c;经检测验证&#xff0c;生物素标记多…

Python多字段排序函数——cmp_to_key()【LeetCode50天刷题计划寒假特别版(Day 1 — 最大数(11.40-12.20)】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言使用方法例子一、题目最大数示例提示二、思路三、代码前言 python标准模块functools中的cmp_to_key可以将一个cmp函数变成一个key函数&#xff0c;从而支持自定…

第二证券|支持多子女购房、提高公积金贷款额度、发放限时补贴

近来&#xff0c;多地购房支持方针密集出台。 12月1日&#xff0c;安徽安庆发布住所公积金新政&#xff0c;对多子女家庭首次请求住所公积金借款购买自住住所&#xff0c;最高借款额度添加10万元。 11月30日&#xff0c;江西九江也优化多子女家庭住所公积金事务&#xff0c;进…

[附源码]Python计算机毕业设计Django公益组织登记与查询系统论文

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

Springboot集成shiro框架:

1&#xff0c; Shiro&#xff1a;是一个强大且易用的Java安全框架&#xff0c;执行身份验证、授权、密码和会话管理。 使用Shiro的易于理解的API&#xff0c;可以快速、轻松地获得任何应用程序&#xff0c;从最小的移动应用程序到最大的网络和企业应用程序。 2&#xff0c;三…