500 likes | 729 Views
第七章. 语义分析和中间代码的产生. 中间语言 后缀式 图表示法 三地址代码 简单赋值语句的翻译 布尔表达式的翻译 数值表示法 作为条件控制的布尔式翻译 控制语句的翻译. 紧接在词法分析和语法分析之后,编译程序要做的工作是进行 静态语义检查 和 翻译 。 静态语义检查通常包括: 1. 类型检查 2. 控制流检查 3. 一致性检查 4. 相关名字检查. 语法分析器. 中间代码 生成器. 中间 代码. 优化器. 静态检查器. 何谓中间代码 ( Intermediate code) 是源程序的一种内部表示 复杂性介于源语言和目标机语言之间
E N D
第七章 语义分析和中间代码的产生
中间语言 • 后缀式 • 图表示法 • 三地址代码 • 简单赋值语句的翻译 • 布尔表达式的翻译 • 数值表示法 • 作为条件控制的布尔式翻译 • 控制语句的翻译
紧接在词法分析和语法分析之后,编译程序要做的工作是进行静态语义检查和翻译。紧接在词法分析和语法分析之后,编译程序要做的工作是进行静态语义检查和翻译。 • 静态语义检查通常包括: • 1.类型检查 • 2.控制流检查 • 3.一致性检查 • 4.相关名字检查
语法分析器 中间代码 生成器 中间 代码 优化器 静态检查器 • 何谓中间代码( Intermediate code) 是源程序的一种内部表示 复杂性介于源语言和目标机语言之间 • 中间代码的作用: 使编译程序的逻辑结构更加简单明确 利于进行与目标机无关的优化 利于在不同目标机上实现同一种语言
7.1 中间语言 • 常见的中间语言形式: • 后缀式 • 图表示法(DAG 和抽象语法树) • 三地址代码(四元式、三元式、间接三元式) • 7.1.1 后缀式 • 后缀式表示法又称逆波兰表示法。 • 方法:运算量(操作数)写在前面,把算符写在后面(后缀)。
一个表达式的后缀式可以如下定义: • (1)如果E是一个变量或常量,则E的后缀式是E自身。 • (2)如果E是E1 op E2形式的表达式,这里op是任何二元操作符,则E的后缀式为 E1’ E2’op,这里E1’和E2’分别为E1和E2的后缀式。 • (3)如果E是(E1)形式的表达式,则E1的后缀式就是E的后缀式。
程序设计语言中的后缀式表示: a+b a+b*c (a+b)*c a:=b*c+b*d -(a+(b-c))+d a:=(b+c)*e+(b+c)/f a≤b+c∧a>d∨a+b≠e ab+ abc*+ ab+c* abc*bd*+:= abc-+@d+ abc+e*bc+f/+:= abc+≤ad>∧ab+e≠∨
7.1.2 图表示法 Operator Operand1 Operand2 • 图表示法包括DAG与抽象语法树。 • 无循环有向图(Directed Acyclic Graph) 用途:提取表达式中的公共子表达式,以取得目标程序的局部优化。 无循环有向图与抽象语法树的不同点: • 在DAG图中代表公共子表达式的结点具有多个父结点, • 一棵抽象语法树中公共子表达式被表示为重复的子树。
c uminus d uminus c c d d b b assign assign a a + + + + (b) DAG (a) 语法树 a := (b + cd ) + cd的图形表示
+ + * * d a – b c 表达式 a+a*(b-c)+(b-c)*d 的DAG
7.1.3 三地址代码 • 三地址代码是由下面一般形式的语句构成的序列: • x:=y op z • 其中:x、y、z为名字、常数或编译时产生的临时变量; • op代表运算符号。 • 每个语句的右边只能有一个运算符。 例如,源语言表达式x+y*z 可以被翻译为如下语句序列: T1:=y*z T2:=x+T1 其中,Tl,T2为编译时产生的临时变量。
assign assign a + a + * * * b @ b @ b @ c c c • 三地址:每条语句包含三个地址,两个用来表示操作数,一个用来存放结果。 a:=b*-c+b*-c T1:=-c T2:=b*T1 T3:=-c T4:=b*T3 T5:=T2+T4 a:=T5 T1:=-c T2:=b*T1 T5:=T2+T2 a:=T5 对于DAG的三地址代码 对于语法树的三地址代码
三地址语句可看成中间代码的一种抽象形式。 • 编译程序中,三地址代码语句的具体实现可以用记录表示,记录中包含表示运算符和操作数的域。 • 通常有三种表示方法: - 四元式: ( op, arg1, arg2, result ) - 三元式: ( op, arg1, arg2) - 间接三元式(三元式的指针表)
运算符 操作数1 操作数2 结果 1.四元式 四元式: 结果:通常是临时变量。 例1: (A+B)*(C+D)-E +, A, B, T1 +, C, D, T2 *, T1, T2, T3 -,T3, E, T4 式中,T1,T2,T3,T4 为临时变量
运算符 左操作数 右操作数 2.三元式 三元式: 表达式的三元式: w*x+(y+z) 第三个三元 式中的操作数(1) (2)表示第(1)和第 (2)条三元式的计 算结果。 (1) *, w, x (2) +, y, z (3) +, (1), (2) 序号的双重含义: 既代表此三元式,又 代表三元式存放的结果。
3.间接三元式 • 为了便于代码优化处理,即用一张间接码表辅以三元式表的办法来表示中间代码。这种表示法称为间接三元式。 用间接三元式表示为: 例: A:=B+C*D/E F:=C*D 间接码表 三元式 1. (1) (1) *, C, D 2. (2) (2) / , (1), E 3. (3) (3) +, B, (2) 4. (4) (4) :=, A, (3) 5. (1) (5) :=, F, (1) 6. (5) 用间接码表表示三元式的执行次序。 在优化时,三元式可以不变,而仅仅改变其执行顺序表。
四元式与三元式和间接三元式作一些比较 四元式之间的联系是通过临时变量实现的。这一点和三元式不同。要更动一张三元表是很困难的,它意味着必须改变其中一系列指示器的值。但要更动四元式表是很容易的,因为调整四元式之间的相对位置并不意味着必须改变其中一系列指示器的值。因此,当需要对中间代码进行优化处理时,四元式比三元式要方便得多。 对优化这一点而言,四元式和间接三元式同样方便。
:= T4/(4) T3/(3) x * T1/(1) + + T2/(2) a b a b 图表示法与其他中间代码的关系 • 图表示法与后缀式和三地址码之间有着内在的联系。 • 对树进行深度优先遍历,得到的线性序列就是后缀式,或者说后缀式是树的一个线性化序列。 • 树的每个内部节点和它的孩子,对应一个三元式或四元式。 赋值句x:=(a+b)*(a+b)的语法树: 后缀式:xab+ab+*:= 三元式: 四元式: (1)(+, a, b, T1) (2)(+, a, b, T2) (3)(*, T1,T2,T3) (4)(:=,x, T3,T4) (1)(+, a, b ) (2)(+, a, b ) (3)(*,(1),(2)) (4)(:=,x, (3))
练习:表达式A-B/(C+D)M+A*(C+D)N的三元式、间接三元式、四元式、三地址代码及图表示法。练习:表达式A-B/(C+D)M+A*(C+D)N的三元式、间接三元式、四元式、三地址代码及图表示法。 三元式: (1) (+,C,D) (2) (,(1),M) (3) (/,B,(2)) (4) (-,A,(3)) (5) (+,C,D) (6) (,(5),N) (7) (*,A,(6)) (8) (+,(4),(7)) • 间接三元式: • 三元式 间接码表 • (1) (+,C,D) (1) • (2) (,(1),M) (2) • (3) (/,B,(2)) (3) • (4) (-,A,(3)) (4) • (,(1),N) (1) • (6) (*,A,(5)) (5) • (7) (+,(4),(6)) (6) • (7)
表达式A-B/(C+D)M+A*(C+D)N 三地址代码: (1) T1:=C+D (2) T2:=T1M (3) T3:=B/T2 (4) T4:=A-T3 (5) T5:=C+D (6) T6:= T5 N (7) T7:=A*T6 (8) T8:=T4+T7 • 四元式: • (1) (+,C,D,T1) • (2) (,T1,M,T2) • (3) (/,B,T2,T3) • (4) (-,A,T3,T4) • (5) (+,C,D,T5) • (6) (,T5,N,T6) • (7) (*,A,T6,T7) • (8) (+,T4,T7,T8)
+ * _ / + + A B C D M A C D N 表达式A-B/(C+D)M+A*(C+D)N 语法树
7.3 简单赋值语句的(四元式)翻译 • 属性:id.name----id所代表的名字本身 • E.place----记住符号表条目的地址 过程: lookup(id.name) ----检查是否在符号表中存在相应此名字的条目,有返回指针,无返回nil emit(t:=arg1 op arg2)----将生成的三地址语句发送到输出文件 newtemp----每次调用送回一个代表新临时变量的序号,可认为是送回T1 、T2这样的一些临时变量
产生赋值语句三地址代码的翻译模式 • S id := E {p := lookup(id.name); • if p nil then • emit ( p, ‘:=’, E.place) • else error } • E E1 + E2 {E.place := newtemp; • emit (E.place, ‘:=’, E1.place, ‘+’, E2.place) } • E E1 {E.place := newtemp; • emit (E.place, ‘:=’, ‘uminus’, E1.place) } • E (E1) {E.place := E1.place } • E id {p := lookup(id.name); • if p nil then E.place := p else error }
7.4 布尔表达式的翻译 布尔表达式有两个基本作用: • 计算逻辑值 • 控制流语句的条件表达式 • 布尔表达式是用布尔运算符号(and、 or 、not ┐)作用到布尔变量或关系表达式上而组成的。 关系表达式形如: E1 relop E2 其中E1和E2是算术表达式, relop为关系运算符 ( < ,<=, >=, > , =, ≠)
产生布尔表达式的文法: • E Eor E | E and E | not E | ( E ) • | id relop id | true | false • 按惯例,假定or和and 是左结合的, • 优先级从高到低:not and or • 计算布尔表达式的值通常有两种办法: • 布尔表达式的完全计算(见课本p185) • 布尔表达式的“短路”计算 • 把A or B解释成 if A then true else B • 把A and B解释成 if A then B else false • 把not A解释成 if A then false else true
#include "stdio.h" int p; int f() { p=0; return(0); } main() { p=1; if(p||f())printf("True!\n"); else printf("False!\n"); } f()||p 结果? False 结果:True
7.4.1 数值表示法 (1表示真,0表示假) 布尔表达式a or b and not c 翻译成的三地址序列为: (1) t1:=not c (2) t2:=b and t1 (3) t3:=a or t2 a < b的翻译(if a<b then 1 else 0) 100: if a < b goto 103 101: t := 0 102: goto 104 103: t := 1 104:
用数值表示布尔值的三地址代码的翻译模式P186用数值表示布尔值的三地址代码的翻译模式P186 E E1 or E2 {E.place := newtemp; emit (E.place, ‘:=’, E1.place, ‘or’ E2.place) } E E1 and E2 {E.place := newtemp; emit (E.place, ‘:=’, E1.place, ‘and’ E2.place) } E not E1 {E.place := newtemp; emit (E.place, ‘:=’‘not’ E1.place) } E( E1) { E.place:= E1.place} E id1 relop id2 {E.place := newtemp; emit (‘if’, id1.place, relop.op, id2.place, ‘goto’, nextstat+3 ); emit (E.place, ‘:=’, ‘0’ ); emit (‘goto’, nextstat + 2 ); emit (E.place, ‘:=’, ‘1’ ) } Eid { E.place:= E1.place}
例:表达式 a < b or c < d and e < f 的三地址代码是: 107: T2:=1 108: if e<f goto 111 109: T3:=0 110: goto 112 111: T3:=1 112: T4:=T2 and T3 113: T5:=T1 or T4 100: if a<b goto 103 101: T1:=0 102: goto 104 103: T1=1 104: if c<d goto 107 105: T2:=0 106: goto 108
指向E.true E.code 指向E.false E.true: S1.code goto S.next E.false: S2.code . . . S.next 7.4.2 作为条件控制的布尔式翻译 • 出现在条件语句: • if E then S1 else S2 (7.10) if-then-else语句的代码结构
F “真”出口 T T “真”出口 ┐ A A B T “真”出口 T “假”出口 T “假”出口 A B F F “假”出口 F F 对于一个布尔表达式E, 引用两个符号标号: • E.true是E为‘真’时的控制流转向的标号 • E.false是E为‘假’时控制流转向的标号 基本翻译思想: • E 形如a<d,则生成E的代码: if a<b goto E.true goto E.false
产生布尔表达式三地址代码的语义规则 P188 E E1 or E2 {E1.true := E.true; E1.false := newlabel; E2.true := E.true; E2.false := E.false; E.code := E1.code || gen(E1.false, ‘:’) || E2.code }
产生布尔表达式三地址代码的语义规则 • E E1 and E2 • {E1.true := newlabel; • E1.false := E.false; • E2.true := E.true; • E2.false := E.false; • E.code := E1.code ||gen(E1.true, ‘:’) || E2.code} • E not E1 • {E1.true := E.false; • E1.false := E.true; • E.code := E1.code }
产生布尔表达式三地址代码的语义规则 • E (E1) • {E1.true := E.true; • E1.false := E.false; • E.code := E1.code } • E id1 relop id2 • {E.code := gen(‘if’, id1.place, relop.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)}
例:表达式 a < b or c < d and e < f 的代码是: if a < b goto Ltrue goto L1 L1: if c < d goto L2 goto Lfalse L2: if e < f goto Ltrue goto Lfalse
假设实现三地址代码时采用四元式形式实现,并且假定:假设实现三地址代码时采用四元式形式实现,并且假定: • (jnz , a ,- , p ) 表示 if a goto p • (jrop, x , y, p ) 表示 if x rop y goto p • (j , -, - , p ) 表示 goto p • 为非终结符E赋予两个综合属性: • E.truelist和E.falselist。 • 它们分别记录布尔表达式E所应的四元式中需回填“真”、“假”出口的四元式的标号所构成的链表。
if a<b or c<d and e>f then S1 else S2的四元式序列为: • ifa<bgoto(7) • /*(7)是整个布尔表达式的真出口*/ • (2) goto(3) • (3) ifc<dgoto(5) • (4) goto(p+1) • /*(p+1)是整个布尔表达式的假出口*/ • (5) ife>fgoto(7) • (6) goto(p+1) • (7) (关于S1的四元式) • …
若有四元式序列: • (10) …gotoE.true • … • (20) …gotoE.true • … • (30) …gotoE.true • 则把需回填E.true的四元式(10),(20)和(30) 链成 • (10) …goto (0) • … • (20) …goto (10) • … • (30) …goto (20) • 把地址(30)称作链首,0为链尾标志,即地址(10)为链尾。
7.5 控制语句的翻译 • 7.5.1 控制流语句 • 考虑文法: • S→ if E then S1 • |if E then S1 else S2 • |while E do S1 • 其中E 布尔表达式。
指向E.true 指向E.true E.code E.code 指向E.false 指向E.false E.true: E.true: S1.code S1.code . . . goto S.next E.false: S2.code (a) if-then . . . (b) if-then-else S.begin: 指向E.true E.code 指向E.false E.true: S1.code goto S.begin . . . (c) while-do • 关于if-then、if-then-else、while-do语句答代码结构
属性文法定义 • S if E then S1 • {E.true := newlabel; • E.false := S.next; • S1.next := S.next; • S.code := E.code || gen(E.true, ‘:’) || S1.code } • S if E then S1 else S2 • {E.true := newlabel; • E.false := newlabel; • 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}
S while E do S1 • {S.begin:= newlabel; • E.true := newlabel; • E.false := S.next; • S1.next := S.begin; • S.code := gen(S.begin, ‘:’) || E.code || • gen(E.true, ‘:’) || S1.code || gen(‘goto’, S.begin) } • 例7.5 课本p194
例题与习题解答 • [例]写出下列各式的逆波兰表示 • (1) -a-(b*c/(c-d) + (-b)*a) • (2) -A+B*C↑ (D/E)/F 解: (1) a@bc*cd-/b@a*+ - (2) A@BCDE/↑*F/+
[例] 写出表达式 A+B*(C-D)-E/F↑G 逆波兰表示, 三元式表示, 四元式表示。 解:逆波兰表示: ABCD -*+EFG↑/ - 三元式表示: • ①( - , C, D ) • ②(* , B, ① ) • ③(+,A , ② ) • ④(↑ ,F, G ) • ⑤( / , E, ④ ) • ⑥( - ,③,⑤) 四元式: ① ( - , C , D , T1 ) ② ( * , B , T1, T2 ) ③ ( + , A , T2, T3 ) ④ (↑ ,F , G , T4 ) ⑤ ( / , E, T4, T5 ) ⑥ ( - , T3, T5, T6 )
例:试给出表达式-(a+b)*(c+d)-(a+b-c)的三元式、间接三元式、四元式及DAG。例:试给出表达式-(a+b)*(c+d)-(a+b-c)的三元式、间接三元式、四元式及DAG。 间接三元式序列: 间接码表 三元式 (1) (1) (+,a,b) (2) (2) (-, _,(1)) (3) (3) (+,c,d) (4) (4) (*,(2),(3)) (1) (5) (-,(1),c) (5) (6) (-,(4),(5)) (6) 三元式序列: (1) (+,a,b) (2) (-, _,(1)) (3) (+,c,d) (4) (*,(2),(3)) (5) (+,a,b) (6) (-,(5),c) (7) (-,(4),(6))
- - * @ + c + c d a b 表达式-(a+b)*(c+d)-(a+b-c) • 四元式序列: • (1) (+,a,b,T1) • (2) (-,T1,_,T2) • (3) (+,c,d,T3) • (4) (*,T2,T3,T4) • (5) (+,a,b,T5) • (6) (-,T5,c,T6) • (7) (-,T4,T6,T7)
例 : A + B * ( C - D ) + E / ( C - D ) ^N 后缀式 : A B C D - * + E C D – N ^ / +
对于作为转移条件的布尔式,可以把它翻译成为一串跳转指令。对于作为转移条件的布尔式,可以把它翻译成为一串跳转指令。 • 例::if a > c or b< d then S1 else S2 • 三地址代码是: • if a > cgoto L2 • goto L1 • L1: if b< d goto L2 • goto L3 • L2: (关于S1的三地址代码序列) • goto Lnext • L3: (关于S2的三地址代码序列) • Lnext:
(1) if a<b goto ( E.true ) (2) goto (3) (3 ) if c<d goto (5) (4 ) goto ( E.false) (5 ) if e<f goto ( E.true ) (6 ) goto ( E.false) 1 ( E.true)( S 的四元式序列 …… ……) ( p ) goto (q) 2 ( E.false) ( S 的四元式序列 …… (q-1) ……) (q)