mirror of
https://github.com/KarolS/millfork.git
synced 2024-06-09 16:29:34 +00:00
590 lines
32 KiB
Scala
590 lines
32 KiB
Scala
package millfork.assembly.z80.opt
|
|
|
|
import millfork.{CompilationFlag, NonOverlappingIntervals}
|
|
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
|
|
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZFlag, ZLine, ZLine0, ZOpcode, ZOpcodeClasses}
|
|
import millfork.env._
|
|
import millfork.error.ConsoleLogger
|
|
import millfork.node.ZRegister
|
|
|
|
import scala.collection.mutable.ListBuffer
|
|
import scala.util.control.TailCalls.{TailRec, done, tailcall}
|
|
|
|
/**
|
|
* @author Karol Stasiak
|
|
*/
|
|
object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
|
|
|
|
override def requiredFlags: Set[CompilationFlag.Value] = Set(CompilationFlag.RegisterVariables)
|
|
|
|
override def name = "Allocating variables to register pairs"
|
|
|
|
override def minimumRequiredLines: Int = 3
|
|
|
|
object CyclesAndBytes {
|
|
val Zero = CyclesAndBytes(0, 0)
|
|
}
|
|
|
|
case class CyclesAndBytes(bytes: Int, cycles: Int) {
|
|
def +(that: CyclesAndBytes) = CyclesAndBytes(this.bytes + that.bytes, this.cycles + that.cycles)
|
|
}
|
|
|
|
override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = {
|
|
val vs = VariableStatus(f, code, optimizationContext, _.size == 2, allowParams = true).getOrElse(return code)
|
|
val options = optimizationContext.options
|
|
val log = options.log
|
|
val removeVariablesForReal = !options.flag(CompilationFlag.InternalCurrentlyOptimizingForMeasurement)
|
|
val costFunction: CyclesAndBytes => Int = if (options.flag(CompilationFlag.OptimizeForSpeed)) _.cycles else _.bytes
|
|
val z80 = optimizationContext.options.flag(CompilationFlag.EmitZ80Opcodes)
|
|
val exdehl = optimizationContext.options.flag(CompilationFlag.EmitIntel8080Opcodes)
|
|
|
|
val hlCandidates = vs.variablesWithLifetimes.filter {
|
|
case (v, range) =>
|
|
val tuple = vs.codeWithFlow(range.start)
|
|
okPrefix(vs, v, range, ZRegister.HL, allowDirectLoad = true, allowIndirectLoad = false) && tuple._1.importanceAfter.h != Important &&
|
|
tuple._1.importanceAfter.l != Important || {
|
|
// println(s"Cannot inline ${v.name} to HL because of early $tuple")
|
|
false
|
|
}
|
|
}.flatMap {
|
|
case (v, range) =>
|
|
canBeInlined(v.name, synced = false, ZRegister.HL, vs.codeWithFlow.slice(range.start, range.end)).map { score =>
|
|
(v.name, range, score +
|
|
(if (vs.variablesWithRegisterHint(v.name)) CyclesAndBytes(16, 16) else CyclesAndBytes.Zero) +
|
|
(if (vs.paramVariables(v.name)) CyclesAndBytes(-16, -3) else CyclesAndBytes.Zero)
|
|
)
|
|
}
|
|
}
|
|
|
|
val bcCandidates = vs.variablesWithLifetimes.filter {
|
|
case (v, range) =>
|
|
val tuple = vs.codeWithFlow(range.start)
|
|
okPrefix(vs, v, range, ZRegister.BC, z80, allowIndirectLoad = false) && tuple._1.importanceAfter.b != Important &&
|
|
tuple._1.importanceAfter.c != Important || {
|
|
// println(s"Cannot inline ${v.name} to BC because of early $tuple")
|
|
false
|
|
}
|
|
}.flatMap {
|
|
case (v, range) =>
|
|
canBeInlined(v.name, synced = false, ZRegister.BC, vs.codeWithFlow.slice(range.start, range.end)).map { score =>
|
|
(v.name, range, score +
|
|
(if (vs.variablesWithRegisterHint(v.name)) CyclesAndBytes(16, 16) else CyclesAndBytes.Zero) +
|
|
(if (vs.paramVariables(v.name)) {
|
|
if (z80) CyclesAndBytes(-20, -4)
|
|
else CyclesAndBytes(-24, -5)
|
|
} else CyclesAndBytes.Zero)
|
|
)
|
|
}
|
|
}
|
|
|
|
val deCandidates = vs.variablesWithLifetimes.filter {
|
|
case (v, range) =>
|
|
val tuple = vs.codeWithFlow(range.start)
|
|
okPrefix(vs, v, range, ZRegister.DE, z80, exdehl) && tuple._1.importanceAfter.d != Important &&
|
|
tuple._1.importanceAfter.e != Important || {
|
|
// println(s"Cannot inline ${v.name} to DE because of early $tuple")
|
|
false
|
|
}
|
|
}.flatMap {
|
|
case (v, range) =>
|
|
canBeInlined(v.name, synced = false, ZRegister.DE, vs.codeWithFlow.slice(range.start, range.end)).map { score =>
|
|
(v.name, range, score +
|
|
(if (vs.variablesWithRegisterHint(v.name)) CyclesAndBytes(16, 16) else CyclesAndBytes.Zero) +
|
|
(if (vs.paramVariables(v.name)) {
|
|
if (z80 || exdehl) CyclesAndBytes(-20, -4)
|
|
else CyclesAndBytes(-24, -5)
|
|
} else CyclesAndBytes.Zero)
|
|
)
|
|
}
|
|
}
|
|
|
|
val hlCandidateSets = NonOverlappingIntervals.apply[(String, Range, CyclesAndBytes)](hlCandidates, _._2.start, _._2.end)
|
|
val bcCandidateSets = NonOverlappingIntervals.apply[(String, Range, CyclesAndBytes)](bcCandidates, _._2.start, _._2.end)
|
|
val deCandidateSets = NonOverlappingIntervals.apply[(String, Range, CyclesAndBytes)](deCandidates, _._2.start, _._2.end)
|
|
|
|
val variants = for {
|
|
vhl <- if (options.flag(CompilationFlag.SingleThreaded)) hlCandidateSets else hlCandidateSets.par
|
|
nhl = vhl.map(_._1)
|
|
|
|
vbc <- bcCandidateSets
|
|
nbc = vbc.map(_._1)
|
|
if (nhl & nbc).isEmpty
|
|
|
|
vde <- deCandidateSets
|
|
nde = vde.map(_._1)
|
|
if (nhl & nde).isEmpty
|
|
if (nhl & nde).isEmpty
|
|
|
|
|
|
score = vhl.toSeq.map(x => costFunction(x._3)).sum +
|
|
vbc.toSeq.map(x => costFunction(x._3)).sum +
|
|
vde.toSeq.map(x => costFunction(x._3)).sum
|
|
} yield (score, vhl, vbc, vde)
|
|
|
|
if (variants.isEmpty) {
|
|
return code
|
|
}
|
|
|
|
// variants.foreach(println)
|
|
|
|
val (_, bestHLs, bestBCs, bestDEs) = variants.maxBy(_._1)
|
|
|
|
def reportOptimizedBlock[T](oldCode: List[(T, ZLine)], newCode: List[ZLine]): Unit = {
|
|
if (log.traceEnabled) {
|
|
oldCode.foreach(l => log.trace(l._2.toString))
|
|
log.trace(" ↓")
|
|
newCode.foreach(l => log.trace(l.toString))
|
|
}
|
|
}
|
|
|
|
if (bestHLs.nonEmpty || bestBCs.nonEmpty || bestDEs.nonEmpty) {
|
|
val output = ListBuffer[ZLine]()
|
|
var i = 0
|
|
while (i < code.length) {
|
|
var done = false
|
|
bestHLs.find(_._2.start == i).foreach {
|
|
case (v, range, _) =>
|
|
log.debug(s"Inlining $v to register pair HL")
|
|
val oldCode = vs.codeWithFlow.slice(range.start, range.end)
|
|
val newCode = inlineVars(v, "", "", oldCode).result
|
|
reportOptimizedBlock(oldCode, newCode)
|
|
if (vs.paramVariables(v)) {
|
|
val addr = vs.localVariables.find(_.name.==(v)).get.asInstanceOf[MemoryVariable].toAddress
|
|
output += ZLine.ldAbs16(ZRegister.HL, addr)
|
|
}
|
|
output ++= newCode
|
|
i = range.end
|
|
if (removeVariablesForReal && !vs.paramVariables(v) && contains(range, vs.variablesWithLifetimesMap(v))) {
|
|
f.environment.removeVariable(v)
|
|
}
|
|
done = true
|
|
}
|
|
if (!done) {
|
|
bestBCs.find(_._2.start == i).foreach {
|
|
case (v, range, _) =>
|
|
log.debug(s"Inlining $v to register pair BC")
|
|
val oldCode = vs.codeWithFlow.slice(range.start, range.end)
|
|
val newCode = inlineVars("", v, "", oldCode).result
|
|
reportOptimizedBlock(oldCode, newCode)
|
|
if (vs.paramVariables(v)) {
|
|
val addr = vs.localVariables.find(_.name.==(v)).get.asInstanceOf[MemoryVariable].toAddress
|
|
if (z80) {
|
|
output += ZLine.ldAbs16(ZRegister.BC, addr)
|
|
} else {
|
|
output += ZLine.ldAbs16(ZRegister.HL, addr)
|
|
output += ZLine.ld8(ZRegister.B, ZRegister.H)
|
|
output += ZLine.ld8(ZRegister.C, ZRegister.L)
|
|
}
|
|
}
|
|
output ++= newCode
|
|
i = range.end
|
|
if (removeVariablesForReal && !vs.paramVariables(v) && contains(range, vs.variablesWithLifetimesMap(v))) {
|
|
f.environment.removeVariable(v)
|
|
}
|
|
done = true
|
|
}
|
|
}
|
|
if (!done) {
|
|
bestDEs.find(_._2.start == i).foreach {
|
|
case (v, range, _) =>
|
|
log.debug(s"Inlining $v to register pair DE")
|
|
val oldCode = vs.codeWithFlow.slice(range.start, range.end)
|
|
val newCode = inlineVars("", "", v, oldCode).result
|
|
reportOptimizedBlock(oldCode, newCode)
|
|
if (vs.paramVariables(v)) {
|
|
val addr = vs.localVariables.find(_.name.==(v)).get.asInstanceOf[MemoryVariable].toAddress
|
|
if (z80) {
|
|
output += ZLine.ldAbs16(ZRegister.DE, addr)
|
|
} else if (exdehl) {
|
|
output += ZLine.ldAbs16(ZRegister.HL, addr)
|
|
output += ZLine.implied(ZOpcode.EX_DE_HL)
|
|
} else {
|
|
output += ZLine.ldAbs16(ZRegister.HL, addr)
|
|
output += ZLine.ld8(ZRegister.D, ZRegister.H)
|
|
output += ZLine.ld8(ZRegister.E, ZRegister.L)
|
|
}
|
|
}
|
|
output ++= newCode
|
|
i = range.end
|
|
if (removeVariablesForReal && !vs.paramVariables(v) && contains(range, vs.variablesWithLifetimesMap(v))) {
|
|
f.environment.removeVariable(v)
|
|
}
|
|
done = true
|
|
}
|
|
}
|
|
if (!done) {
|
|
output += code(i)
|
|
i += 1
|
|
}
|
|
}
|
|
output.toList
|
|
} else {
|
|
code
|
|
}
|
|
}
|
|
|
|
def okPrefix(vs: VariableStatus, v: Variable, range: Range, reg: ZRegister.Value, allowDirectLoad: Boolean, allowIndirectLoad: Boolean): Boolean = {
|
|
if (!vs.paramVariables(v.name)) return true
|
|
if (v.isVolatile) return false
|
|
if (!allowDirectLoad && !allowIndirectLoad) {
|
|
// println(s"okPrefix $v false: better cpu required for $reg")
|
|
return false
|
|
}
|
|
if (vs.paramRegs.contains(reg) || vs.paramRegs.contains(ZRegister.A)) {
|
|
// println(s"okPrefix $v false: reg $reg in use")
|
|
return false
|
|
}
|
|
if (range.size < 2) {
|
|
// println(s"okPrefix $v false: range $range too small")
|
|
return false
|
|
}
|
|
if (range.start < 1) {
|
|
// println(s"okPrefix $v true: range $range early enough")
|
|
return true
|
|
}
|
|
import ZOpcode._
|
|
vs.codeWithFlow.take(range.start) match {
|
|
case Nil =>
|
|
case (_, ZLine0(LABEL, _, _)) :: xs =>
|
|
if (xs.exists { case (_, l) => l.opcode == LABEL || ZOpcodeClasses.NonLinear(l.opcode) }) {
|
|
// println(s"okPrefix false: LABEL in prefix")
|
|
return false
|
|
}
|
|
case _ => return false
|
|
}
|
|
val importance = vs.codeWithFlow(range.start - 1)._1.importanceAfter
|
|
if (allowDirectLoad) {
|
|
importance.getRegister(reg) == Unimportant && importance.a == Unimportant
|
|
} else {
|
|
importance.getRegister(reg) == Unimportant && importance.a == Unimportant && importance.h == Unimportant && importance.l == Unimportant
|
|
}
|
|
}
|
|
|
|
def contains(outer: Range, inner: Range): Boolean = {
|
|
outer.contains(inner.start) && outer.contains(inner.end - 1)
|
|
}
|
|
|
|
import millfork.assembly.z80.ZOpcode._
|
|
import millfork.node.ZRegister._
|
|
|
|
def add(first: Boolean, ifTrue: CyclesAndBytes, ifFalse: CyclesAndBytes): CyclesAndBytes=>CyclesAndBytes = { c =>
|
|
if (first) c + ifTrue else c + ifFalse
|
|
}
|
|
def add(value: CyclesAndBytes): CyclesAndBytes=>CyclesAndBytes = _ + value
|
|
|
|
def canBeInlined(vname: String, synced: Boolean, target: ZRegister.Value, code: List[(FlowInfo, ZLine)]): Option[CyclesAndBytes] = {
|
|
def fail(reason: Int): None.type = {
|
|
// println(s"Cannot inline $vname to $target because of [[$reason]] ${code.head}")
|
|
None
|
|
}
|
|
code match {
|
|
case (_, ZLine(LD_16, TwoRegisters(BC, IMM_16), _, Elidability.Elidable, _))::
|
|
(i, ZLine(ADD_16, TwoRegisters(HL, BC), _, Elidability.Elidable, _)) :: xs if target == BC =>
|
|
if (i.importanceAfter.getRegister(BC) == Important) fail(22)
|
|
else if(i.importanceAfter.getRegister(DE) == Important) canBeInlined(vname, synced, target, xs).map(add(CyclesAndBytes(-21, -2)))
|
|
else canBeInlined(vname, synced = true, target, xs)
|
|
|
|
case (_, ZLine(LD_16, TwoRegisters(DE, IMM_16), _, Elidability.Elidable, _))::
|
|
(i, ZLine(ADD_16, TwoRegisters(HL, DE), _, Elidability.Elidable, _)) :: xs if target == DE =>
|
|
if (i.importanceAfter.getRegister(DE) == Important) fail(23)
|
|
else if (i.importanceAfter.getRegister(BC) == Important) canBeInlined(vname, synced, target, xs).map(add(CyclesAndBytes(-21, -2)))
|
|
else canBeInlined(vname, synced = true, target, xs)
|
|
|
|
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th1), Elidability.Elidable, _)) ::
|
|
(_, ZLine0(ADD_16, TwoRegisters(HL, BC | DE), _)) ::
|
|
(i, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th2), Elidability.Elidable, _)) ::
|
|
xs if target == HL && th1.name != vname && th2.name != vname &&
|
|
i.importanceAfter.getFlag(ZFlag.Z) != Important &&
|
|
i.importanceAfter.getFlag(ZFlag.H) != Important &&
|
|
i.importanceAfter.getFlag(ZFlag.P) != Important &&
|
|
i.importanceAfter.getRegister(A) != Important &&
|
|
i.importanceAfter.getRegister(HL) != Important &&
|
|
i.importanceAfter.getFlag(ZFlag.Z) != Important =>
|
|
// bytes before: 3 + 1 + 3 = 7
|
|
// cycles before: 16 + 11 + 16 = 43
|
|
// bytes after: 3 + 1 + 3 + 3 + 1 + 3 = 14
|
|
// cycles after: 13 + 4 + 13 + 13 + 4 + 13 = 60
|
|
canBeInlined(vname, synced = true, target, xs).map(add(CyclesAndBytes(-17, -7)))
|
|
|
|
case (_, ZLine(LD_16, TwoRegisters(t, _), _, Elidability.Elidable, _)) ::
|
|
(_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), Elidability.Elidable, _)) ::
|
|
(i, ZLine0(ADD_16, TwoRegisters(HL, t2), _)) ::
|
|
xs if th.name == vname && t != HL && t == t2 && i.importanceAfter.getRegister(t) == Unimportant =>
|
|
// LD PP ; LD HL,(qq) ; ADD HL,PP → LD H,P ; LD L,P ; ADD HL,QQ
|
|
canBeInlined(vname, synced = true, target, xs).map(add(target == t, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
|
|
|
|
// case (_, ZLine(LD_16, TwoRegisters(t, _), _, true, _)) ::
|
|
// (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), true, _)) ::
|
|
// (i, ZLine0(ADD_16, TwoRegisters(HL, t2), _)) ::
|
|
// xs if th.name == vname && t == target && t != HL && t == t2 && i.importanceAfter.getRegister(t) == Unimportant =>
|
|
// canBeInlined(vname, synced = true, target, xs).map(add(CyclesAndBytes(16, 3)))
|
|
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), Elidability.Elidable, _)) ::
|
|
(_, ZLine(LD_16, TwoRegisters(t, _), _, Elidability.Elidable, _)) ::
|
|
(i, ZLine0(ADD_16, TwoRegisters(HL, t2), _)) ::
|
|
xs if th.name == vname && t != HL && t == t2 && i.importanceAfter.getRegister(t) == Unimportant =>
|
|
// LD (vv),HL ; LD QQ,__ ; ADD HL,QQ (vv@QQ)→ LD QQ,HL ; LD HL,__, ADD HL,QQ
|
|
canBeInlined(vname, synced = true, target, xs).map(add(target == t, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
|
|
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), Elidability.Elidable, _)) ::
|
|
(_, ZLine(LD_16, TwoRegisters(t, _), _, Elidability.Elidable, _)) ::
|
|
(i, ZLine0(ADD_16, TwoRegisters(HL, t2), _)) ::
|
|
xs if t == target && th.name == vname && t != HL && t == t2 && i.importanceAfter.getRegister(t) == Unimportant =>
|
|
// LD (vv),HL ; LD QQ,__ ; ADD HL,QQ (vv@QQ)→ LD QQ,HL ; LD PP,__, ADD HL,PP
|
|
canBeInlined(vname, synced = true, target, xs).map(add(CyclesAndBytes(16, 3)))
|
|
|
|
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
|
|
canBeInlined(vname, synced = true, target, xs).map(add(target == HL, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
|
|
canBeInlined(vname, synced = true, target, xs).map(add(target == HL, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
|
|
case (_, ZLine(LD_16, TwoRegisters(DE, MEM_ABS_16), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
|
|
canBeInlined(vname, synced = true, target, xs).map(add(target == DE, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
|
|
canBeInlined(vname, synced = true, target, xs).map(add(target == DE, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
|
|
case (_, ZLine(LD_16, TwoRegisters(BC, MEM_ABS_16), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
|
|
canBeInlined(vname, synced = true, target, xs).map(add(target == BC, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
|
|
canBeInlined(vname, synced = true, target, xs).map(add(target == BC, CyclesAndBytes(16, 3), CyclesAndBytes(8, 1)))
|
|
|
|
case (_, ZLine(OR, OneRegister(A), _, Elidability.Elidable, _)) ::
|
|
(f, ZLine(SBC_16, TwoRegisters(HL, BC | DE), _, Elidability.Elidable, _)) :: xs if target == HL =>
|
|
val i = f.importanceAfter
|
|
if (i.h == Unimportant &&
|
|
i.l == Unimportant &&
|
|
i.a == Unimportant &&
|
|
i.hf == Unimportant &&
|
|
i.nf == Unimportant &&
|
|
i.pf == Unimportant &&
|
|
i.sf == Unimportant &&
|
|
i.zf == Unimportant) {
|
|
canBeInlined(vname, synced = true, target, xs).map(add(CyclesAndBytes(3, -1)))
|
|
} else {
|
|
None
|
|
}
|
|
|
|
case (_, x) :: (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, t), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs
|
|
if th.name == vname && t == target && x.changesRegister(t) =>
|
|
canBeInlined(vname, synced = true, target, xs).map(add(CyclesAndBytes(16, 3)))
|
|
|
|
|
|
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
|
|
canBeInlined(vname, synced, target, xs).map(add(CyclesAndBytes(9, 2)))
|
|
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), Elidability.Elidable, _)) :: xs if th.name == vname =>
|
|
canBeInlined(vname, synced, target, xs).map(add(CyclesAndBytes(9, 2)))
|
|
|
|
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), Elidability.Elidable, _)) :: xs if th.name == vname =>
|
|
canBeInlined(vname, synced, target, xs).map(add(CyclesAndBytes(9, 2)))
|
|
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), Elidability.Elidable, _)) :: xs if th.name == vname =>
|
|
canBeInlined(vname, synced, target, xs).map(add(CyclesAndBytes(9, 2)))
|
|
|
|
case (_, ZLine0(_, _, MemoryAddressConstant(th))) :: _ if th.name == vname => fail(4)
|
|
case (_, ZLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _))) :: _ if th.name == vname => fail(5)
|
|
case (_, ZLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _))) :: _ if th.name == vname => fail(6)
|
|
case (_, ZLine0(CALL, _, _)) :: xs => target match {
|
|
// TODO: check return type and allow HL sometimes
|
|
case BC | DE =>
|
|
canBeInlined(vname, synced, target, xs).map(add(CyclesAndBytes(-21, -2)))
|
|
case _ => fail(3)
|
|
}
|
|
case (_, x) :: xs if x.changesRegister(target) => fail(1)
|
|
case (_, x) :: xs if x.readsRegister(target) && !synced => fail(2)
|
|
case (_, ZLine0(LABEL, _, _)) :: xs => canBeInlined(vname, synced = false, target, xs)
|
|
case (_, ZLine0(CALL, _, _)) :: xs => fail(3)
|
|
case _ :: xs => canBeInlined(vname, synced, target, xs)
|
|
case _ => Some(CyclesAndBytes.Zero)
|
|
}
|
|
}
|
|
|
|
def inlineVars(hl: String, bc: String, de: String, code: List[(FlowInfo, ZLine)]): TailRec[List[ZLine]] = {
|
|
// if (code.nonEmpty) println(code.head)
|
|
code match {
|
|
|
|
case (_, ZLine(OR, OneRegister(A), _, _, _)) ::
|
|
(_, l@ZLine(SBC_16, TwoRegisters(HL, BC), _, _, _)) ::
|
|
xs if hl != "" =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(
|
|
ZLine.ld8(A, L).pos(l.source) ::
|
|
ZLine.register(SUB, C).pos(l.source) ::
|
|
ZLine.ld8(A, H).pos(l.source) ::
|
|
ZLine.register(SBC, B).pos(l.source) :: _)
|
|
|
|
case (_, ZLine(OR, OneRegister(A), _, _, _)) ::
|
|
(_, l@ZLine(SBC_16, TwoRegisters(HL, DE), _, _, _)) ::
|
|
xs if hl != "" =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(
|
|
ZLine.ld8(A, L).pos(l.source) ::
|
|
ZLine.register(SUB, E).pos(l.source) ::
|
|
ZLine.ld8(A, H).pos(l.source) ::
|
|
ZLine.register(SBC, D).pos(l.source) :: _)
|
|
|
|
case (_, load@ZLine(LD_16, TwoRegisters(BC, IMM_16), _, _, s1)) ::
|
|
(f, add@ZLine(ADD_16, TwoRegisters(HL, BC), _, _, s2)) ::
|
|
xs if bc != "" =>
|
|
if (f.importanceAfter.getRegister(DE) == Important) {
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(
|
|
ZLine.register(PUSH, BC).pos(s1) :: load :: add :: ZLine.register(POP, BC).pos(s2) :: _)
|
|
} else {
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(
|
|
load.copy(registers = TwoRegisters(DE, IMM_16)) :: ZLine.registers(ADD_16, HL, DE).pos(s2) :: _)
|
|
}
|
|
|
|
case (_, load@ZLine(LD_16, TwoRegisters(DE, IMM_16), _, _, s1)) ::
|
|
(f, add@ZLine(ADD_16, TwoRegisters(HL, DE), _, _, s2)) ::
|
|
xs if de != "" =>
|
|
if (f.importanceAfter.getRegister(BC) == Important) {
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(
|
|
ZLine.register(PUSH, DE).pos(s1) :: load :: add :: ZLine.register(POP, DE).pos(s2) :: _)
|
|
} else {
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(
|
|
load.copy(registers = TwoRegisters(BC, IMM_16)) :: ZLine.registers(ADD_16, HL, BC).pos(s2) :: _)
|
|
}
|
|
|
|
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), a1@MemoryAddressConstant(th1), _, s1)) ::
|
|
(_, ZLine(ADD_16, TwoRegisters(HL, reg@(DE | BC)), _, _, s2)) ::
|
|
(_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), a2@MemoryAddressConstant(th2), _, s3)) ::
|
|
xs if hl != "" && th1.name != hl && th2.name != hl =>
|
|
// bytes before: 3 + 1 + 3 = 7
|
|
// cycles before: 16 + 11 + 16 = 43
|
|
// bytes after: 3 + 1 + 3 + 3 + 1 + 3 = 14
|
|
// cycles after: 13 + 4 + 13 + 13 + 4 + 13 = 60
|
|
val (h, l) = reg match {
|
|
case BC => (B, C)
|
|
case DE => (D, E)
|
|
}
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(
|
|
ZLine.ldAbs8(A, a1).pos(s1) ::
|
|
ZLine.register(ADD, l).pos(s2) ::
|
|
ZLine.ldAbs8(a2, A).pos(s3) ::
|
|
ZLine.ldAbs8(A, a1 + 1).pos(s1) ::
|
|
ZLine.register(ADC, h).pos(s2) ::
|
|
ZLine.ldAbs8(a2 + 1, A).pos(s3) :: _)
|
|
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _, s1)) ::
|
|
(_, loadConst@ZLine(LD_16, TwoRegisters(BC, constSource), _, _, s2)) ::
|
|
(_, add@ZLine(ADD_16, TwoRegisters(HL, BC), _, _, s3)) :: xs if th.name == bc =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(
|
|
ZLine.ld8(B, H).pos(s1) :: ZLine.ld8(C, L).pos(s1) ::
|
|
loadConst.copy(registers = TwoRegisters(HL, constSource)) ::
|
|
add.copy(registers = TwoRegisters(HL, BC)) :: _)
|
|
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _, s1)) ::
|
|
(_, loadConst@ZLine0(LD_16, TwoRegisters(DE, constSource), _)) ::
|
|
(_, add@ZLine0(ADD_16, TwoRegisters(HL, DE), _)) :: xs if th.name == de =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(
|
|
ZLine.ld8(D, H).pos(s1) :: ZLine.ld8(E, L).pos(s1) ::
|
|
loadConst.copy(registers = TwoRegisters(HL, constSource)) ::
|
|
add.copy(registers = TwoRegisters(HL, DE)) :: _)
|
|
// TODO: above with regs swapped
|
|
|
|
case (_, loadConst@ZLine0(LD_16, TwoRegisters(t, constSource), _)) ::
|
|
(_, ZLine0(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th))) ::
|
|
(_, add@ZLine0(ADD_16, TwoRegisters(HL, t2), _)) :: xs if th.name == bc && t == t2 && t != HL =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(
|
|
loadConst.copy(registers = TwoRegisters(HL, constSource)) ::
|
|
add.copy(registers = TwoRegisters(HL, BC)) :: _)
|
|
|
|
case (_, loadConst@ZLine0(LD_16, TwoRegisters(t, constSource), _)) ::
|
|
(_, ZLine0(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th))) ::
|
|
(_, add@ZLine0(ADD_16, TwoRegisters(HL, t2), _)) :: xs if th.name == de && t == t2 && t != HL =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(
|
|
loadConst.copy(registers = TwoRegisters(HL, constSource)) ::
|
|
add.copy(registers = TwoRegisters(HL, DE)) :: _)
|
|
|
|
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
|
|
tailcall(inlineVars(hl, bc, de, xs))
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
|
|
tailcall(inlineVars(hl, bc, de, xs))
|
|
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(H, B).pos(s) :: ZLine.ld8(L, C).pos(s) :: _)
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(B, H).pos(s) :: ZLine.ld8(C, L).pos(s) :: _)
|
|
case (_, ZLine(LD_16, TwoRegisters(HL, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(H, D).pos(s) :: ZLine.ld8(L, E).pos(s) :: _)
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(D, H).pos(s) :: ZLine.ld8(E, L).pos(s) :: _)
|
|
|
|
case (_, ZLine(LD_16, TwoRegisters(DE, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
|
|
tailcall(inlineVars(hl, bc, de, xs))
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
|
|
tailcall(inlineVars(hl, bc, de, xs))
|
|
case (_, ZLine(LD_16, TwoRegisters(DE, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(D, B).pos(s) :: ZLine.ld8(E, C).pos(s) :: _)
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(B, D).pos(s) :: ZLine.ld8(C, E).pos(s) :: _)
|
|
case (_, ZLine(LD_16, TwoRegisters(DE, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(D, H).pos(s) :: ZLine.ld8(E, L).pos(s) :: _)
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(H, D).pos(s) :: ZLine.ld8(L, E).pos(s) :: _)
|
|
|
|
case (_, ZLine(LD_16, TwoRegisters(BC, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
|
|
tailcall(inlineVars(hl, bc, de, xs))
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
|
|
tailcall(inlineVars(hl, bc, de, xs))
|
|
case (_, ZLine(LD_16, TwoRegisters(BC, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(B, H).pos(s) :: ZLine.ld8(C, L).pos(s) :: _)
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(H, B).pos(s) :: ZLine.ld8(L, C).pos(s) :: _)
|
|
case (_, ZLine(LD_16, TwoRegisters(BC, MEM_ABS_16), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(B, D).pos(s) :: ZLine.ld8(C, E).pos(s) :: _)
|
|
case (_, ZLine(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(D, B).pos(s) :: ZLine.ld8(E, C).pos(s) :: _)
|
|
|
|
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(A, L).pos(s) :: _)
|
|
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), _, s)) :: xs if th.name == hl =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(L, A).pos(s) :: _)
|
|
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(A, C).pos(s) :: _)
|
|
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), _, s)) :: xs if th.name == bc =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(C, A).pos(s) :: _)
|
|
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(A, E).pos(s) :: _)
|
|
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), MemoryAddressConstant(th), _, s)) :: xs if th.name == de =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(E, A).pos(s) :: _)
|
|
|
|
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == hl =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(A, H).pos(s) :: _)
|
|
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == hl =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(H, A).pos(s) :: _)
|
|
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == bc =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(A, B).pos(s) :: _)
|
|
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == bc =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(B, A).pos(s) :: _)
|
|
case (_, ZLine(LD, TwoRegisters(A, MEM_ABS_8), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == de =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(A, D).pos(s) :: _)
|
|
case (_, ZLine(LD, TwoRegisters(MEM_ABS_8, A), CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(1, _)), _, s)) :: xs if th.name == de =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.ld8(D, A).pos(s) :: _)
|
|
|
|
case (_, l1@ZLine(LD_16, TwoRegisters(BC, IMM_16), _, _, s1)) :: (_, l2@ZLine(ADD_16, TwoRegisters(HL, BC), _, _, s2)) :: xs if bc != "" =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.register(PUSH, BC).pos(s1) :: l1 :: l2 :: ZLine.register(POP, BC).pos(s2) :: _)
|
|
|
|
case (_, l1@ZLine(LD_16, TwoRegisters(DE, IMM_16), _, _, s1)) :: (_, l2@ZLine(ADD_16, TwoRegisters(HL, DE), _, _, s2)) :: xs if de != "" =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.register(PUSH, DE).pos(s1) :: l1 :: l2 :: ZLine.register(POP, DE).pos(s2) :: _)
|
|
|
|
case (_, x@ZLine(CALL, _, _, _, s)) :: xs =>
|
|
if (bc != "") {
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.register(PUSH, BC).pos(s) :: x :: ZLine.register(POP, BC).pos(s) :: _)
|
|
} else if (de != "") {
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.register(PUSH, DE).pos(s) :: x :: ZLine.register(POP, DE).pos(s) :: _)
|
|
} else {
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(x :: _)
|
|
}
|
|
|
|
|
|
case x :: (_, ZLine0(LD_16, TwoRegisters(MEM_ABS_16, HL), MemoryAddressConstant(th))) :: xs if x._2.changesRegister(HL) && th.name == hl =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(x._2 :: _)
|
|
case x :: (_, ZLine0(LD_16, TwoRegisters(MEM_ABS_16, BC), MemoryAddressConstant(th))) :: xs if x._2.changesRegister(BC) && th.name == bc =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(x._2 :: _)
|
|
case x :: (_, ZLine0(LD_16, TwoRegisters(MEM_ABS_16, DE), MemoryAddressConstant(th))) :: xs if x._2.changesRegister(DE) && th.name == de =>
|
|
tailcall(inlineVars(hl, bc, de, xs)).map(x._2 :: _)
|
|
|
|
case x :: _ if bc != "" && x._2.changesRegister(BC) => throw new IllegalStateException()
|
|
case x :: _ if de != "" && x._2.changesRegister(DE) => throw new IllegalStateException()
|
|
case x :: _ if hl != "" && x._2.changesRegister(HL) => throw new IllegalStateException()
|
|
|
|
case x :: xs => tailcall(inlineVars(hl, bc, de, xs)).map(x._2 :: _)
|
|
case Nil => done(Nil)
|
|
}
|
|
}
|
|
}
|