diff --git a/src/main/scala/millfork/assembly/mos/opt/AlwaysGoodOptimizations.scala b/src/main/scala/millfork/assembly/mos/opt/AlwaysGoodOptimizations.scala index 8d741ad8..fc07b243 100644 --- a/src/main/scala/millfork/assembly/mos/opt/AlwaysGoodOptimizations.scala +++ b/src/main/scala/millfork/assembly/mos/opt/AlwaysGoodOptimizations.scala @@ -1320,44 +1320,52 @@ object AlwaysGoodOptimizations { HasOpcode(TAY) ~ (LinearOrBranch & Not(ChangesY) & DoesntChangeMemoryAt(0, 1)).* ~ (Elidable & HasOpcode(LDY) & HasAddrModeIn(ZeroPage, Absolute) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), + + (HasOpcodeIn(LDA, STA) & MatchAddrMode(0) & MatchParameter(1)) ~ + (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesNotConcernMemoryAt(0, 1)).* ~ + (Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1)) ~~> (code => code.init :+ AssemblyLine.implied(TAX)), + + (HasOpcodeIn(LDA, STA) & MatchAddrMode(0) & MatchParameter(1)) ~ + (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesNotConcernMemoryAt(0, 1)).* ~ + (Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) ~~> (code => code.init :+ AssemblyLine.implied(TAY)), ) val RearrangableLoadFromTheSameLocation = new RuleBasedAssemblyOptimization("Rearrangable load from the same location", - needsFlowInfo = FlowInfoRequirement.NoRequirement, + needsFlowInfo = FlowInfoRequirement.BackwardFlow, (HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~ (HasOpcode(STA) & Not(ReadsX)) ~ - (Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1)) ~~> {code => + (Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> { code => List(code.head, AssemblyLine.implied(TAX), code(1)) }, (HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~ (HasOpcode(STA) & Not(ReadsY)) ~ - (Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) ~~> {code => + (Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> {code => List(code.head, AssemblyLine.implied(TAY), code(1)) }, (HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1)) ~ (HasOpcode(LDA) & Not(ReadsX)) ~ - (Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1)) ~~> {code => + (Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> {code => List(code.head, AssemblyLine.implied(TAX), code(1)) }, (HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1)) ~ (HasOpcode(LDA) & Not(ReadsY)) ~ - (Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) ~~> {code => + (Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> {code => List(code.head, AssemblyLine.implied(TAY), code(1)) }, (HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~ (Linear & Not(ChangesA) & Not(ChangesNAndZ) & DoesntChangeMemoryAt(0, 1)).* ~ - (Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~~> {code => + (Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> {code => code.init }, (HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1)) ~ (Linear & Not(ChangesX) & Not(ChangesNAndZ) & DoesntChangeMemoryAt(0, 1)).* ~ - (Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1)) ~~> {code => + (Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> {code => code.init }, (HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) ~ (Linear & Not(ChangesY) & Not(ChangesNAndZ) & DoesntChangeMemoryAt(0, 1)).* ~ - (Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) ~~> {code => + (Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> {code => code.init }, ) @@ -1787,7 +1795,7 @@ object AlwaysGoodOptimizations { List(code.head, AssemblyLine.implied(ROL)) }, (Elidable & HasOpcode(ADC) & HasImmediate(0) & HasClear(State.C) & DoesntMatterWhatItDoesWith(State.V, State.C, State.N, State.Z)) ~~> (_ => Nil), - (Elidable & HasOpcode(ROL) & HasClear(State.C)) ~~> (code => code.map(_.copy(opcode = ASL))), + (Elidable & HasOpcode(ROL) & HasClear(State.C)) ~ DebugMatching ~~> (code => code.map(_.copy(opcode = ASL))), (Elidable & HasOpcode(ROR) & HasClear(State.C)) ~~> (code => code.map(_.copy(opcode = LSR))), (HasOpcode(AND) & HasImmediate(1)) ~ (Linear & Not(ChangesNAndZ) & Not(HasOpcode(CLC)) & Not(ChangesA)).* ~ diff --git a/src/main/scala/millfork/assembly/mos/opt/ZeropageRegisterOptimizations.scala b/src/main/scala/millfork/assembly/mos/opt/ZeropageRegisterOptimizations.scala index a7b4b5fd..b9f02522 100644 --- a/src/main/scala/millfork/assembly/mos/opt/ZeropageRegisterOptimizations.scala +++ b/src/main/scala/millfork/assembly/mos/opt/ZeropageRegisterOptimizations.scala @@ -175,7 +175,7 @@ object ZeropageRegisterOptimizations { (Elidable & HasOpcode(LDY) & RefersTo("__reg", 0)) ~ (Linear & Not(ConcernsY) & Not(RefersToOrUses("__reg", 0))).*.capture(2) ~ - (Elidable & (HasA(0) & HasOpcode(STA) | HasOpcode(STZ)) & RefersTo("__reg", 0) & DoesntMatterWhatItDoesWith(State.A)) ~ + (Elidable & (HasA(0) & HasOpcode(STA) | HasZ(0) & HasOpcode(STZ)) & RefersTo("__reg", 0) & DoesntMatterWhatItDoesWith(State.A)) ~ ((Linear & Not(ConcernsY) & Not(RefersToOrUses("__reg", 0))).* ~ (Elidable & RefersToOrUses("__reg", 0) & HasAddrMode(IndexedY) & DoesntMatterWhatItDoesWithReg(0) & DoesntMatterWhatItDoesWith(State.Y))).capture(3) ~~> ((code, ctx) => ctx.get[List[AssemblyLine]](2) ++ List(AssemblyLine.immediate(LDY, 0)) ++ ctx.get[List[AssemblyLine]](3) diff --git a/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala b/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala index b401cd81..4d3d000a 100644 --- a/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala +++ b/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala @@ -1044,7 +1044,16 @@ object AlwaysGoodI80Optimizations { val PointlessArithmetic = new RuleBasedAssemblyOptimization("Pointless arithmetic", needsFlowInfo = FlowInfoRequirement.BackwardFlow, - (Elidable & HasOpcodeIn(Set(ADD, ADC, SUB, SBC, OR, AND, XOR, CP)) & DoesntMatterWhatItDoesWithFlags & DoesntMatterWhatItDoesWith(ZRegister.A)) ~~> (_ => Nil) + (Elidable & HasOpcodeIn(Set(ADD, ADC, SUB, SBC, OR, AND, XOR, CP)) & DoesntMatterWhatItDoesWithFlags & DoesntMatterWhatItDoesWith(ZRegister.A)) ~~> (_ => Nil), + for7Registers(register => + (Elidable & HasOpcodeIn(Set(INC, DEC)) + & HasRegisterParam(register) & DoesntMatterWhatItDoesWithFlagsExceptCarry & DoesntMatterWhatItDoesWith(register)) ~~> (_ => Nil) + ), + for7Registers(register => + (Elidable & HasOpcodeIn(Set(SLA, RRC, RR, RL, RLC, SLL, SRL, SRA)) + & HasRegisterParam(register) & DoesntMatterWhatItDoesWithFlags & DoesntMatterWhatItDoesWith(register)) ~~> (_ => Nil) + ), + (Elidable & HasOpcode(DAA) & DoesntMatterWhatItDoesWithFlags & DoesntMatterWhatItDoesWith(ZRegister.A)) ~~> (_ => Nil), ) val ConstantMultiplication = new RuleBasedAssemblyOptimization("Constant multiplication", @@ -1175,7 +1184,6 @@ object AlwaysGoodI80Optimizations { }, (Elidable & HasOpcodeIn(Set(JP, JR)) & MatchJumpTarget(3) & IsUnconditional & MatchRegister(ZRegister.B, 1)) ~ - DebugMatching ~ (Elidable & IsLabelMatching(2)) ~ (Elidable & HasOpcodeIn(Set(ADD, SLA, SRL, SLL, RLC, RLCA, RRC, RRCA, RR, RL, RLA, RRA)) & Not(HasRegisterParam(ZRegister.B))).*.capture(5) ~ (Elidable & IsLabelMatching(3)) ~ diff --git a/src/test/scala/millfork/test/InliningSuite.scala b/src/test/scala/millfork/test/InliningSuite.scala index cb4a65d0..9c5dcec0 100644 --- a/src/test/scala/millfork/test/InliningSuite.scala +++ b/src/test/scala/millfork/test/InliningSuite.scala @@ -10,7 +10,7 @@ import org.scalatest.{FunSuite, Matchers} class InliningSuite extends FunSuite with Matchers { test("Should inline square") { - EmuSizeOptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80)( + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)( """ | import zp_reg | byte output @$c000 @@ -24,7 +24,7 @@ class InliningSuite extends FunSuite with Matchers { } test("Should inline <<") { - EmuSizeOptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80)( + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)( """ | byte output @$c000 | word output2 @$c006 @@ -44,4 +44,19 @@ class InliningSuite extends FunSuite with Matchers { } } + + test("Should inline this weird thing") { + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)( + """ + | byte output @$c000 + | inline word square(word x) { + | return x << x.lo + | } + | void main() { + | output = hi(square(6)) + | } + """.stripMargin) { m => + m.readByte(0xc000) should equal(6.<<(6).>>(8)) + } + } } \ No newline at end of file diff --git a/src/test/scala/millfork/test/emu/EmuOptimizedRun.scala b/src/test/scala/millfork/test/emu/EmuOptimizedRun.scala index 9eea2631..6256ea51 100644 --- a/src/test/scala/millfork/test/emu/EmuOptimizedRun.scala +++ b/src/test/scala/millfork/test/emu/EmuOptimizedRun.scala @@ -18,6 +18,10 @@ object EmuOptimizedRun extends EmuRun( OptimizationPresets.Good ++ LaterOptimizations.Nmos ++ ZeropageRegisterOptimizations.All ++ OptimizationPresets.Good ++ + ZeropageRegisterOptimizations.All ++ + OptimizationPresets.Good ++ + ZeropageRegisterOptimizations.All ++ + OptimizationPresets.Good ++ OptimizationPresets.Good) object EmuSizeOptimizedRun extends EmuRun( @@ -31,6 +35,10 @@ object EmuSizeOptimizedRun extends EmuRun( OptimizationPresets.Good ++ LaterOptimizations.Nmos ++ ZeropageRegisterOptimizations.All ++ OptimizationPresets.Good ++ + ZeropageRegisterOptimizations.All ++ + OptimizationPresets.Good ++ + ZeropageRegisterOptimizations.All ++ + OptimizationPresets.Good ++ OptimizationPresets.Good) { override def optimizeForSize = true }