mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-20 19:37:17 +00:00
Optimization hints
This commit is contained in:
parent
2468d8cca5
commit
1e4a193741
CHANGELOG.md
docs
include
mkdocs.ymlsrc
main/scala/millfork
test/scala/millfork/test
@ -2,6 +2,8 @@
|
||||
|
||||
## Current version
|
||||
|
||||
* Added optimization hints.
|
||||
|
||||
* Added `utf32be`, `utf32le`, `cp1253`, `cp1254`, `cp1257`, `geos_de` 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.
|
||||
|
||||
* `-fhints`, `-fno-hints` –
|
||||
Whether optimization hints should be used.
|
||||
Default: yes.
|
||||
|
||||
* `-finline`, `-fno-inline` – Whether should inline functions automatically.
|
||||
See the [documentation about inlining](../abi/inlining.md). Computationally easy, can give decent gains.
|
||||
`.ini` equivalent: `inline`.
|
||||
@ -312,3 +316,7 @@ You can also enable or disable warnings individually:
|
||||
* `-Wuseless`, `-Wno-useless` –
|
||||
Whether should warn about code that does nothing.
|
||||
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)
|
||||
|
||||
* [Optimization hints](lang/hints.md)
|
||||
|
||||
* [List of keywords](lang/keywords.md)
|
||||
|
||||
## Library reference
|
||||
|
@ -4,17 +4,17 @@
|
||||
|
||||
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:
|
||||
|
||||
void do_nothing() { }
|
||||
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
|
||||
|
||||
|
||||
@ -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 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.
|
||||
|
||||
* `extern` is a keyword than marks functions that are not defined in the current program,
|
||||
|
86
docs/lang/hints.md
Normal file
86
docs/lang/hints.md
Normal file
@ -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:
|
||||
|
||||
`[segment(<segment>)] [volatile] [<storage>] <type> <name> [@<address>] [= <initial_value>]`
|
||||
`[segment(<segment>)] [volatile] [<storage>] <type> <name> [<optimization hints>] [@<address>] [= <initial_value>]`
|
||||
|
||||
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.
|
||||
`register` is only a hint for the optimizer.
|
||||
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.
|
||||
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.)
|
||||
// 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) {
|
||||
JSR chrout
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||
// 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) {
|
||||
JSR chrout
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||
// 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.)
|
||||
// Output: A = Byte read.
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||
// 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() {
|
||||
putchar(13)
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
// CHROUT. Write byte to default output. (If not screen, must call OPEN and CHKOUT beforehands.)
|
||||
// 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) {
|
||||
JSR chrout
|
||||
|
@ -5,16 +5,16 @@
|
||||
|
||||
#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
|
||||
? ret
|
||||
}
|
||||
|
||||
inline void new_line() {
|
||||
inline void new_line() !preserves_bc !preserves_de !preserves_hl {
|
||||
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
|
||||
? ret
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ nav:
|
||||
- Inline 8080/LR35902/Z80 assembly: lang/assemblyz80.md
|
||||
- Inline 6809 assembly: lang/assembly6809.md
|
||||
- Reentrancy guidelines: lang/reentrancy.md
|
||||
- Optimization hints: lang/hints.md
|
||||
- List of keywords: lang/keywords.md
|
||||
- Library reference:
|
||||
- stdlib module: stdlib/stdlib.md
|
||||
|
@ -419,6 +419,7 @@ object Cpu extends Enumeration {
|
||||
RegisterVariables,
|
||||
FunctionDeduplication,
|
||||
EnableBreakpoints,
|
||||
UseOptimizationHints,
|
||||
GenericWarnings,
|
||||
UselessCodeWarning,
|
||||
BuggyCodeWarning,
|
||||
@ -427,6 +428,7 @@ object Cpu extends Enumeration {
|
||||
NonZeroTerminatedLiteralWarning,
|
||||
CallToOverlappingBankWarning,
|
||||
DataMissingInOutputWarning,
|
||||
UnsupportedOptimizationHintWarning,
|
||||
)
|
||||
|
||||
private val mosAlwaysDefaultFlags = alwaysDefaultFlags
|
||||
@ -591,6 +593,7 @@ object CompilationFlag extends Enumeration {
|
||||
NonZeroTerminatedLiteralWarning,
|
||||
CallToOverlappingBankWarning,
|
||||
DataMissingInOutputWarning,
|
||||
UnsupportedOptimizationHintWarning,
|
||||
FatalWarnings,
|
||||
// special options for internal compiler use
|
||||
EnableInternalTestSyntax,
|
||||
@ -606,6 +609,7 @@ object CompilationFlag extends Enumeration {
|
||||
NonZeroTerminatedLiteralWarning,
|
||||
CallToOverlappingBankWarning,
|
||||
DataMissingInOutputWarning,
|
||||
UnsupportedOptimizationHintWarning,
|
||||
)
|
||||
|
||||
val fromString: Map[String, CompilationFlag.Value] = Map(
|
||||
|
@ -712,6 +712,9 @@ object Main {
|
||||
}.description("Optimize code even more.")
|
||||
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 =>
|
||||
c.changeFlag(CompilationFlag.InlineFunctions, true)
|
||||
}.description("Inline functions automatically.").hidden()
|
||||
@ -815,6 +818,10 @@ object Main {
|
||||
c.changeFlag(CompilationFlag.UselessCodeWarning, v)
|
||||
}.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:", "")
|
||||
|
||||
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.z80._
|
||||
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.CompilationFlag
|
||||
|
||||
@ -93,10 +93,11 @@ object CoarseFlowAnalyzer {
|
||||
h = if (preservesH(n) || niceFunctionProperties(DoesntChangeHL -> n)) currentStatus.h else result.h,
|
||||
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,
|
||||
a = extractNiceConstant(n){
|
||||
a = if (niceFunctionProperties(DoesntChangeA -> n)) currentStatus.a else extractNiceConstant(n){
|
||||
case SetsATo(a) => Some(a)
|
||||
case _ => None
|
||||
},
|
||||
cf = if (niceFunctionProperties(DoesntChangeCF -> n)) currentStatus.cf else AnyStatus
|
||||
)
|
||||
}
|
||||
|
||||
|
38
src/main/scala/millfork/env/Environment.scala
vendored
38
src/main/scala/millfork/env/Environment.scala
vendored
@ -1807,13 +1807,18 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
|
||||
def prepareFunctionOptimizationHints(options: CompilationOptions, stmt: FunctionDeclarationStatement): Set[String] = {
|
||||
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{
|
||||
case f@("hot" | "cold" | "idempotent" | "preserves_memory" | "inline" | "odd" | "even") =>
|
||||
Seq(f)
|
||||
case f@("preserves_a" | "preserves_x" | "preserves_y" | "preserves_c")
|
||||
if options.platform.cpuFamily == CpuFamily.M6502 =>
|
||||
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
|
||||
} else {
|
||||
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")
|
||||
if options.platform.cpuFamily == CpuFamily.M6809 =>
|
||||
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
|
||||
} else {
|
||||
Seq(f)
|
||||
@ -1830,21 +1843,21 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
if options.platform.cpuFamily == CpuFamily.M6809 =>
|
||||
Seq(f)
|
||||
case f =>
|
||||
log.warn(s"Unsupported function optimization flag: $f", stmt.position)
|
||||
warn(s"Unsupported function optimization hint: $f")
|
||||
Nil
|
||||
}
|
||||
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")) {
|
||||
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")) {
|
||||
maybeGet[Type](stmt.resultType) match {
|
||||
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) =>
|
||||
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 _ =>
|
||||
}
|
||||
}
|
||||
@ -1853,15 +1866,20 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
|
||||
def prepareVariableOptimizationHints(options: CompilationOptions, stmt: VariableDeclarationStatement): Set[String] = {
|
||||
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{
|
||||
case f@("odd" | "even") =>
|
||||
Seq(f)
|
||||
case f =>
|
||||
log.warn(s"Unsupported variable optimization flag: $f", stmt.position)
|
||||
warn(s"Unsupported variable optimization hint: $f")
|
||||
Nil
|
||||
}
|
||||
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
|
||||
}
|
||||
@ -1871,7 +1889,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
if (!options.flag(CompilationFlag.UseOptimizationHints)) return Set.empty
|
||||
val filteredFlags: Set[String] = stmt.optimizationHints.flatMap{
|
||||
case f =>
|
||||
log.warn(s"Unsupported array optimization flag: $f", stmt.position)
|
||||
log.warn(s"Unsupported array optimization hint: $f", stmt.position)
|
||||
Nil
|
||||
}
|
||||
filteredFlags
|
||||
|
@ -233,9 +233,11 @@ object MosNiceFunctionProperty {
|
||||
}
|
||||
|
||||
object Z80NiceFunctionProperty {
|
||||
case object DoesntChangeA extends NiceFunctionProperty("A")
|
||||
case object DoesntChangeBC extends NiceFunctionProperty("BC")
|
||||
case object DoesntChangeDE extends NiceFunctionProperty("DE")
|
||||
case object DoesntChangeHL extends NiceFunctionProperty("HL")
|
||||
case object DoesntChangeCF extends NiceFunctionProperty("CF")
|
||||
case object DoesntChangeIY extends NiceFunctionProperty("IY")
|
||||
case class SetsATo(value: Int) extends NiceFunctionProperty("A=" + value)
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package millfork.output
|
||||
|
||||
import millfork.assembly.SourceLine
|
||||
import millfork.assembly.m6809.opt.JumpFixing
|
||||
import millfork.{CompilationOptions, Platform}
|
||||
import millfork.{CompilationFlag, CompilationOptions, Platform}
|
||||
import millfork.assembly.m6809.{MOpcode, _}
|
||||
import millfork.compiler.m6809.M6809Compiler
|
||||
import millfork.env.{Environment, FunctionInMemory, Label, MemoryAddressConstant, NormalFunction, NumericConstant}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package millfork.output
|
||||
|
||||
import millfork.CompilationFlag.EmitIllegals
|
||||
import millfork.assembly.OptimizationContext
|
||||
import millfork.assembly.opt.{SingleStatus, Status}
|
||||
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.env._
|
||||
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 scala.annotation.tailrec
|
||||
@ -831,6 +832,7 @@ class Z80Assembler(program: Program,
|
||||
niceFunctionProperties += (niceFunctionProperty -> functionName)
|
||||
}
|
||||
}
|
||||
genericPropertyScan(DoesntChangeA)(l => !l.changesRegister(ZRegister.A))
|
||||
genericPropertyScan(DoesntChangeHL)(l => !l.changesRegister(ZRegister.HL))
|
||||
genericPropertyScan(DoesntChangeDE)(l => !l.changesRegister(ZRegister.DE))
|
||||
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 = {
|
||||
import NiceFunctionProperty._
|
||||
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("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))}
|
||||
|
||||
val optimizationHintsDeclaration: P[Set[String]] =
|
||||
if (options.flag(CompilationFlag.EnableInternalTestSyntax)) {
|
||||
("¥" ~/ HWS ~ "(" ~/ HWS ~/ identifier.rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ HWS ~ ")" ~/ "").?.map {
|
||||
case None => Set()
|
||||
case Some(list) => list.toSet
|
||||
}
|
||||
} else P("").map(_ => Set.empty)
|
||||
("!" ~/ HWS ~/ identifier ~/ "").rep(min = 0, sep = AWS).map { _.toSet }
|
||||
|
||||
val globalVariableDefinition: P[Seq[BankedDeclarationStatement]] = variableDefinition(true)
|
||||
val localVariableDefinition: P[Seq[DeclarationStatement]] = variableDefinition(false)
|
||||
|
@ -11,7 +11,7 @@ class OptimizationHintsSuite extends FunSuite with Matchers {
|
||||
|
||||
test("Optimization hints test 1") {
|
||||
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
|
||||
| void main() {
|
||||
| byte i
|
||||
|
Loading…
x
Reference in New Issue
Block a user