From 86ce1d42f3159ddde696283a74578a39b770a3ad Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Mon, 6 Aug 2018 19:19:13 +0200 Subject: [PATCH] 6502: More optimizations. --- .../scala/millfork/OptimizationPresets.scala | 1 + .../mos/opt/AlwaysGoodOptimizations.scala | 12 ++++++ .../assembly/mos/opt/LaterOptimizations.scala | 43 +++++++++++++++++++ .../opt/RuleBasedAssemblyOptimization.scala | 14 ++++-- 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/main/scala/millfork/OptimizationPresets.scala b/src/main/scala/millfork/OptimizationPresets.scala index ed437c0b..d60ac20d 100644 --- a/src/main/scala/millfork/OptimizationPresets.scala +++ b/src/main/scala/millfork/OptimizationPresets.scala @@ -158,6 +158,7 @@ object OptimizationPresets { AlwaysGoodOptimizations.PointlessStackStore, AlwaysGoodOptimizations.SimplifiableStackOperation, LaterOptimizations.UseBit, + LaterOptimizations.ReplaceableLoad, ) val Good: List[AssemblyOptimization[AssemblyLine]] = List[AssemblyOptimization[AssemblyLine]]( diff --git a/src/main/scala/millfork/assembly/mos/opt/AlwaysGoodOptimizations.scala b/src/main/scala/millfork/assembly/mos/opt/AlwaysGoodOptimizations.scala index 4f9eb463..a8ef6dae 100644 --- a/src/main/scala/millfork/assembly/mos/opt/AlwaysGoodOptimizations.scala +++ b/src/main/scala/millfork/assembly/mos/opt/AlwaysGoodOptimizations.scala @@ -2067,6 +2067,18 @@ object AlwaysGoodOptimizations { (Elidable & (HasOpcode(TYA) & DoesntMatterWhatItDoesWith(State.A) | HasOpcode(CPY) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.C, State.V))) ~~> { code => code.tail.init :+ code.head }, + (Elidable & HasOpcodeIn(DEX, INX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~ + (Not(ConcernsX)).*.capture(1) ~ + Where(ctx => ctx.isExternallyLinearBlock(1)) ~ + (Elidable & (HasOpcode(TXA) & DoesntMatterWhatItDoesWith(State.A) | HasOpcode(CPX) & HasImmediate(0) & DoesntMatterWhatItDoesWith(State.C, State.V))) ~~> { code => + code.tail.init :+ code.head + }, + (Elidable & HasOpcodeIn(DEY, INY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~ + (Not(ConcernsY)).*.capture(1) ~ + Where(ctx => ctx.isExternallyLinearBlock(1)) ~ + (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(DEC, INC) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~ (Linear & Not(ConcernsA)).* ~ (Elidable & ( diff --git a/src/main/scala/millfork/assembly/mos/opt/LaterOptimizations.scala b/src/main/scala/millfork/assembly/mos/opt/LaterOptimizations.scala index 0a3eabaa..116510eb 100644 --- a/src/main/scala/millfork/assembly/mos/opt/LaterOptimizations.scala +++ b/src/main/scala/millfork/assembly/mos/opt/LaterOptimizations.scala @@ -503,6 +503,48 @@ object LaterOptimizations { )), ) + val ReplaceableLoad = new RuleBasedAssemblyOptimization("Replaceable load", + needsFlowInfo = FlowInfoRequirement.BothFlows, + (Elidable & HasOpcode(LDA) & MatchImmediate(1) & MatchA(0) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~ + Where(ctx => ctx.get[Constant](1).quickSimplify.isLowestByteAlwaysEqual(ctx.get[Int](0))) ~~> (_ => Nil), + (Elidable & HasOpcode(LDX) & MatchImmediate(1) & MatchX(0) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~ + Where(ctx => ctx.get[Constant](1).quickSimplify.isLowestByteAlwaysEqual(ctx.get[Int](0))) ~~> (_ => Nil), + (Elidable & HasOpcode(LDY) & MatchImmediate(1) & MatchY(0) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~ + Where(ctx => ctx.get[Constant](1).quickSimplify.isLowestByteAlwaysEqual(ctx.get[Int](0))) ~~> (_ => Nil), + (Elidable & HasOpcode(LDA) & MatchImmediate(1) & MatchX(0)) ~ + Where(ctx => ctx.get[Constant](1).quickSimplify.isLowestByteAlwaysEqual(ctx.get[Int](0))) ~~> (_ => List(AssemblyLine.implied(TXA))), + (Elidable & HasOpcode(LDA) & MatchImmediate(1) & MatchY(0)) ~ + Where(ctx => ctx.get[Constant](1).quickSimplify.isLowestByteAlwaysEqual(ctx.get[Int](0))) ~~> (_ => List(AssemblyLine.implied(TYA))), + (Elidable & HasOpcode(LDX) & MatchImmediate(1) & MatchA(0)) ~ + Where(ctx => ctx.get[Constant](1).quickSimplify.isLowestByteAlwaysEqual(ctx.get[Int](0))) ~~> (_ => List(AssemblyLine.implied(TAX))), + (Elidable & HasOpcode(LDY) & MatchImmediate(1) & MatchA(0)) ~ + Where(ctx => ctx.get[Constant](1).quickSimplify.isLowestByteAlwaysEqual(ctx.get[Int](0))) ~~> (_ => List(AssemblyLine.implied(TAY))), + + (Elidable & HasOpcode(LDA) & HasAddrModeIn(Absolute, ZeroPage, Immediate)) ~ + (Elidable & HasOpcode(TAX)) ~ + (Elidable & HasOpcode(STA) & HasAddrModeIn(Absolute, ZeroPage) & DoesntMatterWhatItDoesWith(State.A)) ~~>{ code => + List(code.head.copy(opcode = LDX), code.last.copy(opcode = STX)) + }, + + (Elidable & HasOpcode(LDA) & HasAddrModeIn(Absolute, ZeroPage, Immediate)) ~ + (Elidable & HasOpcode(TAY)) ~ + (Elidable & HasOpcode(STA) & HasAddrModeIn(Absolute, ZeroPage) & DoesntMatterWhatItDoesWith(State.A)) ~~>{ code => + List(code.head.copy(opcode = LDY), code.last.copy(opcode = STY)) + }, + + (Elidable & HasOpcode(LDX) & HasAddrModeIn(Absolute, ZeroPage, Immediate)) ~ + (Elidable & HasOpcode(TXA)) ~ + (Elidable & HasOpcode(STA) & HasAddrModeIn(Absolute, ZeroPage) & DoesntMatterWhatItDoesWith(State.A)) ~~>{ code => + List(code.head, code.last.copy(opcode = STX)) + }, + + (Elidable & HasOpcode(LDY) & HasAddrModeIn(Absolute, ZeroPage, Immediate)) ~ + (Elidable & HasOpcode(TYA)) ~ + (Elidable & HasOpcode(STA) & HasAddrModeIn(Absolute, ZeroPage) & DoesntMatterWhatItDoesWith(State.A)) ~~>{ code => + List(code.head, code.last.copy(opcode = STY)) + }, + ) + val All = List( DoubleLoadToDifferentRegisters, DoubleLoadToTheSameRegister, @@ -510,6 +552,7 @@ object LaterOptimizations { LoadingBranchesOptimization, PointlessLoadAfterStore, PointessLoadingForShifting, + ReplaceableLoad, LoadingAfterShifting, UseBit, UseXInsteadOfStack, diff --git a/src/main/scala/millfork/assembly/mos/opt/RuleBasedAssemblyOptimization.scala b/src/main/scala/millfork/assembly/mos/opt/RuleBasedAssemblyOptimization.scala index a65bdf07..5099a193 100644 --- a/src/main/scala/millfork/assembly/mos/opt/RuleBasedAssemblyOptimization.scala +++ b/src/main/scala/millfork/assembly/mos/opt/RuleBasedAssemblyOptimization.scala @@ -52,7 +52,13 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf case Nil => Nil case head :: tail => for ((rule, index) <- actualRules.zipWithIndex) { - val ctx = new AssemblyMatchingContext(optimizationContext.options, optimizationContext.labelMap, optimizationContext.zreg, optimizationContext.niceFunctionProperties) + val ctx = new AssemblyMatchingContext( + optimizationContext.options, + optimizationContext.labelMap, + optimizationContext.zreg, + optimizationContext.niceFunctionProperties, + head._1.labelUseCount(_) + ) rule.pattern.matchTo(ctx, code) match { case Some(rest: List[(FlowInfo, AssemblyLine)]) => val matchedChunkToOptimize: List[AssemblyLine] = code.take(code.length - rest.length).map(_._2) @@ -85,7 +91,8 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf class AssemblyMatchingContext(val compilationOptions: CompilationOptions, val labelMap: Map[String, Int], val zeropageRegister: Option[ThingInMemory], - val niceFunctionProperties: Set[(NiceFunctionProperty, String)]) { + val niceFunctionProperties: Set[(NiceFunctionProperty, String)], + val labeUseCount: String => Int) { @inline def log: Logger = compilationOptions.log @inline @@ -202,8 +209,7 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions, } // if a jump leads inside the block, then it's internal // if a jump leads outside the block, then it's external - jumps --= labels - jumps.isEmpty + jumps == labels && labels.forall(l => labeUseCount(l) <= 1) } def zreg(i: Int): Constant = {