1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-07-05 09:28:54 +00:00

Optimizations for LR35902 and 8080

This commit is contained in:
Karol Stasiak 2018-07-30 23:51:41 +02:00
parent 6d7643b817
commit 2bac42c187
8 changed files with 971 additions and 646 deletions

View File

@ -0,0 +1,761 @@
package millfork.assembly.z80.opt
import millfork.assembly.AssemblyOptimization
import millfork.assembly.z80._
import millfork.assembly.z80.ZOpcode._
import millfork.env.{CompoundConstant, Constant, MathOperator, NumericConstant}
import millfork.node.ZRegister
/**
* Optimizations valid for Intel8080, Z80, EZ80 and Sharp
* @author Karol Stasiak
*/
object AlwaysGoodI80Optimizations {
def change8BitLoadTarget(line: ZLine, newTarget: ZRegister.Value): ZLine = {
line match {
case ZLine(LD, TwoRegistersOffset(_, s, o), p, _) => ZLine(LD, TwoRegistersOffset(newTarget, s, o), p)
case ZLine(LD, TwoRegisters(_, s), p, _) => ZLine(LD, TwoRegisters(newTarget, s), p)
}
}
def for7Registers(f: ZRegister.Value => AssemblyRuleSet) = MultipleAssemblyRules(
List(ZRegister.A, ZRegister.B, ZRegister.C, ZRegister.D, ZRegister.E, ZRegister.H, ZRegister.L).map(f))
def for5LargeRegisters(f: ZRegister.Value => AssemblyRuleSet) = MultipleAssemblyRules(
List(ZRegister.HL, ZRegister.BC, ZRegister.DE, ZRegister.IX, ZRegister.IY).map(f))
def for6Registers(f: ZRegister.Value => AssemblyRuleSet) = MultipleAssemblyRules(
List(ZRegister.B, ZRegister.C, ZRegister.D, ZRegister.E, ZRegister.H, ZRegister.L).map(f))
val UsingKnownValueFromAnotherRegister = new RuleBasedAssemblyOptimization("Using known value from another register",
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
for7Registers(register =>
(Elidable & IsRegular8BitLoadFrom(register) & MatchRegister(register, 0)) ~~> ((code, ctx) =>
code.map(x => x.copy(
parameter = NumericConstant(ctx.get[Int](0), 1),
registers = x.registers match {
case TwoRegisters(t, _) => TwoRegisters(t, ZRegister.IMM_8)
case TwoRegistersOffset(t@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), _, o) => TwoRegistersOffset(t, ZRegister.IMM_8, o)
case TwoRegistersOffset(t, ZRegister.MEM_IX_D | ZRegister.MEM_IY_D, _) => TwoRegisters(t, ZRegister.IMM_8)
}
)))
),
(Elidable & MatchSourceIxOffsetOf8BitLoad(0) & MatchValueAtMatchedIxOffset(0, 1)) ~~> ((code, ctx) =>
code.map(x => x.copy(
parameter = NumericConstant(ctx.get[Int](1), 1),
registers = x.registers match {
case TwoRegisters(t, _) => TwoRegisters(t, ZRegister.IMM_8)
case TwoRegistersOffset(t@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), _, o) => TwoRegistersOffset(t, ZRegister.IMM_8, o)
case TwoRegistersOffset(t, ZRegister.MEM_IX_D | ZRegister.MEM_IY_D, _) => TwoRegisters(t, ZRegister.IMM_8)
}
))
),
for6Registers(register =>
(Elidable & HasRegisterParam(register) & HasOpcodeIn(Set(AND, ADD, ADC, SUB, SBC, XOR, OR, CP)) & MatchRegister(register, 0)) ~~> ((code, ctx) =>
code.map(x => x.copy(
parameter = NumericConstant(ctx.get[Int](0), 1),
registers = OneRegister(ZRegister.IMM_8)
)))
),
(Elidable & MatchIxOffset(0) & HasOpcodeIn(Set(AND, ADD, ADC, SUB, SBC, XOR, OR, CP)) & MatchValueAtMatchedIxOffset(0, 1)) ~~> ((code, ctx) =>
code.map(x => x.copy(
parameter = NumericConstant(ctx.get[Int](1), 1),
registers = OneRegister(ZRegister.IMM_8)
))
),
)
val ReloadingKnownValueFromMemory = new RuleBasedAssemblyOptimization("Reloading known value from memory",
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
for7Registers(register =>
Is8BitLoad(ZRegister.MEM_HL, register) ~
(Linear & Not(Changes(ZRegister.H)) & Not(Changes(ZRegister.L)) & Not(ChangesMemory) & Not(Changes(register)) & Not(IsRegular8BitLoadFrom(ZRegister.MEM_HL))).* ~
(Elidable & Is8BitLoad(register, ZRegister.MEM_HL)) ~~> { code => code.init }
),
for7Registers(register =>
Is8BitLoad(ZRegister.MEM_HL, register) ~
(Linear & Not(Changes(ZRegister.H)) & Not(Changes(ZRegister.L)) & Not(ChangesMemory) & Not(Changes(register)) & Not(IsRegular8BitLoadFrom(ZRegister.MEM_HL))).* ~
(Elidable & IsRegular8BitLoadFrom(ZRegister.MEM_HL)) ~~> { code =>
val last = code.last
code.init :+ last.copy(registers = TwoRegisters(last.registers.asInstanceOf[TwoRegisters].target, register))
}
),
(Is8BitLoad(ZRegister.MEM_ABS_8, ZRegister.A) & MatchParameter(0)).captureLine(1) ~
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8) & MatchParameter(0)) ~~> { code => code.init },
(Is8BitLoad(ZRegister.MEM_HL, ZRegister.A) & MatchConstantInHL(0)).captureLine(1) ~
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8) & MatchParameter(0)) ~~> { code => code.init },
(Is8BitLoad(ZRegister.MEM_ABS_8, ZRegister.A) & MatchParameter(0)).captureLine(1) ~
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_HL) & MatchConstantInHL(0)) ~~> { code => code.init },
(Is8BitLoad(ZRegister.MEM_HL, ZRegister.A) & MatchConstantInHL(0)).captureLine(1) ~
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_HL) & MatchConstantInHL(0)) ~~> { code => code.init },
(Is16BitLoad(ZRegister.MEM_ABS_16, ZRegister.HL) & MatchParameter(0)).captureLine(1) ~
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.HL))).* ~
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.MEM_ABS_16) & MatchParameter(0)) ~~> { code => code.init },
(Is8BitLoad(ZRegister.MEM_ABS_8, ZRegister.A) & MatchParameter(0) & MatchRegister(ZRegister.A, 2)).captureLine(1) ~
(Linear & DoesntChangeMemoryAt(1) & Not(Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8))).* ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8) & MatchParameter(0)) ~~> { (code, ctx) =>
code.init :+ ZLine.ldImm8(ZRegister.A, ctx.get[Int](2))
},
(Is16BitLoad(ZRegister.MEM_ABS_16, ZRegister.HL) & MatchParameter(0) & MatchConstantInHL(2)).captureLine(1) ~
(Linear & DoesntChangeMemoryAt(1) & Not(Is16BitLoad(ZRegister.HL, ZRegister.MEM_ABS_16))).* ~
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.MEM_ABS_16) & MatchParameter(0)) ~~> { (code, ctx) =>
code.init :+ ZLine.ldImm16(ZRegister.HL, ctx.get[Constant](2))
},
)
val PointlessLoad = new RuleBasedAssemblyOptimization("Pointless load",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
// 0-6
for7Registers(register =>
(Elidable & Is8BitLoadTo(register) & DoesntMatterWhatItDoesWith(register)) ~~> (_ => Nil)
),
// 7-11
for5LargeRegisters(register =>
(Elidable & Is16BitLoadTo(register) & DoesntMatterWhatItDoesWith(register)) ~~> (_ => Nil)
),
// 12-18
for7Registers(register =>
(Is8BitLoad(register, ZRegister.IMM_8) & MatchImmediate(0)) ~
(Linear & Not(Changes(register))).* ~
(Elidable & Is8BitLoad(register, ZRegister.IMM_8) & MatchImmediate(0)) ~~> (_.init)
),
// 19-23
for5LargeRegisters(register =>
(Is16BitLoad(register, ZRegister.IMM_16) & MatchImmediate(0)) ~
(Linear & Not(Changes(register))).* ~
(Elidable & Is16BitLoad(register, ZRegister.IMM_16) & MatchImmediate(0)) ~~> (_.init)
),
// 24
(Elidable & Is8BitLoadTo(ZRegister.MEM_HL)) ~
(Linear & Not(ConcernsMemory) & Not(Changes(ZRegister.HL))).* ~
Is8BitLoadTo(ZRegister.MEM_HL) ~~> (_.tail),
// 25
(Elidable & Is8BitLoadTo(ZRegister.MEM_DE)) ~
(Linear & Not(ConcernsMemory) & Not(Changes(ZRegister.DE))).* ~
Is8BitLoadTo(ZRegister.MEM_DE) ~~> (_.tail),
// 26
(Elidable & Is8BitLoadTo(ZRegister.MEM_BC)) ~
(Linear & Not(ConcernsMemory) & Not(Changes(ZRegister.BC))).* ~
Is8BitLoadTo(ZRegister.MEM_BC) ~~> (_.tail),
// 27
(Elidable & MatchTargetIxOffsetOf8BitLoad(0) & MatchUnimportantIxOffset(0)) ~~> (_ => Nil),
// 28-34
for7Registers(register =>
(Elidable & Is8BitLoadTo(register) & NoOffset & MatchSourceRegisterAndOffset(1)) ~
(Linear & Not(Concerns(register)) & DoesntChangeMatchedRegisterAndOffset(1)).* ~
(Elidable & IsRegular8BitLoadFrom(register) & DoesntMatterWhatItDoesWith(register)) ~~> { code =>
val last = code.last
val head = code.head
code.tail.init :+ ZLine(LD, (last.registers, head.registers) match {
case (TwoRegisters(t, _), TwoRegisters(_, s)) => TwoRegisters(t, s)
case (TwoRegistersOffset(t, _, o), TwoRegisters(_, s)) => TwoRegistersOffset(t, s, o)
case (TwoRegisters(t, _), TwoRegistersOffset(_, s, o)) => TwoRegistersOffset(t, s, o)
case _ => ???
}, head.parameter)
}
),
// 35-41
for7Registers(register =>
(Elidable & Is8BitLoad(register, register)) ~~> (_ => Nil)
),
// 42-48
for6Registers(register =>
(Elidable & Is8BitLoadTo(register) & MatchSourceRegisterAndOffset(0) & MatchParameterOrNothing(1)) ~
(Linear & Not(Concerns(register)) & DoesntChangeMatchedRegisterAndOffset(0)).* ~
(Elidable & HasOpcodeIn(Set(ADD, ADC, XOR, OR, AND, CP, SUB, SBC)) &
HasRegisters(OneRegister(register)) & DoesntMatterWhatItDoesWith(register)) ~~> { (code, ctx) =>
code.tail.init :+ code.last.copy(registers = ctx.get[RegisterAndOffset](0).toOneRegister, parameter = ctx.get[Constant](1))
}
),
// 49-54
MultipleAssemblyRules {
import ZRegister._
val regs = Seq((BC, B, C), (DE, D, E), (HL, H, L))
for {
(t, th, tl) <- regs
(s, sh, sl) <- regs
if t != HL
if t != s
} yield {
// TODO: make it a bit more universal
(Elidable & Is8BitLoad(th, sh)) ~
(Elidable & Is8BitLoad(tl, sl)) ~
(HasOpcode(OR) & HasRegisterParam(A)).?.capture(1) ~
(Elidable & HasOpcodeIn(Set(ADD_16, ADC_16, SBC_16)) & HasRegisters(TwoRegisters(HL, t)) & DoesntMatterWhatItDoesWith(t)) ~~> {
(code, ctx) =>
ctx.get[List[ZLine]](1) :+ code.last.copy(registers = TwoRegisters(HL, s))
}
}
},
// 55-59
for5LargeRegisters(register =>
(Is16BitLoad(register, ZRegister.MEM_ABS_16) & MatchParameter(0)).captureLine(1) ~
(Linear & Not(Changes(register)) & DoesntChangeMemoryAt(1)).* ~
(Elidable & Is16BitLoad(register, ZRegister.MEM_ABS_16) & MatchParameter(0)) ~~> (_.init)
),
// 60
(HasOpcode(LD) & MatchSourceRegisterAndOffset(0) & MatchTargetRegisterAndOffset(1)) ~
Where(ctx => ctx.get[RegisterAndOffset](0).register != ZRegister.MEM_ABS_8 && ctx.get[RegisterAndOffset](1).register != ZRegister.MEM_ABS_8) ~
(Elidable & HasOpcode(LD) & MatchSourceRegisterAndOffset(1) & MatchTargetRegisterAndOffset(0)) ~~> (_.init)
)
val PointlessStackStashing = new RuleBasedAssemblyOptimization("Pointless stack stashing",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
// 0-4
for5LargeRegisters(register => {
(Elidable & HasOpcode(PUSH) & HasRegisterParam(register)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(Changes(register)) & Not(ReadsStackPointer)).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(register)) ~~> (_.tail.init)
}),
// 5
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.DE) & DoesntMatterWhatItDoesWith(ZRegister.D)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(Changes(ZRegister.D)) & Not(ReadsStackPointer)).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.DE)) ~~> {code =>
ZLine.ld8(ZRegister.D, ZRegister.E) :: (code.tail.init :+ ZLine.ld8(ZRegister.E, ZRegister.D))
},
// 6
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.DE) & DoesntMatterWhatItDoesWith(ZRegister.E)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(Changes(ZRegister.E)) & Not(ReadsStackPointer)).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.DE)) ~~> { code =>
ZLine.ld8(ZRegister.E, ZRegister.D) :: (code.tail.init :+ ZLine.ld8(ZRegister.D, ZRegister.E))
},
// 7
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.BC) & DoesntMatterWhatItDoesWith(ZRegister.B)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(Changes(ZRegister.B)) & Not(ReadsStackPointer)).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.BC)) ~~> (code =>
ZLine.ld8(ZRegister.B, ZRegister.C) :: (code.tail.init :+ ZLine.ld8(ZRegister.C, ZRegister.B))
),
// 8
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.BC) & DoesntMatterWhatItDoesWith(ZRegister.C)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(Changes(ZRegister.C)) & Not(ReadsStackPointer)).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.BC)) ~~> { code =>
ZLine.ld8(ZRegister.C, ZRegister.B) :: (code.tail.init :+ ZLine.ld8(ZRegister.B, ZRegister.C))
},
// 9
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.H)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(Changes(ZRegister.H)) & Not(ReadsStackPointer)).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.HL)) ~~> { code =>
ZLine.ld8(ZRegister.H, ZRegister.L) :: (code.tail.init :+ ZLine.ld8(ZRegister.L, ZRegister.H))
},
// 10
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.L)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(Changes(ZRegister.L)) & Not(ReadsStackPointer)).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.HL)) ~~> { code =>
ZLine.ld8(ZRegister.L, ZRegister.H) :: (code.tail.init :+ ZLine.ld8(ZRegister.H, ZRegister.L))
},
// 11-15
for5LargeRegisters(register => {
(Elidable & HasOpcode(LD_16) & HasRegisters(TwoRegisters(register, ZRegister.IMM_16))) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(register) & DoesntMatterWhatItDoesWith(register)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(ReadsStackPointer)).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(register)) ~~> { code =>
code.drop(2).init :+ code.head
}
}),
)
private def simplifiable16BitAddWithSplitTarget(targetH: ZRegister.Value, targetL: ZRegister.Value, target: ZRegister.Value, source: ZRegister.Value) = MultipleAssemblyRules(List(
(Is8BitLoad(targetH, ZRegister.IMM_8) & MatchImmediate(1)) ~
(Linear & Not(Changes(target))).* ~
(Is8BitLoad(targetL, ZRegister.IMM_8) & MatchImmediate(2)) ~
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
(Is16BitLoad(source, ZRegister.IMM_16) & MatchImmediate(0)) ~
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(target, source)) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
val value = (ctx.get[Constant](0) + ctx.get[Constant](1).asl(8) + ctx.get[Constant](2)).quickSimplify
code.init :+ ZLine.ldImm16(target, value)
},
(Is8BitLoad(targetL, ZRegister.IMM_8) & MatchImmediate(2)) ~
(Linear & Not(Changes(target))).* ~
(Is8BitLoad(targetH, ZRegister.IMM_8) & MatchImmediate(1)) ~
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
(Is16BitLoad(source, ZRegister.IMM_16) & MatchImmediate(0)) ~
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(target, source)) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
val value = (ctx.get[Constant](0) + ctx.get[Constant](1).asl(8) + ctx.get[Constant](2)).quickSimplify
code.init :+ ZLine.ldImm16(target, value)
},
(Is16BitLoad(source, ZRegister.IMM_16) & MatchImmediate(0)) ~
(Linear & Not(Changes(targetL)) & Not(Changes(source))).* ~
(Is8BitLoad(targetL, ZRegister.IMM_8) & MatchImmediate(2)) ~
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
(Is8BitLoad(targetH, ZRegister.IMM_8) & MatchImmediate(1)) ~
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(target, source)) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
val value = (ctx.get[Constant](0) + ctx.get[Constant](1).asl(8) + ctx.get[Constant](2)).quickSimplify
code.init :+ ZLine.ldImm16(target, value)
},
(Is16BitLoad(source, ZRegister.IMM_16) & MatchImmediate(0)) ~
(Linear & Not(Changes(targetH)) & Not(Changes(source))).* ~
(Is8BitLoad(targetH, ZRegister.IMM_8) & MatchImmediate(1)) ~
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
(Is8BitLoad(targetL, ZRegister.IMM_8) & MatchImmediate(2)) ~
(Linear & Not(Changes(target)) & Not(Changes(source))).* ~
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(target, source)) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
val value = (ctx.get[Constant](0) + ctx.get[Constant](1).asl(8) + ctx.get[Constant](2)).quickSimplify
code.init :+ ZLine.ldImm16(target, value)
},
))
val SimplifiableMaths = new RuleBasedAssemblyOptimization("Simplifiable maths",
needsFlowInfo = FlowInfoRequirement.BothFlows,
for6Registers(register =>
(Elidable & HasOpcode(ADD) & MatchRegister(ZRegister.A, 0) & HasRegisterParam(register) & MatchRegister(register, 1) &
DoesntMatterWhatItDoesWithFlags) ~~> ((code, ctx) => List(ZLine.ldImm8(ZRegister.A, (ctx.get[Int](0) + ctx.get[Int](1)) & 0xff))),
),
simplifiable16BitAddWithSplitTarget(ZRegister.H, ZRegister.L, ZRegister.HL, ZRegister.BC),
simplifiable16BitAddWithSplitTarget(ZRegister.H, ZRegister.L, ZRegister.HL, ZRegister.DE),
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC)) & MatchRegister(ZRegister.BC, 0) & MatchRegister(ZRegister.HL, 1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
List(ZLine.ldImm16(ZRegister.HL, ctx.get[Int](0) + ctx.get[Int](1)))
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & MatchRegister(ZRegister.DE, 0) & MatchRegister(ZRegister.HL, 1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
List(ZLine.ldImm16(ZRegister.HL, ctx.get[Int](0) + ctx.get[Int](1)))
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.HL)) & MatchRegister(ZRegister.HL, 1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
List(ZLine.ldImm16(ZRegister.HL, 2 * ctx.get[Int](1) & 0xffff))
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC)) & HasRegister(ZRegister.BC, 0) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
Nil
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & HasRegister(ZRegister.DE, 0) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
Nil
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC)) & HasRegister(ZRegister.BC, 1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
List(ZLine.register(ZOpcode.INC_16, ZRegister.HL))
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & HasRegister(ZRegister.DE, 1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
List(ZLine.register(ZOpcode.INC_16, ZRegister.HL))
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC)) & MatchRegister(ZRegister.BC, 0) & MatchConstantInHL(1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
List(ZLine.ldImm16(ZRegister.HL, (ctx.get[Constant](1) + ctx.get[Int](0)).quickSimplify))
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & MatchRegister(ZRegister.DE, 0) & MatchConstantInHL(1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
List(ZLine.ldImm16(ZRegister.HL, (ctx.get[Constant](1) + ctx.get[Int](0)).quickSimplify))
},
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L)) ~
(Elidable & Is8BitLoadTo(ZRegister.L)) ~
(Elidable & Is8BitLoadTo(ZRegister.H)) ~
(HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & DoesntMatterWhatItDoesWith(ZRegister.D, ZRegister.E)) ~~> { code =>
List(
change8BitLoadTarget(code(2), ZRegister.E),
change8BitLoadTarget(code(3), ZRegister.D),
code.last)
},
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L)) ~
(Elidable & Is8BitLoadTo(ZRegister.H)) ~
(Elidable & Is8BitLoadTo(ZRegister.L)) ~
(HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & DoesntMatterWhatItDoesWith(ZRegister.D, ZRegister.E)) ~~> { code =>
List(
change8BitLoadTarget(code(2), ZRegister.D),
change8BitLoadTarget(code(3), ZRegister.E),
code.last)
},
(Elidable & HasOpcodeIn(Set(ADD, OR, XOR, SUB)) & Has8BitImmediate(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
(Elidable & HasOpcode(AND) & Has8BitImmediate(0xff) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
(Elidable & HasOpcode(AND) & Has8BitImmediate(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => List(ZLine.ldImm8(ZRegister.A, 0))),
(Elidable & HasOpcode(AND) & Has8BitImmediate(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => List(ZLine.ldImm8(ZRegister.A, 0))),
(Elidable & HasOpcode(OR) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0)) ~
(Elidable & HasOpcodeIn(Set(JP, JR)) & HasRegisters(IfFlagSet(ZFlag.S)) & DoesntMatterWhatItDoesWithFlags) ~
Where(ctx => ctx.get[Constant](1).isInstanceOf[NumericConstant]) ~~> { (code, ctx) =>
val value = (ctx.get[Int](0) | ctx.get[NumericConstant](1).value).toInt & 0xff
if (value.&(0x80) == 0) List(ZLine.ldImm8(ZRegister.A, value))
else List(ZLine.ldImm8(ZRegister.A, value), code.last.copy(registers = NoRegisters))
},
(Elidable & HasOpcode(OR) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0)) ~
(Elidable & HasOpcodeIn(Set(JP, JR)) & HasRegisters(IfFlagClear(ZFlag.S)) & DoesntMatterWhatItDoesWithFlags) ~
Where(ctx => ctx.get[Constant](1).isInstanceOf[NumericConstant]) ~~> { (code, ctx) =>
val value = (ctx.get[Int](0) | ctx.get[NumericConstant](1).value).toInt & 0xff
if (value.&(0x80) != 0) List(ZLine.ldImm8(ZRegister.A, value))
else List(ZLine.ldImm8(ZRegister.A, value), code.last.copy(registers = NoRegisters))
},
(Elidable & HasOpcode(ADD) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
List(ZLine.ldImm8(ZRegister.A, (ctx.get[Constant](1) + ctx.get[Int](0)).quickSimplify))
},
(Elidable & HasOpcode(SUB) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
List(ZLine.ldImm8(ZRegister.A, (NumericConstant(ctx.get[Int](0) & 0xff, 1) - ctx.get[Constant](1)).quickSimplify))
},
(Elidable & HasOpcode(OR) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.Or, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
},
(Elidable & HasOpcode(XOR) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.Exor, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
},
(Elidable & HasOpcode(AND) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.And, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
},
(Elidable & HasOpcode(ADD) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0)) ~
(Elidable & HasOpcode(DAA) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.DecimalPlus, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
},
(Elidable & HasOpcode(SUB) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0)) ~
(Elidable & HasOpcode(DAA) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.DecimalMinus, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
},
(Elidable & (Is8BitLoadTo(ZRegister.A) | HasOpcode(LD) & HasRegisters(TwoRegisters(ZRegister.A, ZRegister.MEM_ABS_8)))) ~
(Elidable & HasOpcode(SUB) & Has8BitImmediate(0)) ~
(Is8BitLoadTo(ZRegister.A) | HasOpcode(LD) & HasRegisters(TwoRegisters(ZRegister.A, ZRegister.MEM_ABS_8))) ~
(Elidable & HasOpcode(SBC)) ~~> { code =>
List(code(2), code(3).copy(opcode = SUB))
},
(Elidable & HasOpcode(DEC) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~
(Elidable & Linear & DoesntConcernMatchedRegisterAndOffset(0)).* ~
(Elidable & HasOpcode(INC) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_.tail.init),
(Elidable & HasOpcode(INC) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~
(Elidable & Linear & DoesntConcernMatchedRegisterAndOffset(0)).* ~
(Elidable & HasOpcode(DEC) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_.tail.init),
(Elidable & HasOpcode(DEC_16) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~
(Elidable & Linear & DoesntConcernMatchedRegisterAndOffset(0)).* ~
(Elidable & HasOpcode(INC_16) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_.tail.init),
(Elidable & HasOpcode(INC_16) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~
(Elidable & Linear & DoesntConcernMatchedRegisterAndOffset(0)).* ~
(Elidable & HasOpcode(DEC_16) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_.tail.init),
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.B)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.C)) ~
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE))) ~~> { code =>
ZLine.registers(code.last.opcode, ZRegister.HL, ZRegister.BC) :: code.take(2)
},
(Elidable & Is8BitLoad(ZRegister.B, ZRegister.D)) ~
(Elidable & Is8BitLoad(ZRegister.C, ZRegister.E)) ~
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC))) ~~> { code =>
ZLine.registers(code.last.opcode, ZRegister.HL, ZRegister.DE) :: code.take(2)
},
(Elidable & HasOpcode(INC) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(ADD) & Has8BitImmediate(0xff) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
(Elidable & HasOpcode(SUB) & Has8BitImmediate(0)) ~
(Elidable & Is8BitLoadTo(ZRegister.A)) ~
(Elidable & HasOpcode(SBC)) ~~> { code =>
List(code(1), code.last.copy(opcode = SUB))
}
)
val FreeHL = new RuleBasedAssemblyOptimization("Free HL",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.IMM_16)) ~
(Elidable & Is8BitLoadTo(ZRegister.MEM_HL) & DoesntMatterWhatItDoesWith(ZRegister.HL, ZRegister.A)) ~~> (code =>
List(
code(1).copy(registers = TwoRegisters(ZRegister.A, code(1).registers.asInstanceOf[TwoRegisters].source)),
code.head.copy(opcode = LD, registers = TwoRegisters(ZRegister.MEM_ABS_8, ZRegister.A)),
)),
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.IMM_16)) ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_HL) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
List(
code.head.copy(opcode = LD, registers = TwoRegisters(ZRegister.A, ZRegister.MEM_ABS_8)),
)),
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.IMM_16)) ~
(Elidable & IsRegular8BitLoadFrom(ZRegister.MEM_HL) & DoesntMatterWhatItDoesWith(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.A)) ~~> (code =>
List(
code.head.copy(opcode = LD, registers = TwoRegisters(ZRegister.A, ZRegister.MEM_ABS_8)),
code(1).copy(registers = TwoRegisters(code(1).registers.asInstanceOf[TwoRegisters].target, ZRegister.A)),
)),
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.IMM_16)) ~
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
List(
code.head.copy(registers = TwoRegisters(ZRegister.DE, ZRegister.IMM_16))
)),
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.IMM_16)) ~
(Elidable & Is8BitLoad(ZRegister.B, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.C, ZRegister.L) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
List(
code.head.copy(registers = TwoRegisters(ZRegister.BC, ZRegister.IMM_16))
)),
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.B)) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.C)) ~
(Elidable & HasOpcodeIn(Set(INC_16, DEC_16, PUSH, POP)) & HasRegisterParam(ZRegister.HL)) ~
(Elidable & Is8BitLoad(ZRegister.B, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.C, ZRegister.L) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
List(
code(2).copy(registers = OneRegister(ZRegister.BC))
)),
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.D)) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.E)) ~
(Elidable & HasOpcodeIn(Set(INC_16, DEC_16, PUSH, POP)) & HasRegisterParam(ZRegister.HL)) ~
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
List(
code(2).copy(registers = OneRegister(ZRegister.DE))
)),
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.D)) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.E)) ~
(Elidable & HasOpcodeIn(Set(INC_16, DEC_16, PUSH, POP)) & HasRegisterParam(ZRegister.HL)) ~
(Elidable & Is8BitLoad(ZRegister.B, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.C, ZRegister.L) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
List(
ZLine.ld8(ZRegister.B, ZRegister.D),
ZLine.ld8(ZRegister.C, ZRegister.E),
code(2).copy(registers = OneRegister(ZRegister.BC))
)),
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.B)) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.C)) ~
(Elidable & HasOpcodeIn(Set(INC_16, DEC_16, PUSH, POP)) & HasRegisterParam(ZRegister.HL)) ~
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
List(
ZLine.ld8(ZRegister.D, ZRegister.B),
ZLine.ld8(ZRegister.E, ZRegister.C),
code(2).copy(registers = OneRegister(ZRegister.DE))
)),
// 2 bytes more, but 3 cycles fewer and frees BC
(Elidable & Is16BitLoad(ZRegister.BC, ZRegister.IMM_16) & MatchParameter(0)) ~
(Linear & Not(Concerns(ZRegister.BC)) & Not(Concerns(ZRegister.HL))).*.capture(1) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.A)) ~
(Elidable & Is8BitLoadTo(ZRegister.H) & Has8BitImmediate(0)) ~
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC)) &
DoesntMatterWhatItDoesWithFlagsExceptCarry &
DoesntMatterWhatItDoesWith(ZRegister.BC)) ~~> { (code, ctx) =>
val offset = ctx.get[Constant](0)
ctx.get[List[ZLine]](1) ++ List(
ZLine.imm8(ADD, offset.loByte),
ZLine.ld8(ZRegister.L, ZRegister.A),
ZLine.ldImm8(ZRegister.A, 0),
ZLine.imm8(ADC, offset.hiByte),
ZLine.ld8(ZRegister.H, ZRegister.A))
},
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.D)) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.E)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.DE))
},
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.B)) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.C)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.BC))
},
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.DE) & DoesntMatterWhatItDoesWith(ZRegister.DE)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.HL))
},
(Elidable & Is8BitLoad(ZRegister.B, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.C, ZRegister.L)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.BC) & DoesntMatterWhatItDoesWith(ZRegister.BC)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.HL))
},
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.E)) ~
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.D)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.DE))
},
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.C)) ~
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.B)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.BC))
},
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L)) ~
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.DE) & DoesntMatterWhatItDoesWith(ZRegister.DE)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.HL))
},
(Elidable & Is8BitLoad(ZRegister.C, ZRegister.L)) ~
(Elidable & Is8BitLoad(ZRegister.B, ZRegister.H)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.BC) & DoesntMatterWhatItDoesWith(ZRegister.BC)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.HL))
},
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.B)) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.C)) ~
(Elidable & HasOpcode(EX_DE_HL) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> { _ =>
List(
ZLine.ld8(ZRegister.D, ZRegister.B),
ZLine.ld8(ZRegister.E, ZRegister.C))
},
(Elidable & HasOpcode(LD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.MEM_ABS_16)) & MatchParameter(0)) ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.L) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> { (code, ctx) =>
List(ZLine.ldAbs8(ZRegister.A, ctx.get[Constant](0)))
},
(Elidable & HasOpcode(LD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.MEM_ABS_16)) & MatchParameter(0)) ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.H) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> { (code, ctx) =>
List(ZLine.ldAbs8(ZRegister.A, (ctx.get[Constant](0) + 1).quickSimplify))
},
(Elidable & HasOpcode(LD_16) & HasRegisters(TwoRegisters(ZRegister.DE, ZRegister.MEM_ABS_16)) & MatchParameter(0)) ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.E) & DoesntMatterWhatItDoesWith(ZRegister.DE)) ~~> { (code, ctx) =>
List(ZLine.ldAbs8(ZRegister.A, ctx.get[Constant](0)))
},
(Elidable & HasOpcode(LD_16) & HasRegisters(TwoRegisters(ZRegister.DE, ZRegister.MEM_ABS_16)) & MatchParameter(0)) ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.D) & DoesntMatterWhatItDoesWith(ZRegister.DE)) ~~> { (code, ctx) =>
List(ZLine.ldAbs8(ZRegister.A, (ctx.get[Constant](0) + 1).quickSimplify))
},
(Elidable & HasOpcode(LD_16) & HasRegisters(TwoRegisters(ZRegister.BC, ZRegister.MEM_ABS_16)) & MatchParameter(0)) ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.C) & DoesntMatterWhatItDoesWith(ZRegister.DE)) ~~> { (code, ctx) =>
List(ZLine.ldAbs8(ZRegister.A, ctx.get[Constant](0)))
},
(Elidable & HasOpcode(LD_16) & HasRegisters(TwoRegisters(ZRegister.BC, ZRegister.MEM_ABS_16)) & MatchParameter(0)) ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.B) & DoesntMatterWhatItDoesWith(ZRegister.BC)) ~~> { (code, ctx) =>
List(ZLine.ldAbs8(ZRegister.A, (ctx.get[Constant](0) + 1).quickSimplify))
},
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.B)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.C)) ~
(Not(Concerns(ZRegister.DE)) & Not(Concerns(ZRegister.BC))).* ~
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & DoesntMatterWhatItDoesWith(ZRegister.DE, ZRegister.BC)) ~~> { code =>
code.drop(2).init :+ code.last.copy(registers = TwoRegisters(ZRegister.HL, ZRegister.BC))
},
(Elidable & Is8BitLoad(ZRegister.B, ZRegister.D)) ~
(Elidable & Is8BitLoad(ZRegister.C, ZRegister.E)) ~
(Not(Concerns(ZRegister.DE)) & Not(Concerns(ZRegister.BC))).* ~
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC)) & DoesntMatterWhatItDoesWith(ZRegister.DE, ZRegister.BC)) ~~> { code =>
code.drop(2).init :+ code.last.copy(registers = TwoRegisters(ZRegister.HL, ZRegister.DE))
},
)
val UnusedCodeRemoval = new RuleBasedAssemblyOptimization("Unreachable code removal",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
(HasOpcodeIn(Set(JP, JR)) & HasRegisters(NoRegisters)) ~ (Not(HasOpcode(LABEL)) & Elidable).+ ~~> (c => c.head :: Nil)
)
val UnusedLabelRemoval = new RuleBasedAssemblyOptimization("Unused label removal",
needsFlowInfo = FlowInfoRequirement.JustLabels,
(Elidable & HasOpcode(LABEL) & HasCallerCount(0)) ~~> (_ => Nil)
)
val BranchInPlaceRemoval = new RuleBasedAssemblyOptimization("Branch in place",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
(HasOpcodeIn(Set(JP, JR)) & MatchJumpTarget(0) & Elidable) ~
HasOpcodeIn(ZOpcodeClasses.NoopDiscards).* ~
(HasOpcode(LABEL) & MatchJumpTarget(0)) ~~> (c => c.last :: Nil),
)
val SimplifiableShifting = new RuleBasedAssemblyOptimization("Simplifiable shifting",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~~> { _ =>
List(
ZLine.implied(RRCA),
ZLine.imm8(AND, 0x80))
},
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~~> { _ =>
List(
ZLine.implied(RRCA),
ZLine.implied(RRCA),
ZLine.imm8(AND, 0xc0))
},
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~~> { _ =>
List(
ZLine.implied(RRCA),
ZLine.implied(RRCA),
ZLine.implied(RRCA),
ZLine.imm8(AND, 0xe0))
},
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~~> { _ =>
List(
ZLine.implied(RLCA),
ZLine.imm8(AND, 1))
},
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~~> { _ =>
List(
ZLine.implied(RLCA),
ZLine.implied(RLCA),
ZLine.imm8(AND, 3))
},
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~~> { _ =>
List(
ZLine.implied(RLCA),
ZLine.implied(RLCA),
ZLine.implied(RLCA),
ZLine.imm8(AND, 7))
},
)
val All: List[AssemblyOptimization[ZLine]] = List[AssemblyOptimization[ZLine]](
BranchInPlaceRemoval,
FreeHL,
PointlessLoad,
PointlessStackStashing,
ReloadingKnownValueFromMemory,
SimplifiableMaths,
SimplifiableShifting,
UnusedCodeRemoval,
UnusedLabelRemoval,
UsingKnownValueFromAnotherRegister,
)
}

View File

@ -7,6 +7,7 @@ import millfork.env.{CompoundConstant, Constant, MathOperator, NumericConstant}
import millfork.node.ZRegister
/**
* Optimizations valid for Z80 and EZ80
* @author Karol Stasiak
*/
object AlwaysGoodZ80Optimizations {
@ -27,245 +28,6 @@ object AlwaysGoodZ80Optimizations {
def for6Registers(f: ZRegister.Value => AssemblyRuleSet) = MultipleAssemblyRules(
List(ZRegister.B, ZRegister.C, ZRegister.D, ZRegister.E, ZRegister.H, ZRegister.L).map(f))
val UsingKnownValueFromAnotherRegister = new RuleBasedAssemblyOptimization("Using known value from another register",
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
for7Registers(register =>
(Elidable & IsRegular8BitLoadFrom(register) & MatchRegister(register, 0)) ~~> ((code, ctx) =>
code.map(x => x.copy(
parameter = NumericConstant(ctx.get[Int](0), 1),
registers = x.registers match {
case TwoRegisters(t, _) => TwoRegisters(t, ZRegister.IMM_8)
case TwoRegistersOffset(t@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), _, o) => TwoRegistersOffset(t, ZRegister.IMM_8, o)
case TwoRegistersOffset(t, ZRegister.MEM_IX_D | ZRegister.MEM_IY_D, _) => TwoRegisters(t, ZRegister.IMM_8)
}
)))
),
(Elidable & MatchSourceIxOffsetOf8BitLoad(0) & MatchValueAtMatchedIxOffset(0, 1)) ~~> ((code, ctx) =>
code.map(x => x.copy(
parameter = NumericConstant(ctx.get[Int](1), 1),
registers = x.registers match {
case TwoRegisters(t, _) => TwoRegisters(t, ZRegister.IMM_8)
case TwoRegistersOffset(t@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), _, o) => TwoRegistersOffset(t, ZRegister.IMM_8, o)
case TwoRegistersOffset(t, ZRegister.MEM_IX_D | ZRegister.MEM_IY_D, _) => TwoRegisters(t, ZRegister.IMM_8)
}
))
),
for6Registers(register =>
(Elidable & HasRegisterParam(register) & HasOpcodeIn(Set(AND, ADD, ADC, SUB, SBC, XOR, OR, CP)) & MatchRegister(register, 0)) ~~> ((code, ctx) =>
code.map(x => x.copy(
parameter = NumericConstant(ctx.get[Int](0), 1),
registers = OneRegister(ZRegister.IMM_8)
)))
),
(Elidable & MatchIxOffset(0) & HasOpcodeIn(Set(AND, ADD, ADC, SUB, SBC, XOR, OR, CP)) & MatchValueAtMatchedIxOffset(0, 1)) ~~> ((code, ctx) =>
code.map(x => x.copy(
parameter = NumericConstant(ctx.get[Int](1), 1),
registers = OneRegister(ZRegister.IMM_8)
))
),
)
val ReloadingKnownValueFromMemory = new RuleBasedAssemblyOptimization("Reloading known value from memory",
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
for7Registers(register =>
Is8BitLoad(ZRegister.MEM_HL, register) ~
(Linear & Not(Changes(ZRegister.H)) & Not(Changes(ZRegister.L)) & Not(ChangesMemory) & Not(Changes(register)) & Not(IsRegular8BitLoadFrom(ZRegister.MEM_HL))).* ~
(Elidable & Is8BitLoad(register, ZRegister.MEM_HL)) ~~> { code => code.init }
),
for7Registers(register =>
Is8BitLoad(ZRegister.MEM_HL, register) ~
(Linear & Not(Changes(ZRegister.H)) & Not(Changes(ZRegister.L)) & Not(ChangesMemory) & Not(Changes(register)) & Not(IsRegular8BitLoadFrom(ZRegister.MEM_HL))).* ~
(Elidable & IsRegular8BitLoadFrom(ZRegister.MEM_HL)) ~~> { code =>
val last = code.last
code.init :+ last.copy(registers = TwoRegisters(last.registers.asInstanceOf[TwoRegisters].target, register))
}
),
(Is8BitLoad(ZRegister.MEM_ABS_8, ZRegister.A) & MatchParameter(0)).captureLine(1) ~
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8) & MatchParameter(0)) ~~> { code => code.init },
(Is8BitLoad(ZRegister.MEM_HL, ZRegister.A) & MatchConstantInHL(0)).captureLine(1) ~
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8) & MatchParameter(0)) ~~> { code => code.init },
(Is8BitLoad(ZRegister.MEM_ABS_8, ZRegister.A) & MatchParameter(0)).captureLine(1) ~
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_HL) & MatchConstantInHL(0)) ~~> { code => code.init },
(Is8BitLoad(ZRegister.MEM_HL, ZRegister.A) & MatchConstantInHL(0)).captureLine(1) ~
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_HL) & MatchConstantInHL(0)) ~~> { code => code.init },
(Is16BitLoad(ZRegister.MEM_ABS_16, ZRegister.HL) & MatchParameter(0)).captureLine(1) ~
(Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.HL))).* ~
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.MEM_ABS_16) & MatchParameter(0)) ~~> { code => code.init },
(Is8BitLoad(ZRegister.MEM_ABS_8, ZRegister.A) & MatchParameter(0) & MatchRegister(ZRegister.A, 2)).captureLine(1) ~
(Linear & DoesntChangeMemoryAt(1) & Not(Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8))).* ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8) & MatchParameter(0)) ~~> { (code, ctx) =>
code.init :+ ZLine.ldImm8(ZRegister.A, ctx.get[Int](2))
},
(Is16BitLoad(ZRegister.MEM_ABS_16, ZRegister.HL) & MatchParameter(0) & MatchConstantInHL(2)).captureLine(1) ~
(Linear & DoesntChangeMemoryAt(1) & Not(Is16BitLoad(ZRegister.HL, ZRegister.MEM_ABS_16))).* ~
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.MEM_ABS_16) & MatchParameter(0)) ~~> { (code, ctx) =>
code.init :+ ZLine.ldImm16(ZRegister.HL, ctx.get[Constant](2))
},
)
val PointlessLoad = new RuleBasedAssemblyOptimization("Pointless load",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
// 0-6
for7Registers(register =>
(Elidable & Is8BitLoadTo(register) & DoesntMatterWhatItDoesWith(register)) ~~> (_ => Nil)
),
// 7-11
for5LargeRegisters(register =>
(Elidable & Is16BitLoadTo(register) & DoesntMatterWhatItDoesWith(register)) ~~> (_ => Nil)
),
// 12-18
for7Registers(register =>
(Is8BitLoad(register, ZRegister.IMM_8) & MatchImmediate(0)) ~
(Linear & Not(Changes(register))).* ~
(Elidable & Is8BitLoad(register, ZRegister.IMM_8) & MatchImmediate(0)) ~~> (_.init)
),
// 19-23
for5LargeRegisters(register =>
(Is16BitLoad(register, ZRegister.IMM_16) & MatchImmediate(0)) ~
(Linear & Not(Changes(register))).* ~
(Elidable & Is16BitLoad(register, ZRegister.IMM_16) & MatchImmediate(0)) ~~> (_.init)
),
// 24
(Elidable & Is8BitLoadTo(ZRegister.MEM_HL)) ~
(Linear & Not(ConcernsMemory) & Not(Changes(ZRegister.HL))).* ~
Is8BitLoadTo(ZRegister.MEM_HL) ~~> (_.tail),
// 25
(Elidable & Is8BitLoadTo(ZRegister.MEM_DE)) ~
(Linear & Not(ConcernsMemory) & Not(Changes(ZRegister.DE))).* ~
Is8BitLoadTo(ZRegister.MEM_DE) ~~> (_.tail),
// 26
(Elidable & Is8BitLoadTo(ZRegister.MEM_BC)) ~
(Linear & Not(ConcernsMemory) & Not(Changes(ZRegister.BC))).* ~
Is8BitLoadTo(ZRegister.MEM_BC) ~~> (_.tail),
// 27
(Elidable & MatchTargetIxOffsetOf8BitLoad(0) & MatchUnimportantIxOffset(0)) ~~> (_ => Nil),
// 28-34
for7Registers(register =>
(Elidable & Is8BitLoadTo(register) & NoOffset & MatchSourceRegisterAndOffset(1)) ~
(Linear & Not(Concerns(register)) & DoesntChangeMatchedRegisterAndOffset(1)).* ~
(Elidable & IsRegular8BitLoadFrom(register) & DoesntMatterWhatItDoesWith(register)) ~~> { code =>
val last = code.last
val head = code.head
code.tail.init :+ ZLine(LD, (last.registers, head.registers) match {
case (TwoRegisters(t, _), TwoRegisters(_, s)) => TwoRegisters(t, s)
case (TwoRegistersOffset(t, _, o), TwoRegisters(_, s)) => TwoRegistersOffset(t, s, o)
case (TwoRegisters(t, _), TwoRegistersOffset(_, s, o)) => TwoRegistersOffset(t, s, o)
case _ => ???
}, head.parameter)
}
),
// 35-41
for7Registers(register =>
(Elidable & Is8BitLoad(register, register)) ~~> (_ => Nil)
),
// 42-48
for6Registers(register =>
(Elidable & Is8BitLoadTo(register) & MatchSourceRegisterAndOffset(0) & MatchParameterOrNothing(1)) ~
(Linear & Not(Concerns(register)) & DoesntChangeMatchedRegisterAndOffset(0)).* ~
(Elidable & HasOpcodeIn(Set(ADD, ADC, XOR, OR, AND, CP, SUB, SBC)) &
HasRegisters(OneRegister(register)) & DoesntMatterWhatItDoesWith(register)) ~~> { (code, ctx) =>
code.tail.init :+ code.last.copy(registers = ctx.get[RegisterAndOffset](0).toOneRegister, parameter = ctx.get[Constant](1))
}
),
// 49-54
MultipleAssemblyRules {
import ZRegister._
val regs = Seq((BC, B, C), (DE, D, E), (HL, H, L))
for {
(t, th, tl) <- regs
(s, sh, sl) <- regs
if t != HL
if t != s
} yield {
// TODO: make it a bit more universal
(Elidable & Is8BitLoad(th, sh)) ~
(Elidable & Is8BitLoad(tl, sl)) ~
(HasOpcode(OR) & HasRegisterParam(A)).?.capture(1) ~
(Elidable & HasOpcodeIn(Set(ADD_16, ADC_16, SBC_16)) & HasRegisters(TwoRegisters(HL, t)) & DoesntMatterWhatItDoesWith(t)) ~~> {
(code, ctx) =>
ctx.get[List[ZLine]](1) :+ code.last.copy(registers = TwoRegisters(HL, s))
}
}
},
// 55-59
for5LargeRegisters(register =>
(Is16BitLoad(register, ZRegister.MEM_ABS_16) & MatchParameter(0)).captureLine(1) ~
(Linear & Not(Changes(register)) & DoesntChangeMemoryAt(1)).* ~
(Elidable & Is16BitLoad(register, ZRegister.MEM_ABS_16) & MatchParameter(0)) ~~> (_.init)
),
// 60
(HasOpcode(LD) & MatchSourceRegisterAndOffset(0) & MatchTargetRegisterAndOffset(1)) ~
Where(ctx => ctx.get[RegisterAndOffset](0).register != ZRegister.MEM_ABS_8 && ctx.get[RegisterAndOffset](1).register != ZRegister.MEM_ABS_8) ~
(Elidable & HasOpcode(LD) & MatchSourceRegisterAndOffset(1) & MatchTargetRegisterAndOffset(0)) ~~> (_.init)
)
val PointlessStackStashing = new RuleBasedAssemblyOptimization("Pointless stack stashing",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
// 0-4
for5LargeRegisters(register => {
(Elidable & HasOpcode(PUSH) & HasRegisterParam(register)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(Changes(register))).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(register)) ~~> (_.tail.init)
}),
// 5
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.DE) & DoesntMatterWhatItDoesWith(ZRegister.D)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(Changes(ZRegister.D))).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.DE)) ~~> {code =>
ZLine.ld8(ZRegister.D, ZRegister.E) :: (code.tail.init :+ ZLine.ld8(ZRegister.E, ZRegister.D))
},
// 6
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.DE) & DoesntMatterWhatItDoesWith(ZRegister.E)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(Changes(ZRegister.E))).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.DE)) ~~> { code =>
ZLine.ld8(ZRegister.E, ZRegister.D) :: (code.tail.init :+ ZLine.ld8(ZRegister.D, ZRegister.E))
},
// 7
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.BC) & DoesntMatterWhatItDoesWith(ZRegister.B)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(Changes(ZRegister.B))).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.BC)) ~~> (code =>
ZLine.ld8(ZRegister.B, ZRegister.C) :: (code.tail.init :+ ZLine.ld8(ZRegister.C, ZRegister.B))
),
// 8
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.BC) & DoesntMatterWhatItDoesWith(ZRegister.C)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(Changes(ZRegister.C))).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.BC)) ~~> { code =>
ZLine.ld8(ZRegister.C, ZRegister.B) :: (code.tail.init :+ ZLine.ld8(ZRegister.B, ZRegister.C))
},
// 9
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.H)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(Changes(ZRegister.H))).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.HL)) ~~> { code =>
ZLine.ld8(ZRegister.H, ZRegister.L) :: (code.tail.init :+ ZLine.ld8(ZRegister.L, ZRegister.H))
},
// 10
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.L)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH))) & Not(Changes(ZRegister.L)) ).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.HL)) ~~> { code =>
ZLine.ld8(ZRegister.L, ZRegister.H) :: (code.tail.init :+ ZLine.ld8(ZRegister.H, ZRegister.L))
},
// 11-15
for5LargeRegisters(register => {
(Elidable & HasOpcode(LD_16) & HasRegisters(TwoRegisters(register, ZRegister.IMM_16))) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(register) & DoesntMatterWhatItDoesWith(register)) ~
(Linear & Not(HasOpcodeIn(Set(POP,PUSH)))).* ~
(Elidable & HasOpcode(POP) & HasRegisterParam(register)) ~~> { code =>
code.drop(2).init :+ code.head
}
}),
)
private def simplifiable16BitAddWithSplitTarget(targetH: ZRegister.Value, targetL: ZRegister.Value, target: ZRegister.Value, source: ZRegister.Value) = MultipleAssemblyRules(List(
(Is8BitLoad(targetH, ZRegister.IMM_8) & MatchImmediate(1)) ~
(Linear & Not(Changes(target))).* ~
@ -309,199 +71,20 @@ object AlwaysGoodZ80Optimizations {
},
))
val SimplifiableMaths = new RuleBasedAssemblyOptimization("Simplifiable maths",
val SimplifiableMaths = new RuleBasedAssemblyOptimization("Simplifiable maths (Z80)",
needsFlowInfo = FlowInfoRequirement.BothFlows,
for6Registers(register =>
(Elidable & HasOpcode(ADD) & MatchRegister(ZRegister.A, 0) & HasRegisterParam(register) & MatchRegister(register, 1) &
DoesntMatterWhatItDoesWithFlags) ~~> ((code, ctx) => List(ZLine.ldImm8(ZRegister.A, (ctx.get[Int](0) + ctx.get[Int](1)) & 0xff))),
),
simplifiable16BitAddWithSplitTarget(ZRegister.H, ZRegister.L, ZRegister.HL, ZRegister.BC),
simplifiable16BitAddWithSplitTarget(ZRegister.H, ZRegister.L, ZRegister.HL, ZRegister.DE),
simplifiable16BitAddWithSplitTarget(ZRegister.IXH, ZRegister.IXL, ZRegister.IX, ZRegister.BC),
simplifiable16BitAddWithSplitTarget(ZRegister.IXH, ZRegister.IXL, ZRegister.IX, ZRegister.DE),
simplifiable16BitAddWithSplitTarget(ZRegister.IYH, ZRegister.IYL, ZRegister.IY, ZRegister.BC),
simplifiable16BitAddWithSplitTarget(ZRegister.IYH, ZRegister.IYL, ZRegister.IY, ZRegister.DE),
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC)) & MatchRegister(ZRegister.BC, 0) & MatchRegister(ZRegister.HL, 1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
List(ZLine.ldImm16(ZRegister.HL, ctx.get[Int](0) + ctx.get[Int](1)))
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & MatchRegister(ZRegister.DE, 0) & MatchRegister(ZRegister.HL, 1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
List(ZLine.ldImm16(ZRegister.HL, ctx.get[Int](0) + ctx.get[Int](1)))
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.HL)) & MatchRegister(ZRegister.HL, 1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
List(ZLine.ldImm16(ZRegister.HL, 2 * ctx.get[Int](1) & 0xffff))
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC)) & HasRegister(ZRegister.BC, 0) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
Nil
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & HasRegister(ZRegister.DE, 0) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
Nil
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC)) & HasRegister(ZRegister.BC, 1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
List(ZLine.register(ZOpcode.INC_16, ZRegister.HL))
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & HasRegister(ZRegister.DE, 1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
List(ZLine.register(ZOpcode.INC_16, ZRegister.HL))
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC)) & MatchRegister(ZRegister.BC, 0) & MatchConstantInHL(1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
List(ZLine.ldImm16(ZRegister.HL, (ctx.get[Constant](1) + ctx.get[Int](0)).quickSimplify))
},
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & MatchRegister(ZRegister.DE, 0) & MatchConstantInHL(1) & DoesntMatterWhatItDoesWithFlags) ~~> { (code, ctx) =>
List(ZLine.ldImm16(ZRegister.HL, (ctx.get[Constant](1) + ctx.get[Int](0)).quickSimplify))
},
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L)) ~
(Elidable & Is8BitLoadTo(ZRegister.L)) ~
(Elidable & Is8BitLoadTo(ZRegister.H)) ~
(HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & DoesntMatterWhatItDoesWith(ZRegister.D, ZRegister.E)) ~~> { code =>
List(
change8BitLoadTarget(code(2), ZRegister.E),
change8BitLoadTarget(code(3), ZRegister.D),
code.last)
},
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L)) ~
(Elidable & Is8BitLoadTo(ZRegister.H)) ~
(Elidable & Is8BitLoadTo(ZRegister.L)) ~
(HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE)) & DoesntMatterWhatItDoesWith(ZRegister.D, ZRegister.E)) ~~> { code =>
List(
change8BitLoadTarget(code(2), ZRegister.D),
change8BitLoadTarget(code(3), ZRegister.E),
code.last)
},
(Elidable & HasOpcodeIn(Set(ADD, OR, XOR, SUB)) & Has8BitImmediate(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
(Elidable & HasOpcode(AND) & Has8BitImmediate(0xff) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
(Elidable & HasOpcode(AND) & Has8BitImmediate(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => List(ZLine.ldImm8(ZRegister.A, 0))),
(Elidable & HasOpcode(AND) & Has8BitImmediate(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => List(ZLine.ldImm8(ZRegister.A, 0))),
(Elidable & HasOpcode(OR) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0)) ~
(Elidable & HasOpcodeIn(Set(JP, JR)) & HasRegisters(IfFlagSet(ZFlag.S)) & DoesntMatterWhatItDoesWithFlags) ~
Where(ctx => ctx.get[Constant](1).isInstanceOf[NumericConstant]) ~~> { (code, ctx) =>
val value = (ctx.get[Int](0) | ctx.get[NumericConstant](1).value).toInt & 0xff
if (value.&(0x80) == 0) List(ZLine.ldImm8(ZRegister.A, value))
else List(ZLine.ldImm8(ZRegister.A, value), code.last.copy(registers = NoRegisters))
},
(Elidable & HasOpcode(OR) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0)) ~
(Elidable & HasOpcodeIn(Set(JP, JR)) & HasRegisters(IfFlagClear(ZFlag.S)) & DoesntMatterWhatItDoesWithFlags) ~
Where(ctx => ctx.get[Constant](1).isInstanceOf[NumericConstant]) ~~> { (code, ctx) =>
val value = (ctx.get[Int](0) | ctx.get[NumericConstant](1).value).toInt & 0xff
if (value.&(0x80) != 0) List(ZLine.ldImm8(ZRegister.A, value))
else List(ZLine.ldImm8(ZRegister.A, value), code.last.copy(registers = NoRegisters))
},
(Elidable & HasOpcode(ADD) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
List(ZLine.ldImm8(ZRegister.A, (ctx.get[Constant](1) + ctx.get[Int](0)).quickSimplify))
},
(Elidable & HasOpcode(SUB) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
List(ZLine.ldImm8(ZRegister.A, (NumericConstant(ctx.get[Int](0) & 0xff, 1) - ctx.get[Constant](1)).quickSimplify))
},
(Elidable & HasOpcode(OR) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.Or, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
},
(Elidable & HasOpcode(XOR) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.Exor, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
},
(Elidable & HasOpcode(AND) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.And, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
},
(Elidable & HasOpcode(ADD) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0)) ~
(Elidable & HasOpcode(DAA) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.DecimalPlus, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
},
(Elidable & HasOpcode(SUB) & Match8BitImmediate(1) & MatchRegister(ZRegister.A, 0)) ~
(Elidable & HasOpcode(DAA) & DoesntMatterWhatItDoesWithFlags) ~~> {(code, ctx) =>
List(ZLine.ldImm8(ZRegister.A, CompoundConstant(MathOperator.DecimalMinus, NumericConstant(ctx.get[Int](0) & 0xff, 1), ctx.get[Constant](1)).quickSimplify))
},
(Elidable & (Is8BitLoadTo(ZRegister.A) | HasOpcode(LD) & HasRegisters(TwoRegisters(ZRegister.A, ZRegister.MEM_ABS_8)))) ~
(Elidable & HasOpcode(SUB) & Has8BitImmediate(0)) ~
(Is8BitLoadTo(ZRegister.A) | HasOpcode(LD) & HasRegisters(TwoRegisters(ZRegister.A, ZRegister.MEM_ABS_8))) ~
(Elidable & HasOpcode(SBC)) ~~> { code =>
List(code(2), code(3).copy(opcode = SUB))
},
(Elidable & HasOpcode(DEC) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~
(Elidable & Linear & DoesntConcernMatchedRegisterAndOffset(0)).* ~
(Elidable & HasOpcode(INC) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_.tail.init),
(Elidable & HasOpcode(INC) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~
(Elidable & Linear & DoesntConcernMatchedRegisterAndOffset(0)).* ~
(Elidable & HasOpcode(DEC) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_.tail.init),
(Elidable & HasOpcode(DEC_16) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~
(Elidable & Linear & DoesntConcernMatchedRegisterAndOffset(0)).* ~
(Elidable & HasOpcode(INC_16) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_.tail.init),
(Elidable & HasOpcode(INC_16) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~
(Elidable & Linear & DoesntConcernMatchedRegisterAndOffset(0)).* ~
(Elidable & HasOpcode(DEC_16) & MatchSoleRegisterAndOffset(0) & DoesntMatterWhatItDoesWithFlags) ~~> (_.tail.init),
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.B)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.C)) ~
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.DE))) ~~> { code =>
ZLine.registers(code.last.opcode, ZRegister.HL, ZRegister.BC) :: code.take(2)
},
(Elidable & Is8BitLoad(ZRegister.B, ZRegister.D)) ~
(Elidable & Is8BitLoad(ZRegister.C, ZRegister.E)) ~
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC))) ~~> { code =>
ZLine.registers(code.last.opcode, ZRegister.HL, ZRegister.DE) :: code.take(2)
},
(Elidable & HasOpcode(INC) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(ADD) & Has8BitImmediate(0xff) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
(Elidable & HasOpcode(NEG)) ~
(Elidable & HasOpcode(ADD) & Has8BitImmediate(0xff) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => List(ZLine.implied(CPL))),
)
val FreeHL = new RuleBasedAssemblyOptimization("Free HL",
val FreeHL = new RuleBasedAssemblyOptimization("Free HL (Z80)",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.IMM_16)) ~
(Elidable & Is8BitLoadTo(ZRegister.MEM_HL) & DoesntMatterWhatItDoesWith(ZRegister.HL, ZRegister.A)) ~~> (code =>
List(
code(1).copy(registers = TwoRegisters(ZRegister.A, code(1).registers.asInstanceOf[TwoRegisters].source)),
code.head.copy(opcode = LD, registers = TwoRegisters(ZRegister.MEM_ABS_8, ZRegister.A)),
)),
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.IMM_16)) ~
(Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_HL) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
List(
code.head.copy(opcode = LD, registers = TwoRegisters(ZRegister.A, ZRegister.MEM_ABS_8)),
)),
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.IMM_16)) ~
(Elidable & IsRegular8BitLoadFrom(ZRegister.MEM_HL) & DoesntMatterWhatItDoesWith(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.A)) ~~> (code =>
List(
code.head.copy(opcode = LD, registers = TwoRegisters(ZRegister.A, ZRegister.MEM_ABS_8)),
code(1).copy(registers = TwoRegisters(code(1).registers.asInstanceOf[TwoRegisters].target, ZRegister.A)),
)),
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.IMM_16)) ~
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
List(
code.head.copy(registers = TwoRegisters(ZRegister.DE, ZRegister.IMM_16))
)),
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.IMM_16)) ~
(Elidable & Is8BitLoad(ZRegister.B, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.C, ZRegister.L) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
List(
code.head.copy(registers = TwoRegisters(ZRegister.BC, ZRegister.IMM_16))
)),
(Elidable & Is16BitLoad(ZRegister.HL, ZRegister.MEM_ABS_16)) ~
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
@ -534,64 +117,6 @@ object AlwaysGoodZ80Optimizations {
}
}),
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.B)) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.C)) ~
(Elidable & HasOpcodeIn(Set(INC_16, DEC_16, PUSH, POP)) & HasRegisterParam(ZRegister.HL)) ~
(Elidable & Is8BitLoad(ZRegister.B, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.C, ZRegister.L) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
List(
code(2).copy(registers = OneRegister(ZRegister.BC))
)),
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.D)) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.E)) ~
(Elidable & HasOpcodeIn(Set(INC_16, DEC_16, PUSH, POP)) & HasRegisterParam(ZRegister.HL)) ~
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
List(
code(2).copy(registers = OneRegister(ZRegister.DE))
)),
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.D)) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.E)) ~
(Elidable & HasOpcodeIn(Set(INC_16, DEC_16, PUSH, POP)) & HasRegisterParam(ZRegister.HL)) ~
(Elidable & Is8BitLoad(ZRegister.B, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.C, ZRegister.L) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
List(
ZLine.ld8(ZRegister.B, ZRegister.D),
ZLine.ld8(ZRegister.C, ZRegister.E),
code(2).copy(registers = OneRegister(ZRegister.BC))
)),
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.B)) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.C)) ~
(Elidable & HasOpcodeIn(Set(INC_16, DEC_16, PUSH, POP)) & HasRegisterParam(ZRegister.HL)) ~
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> (code =>
List(
ZLine.ld8(ZRegister.D, ZRegister.B),
ZLine.ld8(ZRegister.E, ZRegister.C),
code(2).copy(registers = OneRegister(ZRegister.DE))
)),
// 2 bytes more, but 3 cycles fewer and frees BC
(Elidable & Is16BitLoad(ZRegister.BC, ZRegister.IMM_16) & MatchParameter(0)) ~
(Linear & Not(Concerns(ZRegister.BC)) & Not(Concerns(ZRegister.HL))).*.capture(1) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.A)) ~
(Elidable & Is8BitLoadTo(ZRegister.H) & Has8BitImmediate(0)) ~
(Elidable & HasOpcode(ADD_16) & HasRegisters(TwoRegisters(ZRegister.HL, ZRegister.BC)) &
DoesntMatterWhatItDoesWithFlagsExceptCarry &
DoesntMatterWhatItDoesWith(ZRegister.BC)) ~~> { (code, ctx) =>
val offset = ctx.get[Constant](0)
ctx.get[List[ZLine]](1) ++ List(
ZLine.imm8(ADD, offset.loByte),
ZLine.ld8(ZRegister.L, ZRegister.A),
ZLine.ldImm8(ZRegister.A, 0),
ZLine.imm8(ADC, offset.hiByte),
ZLine.ld8(ZRegister.H, ZRegister.A))
},
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.B)) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.C)) ~
(Elidable & Is8BitLoad(ZRegister.MEM_HL, ZRegister.A) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> {_ =>
@ -616,150 +141,11 @@ object AlwaysGoodZ80Optimizations {
List(ZLine.ld8(ZRegister.A, ZRegister.MEM_DE))
},
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.D)) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.E)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.DE))
},
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.B)) ~
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.C)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.BC))
},
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.DE) & DoesntMatterWhatItDoesWith(ZRegister.DE)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.HL))
},
(Elidable & Is8BitLoad(ZRegister.B, ZRegister.H)) ~
(Elidable & Is8BitLoad(ZRegister.C, ZRegister.L)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.BC) & DoesntMatterWhatItDoesWith(ZRegister.BC)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.HL))
},
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.E)) ~
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.D)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.DE))
},
(Elidable & Is8BitLoad(ZRegister.L, ZRegister.C)) ~
(Elidable & Is8BitLoad(ZRegister.H, ZRegister.B)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.BC))
},
(Elidable & Is8BitLoad(ZRegister.E, ZRegister.L)) ~
(Elidable & Is8BitLoad(ZRegister.D, ZRegister.H)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.DE) & DoesntMatterWhatItDoesWith(ZRegister.DE)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.HL))
},
(Elidable & Is8BitLoad(ZRegister.C, ZRegister.L)) ~
(Elidable & Is8BitLoad(ZRegister.B, ZRegister.H)) ~
(Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.BC) & DoesntMatterWhatItDoesWith(ZRegister.BC)) ~~> {_ =>
List(ZLine.register(PUSH, ZRegister.HL))
},
)
val UnusedCodeRemoval = new RuleBasedAssemblyOptimization("Unreachable code removal",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
(HasOpcodeIn(Set(JP, JR)) & HasRegisters(NoRegisters)) ~ (Not(HasOpcode(LABEL)) & Elidable).+ ~~> (c => c.head :: Nil)
)
val UnusedLabelRemoval = new RuleBasedAssemblyOptimization("Unused label removal",
needsFlowInfo = FlowInfoRequirement.JustLabels,
(Elidable & HasOpcode(LABEL) & HasCallerCount(0)) ~~> (_ => Nil)
)
val BranchInPlaceRemoval = new RuleBasedAssemblyOptimization("Branch in place",
needsFlowInfo = FlowInfoRequirement.NoRequirement,
(HasOpcodeIn(Set(JP, JR)) & MatchJumpTarget(0) & Elidable) ~
HasOpcodeIn(ZOpcodeClasses.NoopDiscards).* ~
(HasOpcode(LABEL) & MatchJumpTarget(0)) ~~> (c => c.last :: Nil),
)
val SimplifiableShifting = new RuleBasedAssemblyOptimization("Simplifiable shifting",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~~> { _ =>
List(
ZLine.register(RRC, ZRegister.A),
ZLine.imm8(AND, 0x80))
},
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~~> { _ =>
List(
ZLine.register(RRC, ZRegister.A),
ZLine.register(RRC, ZRegister.A),
ZLine.imm8(AND, 0xc0))
},
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SLA) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~~> { _ =>
List(
ZLine.register(RRC, ZRegister.A),
ZLine.register(RRC, ZRegister.A),
ZLine.register(RRC, ZRegister.A),
ZLine.imm8(AND, 0xe0))
},
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~~> { _ =>
List(
ZLine.register(RLC, ZRegister.A),
ZLine.imm8(AND, 1))
},
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~~> { _ =>
List(
ZLine.register(RLC, ZRegister.A),
ZLine.register(RLC, ZRegister.A),
ZLine.imm8(AND, 3))
},
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~~> { _ =>
List(
ZLine.register(RLC, ZRegister.A),
ZLine.register(RLC, ZRegister.A),
ZLine.register(RLC, ZRegister.A),
ZLine.imm8(AND, 7))
},
)
val All: List[AssemblyOptimization[ZLine]] = List[AssemblyOptimization[ZLine]](
BranchInPlaceRemoval,
FreeHL,
PointlessLoad,
PointlessStackStashing,
ReloadingKnownValueFromMemory,
SimplifiableMaths,
SimplifiableShifting,
UnusedCodeRemoval,
UnusedLabelRemoval,
UsingKnownValueFromAnotherRegister,
)
}

View File

@ -0,0 +1,31 @@
package millfork.assembly.z80.opt
import millfork.assembly.AssemblyOptimization
import millfork.assembly.z80.{ZLine, ZOpcode}
import millfork.node.ZRegister
import ZOpcode._
import ZRegister._
/**
* @author Karol Stasiak
*/
object LaterI80Optimizations {
val VariousSmallOptimizations = new RuleBasedAssemblyOptimization("Various small optimizations",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & Is8BitLoadTo(A) & Has8BitImmediate(0) & DoesntMatterWhatItDoesWithFlags) ~~> { _ =>
List(ZLine.register(XOR, A))
},
(Elidable & HasOpcode(CP) & DoesntMatterWhatItDoesWithFlags) ~~> { _ =>
Nil
},
(Elidable & HasOpcode(CP) & Has8BitImmediate(0) & DoesntMatterWhatItDoesWithFlagsOtherThanSZ) ~~> { _ =>
List(ZLine.register(OR, A))
},
)
val All: List[AssemblyOptimization[ZLine]] = List(
VariousSmallOptimizations
)
}

View File

@ -0,0 +1,31 @@
package millfork.assembly.z80.opt
import millfork.assembly.AssemblyOptimization
import millfork.assembly.z80.{ZLine, ZOpcode}
import millfork.node.ZRegister
import ZOpcode._
import ZRegister._
/**
* @author Karol Stasiak
*/
object LaterIntel8080Optimizations {
val UseExDeHl = new RuleBasedAssemblyOptimization("Use EX DE,HL",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & Is8BitLoad(D, H)) ~
(Elidable & Is8BitLoad(E, L) & DoesntMatterWhatItDoesWith(HL)) ~~> { _ =>
List(ZLine.implied(EX_DE_HL))
},
(Elidable & Is8BitLoad(H, D)) ~
(Elidable & Is8BitLoad(L, E) & DoesntMatterWhatItDoesWith(DE)) ~~> { _ =>
List(ZLine.implied(EX_DE_HL))
},
)
val All: List[AssemblyOptimization[ZLine]] = List(
UseExDeHl
)
}

View File

@ -0,0 +1,59 @@
package millfork.assembly.z80.opt
import millfork.assembly.AssemblyOptimization
import millfork.assembly.z80.ZLine
import millfork.assembly.z80.ZOpcode._
import millfork.node.ZRegister._
/**
* @author Karol Stasiak
*/
object LaterSharpOptimizations {
val UseSwap = new RuleBasedAssemblyOptimization("Use SWAP",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & HasOpcode(RRA)) ~
(Elidable & HasOpcode(RRA)) ~
(Elidable & HasOpcode(RRA)) ~
(Elidable & HasOpcode(RRA)) ~
(HasOpcode(AND) & Has8BitImmediate(0xf)) ~~> { code =>
List(ZLine.register(SWAP, A), code.last)
},
(Elidable & HasOpcode(RR) & HasRegisterParam(A)) ~
(Elidable & HasOpcode(RR) & HasRegisterParam(A)) ~
(Elidable & HasOpcode(RR) & HasRegisterParam(A)) ~
(Elidable & HasOpcode(RR) & HasRegisterParam(A)) ~
(HasOpcode(AND) & Has8BitImmediate(0xf)) ~~> { code =>
List(ZLine.register(SWAP, A), code.last)
},
(Elidable & HasOpcode(RRCA)) ~
(Elidable & HasOpcode(RRCA)) ~
(Elidable & HasOpcode(RRCA)) ~
(Elidable & HasOpcode(RRCA)) ~
(HasOpcode(AND) & Has8BitImmediate(0xf)) ~~> { code =>
List(ZLine.register(SWAP, A), code.last)
},
(Elidable & HasOpcode(RRC) & HasRegisterParam(A)) ~
(Elidable & HasOpcode(RRC) & HasRegisterParam(A)) ~
(Elidable & HasOpcode(RRC) & HasRegisterParam(A)) ~
(Elidable & HasOpcode(RRC) & HasRegisterParam(A)) ~
(HasOpcode(AND) & Has8BitImmediate(0xf)) ~~> { code =>
List(ZLine.register(SWAP, A), code.last)
},
(Elidable & HasOpcode(SRL) & HasRegisterParam(A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(A)) ~
(Elidable & HasOpcode(SRL) & HasRegisterParam(A)) ~
(HasOpcode(AND) & Has8BitImmediate(0xf)) ~~> { code =>
List(ZLine.register(SWAP, A), code.last)
},
)
val All: List[AssemblyOptimization[ZLine]] = List(
UseSwap
)
}

View File

@ -610,6 +610,17 @@ case object DoesntMatterWhatItDoesWithFlags extends AssemblyLinePattern {
override def toString: String = "[¯\\_(ツ)_/¯:F]"
}
case object DoesntMatterWhatItDoesWithFlagsOtherThanSZ extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertBackward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
ZFlag.AllButSZ.forall(r => flowInfo.importanceAfter.getFlag(r) != Important)
override def toString: String = "[¯\\_(ツ)_/¯:NPVH]"
}
case object DoesntMatterWhatItDoesWithFlagsExceptCarry extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
@ -701,6 +712,27 @@ case class Reads(register: ZRegister.Value) extends TrivialAssemblyLinePattern {
override def apply(line: ZLine): Boolean = line.readsRegister(register)
}
case object ReadsStackPointer extends TrivialAssemblyLinePattern {
override def apply(line: ZLine): Boolean = {
import ZOpcode._
line.opcode match {
case LD_16 | ADD_16 | ADC_16 | SBC_16 =>
line.registers match {
case TwoRegisters(_, ZRegister.SP) => true
case _ => false
}
case EX_SP => true
case INC_16 | DEC_16 =>
line.registers match {
case OneRegister(ZRegister.SP) => true
case _ => false
}
case LD_HLSP | PUSH | POP => true
case _ => false
}
}
}
case class Changes(register: ZRegister.Value) extends TrivialAssemblyLinePattern {
override def apply(line: ZLine): Boolean = line.changesRegister(register)
}

View File

@ -11,17 +11,42 @@ object Z80OptimizationPresets {
val GoodForZ80: List[AssemblyOptimization[ZLine]] = {
List.fill(5)(
List.fill(5)(
AlwaysGoodZ80Optimizations.All ++
AlwaysGoodI80Optimizations.All ++
AlwaysGoodZ80Optimizations.All ++
List(
EmptyParameterStoreRemoval,
EmptyMemoryStoreRemoval)
).flatten ++
List(WordVariableToRegisterOptimization, ByteVariableToRegisterOptimization, CompactStackFrame)
List(WordVariableToRegisterOptimization, ByteVariableToRegisterOptimization, CompactStackFrame) ++
LaterIntel8080Optimizations.All
).flatten
}
val GoodForIntel8080: List[AssemblyOptimization[ZLine]] = Nil // TODO
val GoodForIntel8080: List[AssemblyOptimization[ZLine]] = {
List.fill(5)(
List.fill(5)(
AlwaysGoodI80Optimizations.All ++
List(
EmptyParameterStoreRemoval,
EmptyMemoryStoreRemoval,
)
).flatten ++
List(WordVariableToRegisterOptimization, ByteVariableToRegisterOptimization) ++
LaterIntel8080Optimizations.All
).flatten
}
val GoodForSharp: List[AssemblyOptimization[ZLine]] = Nil // TODO
val GoodForSharp: List[AssemblyOptimization[ZLine]] = {
List.fill(5)(
List.fill(5)(
AlwaysGoodI80Optimizations.All ++
List(
EmptyParameterStoreRemoval,
EmptyMemoryStoreRemoval)
).flatten ++
List(WordVariableToRegisterOptimization, ByteVariableToRegisterOptimization) ++
LaterSharpOptimizations.All
).flatten
}
}

View File

@ -35,11 +35,11 @@ object EmuZ80BenchmarkRun {
println(f"After inlining: $t2%7d")
println(f"Gain: ${(100L * (t0 - t1) / t0.toDouble).round}%7d%%")
println(f"Gain with inlining: ${(100L * (t0 - t2) / t0.toDouble).round}%7d%%")
println(f"Running unoptimized")
println(f"Running Z80 unoptimized")
verifier(m0)
println(f"Running optimized")
println(f"Running Z80 optimized")
verifier(m1)
println(f"Running optimized inlined")
println(f"Running Z80 optimized inlined")
verifier(m2)
}
}
@ -47,38 +47,38 @@ object EmuZ80BenchmarkRun {
object EmuIntel8080BenchmarkRun {
def apply(source: String)(verifier: MemoryBank => Unit): Unit = {
val (Timings(t0, _), m0) = EmuUnoptimizedIntel8080Run.apply2(source)
// val (Timings(t1, _), m1) = EmuOptimizedIntel8080Run.apply2(source)
// val (Timings(t2, _), m2) = EmuOptimizedInlinedIntel8080Run.apply2(source)
val (Timings(t1, _), m1) = EmuOptimizedIntel8080Run.apply2(source)
val (Timings(t2, _), m2) = EmuOptimizedInlinedIntel8080Run.apply2(source)
println(f"Before optimization: $t0%7d")
// println(f"After optimization: $t1%7d")
// println(f"After inlining: $t2%7d")
// println(f"Gain: ${(100L * (t0 - t1) / t0.toDouble).round}%7d%%")
// println(f"Gain with inlining: ${(100L * (t0 - t2) / t0.toDouble).round}%7d%%")
println(f"Running unoptimized")
println(f"After optimization: $t1%7d")
println(f"After inlining: $t2%7d")
println(f"Gain: ${(100L * (t0 - t1) / t0.toDouble).round}%7d%%")
println(f"Gain with inlining: ${(100L * (t0 - t2) / t0.toDouble).round}%7d%%")
println(f"Running 8080 unoptimized")
verifier(m0)
// println(f"Running optimized")
// verifier(m1)
// println(f"Running optimized inlined")
// verifier(m2)
println(f"Running 8080 optimized")
verifier(m1)
println(f"Running 8080 optimized inlined")
verifier(m2)
}
}
object EmuSharpBenchmarkRun {
def apply(source: String)(verifier: MemoryBank => Unit): Unit = {
val (Timings(t0, _), m0) = EmuUnoptimizedSharpRun.apply2(source)
// val (Timings(t1, _), m1) = EmuOptimizedSharpRun.apply2(source)
// val (Timings(t2, _), m2) = EmuOptimizedInlinedSharpRun.apply2(source)
val (Timings(t1, _), m1) = EmuOptimizedSharpRun.apply2(source)
val (Timings(t2, _), m2) = EmuOptimizedInlinedSharpRun.apply2(source)
println(f"Before optimization: $t0%7d")
// println(f"After optimization: $t1%7d")
// println(f"After inlining: $t2%7d")
// println(f"Gain: ${(100L * (t0 - t1) / t0.toDouble).round}%7d%%")
// println(f"Gain with inlining: ${(100L * (t0 - t2) / t0.toDouble).round}%7d%%")
println(f"Running unoptimized")
println(f"After optimization: $t1%7d")
println(f"After inlining: $t2%7d")
println(f"Gain: ${(100L * (t0 - t1) / t0.toDouble).round}%7d%%")
println(f"Gain with inlining: ${(100L * (t0 - t2) / t0.toDouble).round}%7d%%")
println(f"Running LR35902 unoptimized")
verifier(m0)
// println(f"Running optimized")
// verifier(m1)
// println(f"Running optimized inlined")
// verifier(m2)
println(f"Running LR35902 optimized")
verifier(m1)
println(f"Running LR35902 optimized inlined")
verifier(m2)
}
}