Constant Definitions#
Using literal constants inside instruction patterns reduces legibility and can be a maintenance problem.
To overcome this problem, you may use the define_constants
expression. It contains a vector of name-value pairs. From that
point on, wherever any of the names appears in the MD file, it is as
if the corresponding value had been written instead. You may use
define_constants
multiple times; each appearance adds more
constants to the table. It is an error to redefine a constant with
a different value.
To come back to the a29k load multiple example, instead of
(define_insn ""
[(match_parallel 0 "load_multiple_operation"
[(set (match_operand:SI 1 "gpc_reg_operand" "=r")
(match_operand:SI 2 "memory_operand" "m"))
(use (reg:SI 179))
(clobber (reg:SI 179))])]
""
"loadm 0,0,%1,%2")
You could write:
(define_constants [
(R_BP 177)
(R_FC 178)
(R_CR 179)
(R_Q 180)
])
(define_insn ""
[(match_parallel 0 "load_multiple_operation"
[(set (match_operand:SI 1 "gpc_reg_operand" "=r")
(match_operand:SI 2 "memory_operand" "m"))
(use (reg:SI R_CR))
(clobber (reg:SI R_CR))])]
""
"loadm 0,0,%1,%2")
The constants that are defined with a define_constant are also output in the insn-codes.h header file as #defines.
You can also use the machine description file to define enumerations.
Like the constants defined by define_constant
, these enumerations
are visible to both the machine description file and the main C code.
The syntax is as follows:
(define_c_enum "name" [
value0
value1
(value32 32)
value33
...
valuen
])
This definition causes the equivalent of the following C code to appear
in insn-constants.h
:
enum name {
value0 = 0,
value1 = 1,
value32 = 32,
value33 = 33,
...
valuen = n
};
#define NUM_cname_VALUES (n + 1)
where cname
is the capitalized form of name
.
It also makes each valuei
available in the machine description
file, just as if it had been declared with:
(define_constants [(valuei i)])
Each valuei
is usually an upper-case identifier and usually
begins with cname
.
You can split the enumeration definition into as many statements as you like. The above example is directly equivalent to:
(define_c_enum "name" [value0])
(define_c_enum "name" [value1])
...
(define_c_enum "name" [valuen])
Splitting the enumeration helps to improve the modularity of each
individual .md
file. For example, if a port defines its
synchronization instructions in a separate sync.md
file,
it is convenient to define all synchronization-specific enumeration
values in sync.md
rather than in the main .md
file.
Some enumeration names have special significance to GCC:
unspecv
If an enumeration called
unspecv
is defined, GCC will use it when printing outunspec_volatile
expressions. For example:(define_c_enum "unspecv" [ UNSPECV_BLOCKAGE ])causes GCC to print
(unspec_volatile ... 0)
as:(unspec_volatile ... UNSPECV_BLOCKAGE)
unspec
If an enumeration called
unspec
is defined, GCC will use it when printing outunspec
expressions. GCC will also use it when printing outunspec_volatile
expressions unless anunspecv
enumeration is also defined. You can therefore decide whether to keep separate enumerations for volatile and non-volatile expressions or whether to use the same enumeration for both.
Another way of defining an enumeration is to use define_enum
:
(define_enum "name" [
value0
value1
...
valuen
])
This directive implies:
(define_c_enum "name" [
cname_cvalue0
cname_cvalue1
...
cname_cvaluen
])
where cvaluei
is the capitalized form of valuei
.
However, unlike define_c_enum
, the enumerations defined
by define_enum
can be used in attribute specifications
(see define_enum_attr).