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 out unspec_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 out unspec expressions. GCC will also use it when printing out unspec_volatile expressions unless an unspecv 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).