Optimization hints

This commit is contained in:
Karol Stasiak 2021-03-15 00:44:14 +01:00
parent 2468d8cca5
commit 1e4a193741
22 changed files with 171 additions and 34 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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,

86
docs/lang/hints.md Normal file
View 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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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

View File

@ -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
} }

View File

@ -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

View File

@ -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(

View File

@ -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")

View File

@ -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
) )
} }

View File

@ -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

View File

@ -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)
} }

View File

@ -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}

View File

@ -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
} }

View File

@ -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)

View File

@ -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