理论教育 编译器设计之路-翻译辅助函数实现

编译器设计之路-翻译辅助函数实现

时间:2023-11-04 理论教育 版权反馈
【摘要】:本小节将讲述一些翻译辅助函数的功能及其实现。实际上,Neo Pascal的翻译辅助函数并不少,这里只能选择部分比较重要的函数予以分析。2.临时对象生成函数临时对象指的就是由编译器在编译过程中自动生成的标号、变量、常量等。然而,这种命名规则却是词法分析器无法接受的。本小节将介绍两类生成函数,分别是临时标号生成函数、临时变量生成函数。

编译器设计之路-翻译辅助函数实现

前面介绍了Neo Pascal语句翻译过程中两个非常重要的数据结构,即Statement与IRCode。其中Statement是翻译过程中的一个辅助结构,其目的是临时存储当前语句的翻译信息。而IRCode也就是IR代码的基类型,在介绍三地址代码时,已经作了相关讨论。本小节将讲述一些翻译辅助函数的功能及其实现。从表面上看,这些函数的功能可能并不复杂,例如,IR代码管理、语句标号生成等。不过,在整个系统设计中,它们的地位却是极其重要的。实际上,Neo Pascal的翻译辅助函数并不少,这里只能选择部分比较重要的函数予以分析。

1.IR生成函数-EmitIR

IR生成的工作将贯穿于整个语义处理阶段,然而由各个语义子程序直接访问m_Codes列表可能会大大增加m_Codes列表的管理成本,因此,设置IR生成函数的目的就是为了便于接口统一。当m_Codes类型或结构变化时,不至于大费周章地检查与修改每一个m_Codes的引用点。下面,笔者就介绍一组通用函数EmitIR,这是一组参数重载函数,其功能就是将传入的参数组合生成一个IRCode对象返回。

程序5-1 CommonLib.cpp

函数的功能是非常简单的,笔者觉得关于源码的任何解释可能都是多余的。至于三种参数重载形式的目的也是显而易见的,实际上,三地址代码并不一定需要三个完整的操作数,有些操作符只需要1~2个操作数即可。然而,在Neo Pascal中,并不存在零操作数的操作符,所以并不需要相应的参数重载形式。

2.临时对象生成函数

临时对象指的就是由编译器在编译过程中自动生成的标号、变量、常量等。注意,这些对象并不一定是透明的,它们在目标代码中是可见的。至于为什么需要生成这些临时对象呢?答案非常简单,它们是目标代码中不可或缺的元素。例如,在翻译if语句时,编译器不得不为此自动生成一些标号以实现语义的等价变换。当然,临时对象还不仅限于临时标号,可能还包括一些临时的变量、常量等。

在编译过程中,生成临时对象是非常频繁的操作。输入源程序规模相对较大时,其编译过程中产生的临时对象将是一个非常庞大的集合。由此所付出的资源代价是不能忽视的。

生成临时对象可能并不像读者所想象的那样简单。在生成临时对象时,任何编译器都必须注意以下两个问题:

(1)必须保证临时对象的命名空间与用户命名空间之间不存在任何交集。这个问题对于像Pascal、C语言等而言,并不复杂。实际上,只需突破具体语言的标识符定义规则即可。例如,C语言规定标识符必须以下画线或字母开头,那么,临时对象的名字就可以以“}}”或“*”开头,这样就可以有效避免与用户标识符的冲突。由于临时对象的名字并不需要通过词法分析识别,所以它可以完全颠覆词法分析器对于标识符的限制,即使将临时对象命名为以数字开头字符串也是完全可行的。然而,这种命名规则却是词法分析器无法接受的。(www.daowen.com)

(2)必须保证目标代码中的临时对象名字不存在任何冲突。由于各个语义子程序都是离散的,所以统一的命名管理是必不可少的。通常,顺序种子、长随机编码等都是比较常见的解决方案

除了上面谈到的两个问题之外,临时对象的声明过程中还有一项重要的工作,即登记符号表。对于编译器而言,编译过程中产生的临时对象同样需要准确登记符号信息。实际上,分析临时对象的符号信息并不简单,编译器不可能像分析用户对象一样从显式声明中获取临时对象的相关信息,而只能从输入源程序的上下文语义中分析获得。临时标号、临时常量的信息比较容易获取。然而,临时变量则相对复杂,其类型信息只能通过表达式运算符的语义加以推断。

本小节将介绍两类生成函数,分别是临时标号生成函数、临时变量生成函数。下面,就来看看这两类函数的实现。

程序5-2 SymbolTbl.cpp

GetTmpLabel的主要功能就是生成一个临时标号信息,并将其加入标号信息表。根据Neo Pascal的设计,临时标号的命名规则为“_L×××”,其中“×××”表示一个由编译器顺序分配的id号,以保证系统生成的标号名字是唯一的。而临时标号以“_L”开头的目的就是为了避免与用户标号出现重名现象。当然,针对C语言或者其他语言,这种处理就不可行了。

与临时标号生成函数相比,临时变量生成函数稍复杂,除了变量名字等常规信息之外,还需要考虑类型等相关信息的生成。

程序5-3 SymbolTbl.cpp

GetTmpVar的主要功能就是生成一个临时变量信息,并将其加入变量信息表。根据Neo Pascal的设计,临时变量的命名规则为“T×××”,其中“×××”表示一个由编译器顺序分配的id号,以保证系统生成的变量名字是唯一的。

这里,笔者给出了GetTmpVar函数的最基本实现,根据eStoreType参数,生成一个相应类型的临时变量。在实际应用中,编译器可能无法准确提供临时变量的类型信息,因此就需要一些其他的重载形式。囿于篇幅,不再详细分析重载形式的源代码实现,请读者自行参考Neo Pascal源代码。当然,如何确定临时变量的类型是一个复杂的话题,通常,这是由类型系统决策的。关于类型系统的相关话题,将在介绍表达式翻译时详细阐述,此处并不需过于关注临时变量的实际类型。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈