* 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,noinlineorextern -
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
asmmacros, 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-
asmmacros, 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) -
refparameters exceptionally can have their type declared asvoid; such parameters accept variables of any type -
callparameters 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
constkeyword called with constant arguments will always be inlined, with the whole invocation being converted into a single constant, regardless ofinlineandnoinlinekeywords; calls with non-constant arguments are subject to the regular rules. -
functions declared with the
inlinekeyword will be inlined if possible -
functions declared with the
noinlinekeyword will never be inlined -
the remaining functions may be inlined only if the
-finlinecommand-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.