diff --git a/src/main/scala/millfork/OptimizationPresets.scala b/src/main/scala/millfork/OptimizationPresets.scala index 4fc1ec39..a4afc381 100644 --- a/src/main/scala/millfork/OptimizationPresets.scala +++ b/src/main/scala/millfork/OptimizationPresets.scala @@ -21,6 +21,7 @@ object OptimizationPresets { AlwaysGoodOptimizations.PointlessLoadAfterLoadOrStore, LaterOptimizations.PointessLoadingForShifting, AlwaysGoodOptimizations.SimplifiableBitOpsSequence, + AlwaysGoodOptimizations.SimplifiableIndexChanging, AlwaysGoodOptimizations.IdempotentDuplicateRemoval, AlwaysGoodOptimizations.BranchInPlaceRemoval, UnusedLabelRemoval, @@ -179,6 +180,7 @@ object OptimizationPresets { AlwaysGoodOptimizations.ReverseFlowAnalysis, AlwaysGoodOptimizations.SimplifiableBitOpsSequence, AlwaysGoodOptimizations.SimplifiableCondition, + AlwaysGoodOptimizations.SimplifiableIndexChanging, AlwaysGoodOptimizations.SimplifiableStackOperation, AlwaysGoodOptimizations.SmarterShiftingOfWords, AlwaysGoodOptimizations.SmarterShiftingBytes, diff --git a/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala b/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala index d69335be..469cc98f 100644 --- a/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala +++ b/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala @@ -906,6 +906,30 @@ object AlwaysGoodOptimizations { }, ) + val SimplifiableIndexChanging = new RuleBasedAssemblyOptimization("Simplifiable index changing", + needsFlowInfo = FlowInfoRequirement.NoRequirement, + (Elidable & HasOpcode(LDX) & MatchImmediate(0)) ~ + (Linear & Not(ConcernsX) & Not(ReadsNOrZ)).* ~ + (Elidable & HasOpcode(DEX)) ~~> { (lines, ctx) => + lines.init.tail :+ AssemblyLine.immediate(LDX, (ctx.get[Constant](0) + -1).quickSimplify) + }, + (Elidable & HasOpcode(LDX) & MatchImmediate(0)) ~ + (Linear & Not(ConcernsX) & Not(ReadsNOrZ)).* ~ + (Elidable & HasOpcode(INX)) ~~> { (lines, ctx) => + lines.init.tail :+ AssemblyLine.immediate(LDX, (ctx.get[Constant](0) + 1).quickSimplify) + }, + (Elidable & HasOpcode(LDY) & MatchImmediate(0)) ~ + (Linear & Not(ConcernsY) & Not(ReadsNOrZ)).* ~ + (Elidable & HasOpcode(DEY)) ~~> { (lines, ctx) => + lines.init.tail :+ AssemblyLine.immediate(LDY, (ctx.get[Constant](0) + -1).quickSimplify) + }, + (Elidable & HasOpcode(LDY) & MatchImmediate(0)) ~ + (Linear & Not(ConcernsY) & Not(ReadsNOrZ)).* ~ + (Elidable & HasOpcode(INY)) ~~> { (lines, ctx) => + lines.init.tail :+ AssemblyLine.immediate(LDY, (ctx.get[Constant](0) + 1).quickSimplify) + }, + ) + val RemoveNops = new RuleBasedAssemblyOptimization("Removing NOP instructions", needsFlowInfo = FlowInfoRequirement.NoRequirement, (Elidable & HasOpcode(NOP)) ~~> (_ => Nil) diff --git a/src/main/scala/millfork/assembly/opt/LoopUnrolling.scala b/src/main/scala/millfork/assembly/opt/LoopUnrolling.scala index 5069c99d..946569de 100644 --- a/src/main/scala/millfork/assembly/opt/LoopUnrolling.scala +++ b/src/main/scala/millfork/assembly/opt/LoopUnrolling.scala @@ -115,6 +115,19 @@ object LoopUnrolling { val increasing = isIncreasing(ctx) ctx.get[List[AssemblyLine]](Initialization) ++ (0 until Math.abs(start - end)).flatMap(_ => fixLabels(ctx.get[List[AssemblyLine]](BodyWithStep))) }, + (Elidable & HasOpcode(LDX) & MatchNumericImmediate(Start)).capture(Initialization) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~ + ((Elidable & HasOpcodeIn(Set(DEX, INX))).capture(Step) ~ + (Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL, BNE, CPX, TXA))) & Not(ChangesX)).*.capture(Body) + ).capture(BodyWithStep) ~ + (Elidable & HasOpcode(CPX) & MatchNumericImmediate(End) | Elidable & HasOpcode(TXA)) ~ + (Elidable & HasOpcode(BNE) & MatchParameter(Back)) ~ + Where(ctx => isFeasible(ctx, 2, Unrolling.X)) ~~> { (code, ctx) => + val start = ctx.get[Int](Start) + val end = ctx.getOrDefault[Int](End, 0) + val increasing = isIncreasing(ctx) + ctx.get[List[AssemblyLine]](Initialization) ++ (0 until Math.abs(start - end)).flatMap(_ => fixLabels(ctx.get[List[AssemblyLine]](BodyWithStep))) + }, (Elidable & HasOpcode(LDY) & MatchNumericImmediate(Start)).capture(Initialization) ~ (Elidable & HasOpcode(BEQ) & MatchParameter(Skip)) ~ (Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~ @@ -143,5 +156,18 @@ object LoopUnrolling { val increasing = isIncreasing(ctx) ctx.get[List[AssemblyLine]](Initialization) ++ (0 until Math.abs(start - end)).flatMap(_ => fixLabels(ctx.get[List[AssemblyLine]](BodyWithStep))) }, + (Elidable & HasOpcode(LDY) & MatchNumericImmediate(Start)).capture(Initialization) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(Back)) ~ + ((Elidable & HasOpcodeIn(Set(DEY, INY))).capture(Step) ~ + (Elidable & Not(HasOpcodeIn(Set(RTS, JSR, RTI, RTL, BNE, CPY, TYA))) & Not(ChangesY)).*.capture(Body) + ).capture(BodyWithStep) ~ + (Elidable & HasOpcode(CPY) & MatchNumericImmediate(End) | Elidable & HasOpcode(TYA)) ~ + (Elidable & HasOpcode(BNE) & MatchParameter(Back)) ~ + Where(ctx => isFeasible(ctx, 2, Unrolling.Y)) ~~> { (code, ctx) => + val start = ctx.get[Int](Start) + val end = ctx.getOrDefault[Int](End, 0) + val increasing = isIncreasing(ctx) + ctx.get[List[AssemblyLine]](Initialization) ++ (0 until Math.abs(start - end)).flatMap(_ => fixLabels(ctx.get[List[AssemblyLine]](BodyWithStep))) + }, ) }