[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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
procedure4.4 Default argument values Specifying default values of arguments 4.5 Multiple output expressions Using begin
4.6 Symbol concatenation The .sym
builtin4.7 String concatenation The .str
builtin4.8 Convert a number to a hex The .hex
builtin4.9 Convert a string to uppercase The .upcase
builtin4.10 Convert a string to lowercase The .downcase
builtin4.11 Getting part of a string The .substring
builtin4.12 List splicing The .splice
builtin4.13 Number generation The .iota
builtin4.14 Mapping a macro over a list The .map
builtin4.15 Applying a macro to a list The .apply
builtin4.16 Defining a macro inline The .pmacro
builtin4.17 Passing macros as arguments Passing a macro to another macro
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
Convert a string to uppercase with .upcase
.
Syntax: (.upcase string)
Example:
(.upcase "foo!") --> "FOO!" |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Convert a string to lowercase with .downcase
.
Syntax: (.downcase string)
Example:
(.downcase "BAR?") --> "bar?" |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |