一. 机器描述文件
机器描述(MD,Machine Description),就是使用规范的RTL语言对目标机器的特性进行描述。由于目标机器的特性千差万别,尤其是各种机器的指令系统各不相同,为了方便GCC提取目标机器的各种特性,必须使用规范的描述语言RTL(这里是MD-RTL)进行机器描述。
使用RTL书写的机器描述文件,尤其是其中指令模板的定义,指导了GIMPLE向RTL转换的具体形式,这就使得机器无关的GIMPLE形式转换成RTL后,能够表达目标机器的特点,从而完成从机器无关的GIMPLE表示到机器相关的RTL表示的转换。从这个角度上讲,基于RTL语言的机器描述是GCC支持多目标机器的基础。
GCC为每种所支持的目标机器均提供了机器描述,gcc/config/${target}目录中就包含了GCC关于目标机器${target}的机器描述文件${target}.md。例如,针对于Intel的i386处理器架构所提供的机器描述文件为gcc/config/i386/i386.md,针对于MIPS处理器的机器描述文件为gcc/config/mips/mips.md,等等。
机器描述文件主要包含了表1中的内容,主要包括各种指令模板(Insn Pattern)的定义、常量(Constant)定义、属性(Attribute)定义、自定义断言(User-Defined Predicates)、自定义约束(User-Defined Constraints)、枚举器(Iterators)定义、流水线(Pipeline)声明、窥孔优化(Peephole Optimization)定义等。
机器描述内容 | 意义 | 主要使用的RTX |
指令模板定义 | 定义指令模板 | define_insn define_expand define_split define_insn_and_split |
常量定义 | 定义机器描述文件中所使用的常量 | define_constants |
属性定义 | 定义指令的属性 | define_attr define_mode_attr |
断言定义 | 自定义断言 | define_predicate |
约束定义 | 自定义约束 | define_constraint define_register_constraint |
枚举器定义 | 包括机器模式枚举器和RTX_CODE枚举器,适用于书写一类指令模板形式相同,但具有不同机器模式或者RTX_CODE的指令模板。 | define_code_iterator define_mode_iterator |
流水线定义 | 定义流水线 | define_insn_reservation define_reservation define_cpu_unit define_automaton |
窥孔优化定义 | 定义窥孔(Peephole)优化策略 | define_peephole define_peephole2 |
二. 指令模板
指令模板是GCC机器描述文件中最重要的内容,主要描述目标机器使用何种形式的指令来实现程序代码中的操作。
MD文件包含了目标机器所支持的每一条指令的指令模板,其一般形式如下:
(define_insn 指令模板名称
RTL模板
条件
输出模板
属性
)
指令模板定义中主要由指令模板名称、RTL模板、条件、输出模板及属性部分组成。
- 指令模板的名称(Pattern Name)由字符串给出,唯一地描述了该指令模板。
- RTL模板(RTL Template)提供了一个不完整的RTX向量,用来描述指令的RTX表示形式,如果该RTX向量只有一个元素,则表示普通的指令模板;如果该RTX向量有多个元素,则表示并行的(parallel)指令模板。
- 条件(Condition)部分是一个C语言的表达式,是用来判断某个insn是否与该指令模板匹配的最后条件。
- 输出模板(Output Template)部分描述了该指令模板转换成目标汇编代码时的输出格式。
- 属性(Attribute)部分,用来设置该指令的一些属性值,该部分通常可以省略。
图1给出了MIPS机器中一条指令模板的具体实例(见gcc/config/mips/mips.md)。该指令模板由define_insn描述,其名称为"*addsi3_extended";RTL模板部分为一个不完整的RTX表达式,其中的部分操作数使用match_operand表达式描述,用来描述该操作数的一些匹配条件;条件部分则使用一个C语言的表达式,该表达式用来描述insn与该指令模板是否匹配的最后一个条件,如果返回为true,则给定的insn与该指令模板匹配,否则为不匹配;指令输出模板部分则给出了不同情况下,该指令模板对应的汇编代码的输出格式;最后的属性设置部分则设置了属性"type"的值为"arith",设置属性"mode"的值为"SI"。
图1. 机器描述文件中的指令模板
下面分别对指令模板中的这五个部分,包括指令模板名称(Pattern Name)、RTL模板(RTL Template)、条件(Condition)、输出模板(Output Template)、指令属性(Attribute)等进行详细的论述。
1. 模板名称
指令模板名称是指令模板的标识。只有命名的指令模板才会被GCC处理。
标准指令模板名称(SPN,Standard Pattern Names)是GCC中预定义的一些指令模板的名称。在GIMPLE语句到IR-RTL的转换过程中,每个特定语义的GIMPLE语句都被转换成为某个对应的具有特定标准模板名称的IR-RTL,这些模板的名称在GCC的设计阶段就已经事先确定,但其内容却与目标机器有关。
GIMPLE语句到RTL的转换可以看做两个阶段:
(1) GIMPLE到SPN:在这个阶段中,每个SPN只是某个RTL模板的抽象代表,从GIMPLE语句到SPN的映射是GCC中事先预定好的(Hard-coded),不会随着目标机器的不同而变化。因此,该阶段的映射是与机器无关的。
(2) SPN到RTL。在这个阶段中,根据SPN从机器描述文件中提取名称与SPN相匹配的指令模板,并根据该模板构造出对应的RTL。可以看出,RTL的形式是由目标机器中定义的指令模板所所决定,反应了目标机器的特性,是与机器紧密相关的。
在机器描述文件中,只有命名的define_insn或者define_expand的指令模板才会用来构造insn,此时,编译器只能使用可以识别的指令模板名称,即具有标准模板名称的指令模板,并且忽略所有的无命名、或者编译器本身所不能识别其名称的指令模板。同时,如果机器描述中没有提供编译器所需要的特定名称的指令模板时,系统将会出错并终止。
根据指令模板名称所表达的语义,指令模板名称的定义遵循如下一些规范:
(1) 指令模板名称可以是空字符串"";
(2) 指令模板名称可以是“*”开头的字符串,这些指令模板在生成指令模板时不会被处理;
(3) 其它的字符串,形如{名称}{机器模式}{操作数个数}。这些字符串一般包含一个名称及2个或者3个可选的部分,其中的名称部分描述了实际的操作,第2部分描述机器模式,第3部分用来描述操作数的数目,例如“movsi”表示一个“移动”操作,其机器模式为“SImode”(Single Integer–SI),例如“addsi3”表示一个“加法”操作,其机器模式为“SImode”,操作数的数目为3。
2. RTL模板
从图1可以看出,RTL模板是一个不完整的RTX向量形式,用来描述指令的具体形式,如果该RTX向量只有一个元素,则表示普通的指令模板,也可以有多个元素,表示并行(Parallel)的指令模板。
图2中给出的实例细化了图1中指令模板中的RTL模板部分。在这个例子中,该RTL模板中RTX向量只包含了一个表示赋值(Assignment)语义的RTX表达式,其RTX_CODE为SET,简称为set RTX。set RTX有两个操作数,均为RTX类型。
可以看出,set RTX的第0操作数是待定的,在第0操作数的位置使用了一种操作数占位符(Operand Placeholder),即match_operand表达式,该表达式给出了第0操作数应该满足的一些条件。
set RTX的第1操作数是一个sign_extend表达式,该sign_extend表达式只有一个操作数,即plus RTX表达式。进一步看,该plus表达式使用两个RTX表达式作为其操作数,其第0操作数(即该RTL模板中的第1操作数)和第1操作数(即该RTL模板中的第2操作数)均同样使用match_operand占位符描述了该操作数需要满足的条件。可以看出,RTL模板中所有的操作数(占位符)编号从0开始统一编号。
同样,有些RTL模板中还可以有操作码占位符(Operator Placeholder)的RTX表达式,以及其它的一些表达式,用来描述该RTL所能匹配的RTL形式,以及如何来构造insn。
图2 RTL模板实例
RTL模板(RTL Template)的功能主要包括两种:匹配和构造。
(1) 匹配(Matching):用来描述包含该RTL模板的指令模板是否可以和特定的RTL完成匹配,以及如何获得该RTL的操作数。对于匹配操作来说,RTL的形式及其操作数应该满足该RTL模板所要求的匹配条件。匹配发生在insn(即RTL)已经生成,需要进行insn后续操作,例如利用insn生成目标机器上的汇编代码时。
(2) 构造(Construction):对于命名的指令模板来说,RTL模板也描述了如何利用给定的操作数实现该指令模板对应的insn的构造。对于构造来说,即从GIMPLE形式生成insn时,需要从GIMPLE语句中提取相应的操作数,并替换RTL模板中的操作数占位符。构造的过程发生在insn的生成过程(即从GIMPLE生成RTL的过程)中。
2.1 匹配表达式
在RTL模板中,通过定义一些特殊的匹配表达式来描述匹配条件,主要包括表2中的RTL表达式。
匹配条件 | PRINT_FORMAT | 操作数 | 意义 |
match_operand | iss | n predicate constraint | 判断第n操作数是否满足断言函数predicate及约束条件constraint。 |
match_scratch | is | n constraint | 判断临时寄存器n是否满足约束constraint |
match_operator | isE | n predicate [operands...] | 判断操作数n的RTX_CODE是否满足断言函数predicate,该操作符的操作数为向量[operands...] |
match_parallel | isE | n predicate [subpat...] | 并行操作匹配 |
match_dup | i | n | 同(match_operand n predicate constraint) |
match_op_dup | iE | n [operands...] | 同(match_ operator n predicate [operands...]) |
match_par_dup | iE | n [subpat...] | 同(match_parallel n predicate [subpat...]) |
1> (match_operand:m n predicate constraint)
m:操作数的机器模式。
n:操作数编号(从0开始连续编号)。
predicate:匹配断言。用来表示该操作数需要满足的某种条件。断言由predicate字符串所代表的函数完成,该函数使用两个参数,分别为操作数n的RTX表达式和机器模式m,如果条件满足则该函数返回非0值,否则返回值为0。
constraint:约束。允许对满足predicate断言的一类操作数进行更细的调整,例如,约束可以声明一个操作数是否可以保存在寄存器中以及保存在何种寄存器中,或者一个操作数是否可以是一个内存引用以及其内存地址的类型,或者一个操作数是否可以是一个立即数常量以及其可能的取值等。总之,约束可能会根据目标机器的限制,对操作数的进行更多的限制。
match_operand表达式是该RTL模板中第n个操作数占位符,用来判断机器模式为m的第n操作数是否满足断言函数predicate及约束constraint。在构造insn时,操作数n的值将被替换到该位置;在进行insn匹配时,该位置的RTX表达式就作为操作数n进行匹配判断。
断言与约束是有联系,也有明显的功能区别:
(1) 断言用来决定给定的insn是否与给定的RTL模板相匹配,而约束则是在给定的insn与给定的模式匹配后所进行的一些更细化、更严格的限制。
(2) 满足断言的操作数在某些特殊情况下需要进行约束的细化,否则在目标机器上可能产生不符合目标机器要求的指令形式,从而导致GCC编译器崩溃。
例如在图2中有如下的表达式:
(match_operand:DI 0 "register_operand" "=d,d")
其中DI为机器模式,0代表该RTL模板的第0个操作数,"register_operand"是断言函数的函数名称,一般定义在gcc/config/${target}/${target}.c文件中。"=d,d"字符串给出了一些约束条件。关于断言与约束的详细描述,可以分别参见下面3和4章节。
2> (match_scratch:m n constraint)
m:操作数的机器模式。
n:操作数编号(从0开始连续编号)。
constraint:约束条件。
该表达式同样是操作数n的一个占位符,并且表明操作数的RTX_CODE必须是SCRATCH,或者该操作数是一个寄存器表达式。在匹配时,该表达式与(match_operand:m n "scratch_operand" constraint)的意义相同。但是在生成RTL时,该操作数将被替换成一个(scratch:m)的表达式。
3> (match_dup n)
n:操作数编号(从0开始连续编号)。
该表达式也是操作数n的占位符,表示操作数n在整个RTL模板中出现的次数超过1次。在构造INSN时,match_dup和match_operand一样,直接将操作数n替换为INSN的操作数(该操作数match_dup出现的位置),而在匹配时则不同,match_dup会假设操作数n已经在前面的match_operand中被识别,该操作数只和识别模板(recognition template)中相同的表达式模式匹配。
例8-1 gcc/config/i386/i386.md中match_dup的使用
(define_insn "*swapqi_1"
[(set (match_operand:QI 0 "register_operand" "+r")
(match_operand:QI 1 "register_operand" "+r"))
(set (match_dup 1)
(match_dup 0))]
"!TARGET_PARTIAL_REG_STALL || optimize_function_for_size_p (cfun)"
"xchg{l}\t%k1, %k0"
[(set_attr "type" "imov")
(set_attr "mode" "SI")
(set_attr "pent_pair" "np")
(set_attr "athlon_decode" "vector")
(set_attr "amdfam10_decode" "vector")])
上述指令模板中,RTL模板部分指令提供了一个并行的操作,使用RTX向量表示,第1个RTX为:
(set (match_operand:QI 0 "register_operand" "+r")
(match_operand:QI 1 "register_operand" "+r"))
表示的意义为 将op1赋值到op0
第2个RTX为:
(set (match_dup 1) (match_dup 0))
表示的意义为将op0赋值到op1
可以看出,在第二个RTX中,并没有像第一个RTX中使用(match_operand:QI 0 "register_operand" "+r")来表示操作数,而是使用了(match_dup 0),完全可以这样理解,match_dup就是对前面已提供的match_operand的再次引用,而这种表示方式看起来更清晰,更容易书写。
match_dup主要用于某个操作数在RTL模板中出现多次的情况,例如一条指令有两个操作数,需要计算其相除后的商和余数,那么RTL模板中这两个操作数可能就均会出现两次,一次出现在计算商的模板中,一次出现在计算余数的模板中,此时使用match_dup就显得更加清晰简便。
4> (match_operator:m n predicate [operands...])
m:机器模式
n:操作数编号
predicate:断言,即匹配函数
operands:该表达式的操作数向量
该表达式用来对操作数n的RTX_CODE进行匹配判断。在进行INSN构造时,构造的RTL表达式的RTX_CODE值就是操作数n的RTX_CODE,该表达式的操作数为operands向量所表示;在进行匹配时,当predicate函数返回为非0时表示该表达式及其操作数满足条件。
例如可交换的比较操作符可以定义为:
int commutative_integer_operator (rtx x, enum machine_mode mode)
{
enum rtx_code code = GET_CODE (x);
if (GET_MODE (x) != mode) return 0;
return (GET_RTX_CLASS (code) == RTX_COMM_ARITH || code == EQ || code == NE);
}
该函数用来判断RTX是否满足给定的机器模式,且属于可交换的的操作类型,下面给出的RTL模板就可以使用上述的函数作为断言,描述一个可交换操作的RTX表达式。
(match_operator:SI 3 " commutative_integer_operator"
[(match_operand:SI 1 "general_operand" "g")
(match_operand:SI 2 "general_operand" "g")]
)
在匹配该模板时,RTX的操作码必须满足match_operator的条件,且RTX需具有两个操作数,分别满足(match_operand:SI 1 "general_operand" "g")和(match_operand:SI 2 "general_operand" "g")的要求。在构造RTX时,使用满足match_operator的RTX表达式及其操作数向量[operands...]中所包含的操作数完成insn的构造。
2.2 断言Predicates
断言(Predicate)是RTL模板中操作数是否满足某种条件的判定过程,通常由一个描述断言函数名称的字符串来表示,断言函数定义的一般形式为:
int predicate(rtx op, enum machine_mode mode)
{
……
}
其中op为操作数的rtx指针,mode为机器模式。如果断言成功则返回非0值,否则返回值为0。
GCC中的断言可以分为两类,即目标机器无关的断言(Machine-Independent Predicates)以及目标机器相关的断言(Machine-Specific Predicates)。
目标机器无关的断言通常完成一些通用的操作数和操作符类型的判断,例如常量、立即数、寄存器操作数、内存操作数等等,这些断言断言在大部分机器中均有大量的使用,表4给出了gcc-4.4.0中所给出的一些目标机器无关的断言。
分类 | 断言函数 Predicate | 意义 |
常量、立即数断言 | immediate_operand | 立即数 |
const_int_operand | CONST_INT表达式 | |
const_double_operand | CONST_DOUBLE表达式 | |
寄存器断言 | register_operand | REG或SUBRE表达式 |
pmode_register_operand | 与(match_operand n "pmode_register_operand" constraint)同义。 | |
scratch_operand | 操作数为硬件寄存器或者SCRATCH表达式,而不是虚拟寄存器. | |
内存引用断言 | memory_operand | 合法的内存地址 |
address_operand | 等同于memory_operand (mem:mode (exp)) | |
indirect_operand | 合法的间接内存地址 | |
push_operand | 一个可以作为将值压入堆栈的内存地址引用。 | |
pop_operand | 一个可以作为将值弹出堆栈的内存地址引用。 | |
组合条件断言 | nonmemory_operand | 立即数或寄存器操作数 |
nonimmediate_operand | 非立即数,即寄存器或者内存操作数 | |
general_operand | 立即数、寄存器或内存操作数 | |
比较操作符断言 | comparison_operator | 算数比较操作符断言 |
下面通过几个例子说明这些目标机器无关断言的具体实现。
例8-2 断言const_int_operand在gcc/recog.c中实现
/* Returns 1 if OP is an operand that is a CONST_INT. */
int const_int_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) != CONST_INT) return 0;
if (mode != VOIDmode
&& trunc_int_for_mode (INTVAL (op), mode) != INTVAL (op))
return 0;
return 1;
}
可以看出,第5行描述了该断言的一个基本条件,即操作数的代码必须为CONST_INT,7-9行描述了当机器模式为VOIDmode时的一些特殊处理。
在有些目标机器描述中,操作数不能用机器无关的断言表示时,这种情况下,用户可以使用define_predicate和define_special_predicate自定义其它的断言表达式。这些表达式包括三个操作数:
(1) 断言名称,这些名称会被match_operand或者match_operator所调用。
(2) 一个RTL表达式,用来判断该操作数是否满足条件,这些表达式必须为表5中给出的形式。
RTX_CODE | RTX表示 | 意义 |
MATCH_OPERAND | (match_operand: n predicate constraint) | 操作数n是否满足predicate的要求,操作数编号及约束被忽略。 |
MATCH_CODE | (match_code: RTX_CODE_names_string, subexpression) | 操作数的RTX_CODE是否在RTX_CODE_names_string(允许的RTX_CODE用逗号分隔)所规定的范围内。也可以判断该操作数的子表达式的RTX_CODE。 |
MATCH_TEST | (match_test c_expression_string) | 操作数是否满足c表达式的要求。 |
AND | (and rtx1 rtx2) | 对上述的match_operand、match_code及match_test的结果进行逻辑运算,语义分别和C语言中的‘&&’, ‘||’, ‘!’, and ‘? :’相同。 |
IOR | (ior rtx1 rtx2) | |
NOT | (not rtx1) | |
IF_THEN_ELSE | (if_then_else rtx1 (rtx2) (rtx3)) |
(3)一个可选的c代码块,如果该c的表达式满足,则该断言返回true,否则返回false。
例8-3 IA64机器描述中的自定义断言
;; True if op is a SYMBOL_REF which refers to the sdata section.
(define_predicate "small_addr_symbolic_operand"
(and (match_code "symbol_ref")
(match_test "SYMBOL_REF_SMALL_ADDR_P (op)")
)
)
该自定义的断言描述的名称为"small_addr_symbolic_operand",断言成功的条件就是同时满足(match_code "symbol_ref")及(match_test "SYMBOL_REF_SMALL_ADDR_P(op)"),即操作数的RTX_CODE必须为SYMBOL_REF,并且函数SYMBOL_REF_SMALL_ADDR_P(op)同时返回非0值。
例8-4 i386机器描述中的自定义断言
(define_predicate "general_no_elim_operand"
(if_then_else (match_code "reg, subreg")
(match_operand 0 "register_no_elim_operand")
(match_operand 0 "general_operand")))
定义了一个断言名称为general_no_elim_operand,断言成功的条件使用IF_THEN_ELSE的形式进行表示。
例8-5 使用C代码块的自定义断言
;; True if op is a register operand that is (or could be) a GR reg.
(define_predicate "gr_register_operand"
(match_operand 0 "register_operand")
{
unsigned int regno;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
regno = REGNO (op);
return (regno >= FIRST_PSEUDO_REGISTER || GENERAL_REGNO_P (regno));
}
)
该断言的名称为"gr_register_operand",用来判断一个操作数是否为通用寄存器操作数。断言成功的条件就是(match_operand 0 "register_operand")返回非0值(表示该操作数是一个寄存器操作数),并且后续的c代码返回非0值(该寄存器为虚拟寄存器或者通用寄存器)。
2.3 约束Constraints
在match_operand中,可以指定操作数约束(Operand Constraints),这些约束对断言所允许的操作数进行更详细的描述。例如,约束条件可以进一步定义操作数是否可以使用寄存器以及使用何种寄存器,也可以说明操作数是否可以是一个内存引用以及其地址类型,也可以描述该操作数是否可以是一个立即数以及其可能的值等。GCC中的约束使用字符串表示。
(1)基本约束
表6给出了GCC中所定义的一些基本约束,这些约束均采用特殊的字符进行表示,分别代表某种特定的意义。
约束字符 | 意义 |
空格 | 忽略 |
m | 内存操作数,允许机器支持的所有地址类型 |
o | 内存操作数,且为offsettable的地址类型 |
v | 内存操作数,除了offsettable的地址类型 |
< | 内存操作数,且为自动递减的地址(autodecrement addressing)类型,包括先减(predecrement)和后减(postdecrement)。 |
> | 内存操作数,且为自动递增的地址(autoincrement addressing)类型,包括先增(preincremen)和后增(postincrement)。 |
r | 寄存器操作数,且使用通用寄存器(general register) |
i | 整数立即数操作数,包括汇编时及汇编以后可以确定值的符号常量 |
n | 具有已知数值的整数立即数操作数 |
I,J,K,…P | 机器相关的自定义约束字符,分别描述在不同数值范围内的整数立即数。 |
E | 浮点立即数(const_double),但该浮点立即数的存储格式必须与HOST(编译器运行的主机)上所采用的浮点数格式相同。 |
F | 浮点立即数(const_double或者const_vector) |
G,H | 机器相关的自定义约束字符,分别描述在不同数值范围内的浮点立即数。 |
s | 整数立即数,且编译时并没有显式的整数值。 |
g | 通用寄存器、内存或者整数立即数,不包括其它非通用寄存器的寄存器。 |
x | 所有的操作数 |
0,1,2,…9 | 操作数编号 |
p | 内存地址操作数 |
other-letters | 用来进行机器相关的约束定义 |
(2)多选择约束(Multiple Alternative Constraints)
有些时候,单个指令模板可以使用多种不同的操作组合,例如,在m68k机器中,一个逻辑“或”指令,可以对寄存器和1个立即数进行或操作,并将结果保存到内存地址中,或者对任意两个操作数进行或操作,并将结果保存在寄存器中,但是却不能将两个内存地址进行或操作。这些不同的操作数约束组合可以使用多选择约束进行表示。每个操作数的约束选择可以表示为:“选择1的约束,选择2的约束,…”。每一种选择的约束均由基本约束和约束修饰字符所组成的字符串来表示。例如m68k上述“或”指令中的指令模板定义如下:
(define_insn "iorsi3"
[(set (match_operand:SI 0 "general_operand" "=m,d")
(ior:SI (match_operand:SI 1 "general_operand" "%0,0")
(match_operand:SI 2 "general_operand" "dKs,dmKs")))]
…… //省略
)
可以看出,该约束为多选择约束,共有两种不同的操作数约束的组合。第一种组合中,操作数0的约束为‘m’(memory),操作数1的约束为‘%0’,表示与操作数0的约束相同,操作数2的约束为‘dKs’(dK约束属于m68k机器中的自定义约束,见例8-7)。第二种组合中,操作数0的约束为‘d’(data register),操作数1的约束为‘0’,操作数2的约束为‘dmKs’,其中的‘=’和‘%’对于每个操作数所有的约束选择都是适用的,参见下节“约束修饰字符”。
(3)约束修饰字符(constraint modifier characters)
为了更细致地描述约束条件,GCC的约束还可以使用约束修饰字符(Constraint Modifier Characters),表8-7列出了常用的约束修饰字符,并说明了其表示的具体含义。
约束修饰字符 | 意义 |
= | 对该指令来说,该操作数为只写(write-only),以前的值被指令输出的新值所替换 |
+ | 对该指令来说,该操作数可读可写(即同时为该指令的输入操作数和输出操作数) * 除了‘=’和‘+’之外的操作数均只作为输入操作数。 ‘=’和‘+’修饰符应该出现在整个约束字符串的开头 |
& | 在某些约束选择(constraint alternative)中,该操作数是前面某个clobber的操作数,作为指令的输入操作数,该操作数在指令结束之前它的值已经被修改,因此,该操作数可能不在原来使用的寄存器或内存地址中存储了。 |
% | 声明该指令是可交换的操作,该操作数及其后的操作数可以进行交换 |
# | 表示从‘#’之后,一直到逗号的所有字符在进行约束处理时将被忽略,这些字符只对寄存器选择起作用。 |
* | 表示从‘*’之后,一直到逗号的所有字符在进行约束处理时将被忽略,这些字符在寄存器选择时也将被忽略。 |
例8-6 gcc/config/mips/mips.md中使用操作数约束
(define_insn "add<mode>3"
[(set (match_operand:ANYF 0 "register_operand" "=f")
(plus:ANYF (match_operand:ANYF 1 "register_operand" "f")
(match_operand:ANYF 2 "register_operand" "f")))]
""
"add.<fmt>\t%0,%1,%2"
[(set_attr "type" "fadd")
(set_attr "mode" "<UNITMODE>")])
该指令模板中,操作数0的约束条件为"=f",其中的约束修饰符号为"=",表示该寄存器操作数将作为输出操作数,其内容将被该指令的输出所覆盖。
在目标机器中也可以使用define_register_constraint、define_constraint等自定义约束字符串的意义。例如例8-7中给出了m68k机器中的一些自定义约束。
例8-7 gcc/config/m68k/constraints.md中的自定义约束
(define_register_constraint "a" "ADDR_REGS" "Address register.")
(define_register_constraint "d" "DATA_REGS" "Data register.")
(define_register_constraint "f" "TARGET_HARD_FLOAT ? FP_REGS : NO_REGS"
"Floating point register.")
(define_constraint "I"
"Integer constant in the range 1 @dots 8, for immediate shift counts and addq."
(and (match_code "const_int")
(match_test "ival > 0 && ival <= 8")))
3. 条件
指令模板中的条件(Conditions)是一个C语言的表达式,是判断该指令模板是否匹配的最后条件。例如图8-1的指令模板中,条件部分就是使用下述的语句给出:
"!TARGET_PARTIAL_REG_STALL || optimize_function_for_size_p (cfun)"
该表达式的结果如果非0,则insn与该模板匹配,否则不匹配。
4. 输出模板
指令模板中的输出模板(Output Template)主要用来表示该指令模板匹配后如何输出目标汇编代码。输出模板使用字符串描述,除了字符串中的一些特殊字符串需要进行操作数替换,其它将被原样输出到汇编语言中,输出模板中常见的特殊控制字符串如表8所示。
特殊字符 | 意义 |
%n | 替换为操作数n |
%cdigit | 替换成常量操作数digit(digit为数值,表示操作数的编号,下同) |
%ndigit | 替换成常量操作数digit的相反数 |
%adigit | 替换成操作数digit的地址 |
%ldigit | 将label_ref类型的操作数digit替换成jump指令 |
%= | 输出每条指令所对应的唯一的一个数值编号 |
%% | 汇编语言中输出% |
例8-8 输出模板举例
例如在gcc/config/paag/paag.md中有如下的指令模板:
(define_insn "addisi3"
[(set (match_operand:SI 0 "general_operand" "")
(plus:SI (match_operand:SI 1 "general_operand" "")
(match_operand:SI 2 "immediate_operand" "")))]
""
"ADDI %0, %1, #%2")
生成的对应汇编指令为:
ADDI 4($a10), 4($a10), #1;
其中,4($a10)、4($a10)及#1分别是指令输出模板中要求的三个操作数,且最后一个立即操作数前面按输出模板的要求加上了#符号。
5. 属性
指令模板中的属性部分可以对该指令的一些属性进行设置,这些属性通常在指令模板及流水线优化中有较多的使用。
例如下述的指令模板中,可以使用set_attr表达式设置当前指令中“type”属性的值为“alu”,“pent_pair”属性的值为“pu”以及“mode”属性的值为“DI”。
(define_insn "adddi3_carry_rex64"
[(set (match_operand:DI 0 "nonimmediate_operand" "=rm,r")
(plus:DI (plus:DI (match_operand:DI 3 "ix86_carry_flag_operator" "")
(match_operand:DI 1 "nonimmediate_operand" "%0,0"))
(match_operand:DI 2 "x86_64_general_operand" "re,rm")))
(clobber (reg:CC FLAGS_REG))]
"TARGET_64BIT && ix86_binary_operator_ok (PLUS, DImode, operands)"
"adc{q}\t{%2, %0|%0, %2}"
[(set_attr "type" "alu")
(set_attr "pent_pair" "pu")
(set_attr "mode" "DI")])
属性在设置和使用之前需要定义。属性定义可以使用define_attr表达式来表示:
(define_attr name list-of-values default)
其中name用来描述指令的属性名称,第2个操作数list-of-values描述了属性的可能取值的列表,第3个操作数default给出了该属性的默认值。例如在gcc/config/i386/i386.md文件中,定义了下述的属性:
;; A basic instruction type. Refinements due to arguments to be
;; provided in other attributes.
(define_attr "type"
"other,multi,
alu,alu1,negnot,imov,imovx,lea,
incdec,ishift,ishift1,rotate,rotate1,imul,idiv,
icmp,test,ibr,setcc,icmov,
push,pop,call,callv,leave,
str,bitmanip,
fmov,fop,fsgn,fmul,fdiv,fpspc,fcmov,fcmp,fxch,fistp,fisttp,frndint,
sselog,sselog1,sseiadd,sseiadd1,sseishft,sseimul,
sse,ssemov,sseadd,ssemul,ssecmp,ssecomi,ssecvt,ssecvt1,sseicvt,ssediv,sseins,
ssemuladd,sse4arg,
mmx,mmxmov,mmxadd,mmxmul,mmxcmp,mmxcvt,mmxshft"
(const_string "other"))
其中define_attr表达式定义了属性type,其取值范围为other,multi, alu,alu1,negnot,imov,imovx,lea等等中的任一个,默认值为“other”。
本文简单介绍了GCC机器描述文件中指令模板的基本内容,有兴趣的读者可以打开对应的机器描述文件(例如,针对于Intel的i386处理器架构所提供的机器描述文件为gcc/config/i386/i386.md,针对于MIPS处理器的机器描述文件为gcc/config/mips/mips.md),结合上述内容对照分析。