diff --git a/src/main/scala/millfork/OptimizationPresets.scala b/src/main/scala/millfork/OptimizationPresets.scala index ec328eab..dfcd286a 100644 --- a/src/main/scala/millfork/OptimizationPresets.scala +++ b/src/main/scala/millfork/OptimizationPresets.scala @@ -72,6 +72,7 @@ object OptimizationPresets { AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeReturn, AlwaysGoodOptimizations.PointlessRegisterTransfersBeforeStore, AlwaysGoodOptimizations.PointlessStashingToIndexOverShortSafeBranch, + AlwaysGoodOptimizations.LoopInvariantRegister, AlwaysGoodOptimizations.PointlessStackStashing, AlwaysGoodOptimizations.RearrangeMath, EmptyMemoryStoreRemoval, @@ -146,6 +147,7 @@ object OptimizationPresets { AlwaysGoodOptimizations.IncrementingIndexRegistersAfterTransfer, AlwaysGoodOptimizations.IndexComparisonOptimization, AlwaysGoodOptimizations.IndexSequenceOptimization, + AlwaysGoodOptimizations.LoopInvariantRegister, LoopUnrolling.LoopUnrolling, 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 8500efbe..2f646665 100644 --- a/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala +++ b/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala @@ -1096,6 +1096,35 @@ object AlwaysGoodOptimizations { } ) + val LoopInvariantRegister = new RuleBasedAssemblyOptimization("Loop invariant register", + needsFlowInfo = FlowInfoRequirement.JustLabels, + (HasOpcode(LABEL) & MatchParameter(2) & HasCallerCount(1)) ~ + (Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & IsNonvolatile) ~ + (Linear & Not(ChangesA) & DoesntChangeMemoryAt(0, 1)).* ~ + (ShortConditionalBranching & MatchParameter(2)) ~~> { code => + code(1) :: code(0) :: code.drop(2) + }, + (HasOpcode(LABEL) & MatchParameter(2) & HasCallerCount(1)) ~ + (Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1) & IsNonvolatile) ~ + (Linear & Not(ChangesX) & DoesntChangeMemoryAt(0, 1)).* ~ + (ShortConditionalBranching & MatchParameter(2)) ~~> { code => + code(1) :: code(0) :: code.drop(2) + }, + (HasOpcode(LABEL) & MatchParameter(2) & HasCallerCount(1)) ~ + (Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1) & IsNonvolatile) ~ + (Linear & Not(ChangesY) & DoesntChangeMemoryAt(0, 1)).* ~ + (ShortConditionalBranching & MatchParameter(2)) ~~> { code => + code(1) :: code(0) :: code.drop(2) + }, + (HasOpcode(LABEL) & MatchParameter(2) & HasCallerCount(1)) ~ + (Elidable & HasOpcode(LDZ) & MatchAddrMode(0) & MatchParameter(1)) ~ + (Linear & Not(ChangesIZ) & DoesntChangeMemoryAt(0, 1)).* ~ + (ShortConditionalBranching & MatchParameter(2)) ~~> { code => + code(1) :: code(0) :: code.drop(2) + }, + + ) + val UnusedLabelRemoval = new RuleBasedAssemblyOptimization("Unused label removal", needsFlowInfo = FlowInfoRequirement.JustLabels, diff --git a/src/main/scala/millfork/assembly/opt/RuleBasedAssemblyOptimization.scala b/src/main/scala/millfork/assembly/opt/RuleBasedAssemblyOptimization.scala index 46eac344..b63c1a3f 100644 --- a/src/main/scala/millfork/assembly/opt/RuleBasedAssemblyOptimization.scala +++ b/src/main/scala/millfork/assembly/opt/RuleBasedAssemblyOptimization.scala @@ -198,8 +198,8 @@ object HelperCheckers { def memoryAccessDoesntOverlap(l1: AssemblyLine, l2: AssemblyLine): Boolean = { val a1 = l1.addrMode val a2 = l2.addrMode - if (badAddrModes(a1) || badAddrModes(a2)) return false if (goodAddrModes(a1) || goodAddrModes(a2)) return true + if (badAddrModes(a1) || badAddrModes(a2)) return false if ((a1 == IndexedSY) != (a2 == IndexedSY)) return true // bold assertion, but usually true val p1 = l1.parameter val p2 = l2.parameter @@ -658,6 +658,19 @@ case object ReadsMemory extends TrivialAssemblyLinePattern { } } +case object IsNonvolatile extends TrivialAssemblyLinePattern { + // TODO: this is a big hack + override def apply(line: AssemblyLine): Boolean = + line.addrMode match { + case AddrMode.Implied | AddrMode.Immediate => true + case _ => line.parameter match { + case MemoryAddressConstant(_) => true + case CompoundConstant(_, MemoryAddressConstant(_), _) => true + case _ => false + } + } +} + case object ReadsX extends TrivialAssemblyLinePattern { val XAddrModes = Set(AddrMode.AbsoluteX, AddrMode.IndexedX, AddrMode.ZeroPageX, AddrMode.AbsoluteIndexedX)