4.2 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
-
cannot contain variable or array declarations
-
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.