[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4. Preprocessor macros

Preprocessor macros provide a way of simplifying the writing of `.cpu' files and serve the same purpose that macros do in C.

4.1 Defining a preprocessor macro  define-pmacro
4.2 Using preprocessor macros  
4.3 Macro expansion  The pmacro-expand procedure
4.4 Default argument values  Specifying default values of arguments
4.5 Multiple output expressions  Using begin
4.6 Symbol concatenation  The .sym builtin
4.7 String concatenation  The .str builtin
4.8 Convert a number to a hex  The .hex builtin
4.9 Convert a string to uppercase  The .upcase builtin
4.10 Convert a string to lowercase  The .downcase builtin
4.11 Getting part of a string  The .substring builtin
4.12 List splicing  The .splice builtin
4.13 Number generation  The .iota builtin
4.14 Mapping a macro over a list  The .map builtin
4.15 Applying a macro to a list  The .apply builtin
4.16 Defining a macro inline  The .pmacro builtin
4.17 Passing macros as arguments  Passing a macro to another macro


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.1 Defining a preprocessor macro

Preprocessor macros are defined with:

 
(define-pmacro (name parm1 parm2 ... parmN)
  expansion
)

The result is `expansion' with parameters replaced with the actual arguments of the macro invocation. Free variables are left unchanged. [A "free variable", as defined here, is one that doesn't appear in the parameter list.]

`expansion' must be exactly one expression.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2 Using preprocessor macros

Preprocessor macros are invoked in either of two ways: positional arguments and arguments by name.

 
(define-pmacro (foo arg1 arg2) (bar arg1 arg2))

; Invoke by positional arguments.

(foo abc def) ==> (bar abc def)

; Invoke by naming arguments.

(foo #:arg1 ghi #:arg2 jkl) ==> (bar ghi jkl)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.3 Macro expansion

At the implementation level, pmacros are expand with the pmacro-expand Scheme procedure.

The following is executed from a Guile shell, as opposed to appearing in a cpu description file, hence the extra quoting.

 
guile> (define-pmacro '(foo a b) '(+ a b))
guile> (pmacro-expand '(foo 3 4))
(+ 3 4)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.4 Default argument values

Invoking pmacros by specifying argument names allows some, or all, arguments to be elided and thus allows for arguments to have default values.

Specify default values with the following syntax.

 
(define-pmacro (macro-name (arg1 . default-value)
                           (arg2 . default value) ...)
  ...
)

Example:

 
(define-pmacro (foo (arg1 . 1) (arg2 . 2))
  (bar arg1 arg2)
)

(foo #:arg2 33) ==> (bar 1 33)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.5 Multiple output expressions

The result of a preprocessor macro is exactly one expression. It is often useful, however, to return multiple expressions, say for example when you want one macro to define several instructions.

The way to do this is to enclose all the expressions with begin. begin is only valid at the top [definition] level.

??? It's moderately clumsy to restrict begin like this. Using sequence for this purpose might be cleaner except that sequence locals don't make sense in this context (though perhaps that's a lesser evil). In the end, begin can be shorthand for a void-mode sequence with no locals so I haven't been in a rush to resolve this.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.6 Symbol concatenation

Symbol and string concatenation are supported. Symbol concatenation is done with:

(.sym arg1 arg2 ...)

Acceptable arguments are symbols, strings, and numbers. The result is a symbol with the arguments concatenated together. Numbers are converted to a string, base 10, and then to a symbol. The result must be a valid Scheme symbol with the additional restriction that the first character must be a letter. The resulting symbol is recursively macro-expanded.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.7 String concatenation

String concatenation is done with

(.str arg1 arg2 ...)

Acceptable arguments are symbols, strings, and numbers. The result is a string with the arguments concatenated together. Numbers are converted base 10.

Example:

 
(define-pmacro (bin-op mnemonic op2-op sem-op)
  (dni mnemonic
       (.str mnemonic " reg/reg")
       ()
       (.str mnemonic " $dr,$sr")
       (+ OP1_0 op2-op dr sr)
       (set dr (sem-op dr sr))
       ())
)
(bin-op and OP2_12 and)
(bin-op or OP2_14 or)
(bin-op xor OP2_13 xor)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.8 Convert a number to a hex

Convert a number to a lowercase hex string with .hex. If width is present, the result is that many characters beginning with the least significant digit. Zeros are prepended as necessary.

Syntax: (.hex number [width])

Examples:

 
(.hex 42)   --> "2a"
(.hex 42 1) --> "a"
(.hex 42 4) --> "002a"


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.9 Convert a string to uppercase

Convert a string to uppercase with .upcase.

Syntax: (.upcase string)

Example:

 
(.upcase "foo!") --> "FOO!"


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.10 Convert a string to lowercase

Convert a string to lowercase with .downcase.

Syntax: (.downcase string)

Example:

 
(.downcase "BAR?") --> "bar?"


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.11 Getting part of a string

Extract a part of a string with .substring.

Syntax: (.substring string start end)

where `start' is the starting character, and `end' is one past the ending character. Character numbering begins at position 0. If `start' and `end' are the same, and both valid, the empty string is returned.

Example:

 
(.substring "howzitgoineh?" 2 6) --> "wzit"


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.12 List splicing

It is often useful to splice a list into a "parent" list. This is best explained with an example.

 
(define-pmacro (splice-test a b c)
               (.splice a (.unsplice b) c))
(pmacro-expand (splice-test (1 (2) 3)))

--> (1 2 3)

Note that a level of parentheses around 2 has been removed.

This is useful, for example, when one wants to pass a list of fields to a macro that defines an instruction. For example:

 
(define-pmacro (cond-move-1 name comment mnemonic cc-prefix cc-name cc-opcode
			    src-name src-opcode cond test)
  (dni name
       (.str "move %" cc-name " " comment ", v9 page 191")
       ((MACH64))
       (.str mnemonic " " cc-prefix cc-name ",$" src-name ",$rd")
       (.splice + OP_2 rd OP3_MOVCC cond
		(.unsplice cc-opcode) (.unsplice src-opcode))
       (if (test cc-name)
	   (set rd src-name))
       ())
)

This macro, taken from `sparc64.cpu', defines a conditional move instruction. Arguments cc-opcode and src-opcode are lists of fields. The macro is invoked with (simplified from `sparc64.cpu'):

 
(cond-move-1 mova-icc "blah ..." mova
             "%" icc ((f-fmt4-cc2 1) (f-fmt4-cc1-0 0))
             rs2 ((f-i 0) (f-fmt4-res10-6 0) rs2)
             CC_A test-always)
(cond-move-1 mova-imm-icc "blah ..." mova
             "%" icc ((f-fmt4-cc2 1) (f-fmt4-cc1-0 0))
             simm11 ((f-i 1) simm11)
             CC_A test-always)

Macro cond-move-1 is being used here to define both the register and the immediate value case. Each case has a slightly different list of opcode fields. Without the use of .splice/.unsplice, the resulting formats would be:

 
(+ OP_2 rd OP3_MOVCC CC_A ((f-fmt4-cc2-1) (f-fmt4-cc1-0 0))
   ((f-i 0) (f-fmt4-res10-6 0) rs2))

and

(+ OP_2 rd OP3_MOVCC CC_A ((f-fmt4-cc2-1) (f-fmt4-cc1-0 0))
   ((f-i 1) simm11))

respectively. This is not what is wanted. What is wanted is

 
(+ OP_2 rd OP3_MOVCC CC_A (f-fmt4-cc2-1) (f-fmt4-cc1-0 0)
   (f-i 0) (f-fmt4-res10-6 0) rs2)

and

(+ OP_2 rd OP3_MOVCC CC_A (f-fmt4-cc2-1) (f-fmt4-cc1-0 0)
   (f-i 1) simm11)

respectively, which is what .splice achieves.

.unsplice is a special reserved symbol that is only recognized inside .splice.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.13 Number generation

Machine descriptions often require a list of sequential numbers. Generate a list of numbers with the .iota builtin macro.

The syntax is `(.iota count [start [incr]])'.

Examples:

 
(.iota 5)      --> 0 1 2 3 4
(.iota 5 4)    --> 4 5 6 7 8
(.iota 5 5 -1) --> 5 4 3 2 1


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.14 Mapping a macro over a list

Apply a macro to each element of a list, or set of lists, with .map.

The syntax is `(.map macro-name list1 [list2 ...])'.

The result is a list with `macro-name' applied to each element of `listN'. `macro-name' should take as many arguments as there are lists. This is often useful in constructing enum and register name lists.

Example:

 
(define-pmacro (foo name number) ((.sym X name) number))
(.map foo (A B C D E) (.iota 5))

-->

((XA 0) (XB 1) (XC 2) (XD 3) (XE 4))


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.15 Applying a macro to a list

Invoke a macro with each argument coming from an element of a list, with .apply.

The syntax is `(.apply macro-name list)'.

The result is the result of invoking macro `macro-name'. `macro-name' should take as many arguments as there elements in `list'. If `macro-name' takes a variable number of trailing arguments, there must be at least as many list elements as there are fixed arguments.

Example:

 
(.apply .str (.iota 5))

-->

"01234"

Note that (.str (.iota 5)) is an error. Here the list `(0 1 2 3 4)' is passed as the first argument of .str, which is wrong.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.16 Defining a macro inline

Define a macro inline with .pmacro. This is only supported when passing macros as arguments to other macros.

 
(define-pmacro (load-op suffix op2-op mode ext-op)
  (begin
    (dni (.sym ld suffix) (.str "ld" suffix)
	 ()
	 (.str "ld" suffix " $dr,@$sr")
	 (+ OP1_2 op2-op dr sr)
	 (set dr (ext-op WI (mem: mode sr)))
	 ())
  )
)

(load-op "" OP2_12 WI (.pmacro (mode expr) expr))
(load-op b OP2_8 QI (.pmacro (mode expr) (ext: mode expr)))
(load-op h OP2_10 HI (.pmacro (mode expr) (ext: mode expr)))
(load-op ub OP2_9 QI (.pmacro (mode expr) (zext: mode expr)))
(load-op uh OP2_11 HI (.pmacro (mode expr) (zext: mode expr)))

Currently, .pmacro's don't bind the way Scheme lambda expressions do. For example, arg2 in the second pmacro is not bound to the arg2 argument of the first pmacro.

 
(define-pmacro (foo arg1 arg2) ((.pmacro (bar) (+ arg2 bar)) arg1))
(foo 3 4) ==> (+ arg2 3)

One can make an argument either way. I'm not sure what the right thing to do here is (leave things as is, or have lexical binding like Scheme).


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.17 Passing macros as arguments

Macros may be passed to other macros.

Example:

 
(define-pmacro (no-ext-expr mode expr) expr)
(define-pmacro (ext-expr mode expr) (ext: mode expr))
(define-pmacro (zext-expr mode expr) (zext: mode expr))

(define-pmacro (load-op suffix op2-op mode ext-op)
  (begin
    (dni (.sym ld suffix) (.str "ld" suffix)
	 ()
	 (.str "ld" suffix " $dr,@$sr")
	 (+ OP1_2 op2-op dr sr)
	 (set dr (ext-op WI (mem: mode sr)))
	 ())
  )
)

(load-op "" OP2_12 WI no-ext-expr)
(load-op b OP2_8 QI ext-expr)
(load-op h OP2_10 HI ext-expr)
(load-op ub OP2_9 QI zext-expr)
(load-op uh OP2_11 HI zext-expr)


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Ben Elliston on January, 8 2003 using texi2html