* non-asm macros can now take `const` and `call` parameters * register parameters to asm functions and macros can be given names if annotated explicitly
4.1 KiB
Macros and inlining
Macros
Functions defined with the macro
keyword are not actual functions, but they are used as syntax replacements.
It implies the following:
-
macros must return
void
-
cannot be
inline
,noinline
orextern
-
can be
asm
- in this case, they should not end with a return instruction -
do not have an address
-
their invocations cannot be used as expressions
-
in case of
asm
macros, the parameters:-
must be defined as either
const
(compile-time constants),ref
(variables) orregister(XX)
(registers, where XX is the register you want to use) -
at most one parameter can be defined as a register
-
-
in case of non-
asm
macros, the parameters-
must be defined as either
ref
(variables; default, may be omitted)const
(compile-time constants), orcall
(expressions, which are evaluated every time they are used) -
ref
parameters exceptionally can have their type declared asvoid
; such parameters accept variables of any type -
call
parameters exceptionally can have their type declared asvoid
; such parameters accept expressions of any type, includingvoid
, however, you cannot assign from those expressions
-
-
macros do not have their own scope (they reuse the scope from their invocations) – exceptions: the parameters and the local labels defined in assembly
-
control-flow statements (
break
,continue
,return
,goto
,label
) are run as if places in the caller function
When invoking a macro, you need to pass variables as arguments to parameters annotated with ref
and constants as arguments annotated with const
.
Invoking a non-asm
macro requires the types of variables via ref
parameters to match precisely.
No type conversions are performed.
Exception: parameters of type void
can accept a variable of any type.
For parameters defined as const
, register(XX)
or call
, the usual type conversions are performed.
You can invoke a macro from assembly, by preceding the invocation with +
Examples:
macro void inc_x() {
x += 1
}
byte add_two_1(byte x) {
inc_x()
inc_x()
return x
}
macro void inc(byte b) {
b += 1
}
byte add_two_2(byte x) {
inc(x)
inc(x)
return x
}
macro void perform_twice(void call f) {
f
f
}
byte add_two_3(byte x) {
perform_twice(inc(x))
return x
}
macro void add(byte b, byte v) {
b += v
}
macro void retu(byte result) {
return result
}
byte add_two_4(byte x) {
add(x, 2)
retu(x)
}
macro asm byte add_asm(byte ref b, byte const v) {
LDA b
CLC
ADC #v
STA b
// no RTS!
}
byte add_two_5(byte x) {
add_asm(x, 2)
return x
}
macro asm byte add_asm_2(byte ref b, byte register(x) v) {
TXA
CLC
ADC b
STA b
// no RTS!
}
byte add_two_6(byte x) {
add_asm_2(x, 2)
return x
}
Automatic inlining
You can control inlining behavior in several ways:
-
functions declared with the
const
keyword called with constant arguments will always be inlined, with the whole invocation being converted into a single constant, regardless ofinline
andnoinline
keywords; calls with non-constant arguments are subject to the regular rules. -
functions declared with the
inline
keyword will be inlined if possible -
functions declared with the
noinline
keyword will never be inlined -
the remaining functions may be inlined only if the
-finline
command-line option is enabled and the compiler decides the function is worth doing
Automatic subroutine extraction
Subroutine extraction is the opposite of inlining.
When given the -fsubroutine-extraction
, the compiler will attempt to extract common code fragments to new subroutines.
The code will get smaller and slower.
Generally, when using -fsubroutine-extraction
, it's recommended to also use -finline
.
This allows the compiler to first inline and optimize code and then extract it back when appropriate.