Next: , Previous: Registers, Up: Target Macros


17.8 寄存器类别

在许多机器上,编号寄存器并不都是等价的。例如,一些寄存器不可以用作索引寻址; 一些寄存器不可以用于某些指令。 这些机器限制使用寄存器类别(register classes)来描述给编译器。

你定义一些寄存器类别,给出每个类别的名字并指名哪些寄存器属于它。然后, 你可以指定哪些寄存器类别对于特定的指令模式可以用作操作数。

总的来说,每个寄存器将属于多个类别。实际上, 必须有一个名为ALL_REGS的类别,包含所有的寄存器。 另外必须有一个名为NO_REGS的类别,不包含寄存器。 通常两个类别的并集将成为另一个类别;但并不这么要求。

其中一个类别必须名为GENERAL_REGS。该名字没有什么特殊的, 但是操作数约束字母‘r’和‘g’专门指定该类。 如果GENERAL_REGSALL_REGS相同, 则可以将其定义为扩展成ALL_REGS的宏。

对类别进行排序,使得如果类x包含在类y中, 则x具有比y更低的类别编号。

在操作数约束中指定GENERAL_REGS之外的类别的方法, 是通过机器相关的约束字母。你可以定义该字母来对应于不同的类别, 然后在操作数约束中使用它们。

只要有指令同时允许两个类别,就应该定义一个它们的并集的类别。 例如,如果对于一个特定的操作数, 有一条指令允许一个浮点(协处理器)寄存器或者一个通用寄存器, 则应该定义一个类别FLOAT_OR_GENERAL_REGS,其包含它们两。 否则你不会得到最优的代码。

你还必须指定关于寄存器类别的冗余信息:对于每个类别, 有哪些类别包含它以及哪些被它包含; 对于每个类别对,最大的类包含在它们的并集中。

当一个值占用多个连续的位于特定类别的寄存器时, 所有被使用的寄存器必须属于那个类别。因此, 寄存器类别不能用于要求寄存器对起始于偶数编号的寄存器。 用来指定该要求的方法是使用HARD_REGNO_MODE_OK

用于按位与或者移位指令的操作数的寄存器类别具有特殊的要求: 对于每个定点机器模式,每个这样的类必须具有一个子类, 它的寄存器可以按照该机器模式与内存进行传送值。例如,在一些机器上, 对于单字节值的操作(QImode)被限制为特定的寄存器。这样的话, 每个用于按位与或者移位指令的寄存器类别必须具有一个子类, 组成它的寄存器可以用来加载或存储单字节值。 这使得PREFERRED_RELOAD_CLASS总是具有一个可以返回的值。

— Data type: enum reg_class

一个枚举类型,必须使用所有的寄存器类别名作为枚举值来定义。 NO_REGS必须位于最前面。ALL_REGS必须为最后一个寄存器类别, 后面再跟随一个枚举值,LIM_REG_CLASSES,其不是一个寄存器类别, 但是用来告诉有多少个类别。

每个寄存器类别具有一个编号,其为将类别名映射到int类型的值。 编号在下面描述的许多表中用作索引。

— Macro: N_REG_CLASSES

不同寄存器类别的数目,定义为:

          #define N_REG_CLASSES (int) LIM_REG_CLASSES
— Macro: REG_CLASS_NAMES

一个初始化值,包含了作为C字符串常量的寄存器类别的名字。 这些名字用于书写一些调试转储。

— Macro: REG_CLASS_CONTENTS

一个初始化值,包含了寄存器类别的内容,作为位掩码的整数。 第n个整数指定了类别n的内容。整数掩码的解析方式为, 寄存器r在类别中,如果mask & (1 << r)为1。

当机器具有多于32个寄存器的时候,一个整数还不能满足。这时整数被替换为子初始化值, 为括号包裹的多个整数。每个子初始化值必须适合类型HARD_REG_SET的初始化值, 其在hard-reg-set.h中定义。这种情况下, 每个子初始化值的第一个整数对应于寄存器0到31,第二个对应于32到63,等等。

— Macro: REGNO_REG_CLASS (regno)

一个C表达式,其值为包含了硬件寄存器regno的寄存器类别。总的来说, 会有不止一个这样的类别;选择最小的那个,这意味着没有更小的类别包含该寄存器。

— Macro: BASE_REG_CLASS

一个宏,它的定义为有效的基址寄存器必须属于的类别名字。 基址寄存器用于由寄存器的值加上一个偏移量来表示的地址中。

— Macro: MODE_BASE_REG_CLASS (mode)

这是宏BASE_REG_CLASS的变体,其允许在机器模式相关的方式下选择基址寄存器。 如果mode为VOIDmode,则其应该返回根BASE_REG_CLASS同样的值。

— Macro: MODE_BASE_REG_REG_CLASS (mode)

一个C表达式,其值为有效的基址寄存器必须属于的类别名字, 且用于一个基址寄存器加上索引寄存器的地址中。你应该定义该宏, 如果表示为基址寄存器加上索引寄存器的地址具有不同于其它基址寄存器的用法要求时。

— Macro: MODE_CODE_BASE_REG_CLASS (mode, outer_code, index_code)

一个C表达式,其值为有效的基址寄存器必须属于的类别名字。 outer_codeindex_code定义了基址寄存器出现的上下文。 outer_code为。index_code为相应的索引表达式的代码, 如果outer_codePLUS;否则为SCRATCH

— Macro: INDEX_REG_CLASS

一个C表达式,其值为有效的索引寄存器必须属于的类别名字。 索引寄存器为一个用于地址中的寄存器, 它的值用于乘于一个标量因子或者加上另一个寄存器(也可以加上一个偏移量)。

— Macro: REGNO_OK_FOR_BASE_P (num)

一个C表达式,其为非0, 如果寄存器编号num适合在操作数地址中作为基址寄存器使用。 其可以为一个合适的硬件寄存器,或者一个已经被分配了这样的硬件寄存器的伪寄存器。

Compiler source files that want to use the strict variant of this and other macros define the macro REG_OK_STRICT. You should use an #ifdef REG_OK_STRICT conditional to define the strict variant in that case and the non-strict variant otherwise.

— Macro: REGNO_MODE_OK_FOR_BASE_P (num, mode)

一个C表达式,类似于REGNO_OK_FOR_BASE_P, 除了表达式可以检查内存引用的机器模式mode。你应该定义该宏, 如果内存引用的机器模式影响了一个寄存器是否可以作为基址寄存器使用。 如果你定义了该宏,则编译器将使用它来替代REGNO_OK_FOR_BASE_P。 对于出现在MEM之外的地址,即作为一个address_operand, mode可以为VOIDmode

This macro also has strict and non-strict variants.

— Macro: REGNO_MODE_OK_FOR_REG_BASE_P (num, mode)

一个C表达式,其为非0, 如果寄存器编号num适合在表示为基址加索引的地址中作为一个基址寄存器使用, 并通过模式mode来访问。其可以为一个合适的硬件寄存器, 或者一个已经被分配了这样的硬件寄存器的伪寄存器。你应该定义该宏, 如果表示为基址寄存器加上索引寄存器的地址具有不同于其它基址寄存器的用法要求时。

不赞成使用该宏;请使用更加通用的REGNO_MODE_CODE_OK_FOR_BASE_P

This macro also has strict and non-strict variants.

— Macro: REGNO_MODE_CODE_OK_FOR_BASE_P (num, mode, outer_code, index_code)

一个C表达式,类似于REGNO_MODE_OK_FOR_BASE_P, 除了表达式可以检查寄存器出现内存引用中的上下文。outer_code为。 index_code为相应的索引表达式的代码,如果outer_codePLUS; 否则为SCRATCH。对于出现在MEM之外的地址, 即作为一个address_operand,mode可以为VOIDmode

This macro also has strict and non-strict variants.

— Macro: REGNO_OK_FOR_INDEX_P (num)

一个C表达式,其为非0, 如果寄存器编号num适合作为索引寄存器用于操作数地址中。 其可以为一个合适的硬件寄存器或者一个已经被分配了这样的硬件寄存器的伪寄存器。

索引寄存器和基址寄存器的区别是,索引寄存器可以被标量化。 如果一个地址包含了两个寄存器的和,并且都不被标量化, 则一个可以被标签为“base” 另一个为“index”;但是使用哪个标签, 必须要适合机器的约束。编译器将尝试两种标签方式,来查找有效的一种, 并且当两种方式都无法工作时,重载一个或两个寄存器。

This macro also has strict and non-strict variants.

— Macro: PREFERRED_RELOAD_CLASS (x, class)

一个C表达式,对寄存器类别进行额外的限制, 用于当需要复制值x到类别为class的寄存器中。 值为一个寄存器类别;可能为class或者其它比class小的类别。 在许多机器上,下列定义是安全的:

          #define PREFERRED_RELOAD_CLASS(X,CLASS) CLASS

有时返回一个更加限制的类别将会产生更好的代码。例如,在68000上, 当x为一个整数常量,并且在‘moveq’指令的范围内, 则该宏的值总是为DATA_REGS,只要class包含数据寄存器。 需要一个数据寄存器保证了‘moveq’将使用。

一种PREFERRED_RELOAD_CLASS必须不返回class的情况为, 如果x为一个合法常量其不能被加载到某个寄存器类别中。 通过返回NO_REGS,你可以强迫x放入内存位置中。例如, rs6000可以加载立即数值到通用寄存器中, 但没有指令可以加载立即数值到浮点寄存器中, 所以PREFERRED_RELOAD_CLASS返回NO_REGS, 当x为一个浮点常量时。如果常量不能被加载到任何种类的寄存器中, 如果LEGITIMATE_CONSTANT_P使常量为非法的, 而不是使用PREFERRED_RELOAD_CLASS,则代码生成将会更好。

如果一个insn在寄存器分配之后具有伪寄存器, 则重载将遍历选择项并且反复调用PREFERRED_RELOAD_CLASS来找到最好的一个。 返回NO_REGS,在这种情况下,使得重载在约束前增加一个!: x86后端使用该特征来劝阻使用387寄存器,当算术在SSE寄存器中进行时(反之亦然)。

— Macro: PREFERRED_OUTPUT_RELOAD_CLASS (x, class)

类似于PREFERRED_RELOAD_CLASS,但是用于输出重载而不是输入重载。 如果没有定义该宏,则缺省为使用不变的class

你还可以使用PREFERRED_OUTPUT_RELOAD_CLASS来劝阻使用一些可选项的重载, 类似于PREFERRED_RELOAD_CLASS

— Macro: LIMIT_RELOAD_CLASS (mode, class)

一个C表达式,对寄存器类别实施额外的限制, 用于当需要在一个类别为class的重载寄存器中保存机器模式为mode的值的时候。

不像PREFERRED_RELOAD_CLASS, 该宏应该用于当有特定的机器模式不能简单的放入特定的重载类别中的时候。

值为一个寄存器类别;可能为class,或者其它更小的类别。

不要定义该宏,除非target机器具有一些限制,使得宏需要做一些事情。

— Target Hook: enum reg_class TARGET_SECONDARY_RELOAD (bool in_p, rtx x, enum reg_class reload_class, enum machine_mode reload_mode, secondary_reload_info *sri)

许多机器具有一些寄存器,其不能直接和内存之间进行复制, 甚至不能和其它类型的寄存器。一个例子是‘MQ’寄存器, 在大多数机器上,只能与通用寄存器直接进行复制,而不能和内存之间。 下面,我们将使用术语‘中途寄存器’,当一个move操作不能直接执行, 而必须首先通过将源复制到中途寄存器中,然后再从中途寄存器复制到目的。 一个中途寄存器总是具有与源和目的相同的机器模式。由于其存放了被复制的实际的值, 所以重载可能进行优化来重用一个中途寄存器,并且省略掉从源进行复制的操作, 当它可以确定中途寄存器还保留着所需要的值的时候。

另一种需要执行二次重载的情况是,在一些机器上, 其允许所有的寄存器和内存之间进行复制, 但要求一个scratch寄存器来存储一些内存的位置(例如,在RT上那些具有符号地址的, 以及在SPARC上当编译PIC时那些具有特定符号地址的)。 草稿寄存器不需要与被复制的值具有相同的机器模式,并且通常保留一个不同的值。 在md文件中需要特殊的指令模式来描述复制在草稿寄存器的帮助下如何执行; 这些指令模式还描述了草稿寄存器的编号,寄存器类别和机器模式。

在一些情况下,同时需要中途寄存器和草稿寄存器。

对于输入重载,该target钩子使用非零的in_p来调用,并且x为一个rtx, 其需要被复制到一个类别为reload_class,机器模式为reload_mode的寄存器中。 对于输出重载,该target钩子使用为0的in_p调用,并且一个类别为reload_class, 需要复制到机器模式为reload_mode的rtx x中。

如果在类别为reload_class的寄存器和x直接进行复制需要一个中途寄存器, 则钩子secondary_reload应该返回一个该中途寄存器需要的寄存器类别。 如果不需要中途寄存器,则其应该返回NO_REGS。如果需要多个中途寄存器, 则描述在复制链中最近的那个。

如果需要草稿寄存器, 则还要描述如何如何在重载寄存器和这个最近的中途寄存器直接进行复制。 或者如果不需要中途寄存器,但仍然需要一个草稿寄存器, 则描述重载寄存器和重载操作数x之间的复制。

为此,你需要设置sri->icode为在md文件中执行move的指令模式的代码。 操作数0和1分别为该复制的输出和输入。从2以后的操作数为草稿操作数。 这些草稿操作数必须具有机器模式并且一个single-register-class的输出约束。

当使用中途寄存器的时候,secondary_reload钩子将会被再次调用, 来确定如何在中途寄存器和重载操作数之间进行复制, 所以你的钩子必须还要具有处理中途操作数的寄存器类别的代码。

x可以为伪寄存器或者一个伪寄存器的subreg, 其可以为一个硬件寄存器或者在内存中。使用true_regnum来查看; 其将返回-1如果伪寄存器在内存中,以及硬件寄存器编号,如果其在一个寄存器中。

在内存中的草稿操作数(约束"=m" / "=&m")目前不被支持。 为此,目前你必须继续使用SECONDARY_MEMORY_NEEDED

copy_cost还是要该target钩子来查找值如何被复制。 如果你想让其包含像分配草稿寄存器所需要的额外代价, 则可以设置sri->extra_cost为额外代价。 或者如果两个相关move会具有比两个单个move之和的代价要低, 则可以设置sri->extra_cost为一个负数。

— Macro: SECONDARY_RELOAD_CLASS (class, mode, x)
— Macro: SECONDARY_INPUT_RELOAD_CLASS (class, mode, x)
— Macro: SECONDARY_OUTPUT_RELOAD_CLASS (class, mode, x)

These macros are obsolete, new ports should use the target hook TARGET_SECONDARY_RELOAD instead.

These are obsolete macros, replaced by the TARGET_SECONDARY_RELOAD target hook. Older ports still define these macros to indicate to the reload phase that it may need to allocate at least one register for a reload in addition to the register to contain the data. Specifically, if copying x to a register class in mode requires an intermediate register, you were supposed to define SECONDARY_INPUT_RELOAD_CLASS to return the largest register class all of whose registers can be used as intermediate registers or scratch registers.

If copying a register class in mode to x requires an intermediate or scratch register, SECONDARY_OUTPUT_RELOAD_CLASS was supposed to be defined be defined to return the largest register class required. If the requirements for input and output reloads were the same, the macro SECONDARY_RELOAD_CLASS should have been used instead of defining both macros identically.

The values returned by these macros are often GENERAL_REGS. Return NO_REGS if no spare register is needed; i.e., if x can be directly copied to or from a register of class in mode without requiring a scratch register. Do not define this macro if it would always return NO_REGS.

If a scratch register is required (either with or without an intermediate register), you were supposed to define patterns for ‘reload_inm’ or ‘reload_outm’, as required (参见Standard Names. These patterns, which were normally implemented with a define_expand, should be similar to the ‘movm’ patterns, except that operand 2 is the scratch register.

These patterns need constraints for the reload register and scratch register that contain a single register class. If the original reload register (whose class is class) can meet the constraint given in the pattern, the value returned by these macros is used for the class of the scratch register. Otherwise, two additional reload registers are required. Their classes are obtained from the constraints in the insn pattern.

x might be a pseudo-register or a subreg of a pseudo-register, which could either be in a hard register or in memory. Use true_regnum to find out; it will return −1 if the pseudo is in memory and the hard register number if it is in a register.

These macros should not be used in the case where a particular class of registers can only be copied to memory and not to another class of registers. In that case, secondary reload registers are not needed and would not be helpful. Instead, a stack location must be used to perform the copy and the movm pattern should use memory as an intermediate storage. This case often occurs between floating-point and general registers.

— Macro: SECONDARY_MEMORY_NEEDED (class1, class2, m)

一些机器要求某些寄存器必须使用内存才能与其它寄存器进行复制。 定义该宏在那些机器上,其为一个C表达式, 为非0如果机器模式为m的对象在类别为class1的寄存器中, 只能通过将class1的寄存器存储到内存中并且将内存位置加载到class2的寄存中。

如果其值总是为0,则不要定义该宏。

— Macro: SECONDARY_MEMORY_NEEDED_RTX (mode)

通常当SECONDARY_MEMORY_NEEDED被定义时, 编译器会分配一个栈槽为需要寄存器复制的内存位置。 如果该宏被定义,则编译器会替代的使用该宏定义的内存位置。

如果没有定义SECONDARY_MEMORY_NEEDED,则不要定义该宏。

— Macro: SECONDARY_MEMORY_NEEDED_MODE (mode)

当编译器需要一个二级内存位置在机器模式为mode的寄存器之间进行复制的时候, 其通常分配足够的内存来存放BITS_PER_WORD个位, 并且执行该位数宽度的mode的存储和加载操作。

这在大多数机器上是正确的, 因为其确保寄存器的所有位被复制并且阻止对寄存器按照较窄的机器模式来访问, 这对于浮点寄存器通常是禁止的。

然而,该缺省行为在一些机器上是不正确的,例如DEC Alpha, 其在浮点寄存器中存储short整数与在整数寄存器中是不同的。 在那些机器上,缺省的宽度将不正确,你必须定义该宏来抑制这种宽度。 详情参见alpha.h文件。

如果没有定义,或者如果BITS_PER_WORD个位数的宽度是正确的,则不要定义该宏。

— Target Hook: bool TARGET_CLASS_LIKELY_SPILLED_P (reg_class_t rclass)

A target hook which returns true if pseudos that have been assigned to registers of class rclass would likely be spilled because registers of rclass are needed for spill registers.

The default version of this target hook returns true if rclass has exactly one register and false otherwise. On most machines, this default should be used. Only use this target hook to some other expression if pseudos allocated by local-alloc.c end up in memory because their hard registers were needed for spill registers. If this target hook returns false for those classes, those pseudos will only be allocated by global.c, which knows how to reallocate the pseudo to another register. If there would not be another register available for reallocation, you should not change the implementation of this target hook since the only effect of such implementation would be to slow down register allocation.

— Macro: CLASS_LIKELY_SPILLED_P (class)

A C expression whose value is nonzero if pseudos that have been assigned to registers of class class would likely be spilled because registers of class are needed for spill registers.

The default value of this macro returns 1 if class has exactly one register and zero otherwise. On most machines, this default should be used. Only define this macro to some other expression if pseudos allocated by local-alloc.c end up in memory because their hard registers were needed for spill registers. If this macro returns nonzero for those classes, those pseudos will only be allocated by global.c, which knows how to reallocate the pseudo to another register. If there would not be another register available for reallocation, you should not change the definition of this macro since the only effect of such a definition would be to slow down register allocation.

— Macro: CLASS_MAX_NREGS (class, mode)

一个C表达式,为需要存放机器模式为mode的值所需要的连续的类别为class的寄存器的最大数。

这与宏HARD_REGNO_NREGS很相近。实际上, 宏CLASS_MAX_NREGS (class, mode)的值应该为HARD_REGNO_NREGS (regno,mode)的最大值。

该宏有助于处理多字的值,在重载过程中。

— Macro: CANNOT_CHANGE_MODE_CLASS (from, to, class)

如果被定义,为一个C表达式,其返回非0,对于一个class, 其由机器模式fromto的改变是无效的。

例如,加载32位整数或者浮点对象到浮点寄存器中,在Alpha上将被扩展为64位。 因此加载64位对象并存储为32位对象时将不保存第32位。 因此,alpha.h定义CANNOT_CHANGE_MODE_CLASS如下:

          #define CANNOT_CHANGE_MODE_CLASS(FROM, TO, CLASS) \
            (GET_MODE_SIZE (FROM) != GET_MODE_SIZE (TO) \
             ? reg_classes_intersect_p (FLOAT_REGS, (CLASS)) : 0)
— Target Hook: const reg_class_t * TARGET_IRA_COVER_CLASSES (void)

Return an array of cover classes for the Integrated Register Allocator (IRA). Cover classes are a set of non-intersecting register classes covering all hard registers used for register allocation purposes. If a move between two registers in the same cover class is possible, it should be cheaper than a load or store of the registers. The array is terminated by a LIM_REG_CLASSES element.

The order of cover classes in the array is important. If two classes have the same cost of usage for a pseudo, the class occurred first in the array is chosen for the pseudo.

This hook is called once at compiler startup, after the command-line options have been processed. It is then re-examined by every call to target_reinit.

The default implementation returns IRA_COVER_CLASSES, if defined, otherwise there is no default implementation. You must define either this macro or IRA_COVER_CLASSES in order to use the integrated register allocator with Chaitin-Briggs coloring. If the macro is not defined, the only available coloring algorithm is Chow's priority coloring.

— Macro: IRA_COVER_CLASSES

See the documentation for TARGET_IRA_COVER_CLASSES.