Next: Arithmetic, Previous: Constants, Up: RTL
(reg:
m n)
FIRST_PSEUDO_REGISTER
)的整数n,这表示对机器寄存器号为n的引用。对于值大的n,它表示一个临时的值或者伪寄存器(pseudo register)。编译器的策略是,在生成代码时假设有无限数目的伪寄存器,并在之后将它们转换为硬件寄存器(hard register)或者内存的引用。
m为引用的机器模式。指定机器模式是有必要的,因为机器通常可以使用多种机器模式来引用每个寄存器。例如,一个寄存器可以包含一个整字,但是可以有指令来作为半字或者一个单独的字节来引用,同样,也有指令可以作为不同精度的浮点来引用。
即使对于机器只能使用一种机器模式来引用的寄存器,mode也必须被指定。
符号FIRST_PSEUDO_REGISTER
被机器描述定义,由于机器的硬件寄存器数目是一个不变的特征。然而要注意,并不是所有的机器寄存器都必须是通用寄存器。所有的可以用于存储数据的机器寄存器都为被给定硬件寄存器编号,即使那些只可以被用于特定指令或者只能存放特定类型数据。
一个硬件寄存器可以在整个函数中按照多种机器模式来访问,但是每个伪寄存器都被给定一个自然的机器模式并且只能按照那个机器模式来访问。当需要描述一个使用非自然机器模式来访问伪寄存器的时候,则使用一个subreg
表达式。
一个reg
表达式具有的机器模式,如果指定了多于一个字的数据,则实际上代表了多个连续的寄存器。如果寄存器编号还指定了一个硬件寄存器,则其实际上表示起始于指定寄存器的多个连续的硬件寄存器。
每个在函数的RTL代码中使用的伪寄存器编号,使用一个唯一的reg
表达式来表示。
一些伪寄存器编号,处于FIRST_VIRTUAL_REGISTER
到LAST_VIRTUAL_REGISTER
之间,只在RTL生成阶段出现并且在优化阶段之前被消除。这些表示在栈帧中的位置,并且直到函数的RTL生成完成后才能确定。下列虚寄存器编号被定义:
VIRTUAL_INCOMING_ARGS_REGNUM
当RTL生成完成时,该虚寄存器由ARG_POINTER_REGNUM
给定的寄存器和FIRST_PARM_OFFSET
的值的和替换。
VIRTUAL_STACK_VARS_REGNUM
FRAME_GROWS_DOWNWARD
被定义为非0的值,则该宏指向栈上第一个变量的上一个位置。否则,其指向栈上的第一个变量。
VIRTUAL_STACK_VARS_REGNUM
is replaced with the sum of the
VIRTUAL_STACK_VARS_REGNUM
由FRAME_POINTER_REGNUM
给定的寄存器和STARTING_FRAME_OFFSET
的值的和替换。
VIRTUAL_STACK_DYNAMIC_REGNUM
该虚寄存器由STACK_POINTER_REGNUM
给定的寄存器和STACK_DYNAMIC_OFFSET
的值的和替换。
VIRTUAL_OUTGOING_ARGS_REGNUM
STACK_POINTER_REGNUM
),书写输出参数的位置。
该虚拟寄存器,被替换成由STACK_POINTER_REGNUM
给定的寄存器与值STACK_POINTER_OFFSET
的和。
(subreg:
m1 reg:m2 bytenum)
subreg
表达式用于按照自然的机器模式之外的,其它机器模式来引用一个寄存器,或者引用有多个寄存器组成的reg
的其中一个寄存器。
每个伪寄存器都具有一个自然的机器模式。如果需要按照不同的机器模式来对其操作,则寄存器必须用subreg
进行包含。
目前对于subreg
的第一个操作数,有三种被支持的类型:
subreg
将伪寄存器reg
作为它们的第一个操作数。
mem
的subreg
,在早期版本的GCC中比较常见,现在仍被支持。在重载过程中,这些被普通的mem
替换掉。在不进行指令调度的机器上,仍然使用mem
的subreg
,但是不推荐这样。在重载过程之前和过程之中,这样的subreg
被考虑成register_operand
,而不是memory_operand
。因此,调度过程无法对具有mem
的subreg
这样的指令进行合适的调度。所以,对于进行调度的机器,不要使用mem
的subreg
。为此,当INSN_SCHEDULING
被定义的时候,合并过程和recog过程,具有显式的代码来禁止创建mem
的subreg
。
在重载过程之后使用mem
的subreg
,将难以理解,应该避免这样。编译器中还有一些代码支持这些,但是这些代码可能已经过时了。这种subreg
的用法不被推荐,将来很可能不被支持。
subreg
中包裹硬件寄存器;这样的寄存器通常应该被缩减为一个单独的reg
rtx。这种subreg
的用法不被推荐,将来可能不被支持。
subreg
的subreg
不被支持。推荐使用simplify_gen_subreg
来避免这种问题。
subreg
有两种不同的风格,分别具有自己的用法和规则:
subreg
表达式被称作反常的(paradoxical)。对该类别的subreg
的正规测试为:
GET_MODE_SIZE (m1) > GET_MODE_SIZE (m2)
反常的subreg
可以用于左值和右值。当用于左值时,源值的低位被存储在reg中,高位被丢弃。当用作右值时,subreg
的低位来自reg,而高位可以被定义,也可以未被定义。
右值的高位有以下几种情况:
subreg
s of mem
当m2小于一个字的时候,宏LOAD_EXTEND_OP
可以控制高位如何被定义。
subreg
of reg
s
当SUBREG_PROMOTED_VAR_P
为真时,高位被定义。SUBREG_PROMOTED_UNSIGNED_P
描述了高位的内容。这样的subreg通常表示已经被提升为更宽的机器模式的局部变量,寄存器变量以及参数伪变量。
对于反常的subreg
,bytenum总是为零,即使在大端的目标机上。
例如反常的subreg
:
(set (subreg:SI (reg:HI x) 0) y)
在x中存储了y的低位2个字节,并丢弃高位2个字节。接着:
(set z (subreg:SI (reg:HI x) 0))
将会把z的低位2个字节设置成x,并将高位两个字节设置为未知的值,假定SUBREG_PROMOTED_VAR_P
为假。
subreg
表达式被称作正常的(normal)。
正常的subreg
被限定为reg的特定位。有两种情况。如果m1比一个字小,则subreg
指的是一个reg字的最小有效部分。如果m1为字大小,或者更大,则subreg
指的是一个或者更多个完整的字。
当用作左值时,subreg
为一个基于字的访问。对subreg
进行存储,会修改reg中所有与subreg
重叠的字,并将reg中的其它字保持不变。
当对小于一个字的正常subreg
进行存储的时候,被引用的字的其它位通常处于未定义的状态。这种松弛的方式易于对这样的指令产生高效的代码。要表示保持subreg
之外的所有位的指令,在subreg
周围使用strict_low_part
或者zero_extract
。
bytenum必须标识从reg的起始,subreg
的第一个字节的偏移量,假设reg按照内存的顺序布局。字节的内存顺序通过两个目标宏定义,WORDS_BIG_ENDIAN
和BYTES_BIG_ENDIAN
:
WORDS_BIG_ENDIAN
,如果设为1,则说明第零个字节为最大有效字的部分;否则,为最小有效字的部分。
BYTES_BIG_ENDIAN
,如果设为1,则说明第零个字节为字中的最高有效字节;否则,为字中的最低有效字节。
在一些目标机上,FLOAT_WORDS_BIG_ENDIAN
与WORDS_BIG_ENDIAN
不一致。然而,编译器的大部分地方会将浮点值看作它们与整数值具有相同的大小端。这是因为只将它们作为整数值的集合来处理,没有特定的数值。只有real.c和运行时库关心FLOAT_WORDS_BIG_ENDIAN
。
因此,
(subreg:HI (reg:SI x) 2)
在一个BYTES_BIG_ENDIAN
,‘UNITS_PER_WORD == 4’的目标机上,等同于
(subreg:HI (reg:SI x) 0)
在一个小端,‘UNITS_PER_WORD == 4’的目标机上。两个subreg
都是访问寄存器x的低两个字节。
MODE_PARTIAL_INT
机器模式的行为就好像其与相对应的MODE_INT
机器模式一样宽,只不过其具有未知数目的未定义的位。例如:
(subreg:PSI (reg:SI 0) 0)
访问整个‘(reg:SI 0)’,但是PSImode
值和SImode
值的确切关系没有被定义。如果我们假设‘UNITS_PER_WORD <= 4’,则下面两个subreg
:
(subreg:PSI (reg:DI 0) 0) (subreg:PSI (reg:DI 0) 4)
表示对‘(reg:DI 0)’的两个部分进行无关的四个字节访问。每个subreg
都具有未知数目的未定义位。
如果‘UNITS_PER_WORD <= 2’,则这两个subreg
(subreg:HI (reg:PSI 0) 0) (subreg:HI (reg:PSI 0) 2)
表示无关的两个字节访问,一起贯穿整个‘(reg:PSI 0)’。对第一个subreg
进行存储不影响第二个的值,反之亦然。‘(reg:PSI 0)’具有未知数目的未定义位,所以赋值:
(set (subreg:HI (reg:PSI 0) 0) (reg:HI 4))
不保证‘(subreg:HI (reg:PSI 0) 0)’具有值‘(reg:HI 4)’。
上面的规则应用于伪寄存器reg和硬件寄存器reg。如果对于m1, m2和硬件寄存器reg的特定组合,其语义不正确,则目标机特定的代码必须确保这些组合不会被用到。例如:
CANNOT_CHANGE_MODE_CLASS (m2, m1, class)
必须为真,对于每个包含reg的类别class。
subreg
表达式的第一个操作数通常使用SUBREG_REG
宏来访问,第二个操作数通常使用SUBREG_BYTE
宏来访问。
BYTES_BIG_ENDIAN
不等于WORDS_BIG_ENDIAN
的平台是在很多年前被测试的。对于希望在将来支持这样一个平台的人们,可能会面对一些过时的代码。
(scratch:
m)
reg
。
scratch
通常位于clobber
操作中。(参见Side Effects)。
(cc0)
使用这种技术,(cc0)
只在两种上下文中有效:为一个赋值的对象(在测试和比较指令中)和在跟零进行比较的比较运算符中(值为零的const_int
;也就是说,const0_rtx
)。
使用这种技术,(cc0)
只在两种上下文中有效:为一个赋值的对象(在测试和比较指令中),其中源操作数为一个比较运算符,以及if_then_else
的第一个参数(在条件分支中)。
只有一个代码为cc0
的表达式对象;其为变量cc0_rtx
的值。任何尝试创建一个代码为cc0
的表达式,将返回cc0_rtx
。
指令可以隐式的设置条件代码。在许多机器上,几乎所有的指令根据它们计算或者存储的值来设置条件码。没有必要在RTL中显式的记录这些行为,因为机器描述包含一个对策,用于识别这样做的指令(通过宏NOTICE_UPDATE_CC
)。参见Condition Code. 只有目的纯粹是设置条件码的指令,以及使用条件码的指令,才需要提及(cc0)
。
在一些机器上,条件码寄存器被给定一个寄存器编号,并且一个reg
用于替代(cc0)
。这通常为更好的方式,如果只有一个小的指令子集修改条件码。其它机器将条件码存储在通用寄存器中;这种情况下应该使用伪寄存器。
一些机器,例如SPARC和RS/6000,具有两个算术指令集合,一个设置条件码,另一个不设置。可以通常情况下生成不设置条件码的指令,并创建一个同时执行算术运算并设置条件码寄存器(这种情况下将不会是(cc0)
)的指令模式。例如,搜一下sparc.md中的‘addcc’和‘andcc’。
(pc)
(pc)
只在跳转指令的特定上下文中使用。
只有一个代码为pc
的表达式对象;其为变量pc_rtx
的值。任何尝试创建一个代码为pc
的表达式,将返回pc_rtx
。
所有不进行跳转的指令会隐式的通过递增的方式改变程序计数器,但是不需要在RTL中提起这些。
(mem:
m addr alias)
结构(mem:BLK (scratch))
被认为是所有其它内存的别名。因此其可以在函数尾声的栈销毁中用作内存栅栏。
(concat
m rtx rtx)
(concatn
m [
rtx ...])
concat
,其应该只出现在声明中,不应该出现在insn链上。