1
0
mirror of https://github.com/KarolS/millfork.git synced 2025-01-26 20:33:02 +00:00

8080: Exchange BC/DE registers pairs

This commit is contained in:
Karol Stasiak 2018-12-28 11:25:12 +01:00
parent e66e938469
commit 1c55304bad
3 changed files with 227 additions and 3 deletions

View File

@ -0,0 +1,222 @@
package millfork.assembly.z80.opt
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.z80._
import millfork.env.{AssemblyParamSignature, NormalFunction, NormalParamSignature}
import millfork.error.Logger
import millfork.node.ZRegister
/**
* @author Karol Stasiak
*/
object ChangeRegisterPairPreferringDE extends ChangeRegisterPair(true)
object ChangeRegisterPairPreferringBC extends ChangeRegisterPair(false)
class ChangeRegisterPair(preferBC2DE: Boolean) extends AssemblyOptimization[ZLine] {
import millfork.node.ZRegister._
case class Loaded(b: Boolean = false, c: Boolean = false, d: Boolean = false, e: Boolean = false) {
def load(register: ZRegister.Value): Loaded = register match {
case B => copy(b = true, d = false, e = false)
case C => copy(c = true, d = false, e = false)
case BC => Loaded(b = true, c = true)
case D => copy(d = true, b = false, c = false)
case E => copy(e = true, b = false, c = false)
case DE => Loaded(d = true, e = true)
case _ => this
}
def hasDE: Boolean = d && e
def hasBC: Boolean = b && c
}
object PairDirection extends Enumeration {
val BC2DE, DE2BC = Value
}
import PairDirection._
import millfork.assembly.z80.ZOpcode._
type PairDirection = PairDirection.Value
override def name = "Changing registers pairs"
override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = {
if (f.params.isInstanceOf[AssemblyParamSignature]) return code
val usesBC = code.exists(l => (l.readsRegister(BC) || l.changesRegister(BC)) && l.opcode != CALL)
val usesDE = code.exists(l => (l.readsRegister(DE) || l.changesRegister(DE)) && l.opcode != CALL)
val canDE2BC = f.returnType.size < 3 && canOptimize(code, DE2BC, Loaded()) && (f.params match {
case NormalParamSignature(List(p)) => p.typ.size < 3
case _ => true
})
val canBC2DE = canOptimize(code, BC2DE, Loaded())
implicit val log: Logger = optimizationContext.log
(canDE2BC, canBC2DE) match {
case (false, false) => code
case (true, false) =>
if (!usesDE) code else {
log.debug("Changing register pair from DE to BC")
val changed = switchDE2BC(code)
traceDiff(code, changed)
changed
}
case (false, true) =>
if (!usesBC) code else {
log.debug("Changing register pair from BC to DE")
val changed = switchBC2DE(code)
traceDiff(code, changed)
changed
}
case (true, true) =>
if (preferBC2DE) {
if (!usesBC) code else {
log.debug("Changing register pair from BC to DE (arbitrarily)")
val changed = switchBC2DE(code)
traceDiff(code, changed)
changed
}
} else {
if (!usesDE) code else {
log.debug("Changing register pair from DE to BC (arbitrarily)")
val changed = switchDE2BC(code)
traceDiff(code, changed)
changed
}
}
}
}
private def traceDiff(original: List[ZLine], changed: List[ZLine])(implicit log: Logger): Unit = {
if (log.traceEnabled) {
original.zip(changed).foreach {
case (o, n) =>
if (o.registers == n.registers && o.opcode == n.opcode) log.trace(n.toString)
else log.trace(f"$o%-30s → $n%s")
}
}
}
private def canOptimize(code: List[ZLine], dir: PairDirection, loaded: Loaded): Boolean = code match {
case ZLine0(CALL, _, _) :: xs => false // TODO
case ZLine0(LD_16, TwoRegisters(r, IMM_16), _) :: xs => canOptimize(xs, dir, loaded = loaded.load(r))
case ZLine0(LD,
TwoRegisters(B, C) |
TwoRegisters(B, E) |
TwoRegisters(C, B) |
TwoRegisters(C, D) |
TwoRegisters(D, E) |
TwoRegisters(D, C) |
TwoRegisters(E, D) |
TwoRegisters(E, B), _) :: xs => false
case ZLine0(LD, TwoRegisters(r@(B|C|D|E), _), _) :: xs => canOptimize(xs, dir, loaded = loaded.load(r))
case ZLine0(LD, TwoRegistersOffset(r@(B|C|D|E), _, _), _) :: xs => canOptimize(xs, dir, loaded = loaded.load(r))
case ZLine0(LABEL | CALL | JR | JP, _, _) :: xs => canOptimize(xs, dir, loaded = Loaded())
case ZLine0(EX_DE_HL, _, _) :: xs => if (loaded.hasDE && dir == BC2DE) canOptimize(xs, dir, loaded) else false
case ZLine0(DJNZ, _, _) :: xs => if (loaded.b && dir == DE2BC) canOptimize(xs, dir, loaded) else false
case x :: xs if !loaded.b && (x.readsRegister(B) || x.changesRegister(B)) => false
case x :: xs if !loaded.c && (x.readsRegister(C) || x.changesRegister(C)) => false
case x :: xs if !loaded.d && (x.readsRegister(D) || x.changesRegister(D)) => false
case x :: xs if !loaded.e && (x.readsRegister(E) || x.changesRegister(E)) => false
case x :: xs => canOptimize(xs, dir, loaded)
case _ => true
}
private def switchBC2DE(code: List[ZLine]): List[ZLine] = {
code match {
case (x@ZLine0(_, OneRegister(B), _)) :: xs =>
x.copy(registers = OneRegister(D)) :: switchBC2DE(xs)
case (x@ZLine0(_, OneRegister(C), _)) :: xs =>
x.copy(registers = OneRegister(E)) :: switchBC2DE(xs)
case (x@ZLine0(_, OneRegister(BC), _)) :: xs =>
x.copy(registers = OneRegister(DE)) :: switchBC2DE(xs)
case (x@ZLine0(LD, TwoRegisters(B, D), _)) :: xs =>
switchBC2DE(xs)
case (x@ZLine0(LD, TwoRegisters(C, E), _)) :: xs =>
switchBC2DE(xs)
case (x@ZLine0(LD, TwoRegisters(D, B), _)) :: xs =>
switchBC2DE(xs)
case (x@ZLine0(LD, TwoRegisters(E, C), _)) :: xs =>
switchBC2DE(xs)
case (x@ZLine0(_, TwoRegisters(BC, r), _)) :: xs =>
x.copy(registers = TwoRegisters(DE, r)) :: switchBC2DE(xs)
case (x@ZLine0(_, TwoRegisters(r, BC), _)) :: xs =>
x.copy(registers = TwoRegisters(r, DE)) :: switchBC2DE(xs)
case (x@ZLine0(_, TwoRegisters(B, r), _)) :: xs =>
x.copy(registers = TwoRegisters(D, r)) :: switchBC2DE(xs)
case (x@ZLine0(_, TwoRegisters(r, B), _)) :: xs =>
x.copy(registers = TwoRegisters(r, D)) :: switchBC2DE(xs)
case (x@ZLine0(_, TwoRegisters(C, r), _)) :: xs =>
x.copy(registers = TwoRegisters(E, r)) :: switchBC2DE(xs)
case (x@ZLine0(_, TwoRegisters(r, C), _)) :: xs =>
x.copy(registers = TwoRegisters(r, E)) :: switchBC2DE(xs)
case (x@ZLine0(_, TwoRegistersOffset(B, r, o), _)) :: xs =>
x.copy(registers = TwoRegistersOffset(D, r, o)) :: switchBC2DE(xs)
case (x@ZLine0(_, TwoRegistersOffset(r, B, o), _)) :: xs =>
x.copy(registers = TwoRegistersOffset(r, D, o)) :: switchBC2DE(xs)
case (x@ZLine0(_, TwoRegistersOffset(C, r, o), _)) :: xs =>
x.copy(registers = TwoRegistersOffset(E, r, o)) :: switchBC2DE(xs)
case (x@ZLine0(_, TwoRegistersOffset(r, C, o), _)) :: xs =>
x.copy(registers = TwoRegistersOffset(r, E, o)) :: switchBC2DE(xs)
case x :: xs => x :: switchBC2DE(xs)
case Nil => Nil
}
}
private def switchDE2BC(code: List[ZLine]): List[ZLine] = {
code match {
case (x@ZLine0(_, OneRegister(D), _)) :: xs =>
x.copy(registers = OneRegister(B)) :: switchDE2BC(xs)
case (x@ZLine0(_, OneRegister(E), _)) :: xs =>
x.copy(registers = OneRegister(C)) :: switchDE2BC(xs)
case (x@ZLine0(_, OneRegister(BC), _)) :: xs =>
x.copy(registers = OneRegister(DE)) :: switchDE2BC(xs)
case (x@ZLine0(LD, TwoRegisters(B, D), _)) :: xs =>
switchBC2DE(xs)
case (x@ZLine0(LD, TwoRegisters(C, E), _)) :: xs =>
switchBC2DE(xs)
case (x@ZLine0(LD, TwoRegisters(D, B), _)) :: xs =>
switchDE2BC(xs)
case (x@ZLine0(LD, TwoRegisters(E, C), _)) :: xs =>
switchDE2BC(xs)
case (x@ZLine0(_, TwoRegisters(DE, r), _)) :: xs =>
x.copy(registers = TwoRegisters(BC, r)) :: switchDE2BC(xs)
case (x@ZLine0(_, TwoRegisters(r, DE), _)) :: xs =>
x.copy(registers = TwoRegisters(r, BC)) :: switchDE2BC(xs)
case (x@ZLine0(_, TwoRegisters(D, r), _)) :: xs =>
x.copy(registers = TwoRegisters(B, r)) :: switchDE2BC(xs)
case (x@ZLine0(_, TwoRegisters(r, D), _)) :: xs =>
x.copy(registers = TwoRegisters(r, B)) :: switchDE2BC(xs)
case (x@ZLine0(_, TwoRegisters(E, r), _)) :: xs =>
x.copy(registers = TwoRegisters(C, r)) :: switchDE2BC(xs)
case (x@ZLine0(_, TwoRegisters(r, E), _)) :: xs =>
x.copy(registers = TwoRegisters(r, C)) :: switchDE2BC(xs)
case (x@ZLine0(_, TwoRegistersOffset(D, r, o), _)) :: xs =>
x.copy(registers = TwoRegistersOffset(B, r, o)) :: switchDE2BC(xs)
case (x@ZLine0(_, TwoRegistersOffset(r, D, o), _)) :: xs =>
x.copy(registers = TwoRegistersOffset(r, B, o)) :: switchDE2BC(xs)
case (x@ZLine0(_, TwoRegistersOffset(E, r, o), _)) :: xs =>
x.copy(registers = TwoRegistersOffset(C, r, o)) :: switchDE2BC(xs)
case (x@ZLine0(_, TwoRegistersOffset(r, E, o), _)) :: xs =>
x.copy(registers = TwoRegistersOffset(r, C, o)) :: switchDE2BC(xs)
case x :: xs => x :: switchBC2DE(xs)
case Nil => Nil
}
}
}

View File

@ -50,6 +50,8 @@ object VariableLifetime {
}
}
// val log = new ConsoleLogger
// log.verbosity = 3
// log.trace("Lifetime for " + variableName)
// codeWithFlow.zipWithIndex.foreach {
// case ((_, line), index) =>

View File

@ -17,7 +17,7 @@ object Z80OptimizationPresets {
EmptyParameterStoreRemoval,
EmptyMemoryStoreRemoval)
).flatten ++
List(WordVariableToRegisterOptimization, ByteVariableToRegisterOptimization, CompactStackFrame) ++
List(ChangeRegisterPairPreferringDE, WordVariableToRegisterOptimization, ByteVariableToRegisterOptimization, ChangeRegisterPairPreferringBC, CompactStackFrame) ++
LaterIntel8080Optimizations.All ++ LaterI80Optimizations.All
).flatten
}
@ -31,7 +31,7 @@ object Z80OptimizationPresets {
EmptyMemoryStoreRemoval,
)
).flatten ++
List(WordVariableToRegisterOptimization, ByteVariableToRegisterOptimization) ++
List(ChangeRegisterPairPreferringDE, WordVariableToRegisterOptimization, ByteVariableToRegisterOptimization, ChangeRegisterPairPreferringBC) ++
LaterIntel8080Optimizations.All ++ LaterI80Optimizations.All
).flatten
}
@ -44,7 +44,7 @@ object Z80OptimizationPresets {
EmptyParameterStoreRemoval,
EmptyMemoryStoreRemoval)
).flatten ++
List(WordVariableToRegisterOptimization, ByteVariableToRegisterOptimization) ++
List(ChangeRegisterPairPreferringDE, WordVariableToRegisterOptimization, ByteVariableToRegisterOptimization, ChangeRegisterPairPreferringBC) ++
LaterSharpOptimizations.All ++ LaterI80Optimizations.All
).flatten
}