diff --git a/src/main/scala/millfork/OptimizationPresets.scala b/src/main/scala/millfork/OptimizationPresets.scala index 8be55f51..3dfb8fb2 100644 --- a/src/main/scala/millfork/OptimizationPresets.scala +++ b/src/main/scala/millfork/OptimizationPresets.scala @@ -172,7 +172,8 @@ object OptimizationPresets { AlwaysGoodOptimizations.ReverseFlowAnalysis, AlwaysGoodOptimizations.SimplifiableBitOpsSequence, AlwaysGoodOptimizations.SimplifiableCondition, - AlwaysGoodOptimizations.SmarterShiftingWords, + AlwaysGoodOptimizations.SmarterShiftingOfWords, + AlwaysGoodOptimizations.SmarterShiftingBytes, AlwaysGoodOptimizations.UnconditionalJumpRemoval, UnusedLabelRemoval, AlwaysGoodOptimizations.TailCallOptimization, diff --git a/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala b/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala index f87fe742..f5edbf87 100644 --- a/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala +++ b/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala @@ -825,7 +825,7 @@ object AlwaysGoodOptimizations { } } - val SmarterShiftingWords = new RuleBasedAssemblyOptimization("Smarter shifting of words", + val SmarterShiftingOfWords = new RuleBasedAssemblyOptimization("Smarter shifting of words", needsFlowInfo = FlowInfoRequirement.BackwardFlow, wordShifting(8, hiFirst = false, hiFromX = true), wordShifting(8, hiFirst = false, hiFromX = false), @@ -845,6 +845,62 @@ object AlwaysGoodOptimizations { wordShifting(5, hiFirst = true, hiFromX = false), ) + val SmarterShiftingBytes = new RuleBasedAssemblyOptimization("Smarter shifting of bytes", + needsFlowInfo = FlowInfoRequirement.NoRequirement, + (Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~~> { _ => + List( + AssemblyLine.implied(ROR), + AssemblyLine.implied(ROR), + AssemblyLine.immediate(AND, 0x80) + ) + }, + (Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(ASL) & HasAddrMode(Implied)) ~~> { _ => + List( + AssemblyLine.implied(ROR), + AssemblyLine.implied(ROR), + AssemblyLine.implied(ROR), + AssemblyLine.immediate(AND, 0xC0) + ) + }, + (Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~~> { _ => + List( + AssemblyLine.implied(ROL), + AssemblyLine.implied(ROL), + AssemblyLine.immediate(AND, 0x1) + ) + }, + (Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~ + (Elidable & HasOpcode(LSR) & HasAddrMode(Implied)) ~~> { _ => + List( + AssemblyLine.implied(ROL), + AssemblyLine.implied(ROL), + AssemblyLine.implied(ROL), + AssemblyLine.immediate(AND, 0x3) + ) + }, + ) + private def carryFlagConversionCase(shift: Int, firstSet: Boolean, zeroIfSet: Boolean) = { val nonZero = 1 << shift val test = Elidable & HasOpcode(if (firstSet) BCC else BCS) & MatchParameter(0) @@ -1105,20 +1161,6 @@ object AlwaysGoodOptimizations { code.take(code.length / 2 + 1) }, - ( - ( - (HasOpcodeIn(Set(LDA, LAX)) & MatchAddrMode(0) & MatchParameter(1)) ~ - HasOpcodeIn(Set(LDY, LDX, AND, ORA, EOR, ADC, SBC, CLC, SEC, CPY, CPX, CMP)).* - ).capture(7) ~ - blockIsIdempotentWhenItComesToIndexRegisters(7) ~ - (HasOpcodeIn(ShortConditionalBranching) & MatchParameter(2)) ~ - Not(HasOpcode(LABEL) & MatchParameter(2)).* ~ - (HasOpcode(LABEL) & MatchParameter(2)) - ).capture(3) ~ - MatchElidableCopyOf(7, Anything, DoesntMatterWhatItDoesWith(State.C, State.Z, State.N, State.V)) ~~> { (_, ctx) => - ctx.get[List[AssemblyLine]](3) - }, - (Elidable & HasOpcodeIn(Set(LDA, LAX)) & MatchAddrMode(0) & MatchParameter(1)) ~ (Elidable & HasOpcode(AND) & HasAddrModeIn(Set(Absolute, ZeroPage)) & DoesntMatterWhatItDoesWith(State.C, State.V, State.A)) ~ HasOpcodeIn(Set(BEQ, BNE)) ~ diff --git a/src/test/scala/millfork/test/AssemblyOptimizationSuite.scala b/src/test/scala/millfork/test/AssemblyOptimizationSuite.scala index 6b6fff8c..25e47b55 100644 --- a/src/test/scala/millfork/test/AssemblyOptimizationSuite.scala +++ b/src/test/scala/millfork/test/AssemblyOptimizationSuite.scala @@ -363,4 +363,49 @@ class AssemblyOptimizationSuite extends FunSuite with Matchers { m.readByte(0xc000) should equal(5) } } + + test("Common conditions") { + + new EmuRun(Cpu.StrictMos, + OptimizationPresets.NodeOpt, List( + LaterOptimizations.PointlessLoadAfterStore, + LaterOptimizations.DoubleLoadToDifferentRegisters, + LaterOptimizations.DoubleLoadToTheSameRegister, + AlwaysGoodOptimizations.PointlessRegisterTransfers, + AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeReturn, + AlwaysGoodOptimizations.PointlessLoadBeforeReturn, + AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad, + AlwaysGoodOptimizations.PointlessRegisterTransfers, + AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeReturn, + AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeReturn, + AlwaysGoodOptimizations.PointlessLoadBeforeReturn, + AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad, + AlwaysGoodOptimizations.PointlessStashingToIndexOverShortSafeBranch, + AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeReturn, + AlwaysGoodOptimizations.IdempotentDuplicateRemoval, + AlwaysGoodOptimizations.IdempotentDuplicateRemoval, + AlwaysGoodOptimizations.IdempotentDuplicateRemoval, + AlwaysGoodOptimizations.CommonExpressionInConditional), false)( + """ + | byte output @$C000 + | void main(){ + | byte a + | byte b + | output = 0 + | a = delta() + | if a == 0 { + | output += 1 + | a += 1 + | } + | b = a + | if b == 0 { + | output += 1 + | a += 1 + | } + | } + | byte delta () { + | return 0 + | } + """.stripMargin).readByte(0xc000) should equal(1) + } }