从结构上来看,GIMPLE是GENERIC的一种较低级的子集形式。最初,GIMPLE受到了McCAT (McGill大学的一个编译器项目)的SIMPLE IL的影响。不过,最好不要将GIMPLE理解成一种普通的AST,因为它已经具备了一些线性IR的特性了,例如,它使用临时变量保存表达式的中间结果,这种做法主要应用于三地址码,而AST中是很少见的。
GIMPLE是以函数为单位描述的。通常,并不建议将语言相关的元素引入GIMPLE或者后端,也就是说,前端扩展尽可能应用现存的与语言无关的GENERIC形式,而避免涉及GIMPLE及RTL的修改。但是,这并不是绝对的。有时,某些源语言的特性是非常奇特的,编译器不得不将它们传递到目标代码,在这种情况下,语言相关的元素就必须引入GIMPLE及RTL了。当然,值得注意的是,这种“长距离”传递带来的问题可能是很多的,尤其对于缺乏GCC开发经验的设计者而言,这可能是致命的。如果设计者试图将语言相关的特性提供给后端,那么,就必须提供一个LANG HOOKS GIMPLIFY EXPR的接口定义。在GCC中,虽然GIMPLE的存储形式是一种AST结构,但它通常是以一种类C程序的形式输出的。读者可以使用“-fdump-tree-gimple”参数输出GIMPLE。下面,先来看一个GIMPLE的实例。
例10-1 数组最大值求解程序的GIMPLE形式。
实际上,从本例来看,GIMPLE的输出形式与先前提到的C--比较类似。当然,二者也存在不同,C--的程序是符合标准C的,而GIMPLE的输出只是一种类C代码而已。下面,笔者将结合实例详细阐述GIMPLE的一些特性。
1.表达式
GIMPLE的表达式通常由一个操作符与若干形式简单的操作数组成,这些操作数必须是右值符号或寄存器变量。在GIMPLE中,复杂的运算表达式被处理成若干简单的子表达式,并以临时变量暂存中间结果。这种处理与三地址码的生成比较相似,如例10-2所示。
例10-2 表达式的处理。
(1)复杂对象
这里主要指复杂对象的寻址处理,如数组元素、结构字段等。事实上,在复合左值处理上,GIMPLE并没有将其中的复合左值转换为“简单地址”形式,如例10-3所示。
例10-3 复杂对象的处理。
(2)条件表达式
条件表达式的处理与if语句是一样的,如例10-4所示,只是真、假分支的被赋值变量都是相同的。
例10-4 条件表达式的处理。
(3)逻辑表达式
逻辑表达式的处理需要关注短路的实现。不过,在GIMPLE中,逻辑表达式并没有使用真、假链翻译技术,而使用了数值翻译方案,也就是通常所说的使用0、1表示true、false的翻译方案。如例10-5所示。
例10-5 逻辑表达式的处理。
值得注意的是,当GIMPLE的if结构缺少假分支时,以空语句替代。
(4)逗号表达式
例10-6 逗号表达式将被处理成一系列简单的赋值语句。(www.daowen.com)
2.语句
对于编译器而言,赋值语句通常被视为赋值表达式,因此,并不是语句翻译所关注的。这里,笔者主要想阐述有关控制结构类语句的处理。
(1)循环语句
目前,GIMPLE中的循环语句都将以goto跳转的形式描述,如例10-7所示。
例10-7 循环语句的处理。
(2)选择语句
选择语句都是以goto跳转形式描述的。不过,当条件表达式作用于选择语句时,短路问题是值得注意的,如例10-8所示。而例10-9则是一个switch结构的翻译实例。
例10-8 if语句的处理。
在本例中,读者应该特别注意真、假链的翻译机制。由于Neo Pascal使用了数值翻译方案处理布尔表达式,因此,没有涉及这个话题。在布尔表达式的翻译中,真、假链是一种非常经典的方案,有兴趣的读者可以参考“龙书”相关章节。不过,这种翻译方案有一定难度,会令一些初学者望而却步。
例10-9 switch语句的处理。
特别注意的是,switch的跳转表是升序排列的,这样可以避免许多不必要的麻烦。
(3)跳转语句
跳转语句主要指的就是goto及return,如例10-10所示。
例10-10 跳转语句的处理。
(4)异常处理
在较高级的GIMPLE中,异常处理是以try语句形式出现的。然而在较低级的GIMPLE中,异常处理最终被解释为goto跳转。
3.GIMPLE文法的框架
最后,笔者给出一份摘自《GNU Compiler Collection Internals》的GIMPLE文法框架,以便读者对GIMPLE的形式有更深刻的理解。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。