From 7c4570766a1bd6faf8465cc3f161e4271482145d Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Sun, 21 Jan 2018 21:06:28 +0100 Subject: [PATCH] More loop-related optimizations --- .../scala/millfork/OptimizationPresets.scala | 2 + .../opt/AlwaysGoodOptimizations.scala | 72 ++++++++++++++++--- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/src/main/scala/millfork/OptimizationPresets.scala b/src/main/scala/millfork/OptimizationPresets.scala index 7754a698..91aa10cd 100644 --- a/src/main/scala/millfork/OptimizationPresets.scala +++ b/src/main/scala/millfork/OptimizationPresets.scala @@ -124,11 +124,13 @@ object OptimizationPresets { AlwaysGoodOptimizations.CommonExpressionInConditional, AlwaysGoodOptimizations.ConstantFlowAnalysis, AlwaysGoodOptimizations.ConstantIndexPropagation, + AlwaysGoodOptimizations.DoubleJumpSimplification, EmptyMemoryStoreRemoval, AlwaysGoodOptimizations.FlagFlowAnalysis, AlwaysGoodOptimizations.IdempotentDuplicateRemoval, AlwaysGoodOptimizations.ImpossibleBranchRemoval, AlwaysGoodOptimizations.IncrementingIndexRegistersAfterTransfer, + AlwaysGoodOptimizations.IndexComparisonOptimization, AlwaysGoodOptimizations.IndexSequenceOptimization, AlwaysGoodOptimizations.MathOperationOnTwoIdenticalMemoryOperands, AlwaysGoodOptimizations.ModificationOfJustWrittenValue, diff --git a/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala b/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala index da110efe..679cdb94 100644 --- a/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala +++ b/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala @@ -964,22 +964,22 @@ object AlwaysGoodOptimizations { ctx.get[List[AssemblyLine]](2) }, (Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~ - (Elidable & HasOpcodeIn(Set(ROL, ASL)) & DoesntMatterWhatItDoesWith(State.A)) ~ + (Elidable & HasOpcodeIn(Set(ROL, ASL)) & DoesntMatterWhatItDoesWith(State.A)) ~ (Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).* ~ - (Elidable & HasOpcodeIn(Set(ROL, ASL)) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & MatchAddrMode(0) & MatchParameter(1)) ~~> {code => + (Elidable & HasOpcodeIn(Set(ROL, ASL)) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code => code.last :: code.drop(2).init }, (Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~ - (Elidable & HasOpcodeIn(Set(ROR, LSR)) & DoesntMatterWhatItDoesWith(State.A)) ~ + (Elidable & HasOpcodeIn(Set(ROR, LSR)) & DoesntMatterWhatItDoesWith(State.A)) ~ (Linear & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).* ~ - (Elidable & HasOpcode(LSR) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & MatchAddrMode(0) & MatchParameter(1)) ~~> {code => + (Elidable & HasOpcode(LSR) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code => code.last :: code.drop(2).init }, (Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1)) ~ - (Elidable & HasOpcodeIn(Set(ROR, LSR)) & DoesntMatterWhatItDoesWith(State.A)) ~ + (Elidable & HasOpcodeIn(Set(ROR, LSR)) & DoesntMatterWhatItDoesWith(State.A)) ~ (Linear & Not(HasOpcode(LSR)) & DoesNotConcernMemoryAt(0, 1) & DoesntChangeIndexingInAddrMode(0)).* ~ (Elidable & HasOpcode(LSR) & DoesNotConcernMemoryAt(0, 1)) ~ - (Elidable & HasOpcode(ROR) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & MatchAddrMode(0) & MatchParameter(1)) ~~> {code => + (Elidable & HasOpcode(ROR) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.C) & MatchAddrMode(0) & MatchParameter(1)) ~~> { code => code.init.last :: code.last :: code.drop(2).init.init }, ) @@ -999,7 +999,7 @@ object AlwaysGoodOptimizations { needsFlowInfo = FlowInfoRequirement.BackwardFlow, ( (HasOpcodeIn(Set(LDA, LAX)) & MatchAddrMode(0) & MatchParameter(1)) ~ - HasOpcodeIn(Set(LDY, LDX, AND, ORA, EOR, ADC, SBC, CLC, SEC, CPY, CPX, CMP)).* + HasOpcodeIn(Set(LDY, LDX, AND, ORA, EOR, ADC, SBC, CLC, SEC, CPY, CPX, CMP)).* ).capture(7) ~ blockIsIdempotentWhenItComesToIndexRegisters(7) ~ HasOpcodeIn(ShortConditionalBranching) ~ @@ -1010,7 +1010,7 @@ object AlwaysGoodOptimizations { ( ( (HasOpcodeIn(Set(LDA, LAX)) & MatchAddrMode(0) & MatchParameter(1)) ~ - HasOpcodeIn(Set(LDY, LDX, AND, ORA, EOR, ADC, SBC, CLC, SEC, CPY, CPX, CMP)).* + HasOpcodeIn(Set(LDY, LDX, AND, ORA, EOR, ADC, SBC, CLC, SEC, CPY, CPX, CMP)).* ).capture(7) ~ blockIsIdempotentWhenItComesToIndexRegisters(7) ~ (HasOpcodeIn(ShortConditionalBranching) & MatchParameter(2)) ~ @@ -1036,4 +1036,60 @@ object AlwaysGoodOptimizations { }, ) + + private def negate(branch: AssemblyLine) = branch.opcode match { + case BEQ => branch.copy(opcode = BNE) + case BNE => branch.copy(opcode = BEQ) + case BCC => branch.copy(opcode = BCS) + case BCS => branch.copy(opcode = BCC) + case BVC => branch.copy(opcode = BVS) + case BVS => branch.copy(opcode = BVC) + case BMI => branch.copy(opcode = BPL) + case BPL => branch.copy(opcode = BMI) + } + + val DoubleJumpSimplification = new RuleBasedAssemblyOptimization("Double jump simplification", + needsFlowInfo = FlowInfoRequirement.JustLabels, + ((Elidable & HasOpcode(LABEL) & MatchParameter(1)) ~ LinearOrLabel.*).capture(10) ~ + Where(ctx => ctx.get[List[AssemblyLine]](10).map(_.sizeInBytes).sum < 100) ~ + (Elidable & HasOpcodeIn(ShortConditionalBranching) & MatchParameter(0)) ~ + (Elidable & HasOpcode(JMP) & MatchParameter(1)) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(0)) ~~> { (code, ctx) => + ctx.get[List[AssemblyLine]](10) ++ List(negate(code(code.length - 3).copy(parameter = ctx.get[Constant](1))), code.last) + }, + ((Elidable & HasOpcode(LABEL) & MatchParameter(1)) ~ LinearOrLabel.*).capture(10) ~ + Where(ctx => ctx.get[List[AssemblyLine]](10).map(_.sizeInBytes).sum < 100) ~ + (Elidable & HasOpcodeIn(ShortConditionalBranching) & MatchParameter(0)).capture(13) ~ + ((Elidable & Not(MatchParameter(0))).* ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(0)) ~ + (Elidable & HasOpcode(JMP) & MatchParameter(1))).capture(11) ~~> { (code, ctx) => + ctx.get[List[AssemblyLine]](10) ++ ctx.get[List[AssemblyLine]](13).map(_.copy(parameter = ctx.get[Constant](1))) ++ ctx.get[List[AssemblyLine]](11) + }, + ) + + val IndexComparisonOptimization = new RuleBasedAssemblyOptimization("Index comparison optimization", + needsFlowInfo = FlowInfoRequirement.BackwardFlow, + (Elidable & HasOpcodeIn(Set(DEX, INX)) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~ + (Linear & Not(ConcernsX)).* ~ + (Elidable & (HasOpcode(TXA) & DoesntMatterWhatItDoesWith(State.A) | HasOpcode(CPX) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.C, State.V))) ~~> { code => + code.tail.init :+ code.head + }, + (Elidable & HasOpcodeIn(Set(DEY, INY)) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~ + (Linear & Not(ConcernsY)).* ~ + (Elidable & (HasOpcode(TYA) & DoesntMatterWhatItDoesWith(State.A) | HasOpcode(CPY) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.C, State.V))) ~~> { code => + code.tail.init :+ code.head + }, + (Elidable & HasAddrMode(Implied) & HasOpcodeIn(Set(DEC, INC)) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~ + (Linear & Not(ConcernsA)).* ~ + (Elidable & ( + HasOpcode(TAY) & DoesntMatterWhatItDoesWith(State.Y) + | HasOpcode(TAX) & DoesntMatterWhatItDoesWith(State.X) + | HasOpcode(EOR) & HasImmediate(0) + | HasOpcode(ORA) & HasImmediate(0) + | HasOpcode(AND) & HasImmediate(0xff) + | HasOpcode(ANC) & HasImmediate(0xff) & DoesntMatterWhatItDoesWith(State.C, State.V) + | HasOpcode(CMP) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.C, State.V))) ~~> { code => + code.tail.init :+ code.head + }, + ) }