1
0
mirror of https://github.com/KarolS/millfork.git synced 2025-01-11 12:29:46 +00:00

Z80: Improve optimizations

This commit is contained in:
Karol Stasiak 2019-10-24 00:48:16 +02:00
parent 72f8806c54
commit 7fe32ca564
6 changed files with 105 additions and 13 deletions

View File

@ -36,7 +36,7 @@ object AlwaysGoodI80Optimizations {
List(ZRegister.B, ZRegister.C, ZRegister.D, ZRegister.E, ZRegister.H, ZRegister.L, ZRegister.MEM_HL).map(f))
val UsingKnownValueFromAnotherRegister = new RuleBasedAssemblyOptimization("Using known value from another register",
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
needsFlowInfo = FlowInfoRequirement.BothFlows,
for7Registers(register =>
(Elidable & IsRegular8BitLoadFrom(register) & MatchRegister(register, 0)) ~~> ((code, ctx) =>
code.map(x => x.copy(
@ -71,6 +71,23 @@ object AlwaysGoodI80Optimizations {
registers = OneRegister(ZRegister.IMM_8)
))
),
MultipleAssemblyRules(for {
(useful, useless, both, extractUseful) <- Seq[(ZRegister.Value, ZRegister.Value, ZRegister.Value, Constant => Constant)](
(B, C, BC, _.hiByte),
(C, B, BC, _.loByte),
(D, E, DE, _.hiByte),
(E, D, DE, _.loByte),
(H, L, HL, _.hiByte),
(L, H, HL, _.loByte)
)
} yield {
(Elidable & HasOpcode(LD_16) & HasRegisters(TwoRegisters(both, IMM_16)) & MatchParameter(0)) ~
(Linear & Not(Concerns(useful)) & Not(Concerns(useless))).* ~
(Elidable & HasOpcodeIn(Set(ADD, ADC, SUB, SBC, XOR, OR, AND, CP)) & HasRegisterParam(useful) & DoesntMatterWhatItDoesWith(useless)) ~~> { (code, ctx) =>
code.tail.init :+ code.last.copy(registers = OneRegister(IMM_8), parameter = extractUseful(ctx.get[Constant](0)))
}
}),
)
val ReloadingKnownValueFromMemory = new RuleBasedAssemblyOptimization("Reloading known value from memory",
@ -296,6 +313,13 @@ object AlwaysGoodI80Optimizations {
(HasOpcode(LD) & MatchSourceRealRegister(0) & MatchTargetRealRegister(1)) ~
(Linear & Not(ChangesMatchedRegister(0)) & Not(ChangesMatchedRegister(1))).* ~
(Elidable & HasOpcode(LD) & MatchSourceRealRegister(1) & MatchTargetRealRegister(0)) ~~> (_.init),
// 68
(Elidable & HasOpcode(LD) & Match8BitImmediate(1) & MatchTargetRegisterAndOffset(2)) ~
Where(ctx => ctx.get[RegisterAndOffset](2).isOneOfSeven) ~
(Elidable & HasOpcode(LD) & MatchSourceRegisterAndOffset(2) & MatchTargetRealRegister(3) & DoesntMatterWhatItDoesWithMatchedRegisterOffset(2)) ~~> {(code, ctx) =>
List(code.head.copy(registers = TwoRegisters(ctx.get[ZRegister.Value](3), IMM_8)))
},
)
val PointlessStackStashing = new RuleBasedAssemblyOptimization("Pointless stack stashing",

View File

@ -259,6 +259,10 @@ object CoarseFlowAnalyzer {
currentStatus = currentStatus.setRegister(t, SingleStatus(value.toInt))
case ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), xx) =>
currentStatus = currentStatus.setHL(SingleStatus(xx))
case ZLine0(LD_16, TwoRegisters(ZRegister.DE, ZRegister.IMM_16), xx) if xx.isProvablyDivisibleBy256 =>
currentStatus = currentStatus.copy(d = AnyStatus, e = Status.SingleZero)
case ZLine0(LD_16, TwoRegisters(ZRegister.BC, ZRegister.IMM_16), xx) if xx.isProvablyDivisibleBy256 =>
currentStatus = currentStatus.copy(b = AnyStatus, c = Status.SingleZero)
case ZLine0(LD, TwoRegisters(ZRegister.A, ZRegister.I | ZRegister.R), _) =>
currentStatus = currentStatus.copy(a = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus, nf = AnyStatus)
case ZLine0(LD, TwoRegisters(t, ZRegister.IMM_8), NumericConstant(value, _)) =>

View File

@ -145,8 +145,8 @@ case class CpuImportance(a: Importance = UnknownImportance,
case ZRegister.E => this.copy(e = Unimportant)
case ZRegister.DE => this.copy(d = Unimportant, e = Unimportant)
case ZRegister.MEM_DE => this.copy(d = Important, e = Important)
case ZRegister.H => this.copy(h = Unimportant)
case ZRegister.L => this.copy(l = Unimportant)
case ZRegister.H => this.copy(h = Unimportant, hlNumeric = this.l)
case ZRegister.L => this.copy(l = Unimportant, hlNumeric = this.h)
case ZRegister.HL => this.copy(h = Unimportant, l = Unimportant, hlNumeric = Unimportant)
case ZRegister.MEM_HL => this.copy(h = Important, l = Important)
case ZRegister.IXH => this.copy(ixh = Unimportant)
@ -469,7 +469,7 @@ object ReverseFlowAnalyzer {
currentImportance = currentImportance.copy(cf = Unimportant, hf = Unimportant, nf = Unimportant)
case ZLine0(LD_HLSP, _, _) =>
currentImportance = currentImportance.copy(h = Unimportant, l = Unimportant)
currentImportance = currentImportance.copy(h = Unimportant, l = Unimportant, hlNumeric = Unimportant)
case ZLine0(RIM, _, _) =>
currentImportance = currentImportance.copy(a = Unimportant)

View File

@ -559,12 +559,18 @@ case class MatchImmediate(i: Int) extends AssemblyLinePattern {
}
case class RegisterAndOffset(register: ZRegister.Value, offset: Int) {
def toOneRegister: ZRegisters = register match {
case ZRegister.MEM_IX_D | ZRegister.MEM_IY_D => OneRegisterOffset(register, offset)
case _ =>
if (offset != 0) ???
OneRegister(register)
}
def isOneOfSeven: Boolean = register match {
case ZRegister.A | ZRegister.B | ZRegister.C | ZRegister.D | ZRegister.E | ZRegister.H | ZRegister.L => true
case _ => false
}
}
case class MatchSourceRegisterAndOffset(i: Int) extends AssemblyLinePattern {
@ -745,6 +751,21 @@ case class DoesntMatterWhatItDoesWith(registers: ZRegister.Value*) extends Assem
override def hitRate: Double = 0.058
}
case class DoesntMatterWhatItDoesWithMatchedRegisterOffset(i: Int) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertBackward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = {
val ro = ctx.get[RegisterAndOffset](i)
ro.isOneOfSeven && flowInfo.importanceAfter.getRegister(ro.register) != Important
}
override def toString: String = "[¯\\_(ツ)_/¯:" + i + "]"
override def hitRate: Double = 0.058
}
case object DoesntMatterWhatItDoesWithFlags extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =

View File

@ -18,7 +18,10 @@ class VariableStatus(val paramVariables: Set[String],
val localVariables: List[Variable],
val variablesWithLifetimes: List[(Variable, Range)],
val variablesWithLifetimesMap: Map[String, Range],
val codeWithFlow: List[(FlowInfo, ZLine)])
val codeWithFlow: List[(FlowInfo, ZLine)]) {
override def toString = s"VariableStatus(paramVariables=$paramVariables, stillUsedVariables=$stillUsedVariables, variablesWithAddressesTaken=$variablesWithAddressesTaken, localVariables=$localVariables, variablesWithLifetimesMap=$variablesWithLifetimesMap)"
}
object VariableStatus {
def apply(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext, typFilter: Type => Boolean): Option[VariableStatus] = {
@ -49,20 +52,26 @@ object VariableStatus {
case _ => None
}.toSet
val variablesWithAddressesTaken = code.zipWithIndex.flatMap {
case (ZLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _)), _) =>
case (l@ZLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _)), _) =>
// println(th.name -> l)
Some(th.name)
case (ZLine0(_, _, SubbyteConstant(CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), _)), _) =>
case (l@ZLine0(_, _, SubbyteConstant(CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), _)), _) =>
// println(th.name -> l)
Some(th.name)
case (ZLine0(_,
case (l@ZLine0(_,
TwoRegisters(ZRegister.MEM_HL, _) | TwoRegisters(_, ZRegister.MEM_HL) | OneRegister(ZRegister.MEM_HL),
_), i) =>
flow(i)._1.statusBefore.hl match {
case SingleStatus(MemoryAddressConstant(th)) =>
if (flow(i)._1.importanceAfter.hlNumeric != Unimportant) Some(th.name)
else None
if (flow(i)._1.importanceAfter.hlNumeric != Unimportant) {
// println(th.name -> l)
Some(th.name)
}else None
case SingleStatus(CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _))) =>
if (flow(i)._1.importanceAfter.hlNumeric != Unimportant) Some(th.name)
else None
if (flow(i)._1.importanceAfter.hlNumeric != Unimportant) {
// println(th.name -> l)
Some(th.name)
} else None
case _ => None // TODO: ???
}
case _ => None

View File

@ -2,7 +2,7 @@ package millfork.assembly.z80.opt
import millfork.{CompilationFlag, NonOverlappingIntervals}
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
import millfork.assembly.z80.{TwoRegisters, ZFlag, ZLine, ZLine0}
import millfork.assembly.z80.{OneRegister, TwoRegisters, ZFlag, ZLine, ZLine0}
import millfork.env._
import millfork.error.ConsoleLogger
import millfork.node.ZRegister
@ -263,6 +263,22 @@ object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
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)))
@ -300,6 +316,24 @@ object WordVariableToRegisterOptimization extends AssemblyOptimization[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 != "" =>