From 90a95389363d615cdff9d5a93f6aba500d1b5b5c Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Tue, 1 Dec 2020 18:18:56 +0100 Subject: [PATCH] Various optimizations --- CHANGELOG.md | 2 + .../mos/opt/AlwaysGoodOptimizations.scala | 104 ++++++++++++++---- .../z80/opt/AlwaysGoodI80Optimizations.scala | 96 ++++++++++++++-- .../opt/RuleBasedAssemblyOptimization.scala | 7 ++ .../SecondAssemblyOptimizationSuite.scala | 24 ++++ 5 files changed, 200 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07c36b02..fd9f4f24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,8 @@ * 8080: word negation now works. +* Various optimization improvements. + * Various other fixes. * Improved some error messages. diff --git a/src/main/scala/millfork/assembly/mos/opt/AlwaysGoodOptimizations.scala b/src/main/scala/millfork/assembly/mos/opt/AlwaysGoodOptimizations.scala index dfdfe17a..ce86ce69 100644 --- a/src/main/scala/millfork/assembly/mos/opt/AlwaysGoodOptimizations.scala +++ b/src/main/scala/millfork/assembly/mos/opt/AlwaysGoodOptimizations.scala @@ -1379,103 +1379,103 @@ object AlwaysGoodOptimizations { needsFlowInfo = FlowInfoRequirement.BackwardFlow, (HasOpcodeIn(LDA, STA) & HasAddrMode(Immediate) & MatchParameter(1)) ~ - (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF))).* ~ + (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcode(LDA) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (HasOpcodeIn(LDX, STX) & HasAddrMode(Immediate) & MatchParameter(1)) ~ - (Linear & Not(ChangesX) & Not(HasOpcode(DISCARD_XF))).* ~ + (Linear & Not(ChangesX) & Not(HasOpcode(DISCARD_XF)) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcode(LDX) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (HasOpcodeIn(LDY, STY) & HasAddrMode(Immediate) & MatchParameter(1)) ~ - (Linear & Not(ChangesY) & Not(HasOpcode(DISCARD_YF))).* ~ + (Linear & Not(ChangesY) & Not(HasOpcode(DISCARD_YF)) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcode(LDY) & HasAddrMode(Immediate) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (HasOpcodeIn(LDA, STA) & MatchAddrMode(0) & MatchParameter(1)) ~ - (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~ + (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (HasOpcodeIn(LDX, STX) & MatchAddrMode(0) & MatchParameter(1)) ~ - (Linear & Not(ChangesX) & Not(HasOpcode(DISCARD_XF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~ + (Linear & Not(ChangesX) & Not(HasOpcode(DISCARD_XF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (HasOpcodeIn(LDY, STY) & MatchAddrMode(0) & MatchParameter(1)) ~ - (Linear & Not(ChangesY) & Not(HasOpcode(DISCARD_YF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~ + (Linear & Not(ChangesY) & Not(HasOpcode(DISCARD_YF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (HasOpcode(LDA) & HasAddrMode(Immediate) & MatchParameter(1)) ~ - (Linear & Not(ChangesA) & Not(ChangesNAndZ) & Not(HasOpcode(DISCARD_AF))).* ~ + (Linear & Not(ChangesA) & Not(ChangesNAndZ) & Not(HasOpcode(DISCARD_AF)) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcode(LDA) & HasAddrMode(Immediate) & MatchParameter(1)) ~~> (_.init), (HasOpcode(LDX) & HasAddrMode(Immediate) & MatchParameter(1)) ~ - (Linear & Not(ChangesX) & Not(ChangesNAndZ) & Not(HasOpcode(DISCARD_XF))).* ~ + (Linear & Not(ChangesX) & Not(ChangesNAndZ) & Not(HasOpcode(DISCARD_XF)) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcode(LDX) & HasAddrMode(Immediate) & MatchParameter(1)) ~~> (_.init), (HasOpcode(LDY) & HasAddrMode(Immediate) & MatchParameter(1)) ~ - (Linear & Not(ChangesY) & Not(ChangesNAndZ) & Not(HasOpcode(DISCARD_YF))).* ~ + (Linear & Not(ChangesY) & Not(ChangesNAndZ) & Not(HasOpcode(DISCARD_YF)) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcode(LDY) & HasAddrMode(Immediate) & MatchParameter(1)) ~~> (_.init), (HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~ - (Linear & Not(ChangesA) & Not(ChangesNAndZ) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~ + (Linear & Not(ChangesA) & Not(ChangesNAndZ) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~~> (_.init), (HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1)) ~ - (Linear & Not(ChangesX) & Not(ChangesNAndZ) & Not(HasOpcode(DISCARD_XF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~ + (Linear & Not(ChangesX) & Not(ChangesNAndZ) & Not(HasOpcode(DISCARD_XF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1)) ~~> (_.init), (HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) ~ - (Linear & Not(ChangesY) & Not(ChangesNAndZ) & Not(HasOpcode(DISCARD_YF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~ + (Linear & Not(ChangesY) & Not(ChangesNAndZ) & Not(HasOpcode(DISCARD_YF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) ~~> (_.init), (HasOpcodeIn(LDA, STA) & MatchAddrMode(0) & MatchParameter(1)) ~ (ShortConditionalBranching & MatchParameter(2)) ~ - (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~ + (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (HasOpcode(LABEL) & MatchParameter(2)) ~ (Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (HasOpcodeIn(LDX, STX) & MatchAddrMode(0) & MatchParameter(1)) ~ (ShortConditionalBranching & MatchParameter(2)) ~ - (Linear & Not(ChangesX) & Not(HasOpcode(DISCARD_XF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~ + (Linear & Not(ChangesX) & Not(HasOpcode(DISCARD_XF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (HasOpcode(LABEL) & MatchParameter(2)) ~ (Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (HasOpcodeIn(LDY, STY) & MatchAddrMode(0) & MatchParameter(1)) ~ (ShortConditionalBranching & MatchParameter(2)) ~ - (Linear & Not(ChangesY) & Not(HasOpcode(DISCARD_YF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~ + (Linear & Not(ChangesY) & Not(HasOpcode(DISCARD_YF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (HasOpcode(LABEL) & MatchParameter(2)) ~ (Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (HasOpcodeIn(LDA, STA) & MatchAddrMode(0) & MatchParameter(1)) ~ (ShortBranching & MatchParameter(3)) ~ - (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1)).* ~ + (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesntChangeMemoryAt(0, 1) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (HasOpcode(LABEL) & MatchParameter(3) & HasCallerCount(1)) ~ (Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), HasOpcodeIn(TXA, TAX, LAX, LXA) ~ - (Not(HasOpcodeIn(TXA, TAX)) & Linear & Not(ChangesA) & Not(ChangesX)).* ~ + (Not(HasOpcodeIn(TXA, TAX)) & Linear & Not(ChangesA) & Not(ChangesX) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcodeIn(TXA, TAX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), HasOpcodeIn(TYA, TAY) ~ - (Not(HasOpcodeIn(TYA, TAY)) & Linear & Not(ChangesA) & Not(ChangesY)).* ~ + (Not(HasOpcodeIn(TYA, TAY)) & Linear & Not(ChangesA) & Not(ChangesY) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcodeIn(TYA, TAY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (HasOpcodeIn(STA, LDA) & HasAddrModeIn(ZeroPage, Absolute) & MatchAddrMode(0) & MatchParameter(1)) ~ - (Linear & Not(HasOpcode(TAX)) & Not(ChangesA) & DoesntChangeMemoryAt(0, 1)).* ~ + (Linear & Not(HasOpcode(TAX)) & Not(ChangesA) & DoesntChangeMemoryAt(0, 1) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ HasOpcode(TAX) ~ (LinearOrBranch & Not(ChangesX) & DoesntChangeMemoryAt(0, 1)).* ~ (Elidable & HasOpcode(LDX) & HasAddrModeIn(ZeroPage, Absolute) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~~> (_.init), (HasOpcodeIn(STA, LDA) & HasAddrModeIn(ZeroPage, Absolute) & MatchAddrMode(0) & MatchParameter(1)) ~ - (Linear & Not(HasOpcode(TAY)) & Not(ChangesA) & DoesntChangeMemoryAt(0, 1)).* ~ + (Linear & Not(HasOpcode(TAY)) & Not(ChangesA) & DoesntChangeMemoryAt(0, 1) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ 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)).* ~ + (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesNotConcernMemoryAt(0, 1) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (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)).* ~ + (Linear & Not(ChangesA) & Not(HasOpcode(DISCARD_AF)) & DoesntChangeIndexingInAddrMode(0) & DoesNotConcernMemoryAt(0, 1) | HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)).* ~ (Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) ~~> (code => code.init :+ AssemblyLine.implied(TAY)), ) @@ -3046,6 +3046,66 @@ object AlwaysGoodOptimizations { (Elidable & HasOpcode(LABEL) & MatchParameter(1) & IsNotALabelUsedManyTimes & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C, State.V)) ~~> { code => List(code(1).copy(opcode = LDA), code.head.copy(opcode = AND)) }, + + (Elidable & HasOpcode(CMP) & HasAddrMode(Immediate)) ~ + (Elidable & HasOpcodeIn(BEQ, BNE) & MatchParameter(1)) ~ + (Elidable & HasAddrMode(Implied) & HasOpcodeIn(INC, DEC) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C, State.V)) ~ + (Elidable & HasOpcode(JMP) & HasAddrModeIn(Absolute, LongAbsolute)) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C, State.V, State.A)) ~~> { code => + val delta = code(2).opcode match { + case INC => +1 + case DEC => -1 + } + val branch = code(1).opcode match { + case BEQ => BNE + case BNE => BEQ + } + List( + code(2), + code.head.copy(parameter = (code.head.parameter + delta).quickSimplify), + code(1).copy(opcode = branch, parameter = code(3).parameter), + code(4)) + }, + + (Elidable & HasOpcode(CPX) & HasAddrMode(Immediate)) ~ + (Elidable & HasOpcodeIn(BEQ, BNE) & MatchParameter(1)) ~ + (Elidable & HasAddrMode(Implied) & HasOpcodeIn(INX, DEX) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C, State.V)) ~ + (Elidable & HasOpcode(JMP) & HasAddrModeIn(Absolute, LongAbsolute)) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C, State.V, State.X)) ~~> { code => + val delta = code(2).opcode match { + case INX => +1 + case DEX => -1 + } + val branch = code(1).opcode match { + case BEQ => BNE + case BNE => BEQ + } + List( + code(2), + code.head.copy(parameter = (code.head.parameter + delta).quickSimplify), + code(1).copy(opcode = branch, parameter = code(3).parameter), + code(4)) + }, + + (Elidable & HasOpcode(CPY) & HasAddrMode(Immediate)) ~ + (Elidable & HasOpcodeIn(BEQ, BNE) & MatchParameter(1)) ~ + (Elidable & HasAddrMode(Implied) & HasOpcodeIn(INY, DEY) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C, State.V)) ~ + (Elidable & HasOpcode(JMP) & HasAddrModeIn(Absolute, LongAbsolute)) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(1) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C, State.V, State.Y)) ~~> { code => + val delta = code(2).opcode match { + case INY => +1 + case DEY => -1 + } + val branch = code(1).opcode match { + case BEQ => BNE + case BNE => BEQ + } + List( + code(2), + code.head.copy(parameter = (code.head.parameter + delta).quickSimplify), + code(1).copy(opcode = branch, parameter = code(3).parameter), + code(4)) + }, ) private val powersOf2: List[(Int, Int)] = List( diff --git a/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala b/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala index 0b29f0ce..2f1e1446 100644 --- a/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala +++ b/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala @@ -94,55 +94,76 @@ object AlwaysGoodI80Optimizations { 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))).* ~ + (HasOpcodeIn(Set(JP,JR)) & Not(HasRegisters(NoRegisters)) | + 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(register, ZRegister.MEM_HL) ~ + (HasOpcodeIn(Set(JP,JR)) & Not(HasRegisters(NoRegisters)) | + 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))).* ~ + (HasOpcodeIn(Set(JP, JR)) & Not(HasRegisters(NoRegisters)) | + 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))).* ~ + (HasOpcodeIn(Set(JP, JR)) & Not(HasRegisters(NoRegisters)) | + Linear & DoesntChangeMemoryAt(1) & Not(Changes(ZRegister.A))).* ~ + (Elidable & Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8) & MatchParameter(0)) ~~> { code => code.init }, + (Is8BitLoad(ZRegister.A, ZRegister.MEM_ABS_8) & MatchParameter(0)).captureLine(1) ~ + (HasOpcodeIn(Set(JP, JR)) & Not(HasRegisters(NoRegisters)) | + 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))).* ~ + (HasOpcodeIn(Set(JP, JR)) & Not(HasRegisters(NoRegisters)) | + 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))).* ~ + (HasOpcodeIn(Set(JP, JR)) & Not(HasRegisters(NoRegisters)) | + 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))).* ~ + (HasOpcodeIn(Set(JP, JR)) & Not(HasRegisters(NoRegisters)) | + 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))).* ~ + (HasOpcodeIn(Set(JP, JR)) & Not(HasRegisters(NoRegisters)) | + 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))).* ~ + (HasOpcodeIn(Set(JP, JR)) & Not(HasRegisters(NoRegisters)) | + 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))).* ~ + (HasOpcodeIn(Set(JP, JR)) & Not(HasRegisters(NoRegisters)) | + 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)) }, for5LargeRegisters(register => (Is8BitLoad(MEM_ABS_8, ZRegister.A) & MatchParameter(0) & MatchRegister(A, 2)).captureLine(1) ~ - (Linear & DoesntChangeMemoryAt(1) & Not(Is8BitLoad(MEM_ABS_8, ZRegister.A))).* ~ + (HasOpcodeIn(Set(JP, JR)) & Not(HasRegisters(NoRegisters)) | + Linear & DoesntChangeMemoryAt(1) & Not(Is8BitLoad(MEM_ABS_8, ZRegister.A))).* ~ (Is8BitLoad(MEM_ABS_8, ZRegister.A) & MatchParameter(10) & MatchRegister(A, 12)).captureLine(11) ~ Where(ctx => ctx.get[Constant](0).succ == ctx.get[Constant](10)) ~ - (Linear & DoesntChangeMemoryAt(1) & DoesntChangeMemoryAt(11)).* ~ + (HasOpcodeIn(Set(JP, JR)) & Not(HasRegisters(NoRegisters)) | + Linear & DoesntChangeMemoryAt(1) & DoesntChangeMemoryAt(11)).* ~ (Elidable & Is16BitLoad(register, MEM_ABS_16) & MatchParameter(0) & MatchRegister(A, 12)) ~~> { (code, ctx) => val hi = ctx.get[Int](12) val lo = ctx.get[Int](2) @@ -859,6 +880,50 @@ object AlwaysGoodI80Optimizations { ), ) + val SimplifiableComparison = new RuleBasedAssemblyOptimization("Simplifiable comparison", + needsFlowInfo = FlowInfoRequirement.BackwardFlow, + + (Elidable & HasOpcode(CP) & HasRegisterParam(ZRegister.IMM_8)) ~ + (Elidable & HasOpcodeIn(Set(JR, JP)) & MatchJumpTarget(1) & (HasRegisters(IfFlagSet(ZFlag.Z)) | HasRegisters(IfFlagClear(ZFlag.Z)))) ~ + (Elidable & HasOpcodeIn(Set(INC, DEC)) & HasRegisterParam(ZRegister.A) & DoesntMatterWhatItDoesWithFlags) ~ + (Elidable & HasOpcodeIn(Set(JR, JP)) & IsUnconditional) ~ + (Elidable & HasOpcode(LABEL) & MatchJumpTarget(1) & IsUnconditional & DoesntMatterWhatItDoesWithFlags & DoesntMatterWhatItDoesWith(ZRegister.A)) ~~> { code => + val delta = code(2).opcode match { + case INC => +1 + case DEC => -1 + } + val branch = code(1).registers.negate + List( + code(2), + code.head.copy(parameter = (code.head.parameter + delta).quickSimplify), + code(1).copy(opcode=JP, registers = branch, parameter = code(3).parameter), + code(4)) + }, + + for6Registers(reg => + (Elidable & Is8BitLoad(ZRegister.A, reg)) ~ + (Elidable & HasOpcode(CP) & HasRegisterParam(ZRegister.IMM_8)) ~ + (Elidable & HasOpcodeIn(Set(JR, JP)) & MatchJumpTarget(1) & DoesntMatterWhatItDoesWith(ZRegister.A) & (HasRegisters(IfFlagSet(ZFlag.Z)) | HasRegisters(IfFlagClear(ZFlag.Z)))) ~ + (Elidable & HasOpcodeIn(Set(INC, DEC)) & HasRegisterParam(reg) & DoesntMatterWhatItDoesWithFlags) ~ + (Elidable & HasOpcodeIn(Set(JR, JP)) & IsUnconditional) ~ + (Elidable & HasOpcode(LABEL) & MatchJumpTarget(1) & IsUnconditional & DoesntMatterWhatItDoesWithFlags & DoesntMatterWhatItDoesWith(ZRegister.A, reg)) ~~> { code => + val delta = code(3).opcode match { + case INC => +1 + case DEC => -1 + } + val branch = code(2).registers.negate + List( + code(3), + code.head, + code(1).copy(parameter = (code(1).parameter + delta).quickSimplify), + code(2).copy(opcode=JP, registers = branch, parameter = code(4).parameter), + code(5)) + }, + ) + + + ) + val FreeHL = new RuleBasedAssemblyOptimization("Free HL", needsFlowInfo = FlowInfoRequirement.BackwardFlow, // 0 @@ -1218,6 +1283,14 @@ object AlwaysGoodI80Optimizations { (HasOpcodeIn(Set(JP, JR)) & MatchJumpTarget(0) & Elidable) ~ HasOpcodeIn(ZOpcodeClasses.NoopDiscards).* ~ (HasOpcode(LABEL) & MatchJumpTarget(0)) ~~> (c => c.last :: Nil), + (HasOpcodeIn(Set(JP, JR)) & MatchJumpTarget(0) & IsConditional & Elidable) ~ + (HasOpcodeIn(Set(JP, JR)) & Elidable & IsUnconditional) ~ + (HasOpcode(LABEL) & MatchJumpTarget(0)) ~~> { code => + List( + code(1).copy(opcode = JP, registers = code.head.registers.negate), + code(2) + ) + }, ) val SimplifiableShifting = new RuleBasedAssemblyOptimization("Simplifiable shifting", @@ -1762,6 +1835,7 @@ object AlwaysGoodI80Optimizations { PointlessStackUnstashing, ReloadingKnownValueFromMemory, ShiftingKnownValue, + SimplifiableComparison, SimplifiableMaths, SimplifiableShifting, UnusedCodeRemoval, diff --git a/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala b/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala index ed49fc66..f74d7cbb 100644 --- a/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala +++ b/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala @@ -719,6 +719,13 @@ case object IsUnconditional extends AssemblyLinePattern { override def hitRate: Double = 0.212 } +case object IsConditional extends AssemblyLinePattern { + override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean = + line.registers.isInstanceOf[IfFlagSet] || line.registers.isInstanceOf[IfFlagClear] + + override def hitRate: Double = 0.212 +} + case class MatchConstantInHL(i: Int) extends AssemblyLinePattern { override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = FlowInfoRequirement.assertForward(needsFlowInfo) diff --git a/src/test/scala/millfork/test/SecondAssemblyOptimizationSuite.scala b/src/test/scala/millfork/test/SecondAssemblyOptimizationSuite.scala index e1b5093d..fc77ce36 100644 --- a/src/test/scala/millfork/test/SecondAssemblyOptimizationSuite.scala +++ b/src/test/scala/millfork/test/SecondAssemblyOptimizationSuite.scala @@ -144,4 +144,28 @@ class SecondAssemblyOptimizationSuite extends FunSuite with Matchers { } } + + test("Bubblesort") { + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)( + """ + |array sorttable [2540] @$C000 + | + |void main() { + | byte t,i,n1,n2 + | for t,25,downto,0{ + | for i,0,to,25{ + | n1 = sorttable[i] + | n2 = sorttable[i+1] + | if n1>n2 { + | sorttable[i] = n2 + | sorttable[i+1] = n1 + | } + | } + | } + |} + | + |""".stripMargin) { m => + + } + } }