理论教育 编译器设计之路-RTX格式描述与三地址码的限制

编译器设计之路-RTX格式描述与三地址码的限制

时间:2023-11-04 理论教育 版权反馈
【摘要】:标志是可选的,主要是作为RTX的辅助说明。2.RTX格式描述与三地址码类似,不同的RTX代码对其操作数是有一定限制的。例如,在rtl.def中,__PLUS的格式描述如下:其中,第1项表示RTX代码,第2项是文本输出形式,第4项是所属的类别,而第3项就是用于描述格式的。3.RTX机器模式机器模式主要用于描述数据对象的大小及其表示。参考例10-12就不难发现,在RTL中,机器模式的使用是极其频繁的。在RTX文本模式中,标志是

编译器设计之路-RTX格式描述与三地址码的限制

RTX的书写形式如下:

(RTX代码[/标志1…/标志n][:机器模式]操作数1…操作数n)

其中,RTX代码也就是三地址码中的操作码,主要用于描述该RTX的功能作用。在GCC中,RTX代码是在n1.def文件中定义的,是一组类似于枚举常量的值。在GCC中,通常可以使用GET_CODE(x)和PUT_CODE(x)宏来获取与修改RTX中的代码值。

标志是可选的,主要是作为RTX的辅助说明。标志是非常有用的,但又是非常复杂的。

机器模式是可选的,主要描述的就是RTX的数据对象的大小。在RTL的文本形式中,机器模式紧跟在RTX代码之后,其间用冒号隔开。例如,REG表达式(reg:SI 38)。关于机器模式的详细内容将稍后深入。

操作数通常是一个指向另一个对象的指针,根据RTX代码的不同,操作数个数是不同的。这与三地址码是类似的,应该不难理解。

1.RTX代码的分类

目前,在rlt.def文件中,将RTX代码分成如下几类:

RTX_OBJ:实际的对象,如寄存器(reg)、存储单元(mem、symbol_ref)。

RTX_CONST_OBJ:常量对象,包括一些基本转换等,如HIGH、ADDRESSOF等。

RTX_COMPARE:不满足交换律的比较,包括LE、LT、GE、GT、LEU、LTU、GEU, GTU。

RTX_COMM COMPARE:满足交换律的比较,包括EQ、NE、ORDERED等。

RTX_UNARY:单目操作,如NEG、NOT、ABS、值扩展、整型及浮点型转换。

RTX_COMM_ARITH:满足交换律的双目操作,如PLUS、AND等。

RTX_BIN_ARITH:不满足交换律的双目操作,如MINUS、DIV、ASHIFTRT等。

RTX_BITFIELD_OPS:位域操作,通常有三个操作数。

RTX_TERNARY:其他的三目操作,目前只支持IN_THEN_ELSE、VEC_MERGE。

RTX_INSN:表示一个完整的指令,如INSN、JUMP_INSN、CALL_INSN等。

RTX_MATCH:表示指令内的匹配对象,如MATCH_DUP。通常只存在于机器描述中。

RTX_AUTOINC:表示自增的寻址模式,如POST_INC。

RTX_EXTRA:所有其他的RTX代码,包括一些用于机器描述的代码(如DEFINE_*)、所有描述副作用的代码(如SET、USE)、出现在指令中的非指令代码(如NOTE、CODE_LABEL)。

2.RTX格式描述

与三地址码类似,不同的RTX代码对其操作数是有一定限制的。在GCC中,对此有一套相对完整的描述体系。例如,在rtl.def中,__PLUS的格式描述如下:

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

其中,第1项表示RTX代码,第2项是文本输出形式,第4项是所属的类别,而第3项就是用于描述格式的。在GCC中,格式描述代码“e”就表示操作数可以是一个表达式,而两个“e”就表示该RTX可以且必须有两个表达式操作数。

下面,就来看看常用的RTX格式描述代码的含义:

e:表达式(实际上为指向表达式的指针)。

i:整数。

W:宽整数。

s:字符串。

E:表达式向量。

u:除了在调试信息中不同,“u”等价于“e”,表示指向指令的指针。

n:除了在调试信息中不同,“n”等价于“i”,表示note指令行号或代码编号。

S:说明一个可选字符串。在内部表示的RTL对象里,“S”等价于“s”。但当从机器描述文件中读出一个对象时,这个操作数的值可能被省略,被省略的字符串被认为是空串。

V:说明一个可选向量。在内部表示的RTL对象里,它等价于“E”。但当从机器描述文件中读出一个对象时,其值可能被省略,被省略的向量相当于没有元素的向量。

B:-个指向基本块结构的指针。

0:-个内容不适合任何正常类型的跟踪形式。“0”跟踪信息在RTL调试输出中不显示,通常在编译器中有某些特殊作用。

在GCC中,可以使用以下几个宏来获取一些与RTX代码相关的信息。

GET_RTX_LENGTH(code):获取给定RTX代码的操作数个数。

GET_RTX_FORMAT(code):获取给定RTX代码的格式字符串。

GET_RTX_CLASS(code):获取给定RTX代码所属的类别的缩写形式(一个字符表示)。

关于各个RTX代码的格式描述就不再详细列举了,读者可以参考rtl.def文件。

3.RTX机器模式

机器模式主要用于描述数据对象的大小及其表示。从实现上而言,机器模式表示成枚举类型enum_machine_mode,这个类型的声明在machmode.def文件中。例如:

INT_MODE_(QI,1);

这是一个单字节整型模式的描述。“QI”的含义就是“Quarter-Integer”。在GCC的整型模式中,通常是以4字节int作为基准进行描述。例如,“HI”即表示“Half-Integer”,也就是两字节型。而第2项“1”则表示其所需的字节数,这是依赖于INT_MODE宏的定义。在machmode.def文件中,关于INT_MODE宏的描述为“INT_MODE_(MODE,BYTESIZE);”。再如:

978-7-111-32164-4-Chapter10-26.jpg

978-7-111-32164-4-Chapter10-27.jpg

这两个模式分别是单精度浮点型及双精度浮点型的描述,其中,第3项所描述的就是它们所采用的格式标准。在machmode.def文件中,关于FLOAT MODE宏的描述为“FLOAT_MODE(MODE,BYTESIZE,FORMAT);”。

根据machmode.def文件的注释,可以非常容易地理解机器模式相关声明的含义。这里,笔者不再举例说明了。在RTL中,机器模式是可选的,却是非常重要的。参考例10-12就不难发现,在RTL中,机器模式的使用是极其频繁的。值得注意的是,由于GCC的目标机不局限于i386,还包括向量机、嵌入式计算机等,因此,并非所有模式在i386中都是支持的。

4.RTX的标志

一个RTX通常可以包含若干标志,应用于一些特殊的表达式中,对其进行辅助说明。在RTL内核中,标志是以位形式存储的,而访问则通过GCC定义的宏完成。标志是比较繁琐的话题,且同一标志在不同的表达式中的含义是不同的。在RTX文本模式中,标志是紧接在RTX后面书写的,通常以“/”开头。下面,笔者将详细说明各标志的含义。

(1) call。在mem中,置1表示内存引用不受限。在RTL文本模式中,记作“/c”。

(2) frame_related。在msn或set中,置1表示它是一个函数的首部,并需要设置栈指针。包括重置栈的指针、保护现场寄存器等。在介绍运行时刻环境时,笔者已经详细阐述了相关理论与实现。(www.daowen.com)

在symbol_ref表达式中,置1表示引用的地址是该函数的字符串常量池。

在mem表达式中,置1表示引用一个标准对象。

在RTL文本模式中,记作“/f”。

(3) in_struct。在mem表达式中,置1表示引用的数据对象依赖于一个数组或结构,置0表示引用的数据对象依赖于一个标准类型的变量。这个标准对别名分析是非常有用的,它有助于确定别名引用的关系。

在reg表达式中,置1表示该寄存器的整个生存期被包含在循环条件表达式中。

在subreg表达式中,置1表示此suberg访问的对象的方式是由一个宽方式提升而来。

在label_ref表达式中,置1表示所引用的标号的定义点位于包含此标号引用的最内层循环之外。

在code_label表达式中,置1表示该标号不会被删除。这主要用于描述没有被goto引用的标号。这种标号不一定是冗余的,有可能是分支优化造成的。

在RTL文本模式中,记作“/s”。

(4) integrated。在insn、insn list、const中,置1表示该RTL是由内联函数产生的。

在reg表达式中,置1表示此寄存器包含当前函数的返回值。对于用寄存器传参的机器而言,当相同的寄存器被用于传参时,不设置该标志。

在symbol_ref表达式中,置1表示对该符号弱引用。这里,笔者简单介绍一下“弱引用(weak reference)”的概念。弱引用与强引用主要的区别是在垃圾回收机制中体现的。在Java、.Net之类的语言中,被弱引用的对象仍然可以被垃圾回收。弱引用机制对有效利用缓存资源是积极的,但是弱引用有时也是危险的。

在RTL文本模式中,记作“/i,’。

(5)jump。在mem表达式中,置1表示当访问一个对象时,该mem的别名集合不变。

在set表达式中,置1表示是一个返回。

在call_insn中,置1表示一个同属调用。这里,笔者简单介绍一下“同属调用”的概念。实际上,这是一个编译优化的情形,如下所示:

978-7-111-32164-4-Chapter10-28.jpg

在这个程序中,如果return之前的语句不会产生任何副作用,那么,编译器可以将对aa的调用直接处理成对bb的调用。这样,编译器不必再为aa分配帧空间,而只需为bb分配帧空间。同属调用优化是一种比较高级的优化技术,而且算法实现也较复杂。

在RTL调试输出中,此标志写成“/j,’。

(6)unchanging。在reg、mem表达式中,置1表示表达式的值不变化。

在subreg表达式中,置1表示subreg访问的无符号对象的方式是由一个宽方式提升而来。

在insn、iump_insn中,置1表示一个废弃的分支。

在symbol_ref表达式中,置1表示此符号引用函数常数池中的地址。

在call_insn、note、expr_list中,置1表示一个常数和纯函数的调用。

在RTL调试输出中,此标志写成“/u”。

(7)used。通常,used只临时用在一个函数的RTL生成完成之时,用于计算一个表达式在指令中出现的次数。在GCC中,对于多次出现的表达式,根据结构共享规则将被复制。

在symbol_ref表达式中,置1表示符号的外部说明己输出。简单地说,就是保证外部说明只输出一次。

在reg表达式中,用于重新编码叶子寄存器,以保证每个寄存器仅被重新编码一次。

(8)volatile。在mem、asm_operands或asm_input表达式中,若内存引用是易变(volatile)的,则易变内存引用不能被删除、归并或重新排序。

在symbol_ref表达式中,它是与具体机器相关的标志。

在reg表达式中,置1表示用户级的变量值。置0表示编译器内部临时变量。

在insn中,置1表示此指令被删除。

在label_ref、reg_label表达式中,置1表示引用一个非局部标号。

在RTL调试输出中,写成“/V”。

5.RTX代码

前面,笔者已经详细讲述了RTX结构相关的话题,包括机器模式、标志等。下面将关注一下RTX的代码。这是学习与理解RTL的关键,也是体会经典编译器设计的契机。

表10-1列出了绝大部分常用的RTX代码,供读者参考。

表10-1 常用的RTX代码

978-7-111-32164-4-Chapter10-29.jpg

(续)

978-7-111-32164-4-Chapter10-30.jpg

(续)

978-7-111-32164-4-Chapter10-31.jpg

6.insn序列

在GCC中,一个函数的RTL是以一个双向链表结构存储的,称为“insn序列”。而具体的insn结构基本上就是类似于一条指令,RTX是嵌套,但位于“顶层”的RTL只有insn,它们以空行隔开。有些insn表示可执行的指令,有些表示switch语句的分支列表,有些则表示变量的声明。

除了拥有具体的数据之外,每条insn都有唯一的编号。通常,用户可以通过一些预定义的宏来获取指定编号的insn及其前驱、后继insn。在GCC中,insn可以分为6类:

insn:用于描述非跳转及函数调用的指令。表达式序列通常是包含在insn中的。

jump_insn:用于描述跳转的指令,通常与label_ref表达式一起使用。

call_insn:用于描述函数调用的指令。

code_label:用于描述标号的指令。

note:用于描述一些调试及声明信息。

barrier:置于指令序列中,使控制流不能通过的指令,如exit等。

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

我要反馈