Transcript
《编译原理》
语义分析和中间代码生成的重要数据结构
符号表( symbol tables ) • 名字信息建立后加入 / 更改符号表 名字信息如:种类,类型,偏移地址,占用空间等• 需要获取名字信息时,查找符号表
• 符号表的组织可以体现名字作用域规则
(符号表的组织已在第七讲专门讨论)
语义分析与中间代码生成
《编译原理》
与语义分析相关的工作
语义分析
静态语义检查• 编译期间所进行的语义检查
动态语义检查• 所生成的代码在运行期间进行的语义检查
收集语义信息• 为语义检查收集程序的语义信息
• 为代码生成等后续阶段收集程序的语义信息 有些内容合并到“中间代码生成”部分讨论 (如过程、数组声明的语义处理)
《编译原理》
静态语义检查 代码生成前程序合法性检查的最后阶段
• 静态类型检查( type checks ) 检查每个操作是否遵守语言类型系统的定义• 名字的作用域( scope )分析 建立名字的定义和使用之间联系• 控制流检查( flow-of-control checks ) 控制流语句必须使控制转移到合法的地方(如 break 语句必须有合法的语句包围它)• 唯一性检查( uniqueness checks ) 很多场合要求对 象只能被定义一次(如枚举类型的元素不能重复出现)• 名字相关检查( name-related checks ) (如,一些名字可能被要求配对出现)• ……
语义分析
《编译原理》
类型检查程序( type checker )负责类型检查• 验证语言结构是否匹配上下文所期望的类型• 为相关阶段搜集及建立必要的类型信息• 实现某个类型系统( type system )
静态类型检查• 编译期间进行的类型检查
动态类型检查• 目标程序运行期间进行的类型检查
类型检查
语义分析
《编译原理》
类型表达式( type expressions )• 由基本类型,类型名字,类型变量,及类型构 造子( type constructor )归纳定义的表达式
类型系统( type systems )• 将类型表达式赋给程序各个部分的规则集合
类型表达式和类型系统
语义分析
《编译原理》
某个类型表达式集合(将用于示范类型检查 程序的设计)归纳定义如下:
• 基本类型 bool, char, int, real, type_error 和 ok 是类型表达式(其中, type_error 专用于类型检查 时的出错提示; ok 可用于语句的类型检查)• 若 T 是类型表达式,则 array(I,T) 是类型表达式, 表示元素类型是 T ,下标集合是 I 的数组类型(这 里, I 是一个整数区间,如 1..10 )• 若 T 是类型表达式,则 pointer(T) 是类型表达式, 表示指向类型为 T 的对象的指针类型
类型表达式举例
语义分析
《编译原理》
以下是定义某个简单语言的上下文无关文法 (将用于本讲的设计示例) 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
语义分析
《编译原理》
语法制导的方法• 处理声明的翻译模式(保存标识符的类型)
类型检查程序的设计
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) }
语义分析
《编译原理》
语法制导的方法• 表达式的类型检查
类型检查程序的设计
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 }
语义分析
《编译原理》
语法制导的方法• 表达式的类型检查(续)
类型检查程序的设计
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 }
语义分析
《编译原理》
语法制导的方法• 语句的类型检查
类型检查程序的设计
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 }
语义分析
《编译原理》
语法制导的方法• 增加数自动类型转换( 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 }{ …… }
语义分析
《编译原理》中间代码生成
源程序的不同表示形式 作用
• 源语言和目标语言之间的桥梁,避开二者 之间较大的语义跨度,使编译程序的逻辑 结构更加简单明确• 利于编译程序的重定向• 利于进行与目标机无关的优化
中间代码
《编译原理》中间代码生成
有不同层次不同目的之分 中间代码举例
• Postfix (后缀式,逆波兰式)• AST ( Abstract syntax tree ,抽象语法树)• TAC ( Three-address code,三地址码,四元式)
• P-code (特别用于 Pasal 语言实现)• Bytecode ( Java 编译器的输出 , Java 虚拟机的输入)• SSA ( Static single assignment form ,静态单赋值形式)
中间代码的形式
《编译原理》中间代码生成
算术表达式 A + B * ( C - D ) + E / ( C - D ) ^N (中缀形式)
• Postfix (后缀式,逆波兰式) A B C D - * + E C D – N ^ / +
中间代码举例
《编译原理》中间代码生成
算术表达式 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
中间代码举例
《编译原理》中间代码生成
算术表达式 A + B * ( C - D ) + E / ( C - D ) ^N (中缀形式)
• AST ( Abstract syntax tree ,抽象语法树)
中间代码举例
^
+
+
/
A
*
E-B
DC -
DC
N
《编译原理》中间代码生成
算术表达式 A + B * ( C - D ) + E / ( C - D ) ^N (中缀形式)
• DAG ( Directed Acyclic Graph, 有向无圈图,改进型 AS
T )
中间代码举例
^
+
+
/
A
*
EB
-
DC
N
《编译原理》中间代码生成
静态单赋值形式
中间代码举例
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
《编译原理》中间代码生成
语法制导的方法• 例:生成抽象语法树
中间代码生成
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 : 构造叶子结点
《编译原理》中间代码生成 课程后续部分用到的 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 等的教材(龙书)
《编译原理》中间代码生成
语义属性 id.name : id 的词法名字(符号表中的名字) E.place : 用来存放 E 的值的存储位置 E.code : E 求值的 TAC 语句序列
语义函数 / 过程 lookup ( id.name ) : 从符号表中查找名字为 id.name 的项,返回存放相应值的指针(存储位置),若无该 项,则返回 nil
gen : 生成一条 TAC 语句 newtempt : 返回一个未使用过的存储位置
赋值语句及算数表达式的语法制导翻译
《编译原理》中间代码生成
翻译模式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 := ‘’ }
赋值语句及算数表达式的语法制导翻译
《编译原理》中间代码生成
回顾:类型属性的计算 说明语句的语法制导翻译
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
《编译原理》中间代码生成
语义属性 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
过程(函数)中说明语句的语法制导翻译
《编译原理》中间代码生成 过程中说明语句的语法制导翻译
翻译模式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 …
《编译原理》中间代码生成
数组说明 参考前页的翻译模式,可了解(一维)数组说明的翻译 思想 . 至于符号表中一般情况下是如何组织数组说明信 息的,随后将会讨论 .
数组说明和数组元素引用的语法制导翻译
…
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 } …
《编译原理》中间代码生成
数组引用
数组说明和数组元素引用的语法制导翻译
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 := ‘’}
《编译原理》中间代码生成
数组的内情向量( 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: 随后解释
《编译原理》中间代码生成
数组元素的地址计算 例:对于静态数组 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 )
《编译原理》中间代码生成
直接对布尔表达式求值例如 : 可以用数值“ 1” 表示 true; 用数值“ 0” 表示 false;采用与算术表达式类似的方法对布尔表达式进行求值
通过控制流体现布尔表达式的语义方法:通过转移到程序中的某个位置来表示布尔表达式的求值结果 优点:方便实现控制流语句中布尔表达式的翻译常可以得到短路( short-circuit )代码,而避免不必要的求值,如:在已知 E1 为真时,不必再对 E1E2 中的 E2
进行求值;同样,在已知 E1 为假时,不必再对 E1E2
中的 E2 进行求值
布尔表达式的语法制导翻译
《编译原理》中间代码生成
直接对布尔表达式求值
布尔表达式的语法制导翻译
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 语句的下标
《编译原理》中间代码生成
通过控制流体现布尔表达式的语义 例 : 布尔表达式 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
布尔表达式的语法制导翻译
《编译原理》中间代码生成
翻译布尔表达式至短路代码
布尔表达式的语法制导翻译
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 ) }
《编译原理》中间代码生成
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 语句的标号
《编译原理》中间代码生成
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:
《编译原理》中间代码生成 循环语句的语法制导翻译
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:
《编译原理》中间代码生成 复合语句的语法制导翻译
顺序复合语句 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:
《编译原理》中间代码生成
翻译布尔表达式至短路代码(翻译模式)
布尔表达式的语法制导翻译
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 ) }
《编译原理》中间代码生成
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 语句的标号
《编译原理》中间代码生成
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 }
《编译原理》中间代码生成 循环语句的语法制导翻译
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) }
《编译原理》中间代码生成 复合语句的语法制导翻译
顺序复合语句(翻译模式)
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:
《编译原理》中间代码生成 拉链与代码回填 语义属性 E.truelist : “真链”,链表中的元素表示 一系列跳转语 句的地址,这些跳转语句的目标标号是体现布 尔表达式 E 为“真”的标号 E. falselist : “假链”,链表中的元素表示 一系列跳转语 句的地址,这些跳转语句的目标标号是体现布 尔表达式 E 为假的标号 S. nextlist : “next 链”,链表中的元素表示 一系列跳转 语句的地址,这些跳转语句的目标标号是在执 行序列中紧跟在 S 之后的下条 T
AC 语句的标号
《编译原理》中间代码生成 拉链与代码回填 语义函数 / 过程 makelist(i) : 创建只有一个结点 i 的表,对应存放目标 TAC 语句数组的一个下标 merge(p1,p2) : 连接两个链表 p1 和 p2 ,返回结果链表 backpatch(p,i) : 将链表 p 中每个元素所指向的跳转语句 的标号置为 i
nextstm : 下一条 TAC 语句的地址 emit (…) : 输出一条 TAC 语句,并使 nextstm 加 1
《编译原理》中间代码生成
拉链与代码回填 处理布尔表达式的翻译模式
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 }
注 : 这里可以规定产生式的优先级依次递增来解决冲突问题 (下同)
《编译原理》中间代码生成 拉链与代码回填 处理布尔表达式的翻译模式
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 }
《编译原理》中间代码生成 拉链与代码回填 布尔表达式 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)
《编译原理》中间代码生成 拉链与代码回填 处理条件语句的翻译模式
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 _’) }
《编译原理》中间代码生成 拉链与代码回填 处理循环、复合及其它语句的翻译模式
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 }
《编译原理》中间代码生成
拉链回填技术示例
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:
《编译原理》中间代码生成
拉链回填技术示例
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 ……
《编译原理》中间代码生成
利用标号的符号表项维护拉链 若采用类似 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 语句的语法制导翻译
《编译原理》中间代码生成
标号说明和 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) }
《编译原理》中间代码生成
简单过程调用的翻译• 示例:过程调用 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 // 转子指令
过程调用的语法制导翻译
《编译原理》中间代码生成
简单过程调用的翻译模式
过程调用的语法制导翻译
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 : 在实参表中添加结点
《编译原理》课后作业
1 熟练掌握本讲出现的每个属性文法 / 翻译模式的设计目的 , 设计思想和设计技术
2 参考本讲稿第 39-44页的 L 属性文法及所用到的语义函数 ,
(1) 若在基础文法中增加产生式 E E E ,试给出相应 该产生式的语义规则集合。其中 , “” 代表“与非”逻辑算
符,可用其它逻辑运算定义为 P Q not ( P and Q )
(2) 若在基础文法中增加产生式 S repeat S until E , 试给出相应该产生式的语义规则集合。 注: repeat <循环体 > until <布尔表达式 > 的
语义为 : 至少执行 <循环体 > 一次,直到 <布尔表
达式 > 成真 时结束循环。
《编译原理》课后作业
3 参考本讲稿第 45-49页的翻译模式及所用到的语义函数 ,
重复 2 中 (1), (2) 的工作。
4 参考本讲稿第 50-56页的 S 属性文法 / 翻译模式及所用到
的语义函数 , 重复 2 中 (1), (2) 的工作。
top related