mirror of https://github.com/KarolS/millfork.git
Optimization hints
This commit is contained in:
parent
2468d8cca5
commit
1e4a193741
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
## Current version
|
## Current version
|
||||||
|
|
||||||
|
* Added optimization hints.
|
||||||
|
|
||||||
* Added `utf32be`, `utf32le`, `cp1253`, `cp1254`, `cp1257`, `geos_de` encodings.
|
* Added `utf32be`, `utf32le`, `cp1253`, `cp1254`, `cp1257`, `geos_de` encodings.
|
||||||
|
|
||||||
* Fixed escape sequences in many encodings.
|
* Fixed escape sequences in many encodings.
|
||||||
|
|
|
@ -206,6 +206,10 @@ The compiler doesn't support accessing the stack variables via the S stack point
|
||||||
|
|
||||||
* `-O9` – Optimize code using superoptimizer (experimental). Computationally very expensive, decent results.
|
* `-O9` – Optimize code using superoptimizer (experimental). Computationally very expensive, decent results.
|
||||||
|
|
||||||
|
* `-fhints`, `-fno-hints` –
|
||||||
|
Whether optimization hints should be used.
|
||||||
|
Default: yes.
|
||||||
|
|
||||||
* `-finline`, `-fno-inline` – Whether should inline functions automatically.
|
* `-finline`, `-fno-inline` – Whether should inline functions automatically.
|
||||||
See the [documentation about inlining](../abi/inlining.md). Computationally easy, can give decent gains.
|
See the [documentation about inlining](../abi/inlining.md). Computationally easy, can give decent gains.
|
||||||
`.ini` equivalent: `inline`.
|
`.ini` equivalent: `inline`.
|
||||||
|
@ -312,3 +316,7 @@ You can also enable or disable warnings individually:
|
||||||
* `-Wuseless`, `-Wno-useless` –
|
* `-Wuseless`, `-Wno-useless` –
|
||||||
Whether should warn about code that does nothing.
|
Whether should warn about code that does nothing.
|
||||||
Default: enabled.
|
Default: enabled.
|
||||||
|
|
||||||
|
* `-Whints`, `-Wno-hints` –
|
||||||
|
Whether should warn about unsupported optimization hints.
|
||||||
|
Default: enabled.
|
||||||
|
|
|
@ -44,6 +44,8 @@
|
||||||
|
|
||||||
* [Important guidelines regarding reentrancy](lang/reentrancy.md)
|
* [Important guidelines regarding reentrancy](lang/reentrancy.md)
|
||||||
|
|
||||||
|
* [Optimization hints](lang/hints.md)
|
||||||
|
|
||||||
* [List of keywords](lang/keywords.md)
|
* [List of keywords](lang/keywords.md)
|
||||||
|
|
||||||
## Library reference
|
## Library reference
|
||||||
|
|
|
@ -4,17 +4,17 @@
|
||||||
|
|
||||||
Syntax:
|
Syntax:
|
||||||
|
|
||||||
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [@ <address>] { <body> }`
|
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [<optimization hints>] [@ <address>] { <body> }`
|
||||||
|
|
||||||
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [@ <address>] = <expression>`
|
`[segment (<segment>)] [<modifiers>] <return_type> <name> ( <params> ) [align ( <alignment> )] [<optimization hints>] [@ <address>] = <expression>`
|
||||||
|
|
||||||
`[segment (<segment>)] asm <return_type> <name> ( <params> ) @ <address> extern`
|
`[segment (<segment>)] asm <return_type> <name> ( <params> ) [<optimization hints>] @ <address> extern`
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
void do_nothing() { }
|
void do_nothing() { }
|
||||||
inline byte two() = 2
|
inline byte two() = 2
|
||||||
asm void chkout(byte register(a) char) @ $FFD2 extern
|
asm void chkout(byte register(a) char) !preserves_x !preserves_y @ $FFD2 extern
|
||||||
segment(prgrom0) void main_loop(word w, byte x) align(fast) { // body omitted
|
segment(prgrom0) void main_loop(word w, byte x) align(fast) { // body omitted
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,6 +68,8 @@ For assembly functions, certain parameter names are interpreted as CPU registers
|
||||||
* on 6502, it means that the function will not cross a page boundary if possible
|
* on 6502, it means that the function will not cross a page boundary if possible
|
||||||
* on Z80, it is ignored
|
* on Z80, it is ignored
|
||||||
|
|
||||||
|
* `<optimization hints>` is a list of [optimization hints](./hints.md), separated by spaces
|
||||||
|
|
||||||
* `<address>` is a constant expression that defines where in the memory the function is or will be located.
|
* `<address>` is a constant expression that defines where in the memory the function is or will be located.
|
||||||
|
|
||||||
* `extern` is a keyword than marks functions that are not defined in the current program,
|
* `extern` is a keyword than marks functions that are not defined in the current program,
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
[< back to index](../doc_index.md)
|
||||||
|
|
||||||
|
# Optimization hints
|
||||||
|
|
||||||
|
Optimization hints are optional annotations for functions and variables
|
||||||
|
that allow the compiler to make extra assumptions that help with code optimization.
|
||||||
|
|
||||||
|
The general idea is that removing or disabling optimization hints will not break the code,
|
||||||
|
but adding invalid optimization hints may break the code.
|
||||||
|
|
||||||
|
Every optimization hint's name starts with an exclamation mark.
|
||||||
|
|
||||||
|
Optimization hints marked with **(X)** currently do nothing and are planned to be implemented in the future.
|
||||||
|
|
||||||
|
## Hints for functions
|
||||||
|
|
||||||
|
* `!preserves_memory` – the function does not write to memory
|
||||||
|
|
||||||
|
* `!idempotent` – calling the function multiple times in succession has no effect
|
||||||
|
|
||||||
|
* `!hot` – the function is hot and should be optimized for speed at the cost of increased size
|
||||||
|
|
||||||
|
* `!cold` **(X)** – the function is cold and should be optimized for size at the cost of increased run time
|
||||||
|
|
||||||
|
* `!odd` – the function returns an odd value
|
||||||
|
|
||||||
|
* `!even` – the function returns an even value
|
||||||
|
|
||||||
|
## Hints for 6502 assembly functions
|
||||||
|
|
||||||
|
These hints have only effect when used on an assembly function on a 6502 target:
|
||||||
|
|
||||||
|
* `!preserves_a` – the function preserves the contents of the A register
|
||||||
|
|
||||||
|
* `!preserves_x` – the function preserves the contents of the X register
|
||||||
|
|
||||||
|
* `!preserves_y` – the function preserves the contents of the Y register
|
||||||
|
|
||||||
|
* `!preserves_c` – the function preserves the contents of the carry flag
|
||||||
|
|
||||||
|
## Hints for 8080/Z80/LR35902 assembly functions
|
||||||
|
|
||||||
|
These hints have only effect when used on an assembly function on a 8080-like target:
|
||||||
|
|
||||||
|
* `!preserves_a` – the function preserves the contents of the A register
|
||||||
|
|
||||||
|
* `!preserves_bc` – the function preserves the contents of the B and C registers
|
||||||
|
|
||||||
|
* `!preserves_de` – the function preserves the contents of the D and E registers
|
||||||
|
|
||||||
|
* `!preserves_hl` – the function preserves the contents of the H and L registers
|
||||||
|
|
||||||
|
* `!preserves_hl` – the function preserves the contents of the H and L registers
|
||||||
|
|
||||||
|
* `!preserves_cf` – the function preserves the contents of the carry flag
|
||||||
|
|
||||||
|
## Hints for 6809 assembly functions
|
||||||
|
|
||||||
|
These hints have only effect when used on an assembly function on a 6809 target:
|
||||||
|
|
||||||
|
* `!preserves_a` – the function preserves the contents of the A register
|
||||||
|
|
||||||
|
* `!preserves_b` – the function preserves the contents of the B register
|
||||||
|
|
||||||
|
* `!preserves_d` – the function preserves the contents of the A and B register
|
||||||
|
|
||||||
|
* `!preserves_dp` **(X)** – the function preserves the contents of the DP register; exceptionally this can also be used on non-assembly functions
|
||||||
|
|
||||||
|
* `!preserves_x` – the function preserves the contents of the X register
|
||||||
|
|
||||||
|
* `!preserves_y` – the function preserves the contents of the Y register
|
||||||
|
|
||||||
|
* `!preserves_u` – the function preserves the contents of the U register
|
||||||
|
|
||||||
|
* `!preserves_c` – the function preserves the contents of the carry flag
|
||||||
|
|
||||||
|
## Hints for variables
|
||||||
|
|
||||||
|
* `!odd` **(X)** – the variable can only contain odd values
|
||||||
|
|
||||||
|
* `!even` **(X)** – the variable can only contain even values
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ or a top level of a function (*local* variables).
|
||||||
|
|
||||||
Syntax:
|
Syntax:
|
||||||
|
|
||||||
`[segment(<segment>)] [volatile] [<storage>] <type> <name> [@<address>] [= <initial_value>]`
|
`[segment(<segment>)] [volatile] [<storage>] <type> <name> [<optimization hints>] [@<address>] [= <initial_value>]`
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
|
@ -69,6 +69,8 @@ Volatile variables cannot be declared as `register` or `stack`.
|
||||||
* `<storage>` can be only specified for local variables. It can be either `stack`, `static`, `register` or nothing.
|
* `<storage>` can be only specified for local variables. It can be either `stack`, `static`, `register` or nothing.
|
||||||
`register` is only a hint for the optimizer.
|
`register` is only a hint for the optimizer.
|
||||||
See [the description of variable storage](../abi/variable-storage.md).
|
See [the description of variable storage](../abi/variable-storage.md).
|
||||||
|
|
||||||
|
* `<optimization hints>` is a list of [optimization hints](./hints.md), separated by spaces
|
||||||
|
|
||||||
* `<address>` is a constant expression that defines where in the memory the variable will be located.
|
* `<address>` is a constant expression that defines where in the memory the variable will be located.
|
||||||
If not specified, it will be located according to the usual allocation rules.
|
If not specified, it will be located according to the usual allocation rules.
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||||
// Input: A = Byte to write.
|
// Input: A = Byte to write.
|
||||||
asm void chrout(byte register(a) char) @$FFD2 extern
|
asm void chrout(byte register(a) char) @$FFD2 !preserves_a !preserves_x !preserves_y extern
|
||||||
|
|
||||||
asm void putchar(byte register(a) char) {
|
asm void putchar(byte register(a) char) {
|
||||||
JSR chrout
|
JSR chrout
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||||
// Input: A = Byte to write.
|
// Input: A = Byte to write.
|
||||||
asm void chrout(byte register(a) char) @$FFD2 extern
|
asm void chrout(byte register(a) char) @$FFD2 !preserves_a !preserves_x !preserves_y extern
|
||||||
|
|
||||||
asm void putchar(byte register(a) char) {
|
asm void putchar(byte register(a) char) {
|
||||||
JSR chrout
|
JSR chrout
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||||
// Input: A = Byte to write.
|
// Input: A = Byte to write.
|
||||||
asm void chrout(byte register(a) char) @$FFD2 extern
|
asm void chrout(byte register(a) char) @$FFD2 !preserves_a !preserves_x !preserves_y extern
|
||||||
|
|
||||||
// CHRIN. Read byte from default input (for keyboard, read a line from the screen). (If not keyboard, must call OPEN and CHKIN beforehands.)
|
// CHRIN. Read byte from default input (for keyboard, read a line from the screen). (If not keyboard, must call OPEN and CHKIN beforehands.)
|
||||||
// Output: A = Byte read.
|
// Output: A = Byte read.
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||||
// Input: A = Byte to write.
|
// Input: A = Byte to write.
|
||||||
asm void putchar(byte register(a) char) @$FFD2 extern
|
asm void putchar(byte register(a) char) @$FFD2 !preserves_a !preserves_x !preserves_y extern
|
||||||
|
|
||||||
inline void new_line() {
|
inline void new_line() {
|
||||||
putchar(13)
|
putchar(13)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||||
// Input: A = Byte to write.
|
// Input: A = Byte to write.
|
||||||
asm void chrout(byte register(a) char) @$FFD2 extern
|
asm void chrout(byte register(a) char) @$FFD2 !preserves_a !preserves_x !preserves_y extern
|
||||||
|
|
||||||
asm void putchar(byte register(a) char) {
|
asm void putchar(byte register(a) char) {
|
||||||
JSR chrout
|
JSR chrout
|
||||||
|
|
|
@ -5,16 +5,16 @@
|
||||||
|
|
||||||
#pragma zilog_syntax
|
#pragma zilog_syntax
|
||||||
|
|
||||||
inline asm void putchar(byte register(a) char) {
|
inline asm void putchar(byte register(a) char) !preserves_bc !preserves_de !preserves_hl {
|
||||||
rst $10
|
rst $10
|
||||||
? ret
|
? ret
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void new_line() {
|
inline void new_line() !preserves_bc !preserves_de !preserves_hl {
|
||||||
putchar(13)
|
putchar(13)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline asm void set_border(byte register(a) colour) {
|
inline asm void set_border(byte register(a) colour) !preserves_bc !preserves_de !preserves_hl {
|
||||||
out (254),a
|
out (254),a
|
||||||
? ret
|
? ret
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ nav:
|
||||||
- Inline 8080/LR35902/Z80 assembly: lang/assemblyz80.md
|
- Inline 8080/LR35902/Z80 assembly: lang/assemblyz80.md
|
||||||
- Inline 6809 assembly: lang/assembly6809.md
|
- Inline 6809 assembly: lang/assembly6809.md
|
||||||
- Reentrancy guidelines: lang/reentrancy.md
|
- Reentrancy guidelines: lang/reentrancy.md
|
||||||
|
- Optimization hints: lang/hints.md
|
||||||
- List of keywords: lang/keywords.md
|
- List of keywords: lang/keywords.md
|
||||||
- Library reference:
|
- Library reference:
|
||||||
- stdlib module: stdlib/stdlib.md
|
- stdlib module: stdlib/stdlib.md
|
||||||
|
|
|
@ -419,6 +419,7 @@ object Cpu extends Enumeration {
|
||||||
RegisterVariables,
|
RegisterVariables,
|
||||||
FunctionDeduplication,
|
FunctionDeduplication,
|
||||||
EnableBreakpoints,
|
EnableBreakpoints,
|
||||||
|
UseOptimizationHints,
|
||||||
GenericWarnings,
|
GenericWarnings,
|
||||||
UselessCodeWarning,
|
UselessCodeWarning,
|
||||||
BuggyCodeWarning,
|
BuggyCodeWarning,
|
||||||
|
@ -427,6 +428,7 @@ object Cpu extends Enumeration {
|
||||||
NonZeroTerminatedLiteralWarning,
|
NonZeroTerminatedLiteralWarning,
|
||||||
CallToOverlappingBankWarning,
|
CallToOverlappingBankWarning,
|
||||||
DataMissingInOutputWarning,
|
DataMissingInOutputWarning,
|
||||||
|
UnsupportedOptimizationHintWarning,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val mosAlwaysDefaultFlags = alwaysDefaultFlags
|
private val mosAlwaysDefaultFlags = alwaysDefaultFlags
|
||||||
|
@ -591,6 +593,7 @@ object CompilationFlag extends Enumeration {
|
||||||
NonZeroTerminatedLiteralWarning,
|
NonZeroTerminatedLiteralWarning,
|
||||||
CallToOverlappingBankWarning,
|
CallToOverlappingBankWarning,
|
||||||
DataMissingInOutputWarning,
|
DataMissingInOutputWarning,
|
||||||
|
UnsupportedOptimizationHintWarning,
|
||||||
FatalWarnings,
|
FatalWarnings,
|
||||||
// special options for internal compiler use
|
// special options for internal compiler use
|
||||||
EnableInternalTestSyntax,
|
EnableInternalTestSyntax,
|
||||||
|
@ -606,6 +609,7 @@ object CompilationFlag extends Enumeration {
|
||||||
NonZeroTerminatedLiteralWarning,
|
NonZeroTerminatedLiteralWarning,
|
||||||
CallToOverlappingBankWarning,
|
CallToOverlappingBankWarning,
|
||||||
DataMissingInOutputWarning,
|
DataMissingInOutputWarning,
|
||||||
|
UnsupportedOptimizationHintWarning,
|
||||||
)
|
)
|
||||||
|
|
||||||
val fromString: Map[String, CompilationFlag.Value] = Map(
|
val fromString: Map[String, CompilationFlag.Value] = Map(
|
||||||
|
|
|
@ -712,6 +712,9 @@ object Main {
|
||||||
}.description("Optimize code even more.")
|
}.description("Optimize code even more.")
|
||||||
if (i == 1 || i > 4) f.hidden()
|
if (i == 1 || i > 4) f.hidden()
|
||||||
}
|
}
|
||||||
|
boolean("-fhints", "-fnohints").action{ (c,v) =>
|
||||||
|
c.changeFlag(CompilationFlag.UseOptimizationHints, v)
|
||||||
|
}.description("Whether optimization hints should be used.")
|
||||||
flag("--inline").repeatable().action { c =>
|
flag("--inline").repeatable().action { c =>
|
||||||
c.changeFlag(CompilationFlag.InlineFunctions, true)
|
c.changeFlag(CompilationFlag.InlineFunctions, true)
|
||||||
}.description("Inline functions automatically.").hidden()
|
}.description("Inline functions automatically.").hidden()
|
||||||
|
@ -815,6 +818,10 @@ object Main {
|
||||||
c.changeFlag(CompilationFlag.UselessCodeWarning, v)
|
c.changeFlag(CompilationFlag.UselessCodeWarning, v)
|
||||||
}.description("Whether should warn about code that does nothing. Default: enabled.")
|
}.description("Whether should warn about code that does nothing. Default: enabled.")
|
||||||
|
|
||||||
|
boolean("-Whints", "-Wno-hints").repeatable().action { (c, v) =>
|
||||||
|
c.changeFlag(CompilationFlag.UnsupportedOptimizationHintWarning, v)
|
||||||
|
}.description("Whether should warn about unsupported optimization hints. Default: enabled.")
|
||||||
|
|
||||||
fluff("", "Other options:", "")
|
fluff("", "Other options:", "")
|
||||||
|
|
||||||
expansion("-Xd")("-O1", "-s", "-fsource-in-asm", "-g").description("Do a debug build. Equivalent to -O1 -s -fsource-in-asm -g")
|
expansion("-Xd")("-O1", "-s", "-fsource-in-asm", "-g").description("Do a debug build. Equivalent to -O1 -s -fsource-in-asm -g")
|
||||||
|
|
|
@ -4,7 +4,7 @@ import millfork.assembly.OptimizationContext
|
||||||
import millfork.assembly.opt.{AnyStatus, FlowCache, SingleStatus, Status}
|
import millfork.assembly.opt.{AnyStatus, FlowCache, SingleStatus, Status}
|
||||||
import millfork.assembly.z80._
|
import millfork.assembly.z80._
|
||||||
import millfork.env._
|
import millfork.env._
|
||||||
import millfork.node.Z80NiceFunctionProperty.{DoesntChangeBC, DoesntChangeDE, DoesntChangeHL, SetsATo}
|
import millfork.node.Z80NiceFunctionProperty.{DoesntChangeA, DoesntChangeBC, DoesntChangeCF, DoesntChangeDE, DoesntChangeHL, SetsATo}
|
||||||
import millfork.node.{NiceFunctionProperty, ZRegister}
|
import millfork.node.{NiceFunctionProperty, ZRegister}
|
||||||
import millfork.CompilationFlag
|
import millfork.CompilationFlag
|
||||||
|
|
||||||
|
@ -93,10 +93,11 @@ object CoarseFlowAnalyzer {
|
||||||
h = if (preservesH(n) || niceFunctionProperties(DoesntChangeHL -> n)) currentStatus.h else result.h,
|
h = if (preservesH(n) || niceFunctionProperties(DoesntChangeHL -> n)) currentStatus.h else result.h,
|
||||||
l = if (preservesL(n) || niceFunctionProperties(DoesntChangeHL -> n)) currentStatus.l else result.l,
|
l = if (preservesL(n) || niceFunctionProperties(DoesntChangeHL -> n)) currentStatus.l else result.l,
|
||||||
hl = if (preservesH(n) && preservesL(n) || niceFunctionProperties(DoesntChangeHL -> n)) currentStatus.hl else result.hl,
|
hl = if (preservesH(n) && preservesL(n) || niceFunctionProperties(DoesntChangeHL -> n)) currentStatus.hl else result.hl,
|
||||||
a = extractNiceConstant(n){
|
a = if (niceFunctionProperties(DoesntChangeA -> n)) currentStatus.a else extractNiceConstant(n){
|
||||||
case SetsATo(a) => Some(a)
|
case SetsATo(a) => Some(a)
|
||||||
case _ => None
|
case _ => None
|
||||||
},
|
},
|
||||||
|
cf = if (niceFunctionProperties(DoesntChangeCF -> n)) currentStatus.cf else AnyStatus
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1807,13 +1807,18 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||||
|
|
||||||
def prepareFunctionOptimizationHints(options: CompilationOptions, stmt: FunctionDeclarationStatement): Set[String] = {
|
def prepareFunctionOptimizationHints(options: CompilationOptions, stmt: FunctionDeclarationStatement): Set[String] = {
|
||||||
if (!options.flag(CompilationFlag.UseOptimizationHints)) return Set.empty
|
if (!options.flag(CompilationFlag.UseOptimizationHints)) return Set.empty
|
||||||
|
def warn(msg: String): Unit = {
|
||||||
|
if (options.flag(CompilationFlag.UnsupportedOptimizationHintWarning)){
|
||||||
|
log.warn(msg, stmt.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
val filteredFlags = stmt.optimizationHints.flatMap{
|
val filteredFlags = stmt.optimizationHints.flatMap{
|
||||||
case f@("hot" | "cold" | "idempotent" | "preserves_memory" | "inline" | "odd" | "even") =>
|
case f@("hot" | "cold" | "idempotent" | "preserves_memory" | "inline" | "odd" | "even") =>
|
||||||
Seq(f)
|
Seq(f)
|
||||||
case f@("preserves_a" | "preserves_x" | "preserves_y" | "preserves_c")
|
case f@("preserves_a" | "preserves_x" | "preserves_y" | "preserves_c")
|
||||||
if options.platform.cpuFamily == CpuFamily.M6502 =>
|
if options.platform.cpuFamily == CpuFamily.M6502 =>
|
||||||
if (stmt.statements.isDefined && !stmt.assembly) {
|
if (stmt.statements.isDefined && !stmt.assembly) {
|
||||||
log.warn(s"Cannot use the $f optimization flags on non-assembly functions", stmt.position)
|
warn(s"Cannot use the $f optimization hint on non-assembly functions")
|
||||||
Nil
|
Nil
|
||||||
} else {
|
} else {
|
||||||
Seq(f)
|
Seq(f)
|
||||||
|
@ -1821,7 +1826,15 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||||
case f@("preserves_a" | "preserves_b" | "preserves_d" | "preserves_c" | "preserves_x" | "preserves_y" | "preserves_u")
|
case f@("preserves_a" | "preserves_b" | "preserves_d" | "preserves_c" | "preserves_x" | "preserves_y" | "preserves_u")
|
||||||
if options.platform.cpuFamily == CpuFamily.M6809 =>
|
if options.platform.cpuFamily == CpuFamily.M6809 =>
|
||||||
if (stmt.statements.isDefined && !stmt.assembly) {
|
if (stmt.statements.isDefined && !stmt.assembly) {
|
||||||
log.warn(s"Cannot use the $f optimization flags on non-assembly functions", stmt.position)
|
warn(s"Cannot use the $f optimization hints on non-assembly functions")
|
||||||
|
Nil
|
||||||
|
} else {
|
||||||
|
Seq(f)
|
||||||
|
}
|
||||||
|
case f@("preserves_a" | "preserves_bc" | "preserves_de" | "preserves_hl" | "preserves_cf")
|
||||||
|
if options.platform.cpuFamily == CpuFamily.I80 =>
|
||||||
|
if (stmt.statements.isDefined && !stmt.assembly) {
|
||||||
|
warn(s"Cannot use the $f optimization hints on non-assembly functions")
|
||||||
Nil
|
Nil
|
||||||
} else {
|
} else {
|
||||||
Seq(f)
|
Seq(f)
|
||||||
|
@ -1830,21 +1843,21 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||||
if options.platform.cpuFamily == CpuFamily.M6809 =>
|
if options.platform.cpuFamily == CpuFamily.M6809 =>
|
||||||
Seq(f)
|
Seq(f)
|
||||||
case f =>
|
case f =>
|
||||||
log.warn(s"Unsupported function optimization flag: $f", stmt.position)
|
warn(s"Unsupported function optimization hint: $f")
|
||||||
Nil
|
Nil
|
||||||
}
|
}
|
||||||
if (filteredFlags("hot") && filteredFlags("cold")) {
|
if (filteredFlags("hot") && filteredFlags("cold")) {
|
||||||
log.warn(s"Conflicting optimization flags used: `hot` and `cold`", stmt.position)
|
warn(s"Conflicting optimization hints used: `hot` and `cold`")
|
||||||
}
|
}
|
||||||
if (filteredFlags("even") && filteredFlags("odd")) {
|
if (filteredFlags("even") && filteredFlags("odd")) {
|
||||||
log.warn(s"Conflicting optimization flags used: `even` and `odd`", stmt.position)
|
warn(s"Conflicting optimization hints used: `even` and `odd`")
|
||||||
}
|
}
|
||||||
if (filteredFlags("even") || filteredFlags("odd")) {
|
if (filteredFlags("even") || filteredFlags("odd")) {
|
||||||
maybeGet[Type](stmt.resultType) match {
|
maybeGet[Type](stmt.resultType) match {
|
||||||
case Some(t) if t.size < 1 =>
|
case Some(t) if t.size < 1 =>
|
||||||
log.warn(s"Cannot use `even` or `odd` flags with an empty return type", stmt.position)
|
warn(s"Cannot use `even` or `odd` hints with an empty return type")
|
||||||
case Some(t: CompoundVariableType) =>
|
case Some(t: CompoundVariableType) =>
|
||||||
log.warn(s"Cannot use `even` or `odd` flags with a compound return type", stmt.position)
|
warn(s"Cannot use `even` or `odd` hints with a compound return type")
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1853,15 +1866,20 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||||
|
|
||||||
def prepareVariableOptimizationHints(options: CompilationOptions, stmt: VariableDeclarationStatement): Set[String] = {
|
def prepareVariableOptimizationHints(options: CompilationOptions, stmt: VariableDeclarationStatement): Set[String] = {
|
||||||
if (!options.flag(CompilationFlag.UseOptimizationHints)) return Set.empty
|
if (!options.flag(CompilationFlag.UseOptimizationHints)) return Set.empty
|
||||||
|
def warn(msg: String): Unit = {
|
||||||
|
if (options.flag(CompilationFlag.UnsupportedOptimizationHintWarning)){
|
||||||
|
log.warn(msg, stmt.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
val filteredFlags = stmt.optimizationHints.flatMap{
|
val filteredFlags = stmt.optimizationHints.flatMap{
|
||||||
case f@("odd" | "even") =>
|
case f@("odd" | "even") =>
|
||||||
Seq(f)
|
Seq(f)
|
||||||
case f =>
|
case f =>
|
||||||
log.warn(s"Unsupported variable optimization flag: $f", stmt.position)
|
warn(s"Unsupported variable optimization hint: $f")
|
||||||
Nil
|
Nil
|
||||||
}
|
}
|
||||||
if (filteredFlags("even") && filteredFlags("odd")) {
|
if (filteredFlags("even") && filteredFlags("odd")) {
|
||||||
log.warn(s"Conflicting optimization flags used: `even` and `odd`", stmt.position)
|
warn(s"Conflicting optimization hints used: `even` and `odd`")
|
||||||
}
|
}
|
||||||
filteredFlags
|
filteredFlags
|
||||||
}
|
}
|
||||||
|
@ -1871,7 +1889,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||||
if (!options.flag(CompilationFlag.UseOptimizationHints)) return Set.empty
|
if (!options.flag(CompilationFlag.UseOptimizationHints)) return Set.empty
|
||||||
val filteredFlags: Set[String] = stmt.optimizationHints.flatMap{
|
val filteredFlags: Set[String] = stmt.optimizationHints.flatMap{
|
||||||
case f =>
|
case f =>
|
||||||
log.warn(s"Unsupported array optimization flag: $f", stmt.position)
|
log.warn(s"Unsupported array optimization hint: $f", stmt.position)
|
||||||
Nil
|
Nil
|
||||||
}
|
}
|
||||||
filteredFlags
|
filteredFlags
|
||||||
|
|
|
@ -233,9 +233,11 @@ object MosNiceFunctionProperty {
|
||||||
}
|
}
|
||||||
|
|
||||||
object Z80NiceFunctionProperty {
|
object Z80NiceFunctionProperty {
|
||||||
|
case object DoesntChangeA extends NiceFunctionProperty("A")
|
||||||
case object DoesntChangeBC extends NiceFunctionProperty("BC")
|
case object DoesntChangeBC extends NiceFunctionProperty("BC")
|
||||||
case object DoesntChangeDE extends NiceFunctionProperty("DE")
|
case object DoesntChangeDE extends NiceFunctionProperty("DE")
|
||||||
case object DoesntChangeHL extends NiceFunctionProperty("HL")
|
case object DoesntChangeHL extends NiceFunctionProperty("HL")
|
||||||
|
case object DoesntChangeCF extends NiceFunctionProperty("CF")
|
||||||
case object DoesntChangeIY extends NiceFunctionProperty("IY")
|
case object DoesntChangeIY extends NiceFunctionProperty("IY")
|
||||||
case class SetsATo(value: Int) extends NiceFunctionProperty("A=" + value)
|
case class SetsATo(value: Int) extends NiceFunctionProperty("A=" + value)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package millfork.output
|
||||||
|
|
||||||
import millfork.assembly.SourceLine
|
import millfork.assembly.SourceLine
|
||||||
import millfork.assembly.m6809.opt.JumpFixing
|
import millfork.assembly.m6809.opt.JumpFixing
|
||||||
import millfork.{CompilationOptions, Platform}
|
import millfork.{CompilationFlag, CompilationOptions, Platform}
|
||||||
import millfork.assembly.m6809.{MOpcode, _}
|
import millfork.assembly.m6809.{MOpcode, _}
|
||||||
import millfork.compiler.m6809.M6809Compiler
|
import millfork.compiler.m6809.M6809Compiler
|
||||||
import millfork.env.{Environment, FunctionInMemory, Label, MemoryAddressConstant, NormalFunction, NumericConstant}
|
import millfork.env.{Environment, FunctionInMemory, Label, MemoryAddressConstant, NormalFunction, NumericConstant}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package millfork.output
|
package millfork.output
|
||||||
|
|
||||||
|
import millfork.CompilationFlag.EmitIllegals
|
||||||
import millfork.assembly.OptimizationContext
|
import millfork.assembly.OptimizationContext
|
||||||
import millfork.assembly.opt.{SingleStatus, Status}
|
import millfork.assembly.opt.{SingleStatus, Status}
|
||||||
import millfork.{CompilationFlag, CompilationOptions, Cpu, Platform}
|
import millfork.{CompilationFlag, CompilationOptions, Cpu, Platform}
|
||||||
|
@ -8,7 +9,7 @@ import millfork.assembly.z80.opt.{CoarseFlowAnalyzer, ConditionalInstructions, C
|
||||||
import millfork.compiler.z80.Z80Compiler
|
import millfork.compiler.z80.Z80Compiler
|
||||||
import millfork.env._
|
import millfork.env._
|
||||||
import millfork.node.NiceFunctionProperty.DoesntWriteMemory
|
import millfork.node.NiceFunctionProperty.DoesntWriteMemory
|
||||||
import millfork.node.Z80NiceFunctionProperty.{DoesntChangeBC, DoesntChangeDE, DoesntChangeHL, DoesntChangeIY, SetsATo}
|
import millfork.node.Z80NiceFunctionProperty.{DoesntChangeA, DoesntChangeBC, DoesntChangeCF, DoesntChangeDE, DoesntChangeHL, DoesntChangeIY, SetsATo}
|
||||||
import millfork.node.{NiceFunctionProperty, Position, Program, ZRegister}
|
import millfork.node.{NiceFunctionProperty, Position, Program, ZRegister}
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
@ -831,6 +832,7 @@ class Z80Assembler(program: Program,
|
||||||
niceFunctionProperties += (niceFunctionProperty -> functionName)
|
niceFunctionProperties += (niceFunctionProperty -> functionName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
genericPropertyScan(DoesntChangeA)(l => !l.changesRegister(ZRegister.A))
|
||||||
genericPropertyScan(DoesntChangeHL)(l => !l.changesRegister(ZRegister.HL))
|
genericPropertyScan(DoesntChangeHL)(l => !l.changesRegister(ZRegister.HL))
|
||||||
genericPropertyScan(DoesntChangeDE)(l => !l.changesRegister(ZRegister.DE))
|
genericPropertyScan(DoesntChangeDE)(l => !l.changesRegister(ZRegister.DE))
|
||||||
genericPropertyScan(DoesntChangeBC)(l => !l.changesRegister(ZRegister.BC))
|
genericPropertyScan(DoesntChangeBC)(l => !l.changesRegister(ZRegister.BC))
|
||||||
|
@ -841,6 +843,11 @@ class Z80Assembler(program: Program,
|
||||||
override def gatherFunctionOptimizationHints(options: CompilationOptions, niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], function: FunctionInMemory): Unit = {
|
override def gatherFunctionOptimizationHints(options: CompilationOptions, niceFunctionProperties: mutable.Set[(NiceFunctionProperty, String)], function: FunctionInMemory): Unit = {
|
||||||
import NiceFunctionProperty._
|
import NiceFunctionProperty._
|
||||||
val functionName = function.name
|
val functionName = function.name
|
||||||
|
if (function.optimizationHints("preserves_a")) niceFunctionProperties += DoesntChangeA -> functionName
|
||||||
|
if (function.optimizationHints("preserves_bc")) niceFunctionProperties += DoesntChangeBC -> functionName
|
||||||
|
if (function.optimizationHints("preserves_de")) niceFunctionProperties += DoesntChangeDE -> functionName
|
||||||
|
if (function.optimizationHints("preserves_hl")) niceFunctionProperties += DoesntChangeHL -> functionName
|
||||||
|
if (function.optimizationHints("preserves_cf")) niceFunctionProperties += DoesntChangeCF -> functionName
|
||||||
if (function.optimizationHints("preserves_memory")) niceFunctionProperties += DoesntWriteMemory -> functionName
|
if (function.optimizationHints("preserves_memory")) niceFunctionProperties += DoesntWriteMemory -> functionName
|
||||||
if (function.optimizationHints("idempotent")) niceFunctionProperties += Idempotent -> functionName
|
if (function.optimizationHints("idempotent")) niceFunctionProperties += Idempotent -> functionName
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,12 +210,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||||
map{case (name, params) => Seq(ImportStatement(name.mkString("/"), params.getOrElse(Nil).toList))}
|
map{case (name, params) => Seq(ImportStatement(name.mkString("/"), params.getOrElse(Nil).toList))}
|
||||||
|
|
||||||
val optimizationHintsDeclaration: P[Set[String]] =
|
val optimizationHintsDeclaration: P[Set[String]] =
|
||||||
if (options.flag(CompilationFlag.EnableInternalTestSyntax)) {
|
("!" ~/ HWS ~/ identifier ~/ "").rep(min = 0, sep = AWS).map { _.toSet }
|
||||||
("¥" ~/ HWS ~ "(" ~/ HWS ~/ identifier.rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ HWS ~ ")" ~/ "").?.map {
|
|
||||||
case None => Set()
|
|
||||||
case Some(list) => list.toSet
|
|
||||||
}
|
|
||||||
} else P("").map(_ => Set.empty)
|
|
||||||
|
|
||||||
val globalVariableDefinition: P[Seq[BankedDeclarationStatement]] = variableDefinition(true)
|
val globalVariableDefinition: P[Seq[BankedDeclarationStatement]] = variableDefinition(true)
|
||||||
val localVariableDefinition: P[Seq[DeclarationStatement]] = variableDefinition(false)
|
val localVariableDefinition: P[Seq[DeclarationStatement]] = variableDefinition(false)
|
||||||
|
|
|
@ -11,7 +11,7 @@ class OptimizationHintsSuite extends FunSuite with Matchers {
|
||||||
|
|
||||||
test("Optimization hints test 1") {
|
test("Optimization hints test 1") {
|
||||||
EmuBenchmarkRun("""
|
EmuBenchmarkRun("""
|
||||||
| asm void putchar(byte register(a) character) ¥( preserves_a, preserves_x, preserves_y ) @$ffd2 extern
|
| asm void putchar(byte register(a) character) !preserves_a !preserves_x !preserves_y @$ffd2 extern
|
||||||
| noinline bool should_print(byte a) = a == 5
|
| noinline bool should_print(byte a) = a == 5
|
||||||
| void main() {
|
| void main() {
|
||||||
| byte i
|
| byte i
|
||||||
|
|
Loading…
Reference in New Issue