理论教育 手工编码实现指令模板NeoPascal的指令模板形式

手工编码实现指令模板NeoPascal的指令模板形式

时间:2023-11-04 理论教育 版权反馈
【摘要】:手工编码实现指令模板。这种方法主要是在代码生成阶段从外部文件动态读入指令模板库。下面,笔者就来介绍Neo Pascal的指令模板形式。在指令模板的设计中,最复杂的描述可能就是操作数的来源,不同的指令系统对此的限制是非常奇怪的。然后,根据得到的模式串,在指令模板库中检索。例9-4 基于指令模板的代码生成。处理:5)根据ADD_4的指令模板,操作数1必须为寄存器寻址,C与eax寄存器是绑定的。

手工编码实现指令模板NeoPascal的指令模板形式

在经典编译技术中,指令模板的实现技术方式主要有以下两种:

(1)使用自动代码生成器的生成器来实现基于指令模板的代码生成。这种方法与语法分析的生成器yacc比较类似。根据生成器规则文件的要求,逐一书写模板规则,再由自动代码生成器的生成器将模板规则文件转换为相应的C/C++源代码。这就是代码生成器的源代码,只需将其加入编译器项目重新编译即可。最著名的自动代码生成器的生成器称为“lburg”,它习惯上将规则文件称为“紧缩规范”。

(2)手工编码实现指令模板。这种方法主要是在代码生成阶段从外部文件动态读入指令模板库。相比前者,这种方式的实现代价可能大一些,同时,由于必须借助外部IQ,代码生成的效率可能低一些。然而,这种方式较灵活,尤其在指令选择方案的灵活变化方面是有优势的。由于Neo Pascal是基于这种方法实现的,因此,本书将详细讨论其设计与实现的细节。

下面,笔者就来介绍Neo Pascal的指令模板形式。

由于NPIR是一种较低级的MIR形式,因此,给指令模板的实现带来了便利。Neo Pascal并不需要考虑AST或DAG到LIR的转换。在指令模板的设计中,最复杂的描述可能就是操作数的来源,不同的指令系统对此的限制是非常奇怪的。例如,在i386中,浮点指令规定数据一定是来自内存的,而主CPU的大多数指令最多只允许存在一个内存操作数。类似的情况在指令模板的设计中必须予以体现,否则无法保证代码生成的正确性。设计指令模板是一项极富有挑战性的工作,而设计出经典的指令模板就更困难了,即使是经典的

Graham-Glanville方法也没有太多的建议。下面,就来看看Neo Pascal指令模板的实例。

978-7-111-32164-4-Chapter09-20.jpg

大括号:一个完整的指令模板必须用大括号括起来,以便代码生成器解析。

模式串:指令模板的第一行是模板的模式串,用于匹配模板。在代码生成过程中,生成器根据IR的实现情形,可以分析得到类似的模式串。然后,根据得到的模式串,在指令模板库中检索。这个过程与数据库检索比较类似,而这里的关键字就是模式串。

操作数来源标记:ADD_4一共有3个操作数。以“@”开头的行文本正是用于标识操作数的来源。其中,逗号之后的数值是用于标识操作数的字节数。而逗号之前的数值则用于标识操作数的来源,详细的说明见表9-4。

指令序列:其中,除了“%V.l%”、%V.2%等之外的文本将在目标代码中输出,而“%V.l%”等标记将在代码生成中被替换。

表9-4 指令模板的操作数来源标记

978-7-111-32164-4-Chapter09-21.jpg

下面再举一个指令模板的例子。

978-7-111-32164-4-Chapter09-22.jpg

978-7-111-32164-4-Chapter09-23.jpg

这是一个比较特殊的应用。其中,lahf指令的功能是将标志寄存器值存入ah寄存器中,因此,并不需要重新申请一个寄存器用于存储运算结果。其中,“@lah,l”表示该操作数所属的寄存器是特指的。在本例中,即表示存储操作数3的寄存器只能是ah,而不需要也不能申请其他寄存器。而本例中的“#ah”则表示该指令模板对ah寄存器有特殊需要,因此,在应用该指令模板生成代码时,不能将ah寄存器挪作他用。

最后,笔者通过一个实例来解释基于指令模板的代码生成过程。

例9-4 基于指令模板的代码生成。(www.daowen.com)

978-7-111-32164-4-Chapter09-24.jpg

处理(ADD_4 A.B,C):

1)根据ADD_4的指令模板,操作数1必须为寄存器寻址,由于变量A没有绑定任何寄存器,因此,代码生成器必须为A绑定一个4字节的寄存器(这里,假设为eax)。并生成指令将A的值装入eax中。

指令:mov eax,A

2)操作数2是优先寄存器寻址。B变量不存在绑定寄存器,因此,只需从内存寻址即可。

3)处理模板中的指令序列,将文本中的“%V.l%”、“%V.2%”替换为实际的操作数。

指令:add eax,dword ptr[B]

4)根据ADD_4的指令模板,运算结果存放在操作数l所属的寄存器中,至此,eax寄存器绑定的变量是C,而不是A。

处理(ADD_4 C,A,B):

5)根据ADD_4的指令模板,操作数1必须为寄存器寻址,C与eax寄存器是绑定的。

6)操作数2是优先寄存器寻址。B变量不存在绑定寄存器,因此,只需从内存寻址即可。

7)处理模板中的指令序列,将文本中的“%V.1%”、“%V.2%”替换为实际的操作数。

指令:add eax,dword ptr[A]

8)根据ADD_4的指令模板,运算结果存放在操作数1所属的寄存器中,至此,eax寄存器绑定的变量是B,而不是C。

生成的代码序列如下:

978-7-111-32164-4-Chapter09-25.jpg

通过例9-4的讲解,笔者试图揭示基于指令模板进行代码生成的基本过程。不过,这个过程比较抽象,其中,忽略了许多实现细节,例如,指令序列的文本替换、寄存器的绑定、寄存器的分配、寄存器装入与回存等,这些细节将在后续章节中讨论。

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

我要反馈