第六章中间代码生成

《第六章中间代码生成》由会员分享,可在线阅读,更多相关《第六章中间代码生成(59页珍藏版)》请在文档大全上搜索。
1、第六章 中间代码生成赵建华南京大学计算机系本章内容 中间代码表示 抽象语法树 三地址代码:x=y op z 静态类型检查 类型检查(type checking) 语法分析之后的抽象语法(syntax)检查,比如break的位置,goto的目标. 中间代码生成编译器前端的逻辑结构三地址代码(1) 每条指令右侧最多有一个运算符 一般情况可以写成x = y op z 允许的运算分量: 名字:源程序中的名字作为三地址代码的地址 常量:源程序中出现或生成的常量 编译器生成的临时变量三地址代码(2) 指令集合 (1) 运算/赋值指令:x=y op zx = op y 复制指令:x=y 无条件转移指令:go
2、to L 条件转移指令:if x goto Lif False x goto L 条件转移指令:if x relop y goto L三地址代码(3) 指令集合(2) 过程调用/返回: param x1/设置参数 param x2 param xn call p, n/调用子过程p,n为参数个数 带下标的复制指令:x=yi xi=y 注意:i表示离开数组位置第i个字节,而不是数组的第i个元素 地址/指针赋值指令: x=&yx=*y*x=y例子 语句 do i = i + 1; while (aiv);三地址指令的四元式表示方法 在实现时,可以使用四元式/三元式/间接三元式来表示三地址指令 四元
3、式:可以实现为纪录(或结构) 格式(字段):oparg1arg2result op: 运算符的内部编码 arg1,arg2,result是地址 x=y+z+ y z x 单目运算符不使用arg2 param运算不使用arg2和result 条件转移/非条件转移将目标标号放在result字段四元式的例子 赋值语句:a=b* -c + b* -c三元式表示 三元式(triple)oparg1 arg2 使用三元式的位置来引用三元式的运算结果 xi=y需要拆分为两个三元式 求xi的地址,然后再赋值 x=y op z需要拆分为(这里?是编号) (?)opyz =x? 问题:在优化时经常需要移动/删除/
4、添加三元式,导致三元式的移动。三元式的例子 a=b*-c + b * -c间接三元式 包含了一个指向三元式的指针的列表 我们可以对这个列表进行操作,完成优化功能;操作时不需要修改三元式中的参数。静态单赋值(SSA) SSA中的所有赋值都是针对不同名的变量 对于同一个变量在不同路径中定值的情况,可以使用函数来合并不同的定值 if (flag) x=-1; else x = 1;y = x*a if (flag) x1=-1; else x2 = 1; x3=(x1,x2); y = x3*a类型和声明 类型检查(Type Checking) 利用一组规则来检查运算分量的类型和运算符的预期类型是否
5、匹配。 类型信息的用途 查错、确定名字需要的内存空间、计算数组元素的地址、类型转换、选择正确的运算符 本节的内容 确定名字的类型, 变量的存储空间布局(相对地址)类型表达式 类型表达式(type expression):表示类型的结构 基本类型 类名 类型构造算子作用于类型 array数字,类型表达式 record字段/类型对的列表(可以用符号表表示) 函数类型构造算子:参数类型结果类型 笛卡尔积:s X t 可以包含取值为类型表达式的变量类型表达式的例子 类型例子 元素个数为3X4的二维数组 数组的元素的记录类型 该记录类型中包含两个字段: x和y,其类型分别是float和integer 类
6、型表达式 array3, array4,record(x,float),(y,float)类型等价 不同的语言有不同的类型等价的定义 结构等价 或者它们是相同的基本类型 或者是相同的构造算子作用于结构等价的类型而得到的。 或者一个类型是另一个类型表达式的名字 名等价 类型名仅仅代表其自身声明 文法 D T id ; D | T B C | record D C int | float C | num C 含义: D生成一系列声明; T生成不同的类型; B生成基本类型int/float; C表示分量,生成num序列; 注意record中包含了各个字段的声明。字段声明和变量声明的文法一致。局部变量
7、的存储布局 变量的类型可以确定变量需要的内存 即类型的宽度 可变大小的数据结构只需要考虑指针 函数的局部变量总是分配在连续的区间; 因此给每个变量分配一个相对于这个区间开始处的相对地址 变量的类型信息保存在符号表中;计算T的类型和宽度的SDT 综合属性:type, width 全局变量t和w用于将类型和宽度信息从B传递到C 相当于C的继承属性,因为总是通过拷贝来传递,所以在SDT中只赋值一次。也可以把t和w替换为C.t和C.wSDT运行的例子 输入:int23作用域和符号表 在具有语句块概念的编程语言中,标识符x在最内层的x声明的作用域中。 每个作用域对应于一个符号表;多个符号表形成树状结构。
8、 在语义分析时,通过栈来存放当前符号表及其祖先。声明序列的SDT(1) 在处理一个过程/函数时,局部变量应该放到单独的符号表中去; 这些变量的内存布局独立 相对地址从0开始; 假设变量的放置和声明的顺序相同; SDT的处理方法 变量offset记录当前可用的相对地址; 每“分配”一个变量,offset的值增加相应的值 top.put(id.lexeme, T.type, offset) 在当前符号表(位于栈顶)中创建符号表条目,记录标识符的类型,偏移量声明序列的SDT(2) 我们可以把offset看作D的继承属性 D.offset表示D中第一个变量的相对地址 PD.offset =0 D D
9、T id; D1.offset = D.offset + T.width; D1记录字段的处理 Trecord D 为每个记录创建单独的符号表 首先创建一个新的符号表,压到栈顶; 然后处理对应于字段声明的D,字段都被加入到新符号表中; 最后根据栈顶的符号表构造出record类型表达式;符号表出栈表达式代码的SDD 将表达式翻译成三地址指令序列 表达式的SDD 属性code表示代码 addr表示存放表达式结果的地址(临时变量) new Temp()可以生成一个临时变量 gen()生成一个指令增量式翻译方案主属性code满足增量式翻译的条件。注意: top.get()从栈顶符号表开始,逐个向下寻找
10、id的信息。 这里的gen发出相应的代码数组元素的寻址 假设数组元素被存放在连续的存储空间中。 元素从0到n-1编号,第i个元素的地址为base+i*w K维数组的寻址:假设数组按行存放,即首先存放A0i2ik,然后存放A1i2ik, Ai1i2ik的地址base+i1*w1+i2*w2+ik*wk 或者base+(i1*n2+i2)*n3+i3)*nk+ik)*w 其中:base、w、i、n的值可以从符号表中找到。新的文法产生式数组元素L:LLE | idE以数组元素为左部的赋值:SL=E;数组元素作为表达式中的因子:ELL的代码计算偏移量,将结果存放于L.addr所指的临时变量中 综合属性