Top Banner
编编编编编 《》 语语语语语语语语语语语 语语语
66

Slide08 807007748

Aug 06, 2015

Download

Education

Shiyao Ma
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Slide08 807007748

《编译原理》

语义分析与中间代码生成

第八讲

Page 2: Slide08 807007748

《编译原理》

语义分析和中间代码生成在编译程序中的 逻辑位置

语义分析与中间代码生成

词法分析

语法分析

中间代码生成

中间代码优化

目标代码优化

目标代码生成

静态语义分析

语义处理

Page 3: Slide08 807007748

《编译原理》

语义分析和中间代码生成的重要数据结构

符号表( symbol tables ) • 名字信息建立后加入 / 更改符号表 名字信息如:种类,类型,偏移地址,占用空间等• 需要获取名字信息时,查找符号表

• 符号表的组织可以体现名字作用域规则

(符号表的组织已在第七讲专门讨论)

语义分析与中间代码生成

Page 4: Slide08 807007748

《编译原理》

语义分析 中间代码生成

语义分析与中间代码生成

Page 5: Slide08 807007748

《编译原理》

与语义分析相关的工作

语义分析

静态语义检查• 编译期间所进行的语义检查

动态语义检查• 所生成的代码在运行期间进行的语义检查

收集语义信息• 为语义检查收集程序的语义信息

• 为代码生成等后续阶段收集程序的语义信息 有些内容合并到“中间代码生成”部分讨论 (如过程、数组声明的语义处理)

Page 6: Slide08 807007748

《编译原理》

静态语义检查 代码生成前程序合法性检查的最后阶段

• 静态类型检查( type checks ) 检查每个操作是否遵守语言类型系统的定义• 名字的作用域( scope )分析 建立名字的定义和使用之间联系• 控制流检查( flow-of-control checks ) 控制流语句必须使控制转移到合法的地方(如 break 语句必须有合法的语句包围它)• 唯一性检查( uniqueness checks ) 很多场合要求对 象只能被定义一次(如枚举类型的元素不能重复出现)• 名字相关检查( name-related checks ) (如,一些名字可能被要求配对出现)• ……

语义分析

Page 7: Slide08 807007748

《编译原理》

类型检查程序( type checker )负责类型检查• 验证语言结构是否匹配上下文所期望的类型• 为相关阶段搜集及建立必要的类型信息• 实现某个类型系统( type system )

静态类型检查• 编译期间进行的类型检查

动态类型检查• 目标程序运行期间进行的类型检查

类型检查

语义分析

Page 8: Slide08 807007748

《编译原理》

类型表达式( type expressions )• 由基本类型,类型名字,类型变量,及类型构 造子( type constructor )归纳定义的表达式

类型系统( type systems )• 将类型表达式赋给程序各个部分的规则集合

类型表达式和类型系统

语义分析

Page 9: Slide08 807007748

《编译原理》

某个类型表达式集合(将用于示范类型检查 程序的设计)归纳定义如下:

• 基本类型 bool, char, int, real, type_error 和 ok 是类型表达式(其中, type_error 专用于类型检查 时的出错提示; ok 可用于语句的类型检查)• 若 T 是类型表达式,则 array(I,T) 是类型表达式, 表示元素类型是 T ,下标集合是 I 的数组类型(这 里, I 是一个整数区间,如 1..10 )• 若 T 是类型表达式,则 pointer(T) 是类型表达式, 表示指向类型为 T 的对象的指针类型

类型表达式举例

语义分析

Page 10: Slide08 807007748

《编译原理》

以下是定义某个简单语言的上下文无关文法 (将用于本讲的设计示例) G[P] :

一个简单语言

P D ; S

D D ; D id : T

T boolean char integer real array [ num ] of T T

E true false literal num num.num id E op E E rop E E [ E ] E

S id := E if E then S while E do S S ; S

语义分析

Page 11: Slide08 807007748

《编译原理》

语法制导的方法• 将类型表达式作为属性值赋给程序各个部分• 设计恰当的翻译模式• 可实现相应语言的一个类型系统

类型检查程序的设计

语义分析

Page 12: Slide08 807007748

《编译原理》

语法制导的方法• 处理声明的翻译模式(保存标识符的类型)

类型检查程序的设计

P D ; ED D1 ; D2

D id : TT boolean T charT integerT real T array [ num ] of T1

T T1

{ addtype(id.entry ,T.type)}{ T.type := bool }{ T.type := char }{ T.type := int }{ T.type := real }{ T.type := array(1..num.val, T1.type) }{ T.type := pointer(T1.type) }

语义分析

Page 13: Slide08 807007748

《编译原理》

语法制导的方法• 表达式的类型检查

类型检查程序的设计

E trueE falseE literalE numE num.num E idE E1 op E2

{ E.type := bool }{ E.type := bool }{ E.type := char }{ E.type := int }{ E.type := real }{ E.type := lookup_type (id.entry) }{ E.type := if E1.type=real and E2.type=real then real else if E1.type=int and E2.type=int then int else type_error }

语义分析

Page 14: Slide08 807007748

《编译原理》

语法制导的方法• 表达式的类型检查(续)

类型检查程序的设计

E E1 rop E2

E E1 [E2 ]

E E1

{ E.type := if E1.type=real and E2.type=real then bool else if E1.type=int and E2.type=int then bool else type_error }{ E.type := if E2.type=int and E1.type=array(s,t) then t else type_error }{ E.type := if E1.type= pointer(t) then t else type_error }

语义分析

Page 15: Slide08 807007748

《编译原理》

语法制导的方法• 语句的类型检查

类型检查程序的设计

S id := E

S if E then S1

S while E do S1

S S1 ; S2

{ S.type := if lookup_type (id.entry) = E.type then ok else type_error }{ S.type := if E.type=bool then S1.type else type_error }{ S.type := if E.type=bool then S1.type else type_error }{ S.type := if S1.type=ok and S2.type =ok then ok else type_error }

语义分析

Page 16: Slide08 807007748

《编译原理》

语法制导的方法• 增加数自动类型转换( type casting )的支持

类型检查程序的设计

E E1 op E2

E E1 rop E2

{ E.type := if E1.type=real and E2.type=real then real else if E1.type=int and E2.type=int then int else if E1.type=real and E2.type=int then real else if E1.type=int and E2.type=real then real else type_error }{ …… }

语义分析

Page 17: Slide08 807007748

《编译原理》

静态作用域• 通过符号表实现 (已在第七讲讨论)

动态作用域 • 通过运行时活动记录实现 (参见下一讲)

作用域分析

语义分析

Page 18: Slide08 807007748

《编译原理》中间代码生成

源程序的不同表示形式 作用

• 源语言和目标语言之间的桥梁,避开二者 之间较大的语义跨度,使编译程序的逻辑 结构更加简单明确• 利于编译程序的重定向• 利于进行与目标机无关的优化

中间代码

Page 19: Slide08 807007748

《编译原理》中间代码生成

有不同层次不同目的之分 中间代码举例

• Postfix (后缀式,逆波兰式)• AST ( Abstract syntax tree ,抽象语法树)• TAC ( Three-address code,三地址码,四元式)

• P-code (特别用于 Pasal 语言实现)• Bytecode ( Java 编译器的输出 , Java 虚拟机的输入)• SSA ( Static single assignment form ,静态单赋值形式)

中间代码的形式

Page 20: Slide08 807007748

《编译原理》中间代码生成

算术表达式 A + B * ( C - D ) + E / ( C - D ) ^N (中缀形式)

• Postfix (后缀式,逆波兰式) A B C D - * + E C D – N ^ / +

中间代码举例

Page 21: Slide08 807007748

《编译原理》中间代码生成

算术表达式 A + B * ( C - D ) + E / ( C - D ) ^N (中缀形式)

• TAC ( Three-address code,三地址码,四元式)

(1) ( - C D T1 ) T1 := C - D (2) ( * B T1 T2) T2 := B * T1 (3) ( + A T2 T3) T3 := A + T2 (4) ( - C D T4) 或 T4 := C - D (5) ( ^ T4 N T5) T5 := T4 ^ N (6) ( / E T5 T6) T6 := E / T5 (7) (+ T3 T6 T7) T7 := T3 + T6

中间代码举例

Page 22: Slide08 807007748

《编译原理》中间代码生成

算术表达式 A + B * ( C - D ) + E / ( C - D ) ^N (中缀形式)

• AST ( Abstract syntax tree ,抽象语法树)

中间代码举例

^

+

+

/

A

*

E-B

DC -

DC

N

Page 23: Slide08 807007748

《编译原理》中间代码生成

算术表达式 A + B * ( C - D ) + E / ( C - D ) ^N (中缀形式)

• DAG ( Directed Acyclic Graph, 有向无圈图,改进型 AS

T )

中间代码举例

^

+

+

/

A

*

EB

-

DC

N

Page 24: Slide08 807007748

《编译原理》中间代码生成

静态单赋值形式

中间代码举例

x ¬ 5 x ¬ x - 3 if x < 3 then y ¬ x * 2 w ¬ y else y ¬ x - 3 w ¬ x - y z ¬ x + y

x1 ¬ 5 x2 ¬ x1 - 3 if x2 < 3 then y1 ¬ x2 * 2 w1 ¬ y1 else y2 ¬ x2 - 3y3 ¬ f(y1 , y2) w2 ¬ x2 - y3 z ¬ x2 + y3

Page 25: Slide08 807007748

《编译原理》中间代码生成

语法制导的方法• 例:生成抽象语法树

中间代码生成

S id := E

S if E then S1

S while E do S1

S S1 ; S2

E idE E1 + E2 E E1 * E2

E ( E1 ) ……

{ S.ptr := mknode(‘assign’, mkleaf(id.entry), E.ptr) }{ S.ptr := mknode(‘if_then’, E.ptr, S1.ptr) } { S.ptr := mknode(‘while_do’, E.ptr, S1.ptr) } { S.ptr := mknode(‘;’ , S1.ptr , S2.ptr) } { E.ptr := mkleaf(id.entry) }{ E.ptr := mknode(‘+’ , E1.ptr , E2.ptr)}{ E.ptr := mknode(‘*’ , E1.ptr , E2.ptr)}{ E.ptr := E1.ptr }……

mknode: 构造内部结点Mkleaf : 构造叶子结点

Page 26: Slide08 807007748

《编译原理》中间代码生成

顺序的语句序列 其语句一般具有如下形式 x := y op z

(op 为操作符, y 和 z 为操作数, x 为结果 )

三地址码 TAC

Page 27: Slide08 807007748

《编译原理》中间代码生成 课程后续部分用到的 TAC 语句类型

• 赋值语句 x := y op z ( op 代表二元算术 / 逻辑运算)• 赋值语句 x := op y ( op 代表一元运算)• 复写语句 x := y ( y 的值赋值给 x )• 无条件跳转语句 goto L (无条件跳转至标号 L )• 条件跳转语句 if x rop y goto L ( rop 代表关系运算)• 过程调用语句序列 param x1 … param xn call p,n• 过程返回语句 return y ( y 可选,存放返回值)• 下标赋值语句 x := y[i] 和 x[i] := y (前者表示将地 址 y 起第 i 个存储单元的值赋给 x ,后者类似)• 地址和指针赋值语句 x := &y, x := *y 和 *x := y

参考 A.V.Aho 等的教材(龙书)

Page 28: Slide08 807007748

《编译原理》中间代码生成

语义属性 id.name : id 的词法名字(符号表中的名字) E.place : 用来存放 E 的值的存储位置 E.code : E 求值的 TAC 语句序列

语义函数 / 过程 lookup ( id.name ) : 从符号表中查找名字为 id.name 的项,返回存放相应值的指针(存储位置),若无该 项,则返回 nil

gen : 生成一条 TAC 语句 newtempt : 返回一个未使用过的存储位置

赋值语句及算数表达式的语法制导翻译

Page 29: Slide08 807007748

《编译原理》中间代码生成

翻译模式S id := E

E E1 + E2

E E1 * E2

E - E1

E ( E1 ) E id

{ p := lookup (id.name); if ( pnil) then S.code := E.code || gen (p ‘:=‘ E.place) else error }{ E.place := newtemp; E.code := E1.code || E2.code || gen (E.place ‘:=‘E1.place ‘+’ E2.place) }{ E.place := newtemp; E.code := E1.code || E2.code || gen (E.place ‘:=‘E1.place ‘*’ E2.place) }{ E.place := newtemp; E.code := E1.code || gen (E.place ‘:=‘ ‘uminus‘ E1.palce) }{ E.place := E1.place ; E.code := E1.code }{ p := lookup (id.name); if ( pnil) then E.place := p else error ; E.code := ‘’ }

赋值语句及算数表达式的语法制导翻译

Page 30: Slide08 807007748

《编译原理》中间代码生成

回顾:类型属性的计算 说明语句的语法制导翻译

P D ; ED D1 ; D2

D id : TT charT integerT real T array [ num ] of T1

T T1

E …

{ enter (id.name ,T.type)}{ T.type := char }{ T.type := integer }{ T.type := real }{ T.type := array(1..num.val, T1.type) }{ T.type := pointer(T1.type) }

enter (id.name ,T.type) : 将符号表中 id.name 所对应表项的 type 域置为 T.type

Page 31: Slide08 807007748

《编译原理》中间代码生成

语义属性 id.name : id 的词法名字(符号表中的名字) T.type : 类型属性 T.width , D.width : 数据宽度(字节数) D.offset : 相对于过程数据区基址的下一个可用的相 对偏移地址(参考下一讲 : 运行时存储组织)

语义函数 / 过程 enter (id.name ,T.type, D.offset) : 将符号表中 id.name 所对应表项的 type 域置为 T.type, offset 域置为 D.offset

过程(函数)中说明语句的语法制导翻译

Page 32: Slide08 807007748

《编译原理》中间代码生成 过程中说明语句的语法制导翻译

翻译模式P { D.offset := 0 } D ; ED {D1.offset := D.offset } D1 ; {D2.offset := D.offset + D1.width } D2

{D.width := D1.width + D2.width }D id : T { enter (id.name ,T.type, D.offset) ; D.width := T.width } T char { T.type := char ; T.width := 1 } T integer { T.type := integer ; T.width := 4 } T real { T.type := real ; T.width := 8 } T array [ num ] of T1 { T.type := array(num.val, T1.type ) ;

T.width := num.val T1.width } T T1 { T.type := pointer ( T1.type ) ;

T.width := 4 } E …

Page 33: Slide08 807007748

《编译原理》中间代码生成

数组说明 参考前页的翻译模式,可了解(一维)数组说明的翻译 思想 . 至于符号表中一般情况下是如何组织数组说明信 息的,随后将会讨论 .

数组说明和数组元素引用的语法制导翻译

D id : T { enter (id.name ,T.type, D.offset) ; D.width := T.width } …

T array [ num ] of T1 { T.type := array(num.val, T1.width ) ;

T.width := num.val T1.width } …

Page 34: Slide08 807007748

《编译原理》中间代码生成

数组引用

数组说明和数组元素引用的语法制导翻译

S id := E

S E1[E2 ] := E3

E E1[E2]

E id

{ p := lookup (id.name); if ( pnil) then S.code := E.code || gen (p ‘:=‘ E.place) else error }{ S.code := E2.code || E3.code || gen (E1.place ‘[’ E2.place ‘]’ ‘:=‘ E3.place) }{ E.place := newtemp; E.code := E2.code || gen (E.place ‘:=‘ E1.place ‘[’ E2.place ‘]’ )}{ p := lookup (id.name); if ( pnil) then E.place := p else error ; E.code := ‘’}

Page 35: Slide08 807007748

《编译原理》中间代码生成

数组的内情向量( dove vector ) 在处理数组时,通常会将数组的有关信息记录在一些单 元中,称为“内情向量” . 对于静态数组,内情向量可放在 符号表中;对于可变数组,运行时建立相应的内情向量 例: 对于静态数组说明 A[l1:u1,l2:u2,…,ln:un] ,可以在符 号表中建立如下形式的内情向量 :

数组说明和数组元素引用的语法制导翻译

l1 u1

l2 u2

ln un

type a

n C

… …

li : 第 i 维的下界ui : 第 i 维的上界type: 数组元素的类型a: 数组首元素的地址n: 数组维数C: 随后解释

Page 36: Slide08 807007748

《编译原理》中间代码生成

数组元素的地址计算 例:对于静态数组 A[l1:u1,l2:u2,…,ln:un] ,若数组布局采 用行优先的连续布局,数组首元素的地址为 a ,则数组 元素 A[i1,i2,…,in] 的地址 D 可以如下计算 :

D = a + (i1-l1)(u2-l2)(u3-l3) …(un-ln)

+ (i2-l2)(u3-l3)(u4-l4) …(un-ln)

+…+ (in-1-ln-1)(un-ln) + (in-ln)

数组说明和数组元素引用的语法制导翻译

重新整理后得 : D = a – C + V ,其中 C = (…(l1 (u2-l2) +l2)(u3-l3) + l3)(u4-l4) +…+ l n-1)(un-ln) + ln

V = (…((i1 (u2-l2) +i2)(u3-l3) + i3)(u4-l4) +……+ i n-1)(un-ln) + in

(这里的 C 即为前页内情向量中的 C )

Page 37: Slide08 807007748

《编译原理》中间代码生成

直接对布尔表达式求值例如 : 可以用数值“ 1” 表示 true; 用数值“ 0” 表示 false;采用与算术表达式类似的方法对布尔表达式进行求值

通过控制流体现布尔表达式的语义方法:通过转移到程序中的某个位置来表示布尔表达式的求值结果 优点:方便实现控制流语句中布尔表达式的翻译常可以得到短路( short-circuit )代码,而避免不必要的求值,如:在已知 E1 为真时,不必再对 E1E2 中的 E2

进行求值;同样,在已知 E1 为假时,不必再对 E1E2

中的 E2 进行求值

布尔表达式的语法制导翻译

Page 38: Slide08 807007748

《编译原理》中间代码生成

直接对布尔表达式求值

布尔表达式的语法制导翻译

E E1 or E2

E E1 and E2

E not E1

E ( E1 ) E id1 rop id2

E trueE false

{ E.place := newtemp; E.code := E1.code || E2.code || gen (E.place ‘:=‘ E1.place ‘or’ E2.place) }{ E.place := newtemp; E.code := E1.code || E2.code || gen (E.place ‘:=‘ E1.place ‘and’ E2.place) }{ E.place := newtemp; E.code := E1.code || gen (E.place ‘:=‘ ‘not’ E1.palce) }{ E.place := E1.place ; E.code := E1.code }{ E.place := newtemp; E.code := gen ( ‘if‘ id1.place rop.op id2.place ‘goto’ nextstat+3) || gen (E.place ‘:=‘ ‘0’) || gen (‘goto’ nextstat+2) || gen (E.place ‘:=‘ ‘1’) }{ E.place := newtemp; E.code := gen(E.place ‘:=‘ ‘1’) }{ E.place := newtemp; E.code := gen(E.place ‘:=‘ ‘0’) }

nextstat 返回输出代码序列中下一条 TAC 语句的下标

Page 39: Slide08 807007748

《编译原理》中间代码生成

通过控制流体现布尔表达式的语义 例 : 布尔表达式 E = a<b or c<d and e<f 可能翻译为如 下 TAC 语句序列(采用短路代码, E.true 和 E.false 分别代表 E 为真和假时对应于程序中的位置,可用 标号体现): if a<b goto E.true goto label1 label1: if c<d goto label2 goto E.false label2: if e<f goto E.true goto E.false

布尔表达式的语法制导翻译

Page 40: Slide08 807007748

《编译原理》中间代码生成

翻译布尔表达式至短路代码

布尔表达式的语法制导翻译

E E1 or E2

E E1 and E2

E not E1

E ( E1 ) E id1 rop id2

E trueE false

{ E1.true := E.true ; E1.false := newlabel; E2.true := E.true ; E2.false := E.false ; E.code := E1.code || gen (E1.false ‘:‘) || E2.code }{ E1.false := E.false ; E1.true := newlabel; E2.true := E.true ; E2.false := E.false ; E.code := E1.code || gen (E1.true ‘:‘) || E2.code }{ E1.true := E.false; E1.false := E.true; E.code := E1.code}{ E1.true := E.true; E1.false := E.false; E.code := E1.code}{ E.code := gen ( ‘if‘ id1.place rop.op id2.place ‘goto’ E.true) || gen (‘goto’ E.false ) }{ E.code := gen (‘goto’ E.true ) }{ E.code := gen (‘goto’ E.false ) }

Page 41: Slide08 807007748

《编译原理》中间代码生成

if-then 语句 S if E then S1

{ E.true := newlable; E.false := S.next; S1.next := S.next;

S.code := E.code || gen(E.true ‘:’) || S1.code }

条件语句的语法制导翻译

E.code

S1.codeE.true:

E.false: ……

to E.true

to E.false

newlable 返回一个新的语句标号S.next 属性表示 S 之后要执行的首条 TAC 语句的标号

Page 42: Slide08 807007748

《编译原理》中间代码生成

if-then-else 语句 S if E then S1 else S2

{ E.true := newlable; E.false := newlable; S1.next := S.next; S2.next := S.next;

S.code := E.code || gen(E.true ‘:’) || S1.code || gen(‘goto’ S.next) || gen(E.false ‘:’) || S2.code }

条件语句的语法制导翻译

E.code

S1.codeE.true:

E.false:goto S.next

to E.true

to E.fase

……

S2.code

S.next:

Page 43: Slide08 807007748

《编译原理》中间代码生成 循环语句的语法制导翻译

while 语句 S while E do S1

{ S.begin := newlable; E.true := newlable; E.false := S.next; S1.next := S.begin;

S.code := gen(S.begin ‘:’) || E.code || gen(E.true ‘:’) || S1.code || gen(‘goto’ S.begin) }

E.code

S1.code

S.begin:

E.false:

goto S.begin

to E.true

to E.false

……

E.true:

Page 44: Slide08 807007748

《编译原理》中间代码生成 复合语句的语法制导翻译

顺序复合语句 S S1 ; S2

{ S1.next := newlable; S2.next := S.next;

S.code := S1.code || gen(S1.next ‘:’) || S2.code }

S1.code

S2.code

S.next: ……

S1.next:

Page 45: Slide08 807007748

《编译原理》中间代码生成

翻译布尔表达式至短路代码(翻译模式)

布尔表达式的语法制导翻译

E { E1.true := E.true ; E1.false := newlabel } E1 or { E2.true := E.true ; E2.false := E.false } E2

{ E.code := E1.code || gen (E1.false ‘:‘) || E2.code }

E { E1.false := E.false ; E1.true := newlabel } E1 and { E2.true := E.true ; E2.false := E.false } E2

{ E.code := E1.code || gen (E1.true ‘:‘) || E2.code }

E not { E1.true := E.false; E1.false := E.true} E1 {E.code := E1.code}

E ( { E1.true := E.true; E1.false := E.false } E1 ) { E.code := E1.code }

E id1 rop id2   { E.code := gen ( ‘if‘ id1.place rop.op id2.place ‘goto’ E.true) || gen (‘goto’ E.false ) }

E true { E.code := gen (‘goto’ E.true ) }

E false { E.code := gen (‘goto’ E.false ) }

Page 46: Slide08 807007748

《编译原理》中间代码生成

if-then 语句(翻译模式) S if { E.true := newlable ; E.false := S.next } E       then { S1.next := S.next }   S1      { S.code := E.code || gen(E.true ‘:’) || S1.code }

条件语句的语法制导翻译

E.code

S1.codeE.true:

E.false: ……

to E.true

to E.false

newlable 返回一个新的语句标号S.next 属性表示 S 之后要执行的首条 TAC 语句的标号

Page 47: Slide08 807007748

《编译原理》中间代码生成

if-then-else 语句(翻译模式) 条件语句的语法制导翻译

E.code

S1.codeE.true:

E.false:goto S.next

to E.true

to E.fase

……

S2.code

S.next:

S if { E.true := newlable;     E.false := newlable }    E   then      { S1.next := S.next }    S1   else      { S2.next := S.next }    S2       { S.code := E.code ||       gen(E.true ‘:’) ||       S1.code ||       gen(‘goto’ S.next) ||      gen(E.false ‘:’) ||      S2.code      }

Page 48: Slide08 807007748

《编译原理》中间代码生成 循环语句的语法制导翻译

while 语句(翻译模式)

E.code

S1.code

S.begin:

E.false:

goto S.begin

to E.true

to E.false

……

E.true:

S while

      { E.true := newlable;     E.false := S.next }    E do     { S1.next := newlable }   S1  

     { S.begin :=   S1.next;    S.code := gen(S.begin ‘:’)      || E.code       || gen(E.true ‘:’)      || S1.code      || gen(‘goto’ S.begin)    }

Page 49: Slide08 807007748

《编译原理》中间代码生成 复合语句的语法制导翻译

顺序复合语句(翻译模式)

S { S1.next := newlable } S1 ; { S2.next := S.next } S2

{ S.code := S1.code    || gen(S1.next ‘:’)    || S2.code }

S1.code

S2.code

S.next: ……

S1.next:

Page 50: Slide08 807007748

《编译原理》中间代码生成

拉链与代码回填( backpatching )

另一种控制流中间代码生成技术 比较:前面的方法采用 L- 属性文法 / 翻译模式 下面的方法采用 S- 属性文法 / 翻译模式

Page 51: Slide08 807007748

《编译原理》中间代码生成 拉链与代码回填 语义属性 E.truelist : “真链”,链表中的元素表示 一系列跳转语 句的地址,这些跳转语句的目标标号是体现布 尔表达式 E 为“真”的标号 E. falselist : “假链”,链表中的元素表示 一系列跳转语 句的地址,这些跳转语句的目标标号是体现布 尔表达式 E 为假的标号 S. nextlist : “next 链”,链表中的元素表示 一系列跳转 语句的地址,这些跳转语句的目标标号是在执 行序列中紧跟在 S 之后的下条 T

AC 语句的标号

Page 52: Slide08 807007748

《编译原理》中间代码生成 拉链与代码回填 语义函数 / 过程 makelist(i) : 创建只有一个结点 i 的表,对应存放目标 TAC 语句数组的一个下标 merge(p1,p2) : 连接两个链表 p1 和 p2 ,返回结果链表 backpatch(p,i) : 将链表 p 中每个元素所指向的跳转语句 的标号置为 i

nextstm : 下一条 TAC 语句的地址 emit (…) : 输出一条 TAC 语句,并使 nextstm 加 1

Page 53: Slide08 807007748

《编译原理》中间代码生成

拉链与代码回填 处理布尔表达式的翻译模式

E E1 or M E2

E E1 and M E2

E not E1

{ backpatch(E1.falselist,M.gotostm) ; E.truelist := merge(E1.truelist, E2.truelist) ; E.falselist := E2.falselist }

{ backpatch(E1.truelist,M.gotostm) ; E.falselist := merge(E1.falselist, E2.falselist) ; E.truelist := E2.truelist }

{ E.truelist := E1.falselist ; E.falselist := E1.truelist }

注 : 这里可以规定产生式的优先级依次递增来解决冲突问题 (下同)

Page 54: Slide08 807007748

《编译原理》中间代码生成 拉链与代码回填 处理布尔表达式的翻译模式

E ( E1 )

E id1 rop id2

E true

E false

M

{ E.truelist := E1.truelist ; E.falselist := E1.falselist }

{ E.truelist := makelist ( nextstm); E.falselist := makelist ( nextstm+1); emit ( ‘if‘ id1.place rop.op id2.place ‘goto _’ ); emit (‘goto _’) }

{ E.truelist := makelist ( nextstm); emit (‘goto _’) }

{ E.falselist := makelist ( nextstm); emit (‘goto _’) }

{ M.gotostm := nextstm }

Page 55: Slide08 807007748

《编译原理》中间代码生成 拉链与代码回填 布尔表达式 E = a<b or c<d and e<f 的翻译示意

( 0 ) if a<b goto _

or

E.truelist={0,4}E.falselist={3,5}

and

M.gotostm=2

M.gotostm=4

E.truelist={4}E.falselist={3,5}

E.truelist={0}E.falselist={1}

<a b

E.truelist={2}E.falselist={3}

<c d

E.truelist={4}E.falselist={5}

<e f

( 1 ) goto _

( 3 ) goto _

( 2 ) if c<d goto _

( 4 ) if e<f goto _

( 5 ) goto _

(4)

(2)

Page 56: Slide08 807007748

《编译原理》中间代码生成 拉链与代码回填 处理条件语句的翻译模式

S if E then M S1 { backpatch(E.truelist,M.gotostm) ; S.nextlist := merge(E.falselist, S1.nextlist) }

S if E then M1 S1 N else M2 S2 { backpatch(E.truelist, M1.gotostm) ; backpatch(E.falselist, M2.gotostm) ; S.nextlist := merge(S1.nextlist, merge(N.nextlist, S2.nextlist) ) }

M { M.gotostm := nextstm }

N { N.nextlist := makelist(nextstm); emit(‘goto _’) }

Page 57: Slide08 807007748

《编译原理》中间代码生成 拉链与代码回填 处理循环、复合及其它语句的翻译模式

S while M1 E do M2 S1 { backpatch(S1.nextlist, M1.gotostm) ; backpatch(E.truelist, M2.gotostm) ; S.nextlist := E.falselist; emit(‘goto’, M1.gotostm)}

S begin L end { S.nextlist := L.nextlist }

S A { S.nextlist := nil }

L L1; M S { backpatch(L1.nextlist, M.gotostm) ; L.nextlist := S.nextlist }

L S { L.nextlist := S.nextlist }

Page 58: Slide08 807007748

《编译原理》中间代码生成

拉链回填技术示例

GOTO 语句的语法制导翻译

……

(10) goto L ……

(20) goto L ……

(30) goto L ……

(40) L: ……

(50) goto L ……

……

(100) goto 0 ……

(200) goto 100 ……

(300) goto 200 ……

(400) L:

Page 59: Slide08 807007748

《编译原理》中间代码生成

拉链回填技术示例

GOTO 语句的语法制导翻译

……

(10) goto L ……

(20) goto L ……

(30) goto L ……

(40) L: ……

(50) goto L ……

……

(100) goto 400 ……

(200) goto 400 ……

(300) goto 400 ……

(400) L: ……

(500) goto 400 ……

Page 60: Slide08 807007748

《编译原理》中间代码生成

利用标号的符号表项维护拉链 若采用类似 PL0 的符号表结构,可以设计标号表项包 括如下域: name , kind , level 等,与其它类别的符号一样 defined : 表示该标号的说明是否已处理过 add : 该标号的说明处理之前用于拉链,处理过后表 示该标号的说明翻译后所指向的 TAC 语句位置

语义函数 / 过程 setlbdefined (id.name,x), getlbdefined (id.name) setlbadd (id.name,x), getlbadd (id.name) 分别表示设置和获取标号的 defined 、 add 值 backpatch (nextstm): 沿拉链反向将所有 goto 语句的目标返填为 nextstm

GOTO 语句的语法制导翻译

Page 61: Slide08 807007748

《编译原理》中间代码生成

标号说明和 GOTO 语句的翻译模式 GOTO 语句的语法制导翻译

S id : S { p := lookup (id.name); if (p=nil) then enter(id.name) ; setlbdefined(id.name,1) ; setlbadd(id.name, nextstm); backpatch(nextstm) }

S goto id { p := lookup (id.name); if (p=nil) then { enter(id.name) ; setlbdefined(id.name,0) ; setlbadd(id.name,0); emit(‘goto’, 0)}; else emit(‘goto’, getlbadd(id.name) )

if getlbdefined (id.name)=0 then setlbadd(id.name, nextstm-1) }

Page 62: Slide08 807007748

《编译原理》中间代码生成

简单过程调用的翻译• 示例:过程调用 CALL S (A+B,A*B ) 将被翻译为:

计算A+B 置于T中的代码 // T := A+B 计算A * B 置于Z中的代码 // Z := A*B param T // 第一个实参地址 param Z // 第二个实参地址 call S , 2 // 转子指令

过程调用的语法制导翻译

Page 63: Slide08 807007748

《编译原理》中间代码生成

简单过程调用的翻译模式

过程调用的语法制导翻译

S call id ( L )

{ S.code := L.code ; for L.arglist 中的每一项 p do

S.code := S.code || gen(‘param’ p ) ; S.code := S.code || gen ( ‘call’ id.place , L.n ) }

L L1 , E { L.n := L1.n +1; L.arglist := append(L1.arglist ,makelist(E.place)); L.code := L1.code || E.code }

L { L.n := 0; L.arglist := null ; L.code := ‘ ’ }

L.n : 参数个数 L.arglist : 实参地址的列表makelist :创建实参地址结点append : 在实参表中添加结点

Page 64: Slide08 807007748

《编译原理》课后作业

1 熟练掌握本讲出现的每个属性文法 / 翻译模式的设计目的 , 设计思想和设计技术

2 参考本讲稿第 39-44页的 L 属性文法及所用到的语义函数 ,

(1) 若在基础文法中增加产生式 E E E ,试给出相应 该产生式的语义规则集合。其中 , “” 代表“与非”逻辑算

符,可用其它逻辑运算定义为 P Q not ( P and Q )

(2) 若在基础文法中增加产生式 S repeat S until E , 试给出相应该产生式的语义规则集合。 注: repeat <循环体 > until <布尔表达式 > 的

语义为 : 至少执行 <循环体 > 一次,直到 <布尔表

达式 > 成真 时结束循环。

Page 65: Slide08 807007748

《编译原理》课后作业

3 参考本讲稿第 45-49页的翻译模式及所用到的语义函数 ,

重复 2 中 (1), (2) 的工作。

4 参考本讲稿第 50-56页的 S 属性文法 / 翻译模式及所用到

的语义函数 , 重复 2 中 (1), (2) 的工作。

Page 66: Slide08 807007748

《编译原理》

Thank You

That’s all for today.