
四元式是一毛型县排情余种更接近目标代码的中间代码形式。由于这种形式的中间代码便于优化处理,因此,在目前许多编译程序中得到了广泛的应用。
四元式
四元式实际上是一种"三地址语句"的等价表示。它的一般形式为:
(op,arg1,arg2,result)
其中, op为一个二元 (也可来自是一元或零元)运算符;arg1,arg2分别为它的两个运算 (或操作)对象,它们可以是变量、常数或系统定义的临时变量名;运算的结果将放入result中。四元式还可写为类似于PASCAL语言赋值语句的形式:
result ∶= arg1 op arg2
需要指出的是,每抓个四元式只能有一个运算符,360百科所以,一个复杂的表达式须由多个四元式构成的序列来表示。例如,表达式A+B能句换*C可写为序列
T1∶=B*C
T2∶=A+T1
其中,T1,T2是编译系统所产生的临时变量名。当op为一元、零元运算符 (如无条件转移)时,arg2甚至arg1应缺省,即r础结esult∶=op arg1或 胶怕足永孩厚确正op result ;对应的一般形式为:
(op,arg1,,result)
或
语露运友沉投他级分还 (op,,,result)
在实际产生的四元式中,op往往用一整型数表示 (操作符的代码),它可能附带有不止一种属性。例如,加运算可以分为定点加法和浮点加法两种,我们可用不满支律感同的整数值区分这两种加法。矛至于四元式中运算以站找剂袁万伤另对象arg1、arg2和结果域result,它们可以是指向符号表中某项的指示字,也可以是某个临时变量的序号,因此,在实际的翻译过程中,还需要进行相应的查填符号表工作。在本章中,由于我们只作原理性讨论,所以假定临时变量来自一个用之不竭的集合,而否套光不去追求其经济性。
三元式
为了节省临时变量的开销,有时也可采用一种三元式结构来作为中间代码,其一明拉步规钱变百般形式为:
○i(op,arg1,arg2)
其中,○i为三元式的编号,也代表了该三触数接杀元式的运算结果;o久波待差身乡费法很p,arg1,arg2的含义与四元式类似,区别仅在于arg可以是某三元式的序号,表示用该三元式的运算结果作为运算对象。例如,对于赋值语句
a∶=-b*(c+d)
若用三元式表示,则可写成
①(Uminus, b, - )
②( + , c, d )
③( * , ①, ② )
④( ∶= , a, ③ )
式①中的运算符Uminus表示一元减运算。
下面,我们仍以算术表达式到三元式希棉的翻译为例,说明如何为各产生式设计语义动作。为此,先定义翻译过程中要使用的若干辅助函数:
int LookUp(char*Name)--以Name (变量名)查符号表,若查到则返回相应登记项的序号(≥1),否则,返回0。
int Enter(char*Name)--以Name为名字在符号表中登录新的一项,返回值为该项的序号。
int 找阳争台谁存转鲜云Entry(char*N置离甲味植开空ame)--以Name为快以排封名字查、填符号表,即
int Entry(char*Name)
{ int i=LookUp(Name);
if(i) return i;
合迫养图权论取 else return Enter(Name);
}
注意,在实际的编译系统中,还应区分当前是否在处理说明部分:若是说明部分,则查表时i应为0 (否则Name异被重复定义);若非,则i不能为0(否则Name尚未被定义)。
int Trip(int op,int arg1,int arg2)--根据给定的参数产生一个三元式
(op,arg1,arg2)
并将它送入三元式表中,其返回值为该三元式在表中序号。为区分参数arg所表示的是三元式编号还是变量,约定当arg<0时表示三元序号;arg>0表示变量在符号表中登记项的序号;arg=0表示参数为空。
利用这些函数,我们可构造将表达式翻译成三元式的S属性翻译文法如下:
1Expr′→Expr{$$=$1;}
2Expr→Expr ′+′ Term{$$=Trip(′+′,$1,$3);}
3|Term{$$=$1;}
4|′-′Term{$$=Trip(Uminus,$2,0);}
5Term→Term′*′Factor{$$=Trip(′*′,$1,$3);}
6| Factor{$$=$1;}
7Factor→ ′(′ Expr ′)′ {$$=$2;}
8| iden{$$=Entry($1);}
在产生式8中,终结符号iden的属性是它的词文yytext(其值由LEX所生成的扫描器提供),用$1表示。每个非终结符号都有一个属性,代表以该符号为根的语法树的一个子树的运算结果,该属性的取值可以是整型数,或为一个三元式的序号,或为符号表项的序号。
比较三元式和四元式这两种表示方法可以看出,无论在一个三元式序列还是四元式序列中,各个三元式或四元式都是按相应表达式的实际运算顺序出现的。其次,对同一表达式而言,所需的三元式或四元式的个数一般是相同的。不过,由于三元式没有Result字段,且不需要临时变量,故三元式比四元式占用的存储空间少。另一方面,当进行代码优化处理时,常常需要从现有的运算序列中删去某些运算,或者需要挪动一些运算的位置,这对于三元式序列来说,是很困难的。因为,三元式间的相互引用一般非常频繁,而这些引用又是通过其中的指示字来实现的,所以,一些三元式的删除或挪动,有时会造成需要大量修改指示字内容的局面。但对于四元式序列来说,由于四元式之间的相互联系是通过临时变量来实现的,所以,更改其中一些四元式给整个序列带来的影响比三元式的情况小得多。
为了克服三元式表示不便于优化的缺点,可在中间代码生成过程中,建立两个与三元式有关的表格。一个称为三元式表,用于存放各三元式本身;另一个称为执行表,它按照各三元式的执行顺序,依次列出相应各三元式在三元式表中的编号。也就是说,现在我们用一个三元式表连同执行表来表示中间代码,通常我们将此种表示方式称为间接三元式。例如,对于如下赋值语句
x∶=(a+b)*c;
b∶=a+b;
y∶=c*(a+b)
若按三元式表示,可写成
①(+, a,b)⑤(+, a,b)
②(*, ①,c)⑥(*, c,⑤)
③(∶=, x,②)⑦(∶=, y,⑥)
④(∶=, b,①)
其中,三元式①和⑤形式完全一致,但却不能将⑤省去;对于②,⑥来说,也应当说是一致的 (因为乘法满足交换律),同样不能将⑥省去。然而若按间接三元式表示,则可写成
执行表三元式表
①①(+, a, b)
②②(*, ①,c)
③③(∶=,x,②)
④④(∶=,b,①)
①⑤(∶=,y,②)
②
⑤
由此可见,这两种表示法的区别之一就是,对于间接三元式表示而言,由于在执行表中已经依次列出每次要执行的那个三元式,若其中有相同的三元式,则仅需在三元式表中保存其中之一。这就意味着,三元式表的项数一般比执行表的项数少。
另外,当进行代码优化需要挪动运算顺序时,则只需对执行表进行相应的调整,而不必再改动三元式表本身。这样,就避免了前述因改变三元式的顺序所引起的麻烦。
评论留言