Next: , Previous: Constants, Up: RTL


10.8 寄存器和内存

这些是描述访问机器寄存器和内存的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_REGISTERLAST_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_REGNUMFRAME_POINTER_REGNUM给定的寄存器和STARTING_FRAME_OFFSET的值的和替换。


VIRTUAL_STACK_DYNAMIC_REGNUM
该宏指向栈指针根据内存需要已经被调整后的栈上动态分配内存的位置。

该虚寄存器由STACK_POINTER_REGNUM给定的寄存器和STACK_DYNAMIC_OFFSET的值的和替换。


VIRTUAL_OUTGOING_ARGS_REGNUM
其指向栈中,当预先压栈时(使用push insn压栈的参数应该总是使用STACK_POINTER_REGNUM),书写输出参数的位置。

该虚拟寄存器,被替换成由STACK_POINTER_REGNUM给定的寄存器与值STACK_POINTER_OFFSET的和。


(subreg:m1 reg:m2 bytenum)
subreg表达式用于按照自然的机器模式之外的,其它机器模式来引用一个寄存器,或者引用有多个寄存器组成的reg的其中一个寄存器。

每个伪寄存器都具有一个自然的机器模式。如果需要按照不同的机器模式来对其操作,则寄存器必须用subreg进行包含。

目前对于subreg的第一个操作数,有三种被支持的类型:

subregsubreg不被支持。推荐使用simplify_gen_subreg来避免这种问题。

subreg有两种不同的风格,分别具有自己的用法和规则:

Paradoxical subregs
m1严格宽于m2的时候,subreg表达式被称作反常的(paradoxical)。对该类别的subreg的正规测试为:
               GET_MODE_SIZE (m1) > GET_MODE_SIZE (m2)

反常的subreg可以用于左值和右值。当用于左值时,源值的低位被存储在reg中,高位被丢弃。当用作右值时,subreg的低位来自reg,而高位可以被定义,也可以未被定义。

右值的高位有以下几种情况:

  • subregs of mem m2小于一个字的时候,宏LOAD_EXTEND_OP可以控制高位如何被定义。
  • subreg of regs SUBREG_PROMOTED_VAR_P为真时,高位被定义。SUBREG_PROMOTED_UNSIGNED_P描述了高位的内容。这样的subreg通常表示已经被提升为更宽的机器模式的局部变量,寄存器变量以及参数伪变量。

对于反常的subregbytenum总是为零,即使在大端的目标机上。

例如反常的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为假。

Normal subregs
m1最多跟m2一样宽的时候,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_ENDIANBYTES_BIG_ENDIAN

  • WORDS_BIG_ENDIAN,如果设为1,则说明第零个字节为最大有效字的部分;否则,为最小有效字的部分。
  • BYTES_BIG_ENDIAN,如果设为1,则说明第零个字节为字中的最高有效字节;否则,为字中的最低有效字节。

在一些目标机上,FLOAT_WORDS_BIG_ENDIANWORDS_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)
这表示一个scratch寄存器,其在单个指令的执行中用到,并随后不再被使用。其被局部寄存器分配或者重载过程,转换成一个reg

scratch通常位于clobber操作中。(参见Side Effects)。


(cc0)
为机器的条件代码寄存器。其没有参数,并可以没有机器模式。有两种使用它的方式:

只有一个代码为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)
该RTX表示对表达式addr所表示的地址的主内存进行引用。m描述了被访问的内存的单元大小。alias描述了该引用的别名集合。总得来说,两个项如果不引用相同的内存地址,则在不同的别名集合里。

结构(mem:BLK (scratch))被认为是所有其它内存的别名。因此其可以在函数尾声的栈销毁中用作内存栅栏。


(concatm rtx rtx)
该RTX表示对两个其它RTX的连结。这用于复数值。其应该只出现在附加在声明中的RTL中,以及RTL生成中。不应该出现在普通的insn链上。


(concatnm [rtx ...])
该RTX表示将所有的rtx进行连结,生成一个单个的值。类似concat,其应该只出现在声明中,不应该出现在insn链上。