Next: , Up: Constraints


16.8.1 简单约束

最简单的约束种类是一个由字母组成的字符串, 每个字母描述一种所允许的操作数。这里是所允许的字母:

whitespace
空格字符将被忽略,并且可以插到除了起始处的任何地方。这使得机器描述中, 不同操作数的每个可选项可以被可视化的对齐,即使它们具有不同数目的约束和修饰符。


m
内存操作数将被允许,包括机器支持的任何寻址方式。


o
内存操作数将被允许,但只有当地址为偏移表的时候。 这意味着可以对地址加上一个小的整数(实际上,是为操作数的数个字节宽度, 这由它的机器模式决定),其结果也为一个有效的内存地址。

例如,地址为常数的为一个偏移表;所以地址为一个寄存器和常数(只要常数在机器所支持 的地址偏移范围)的和;但是递增或者递减地址不是偏移表。更加复杂的间接/索引地址可 能是或者可能不是偏移表,这取决于机器支持的其它寻址模式。


V
一个不是offsettable的内存操作数。换句话说, 任何适合‘m’约束但不是‘o’约束的。


<
允许具有自动减量寻址(先减或者后减)的内存操作数。


>
允许具有自动增量寻址(先增或者后增)的内存操作数。


r
允许为通用寄存器的寄存器操作数。


i
立即数(具有常数值)将被允许。这包括符号常量, 其值将在汇编时候或者更晚的时候才被知道。


n
立即数,其具有已知的数值。 许多系统不支持汇编时间常量作为小于一个字的宽度的操作数。 这些操作数的约束应该为‘n’而不是‘i’。


I’, ‘J’, ‘K’, ...P
从‘I’到‘P’的其它字母可以被定义为机器特定的, 用来运行立即数具有显示指定范围的整数值。例如,在68000上, ‘I’被定义为代表1到8的值。这是在移位指令中被允许作为移位数的范围。


E
浮点立即数(表达式代码为const_double), 但是必须target浮点格式与host机器(编译器运行的机器)的相同才行。


F
浮点立即数(表达式代码为const_double或者const_vector)。


G’, ‘H
G’和‘H’可以被定义为机器特定的方式来允许浮点立即数具有特定范围的值。


s
整数立即数,其值不是一个显式的整数。

这可能有点奇怪;如果insn允许常量操作数具有在编译时不可知的值, 它当然必须允许任何可知的值。所以为什么用‘s’,而不是‘i’能?有时候, 它会允许生成更好的代码。

例如,在68000上的全字指令,有可能使用一个立即数操作数; 但是如果立即数的值是处于-128和127之间,更好的代码是将值加载到寄存器中, 使用寄存器。这是因为加载到寄存器中可以由‘moveq’指令来完成。 我们对此通过定义字母‘K’来表示任意范围超出-128和127的整数, 然后在操作数约束中指定‘Ks’。


g
任何寄存器,内存或整数立即数,除了不是通用寄存器的寄存器。


X
任何操作数都被允许,即使其不满足general_operand。 这通常用于match_scratch的约束中,当一些的可选项实际上不需要scratch寄存器的时候。


0’, ‘1’, ‘2’, ...9
匹配指定操作数编号的操作数。如果数字与字母一起使用,则数字应该放在最后。

该编号允许多于单个数字。如果多个数字连续的在一起, 则它们被解析为一个单独的十进制整数。很少会因此产生不明确,因为到目前为止, 还没有想要将‘10’解析为匹配操作数1或者0的。如果有这样的需要, 则可以使用多个可选项来替代。

这被称为匹配约束,其实际上是指汇编器只有一个单独的操作数, 却在RTL insn中扮演两个角色。例如,add insn在RTL中具有两个输入操作数和一个输出操作数, 但是多数CISC机器上,add指令实际上只有两个操作数,其中一个为输入输出操作数:

          addl #35,r12

匹配约束被用于这些情况。更确切的说, 匹配的两个操作数必须包括一个只作输入的操作数和一个只作输出的操作数。


p
允许一个为有效内存地址的操作数。这用于“加载地址”和“地址压栈”指令。

约束中的‘p’必须由match_operand中的作为断言的address_operand协同工作。该断言将match_operand中指定的机器模式解析为地址有效的内存引用的机器模式。


other-letters
其它字母可以采用机器相关的方式被定义,用于代表寄存器的特定类别或者其它任意的操作数类型。‘d’, ‘a’和‘f’在68000/68020被定义用来代表数据,地址和浮点寄存器。

为了具有有效的汇编代码,每个操作数必须满足它的约束。但是如果不满足的话,也不会阻止将该指令模式应用到insn上。替代的,它会指示编译器去修改代码以至于约束将被满足。通常,这是通过将一个操作数复制到寄存器中来完成的。

因此,对比下面的两条指令模式:

     (define_insn ""
       [(set (match_operand:SI 0 "general_operand" "=r")
             (plus:SI (match_dup 0)
                      (match_operand:SI 1 "general_operand" "r")))]
       ""
       "...")

其具有两个操作数,其中一个必须出现在两个位置,

     (define_insn ""
       [(set (match_operand:SI 0 "general_operand" "=r")
             (plus:SI (match_operand:SI 1 "general_operand" "0")
                      (match_operand:SI 2 "general_operand" "r")))]
       ""
       "...")

其具有三个操作数,其中两个通过约束被要求为是相同的。如果我们考虑如下形式的一条insn

     (insn n prev next
       (set (reg:SI 3)
            (plus:SI (reg:SI 6) (reg:SI 109)))
       ...)

第一个指令模式将根本不会被应用,因为该insn不在合适的地方包含两个相同的子表达式。指令模式会说“这看起来不像是一条加法指令;试一下其它模式”。第二个指令模式将会说,“是的,这是一条加法指令,但是有些问题”。它将指使编译器的重载过程生成额外的insn,使得约束为真。结果可能看起来像:

     (insn n2 prev n
       (set (reg:SI 3) (reg:SI 6))
       ...)
     
     (insn n n2 next
       (set (reg:SI 3)
            (plus:SI (reg:SI 3) (reg:SI 109)))
       ...)

你必须确保每个操作数,在每个指令模式中,具有能够处理可能会出现的任何RTL表达式的约束。(当使用多个可选项时,每个指令模式,对于每个可能的操作数表达式组合,必须至少具有一个可选项可以处理该操作数的组合。)约束不需要允许任何可能的操作数——如果是这种情况,它们就不做约束了——但是它们必须至少指出可以加载任何可能操作数,使得适合约束的方法。

如果操作数的断言能够识别寄存器,但是约束不允许它们,则能够使编译器崩溃。当该操作数正好是寄存器时,重载过程将被打乱,因为它不知道如何将寄存器临时复制到内存中。

如果断言接受一元操作符,约束将被应用到操作数上。例如,MIPS处理器在ISA3级时,支持一条指令,其将两个SImode的寄存器相加产生一个 DImode的结果,但是必须寄存器能够被正确的符号扩展。该断言对于输入操作数接受一个SImode寄存器的sign_extend。将约束写成指示寄存器的类型需要为sign_extend的操作数。