From e9cfec54b50558987a107f53b5369db5595699e0 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Fri, 27 Jan 2023 17:34:09 +0100 Subject: [PATCH] Various 6502 optimization fixes --- .../opt/ChangeIndexRegisterOptimization.scala | 46 +++++++++++-------- .../assembly/mos/opt/LaterOptimizations.scala | 4 +- .../assembly/mos/opt/VariableLifetime.scala | 6 +-- .../opt/RuleBasedAssemblyOptimization.scala | 4 +- .../WordVariableToRegisterOptimization.scala | 2 +- 5 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/main/scala/millfork/assembly/mos/opt/ChangeIndexRegisterOptimization.scala b/src/main/scala/millfork/assembly/mos/opt/ChangeIndexRegisterOptimization.scala index ea80cda5..c8d15994 100644 --- a/src/main/scala/millfork/assembly/mos/opt/ChangeIndexRegisterOptimization.scala +++ b/src/main/scala/millfork/assembly/mos/opt/ChangeIndexRegisterOptimization.scala @@ -1,10 +1,11 @@ package millfork.assembly.mos.opt -import millfork.assembly.mos.{AssemblyLine, AssemblyLine0, OpcodeClasses} +import millfork.assembly.mos.{AddrMode, AssemblyLine, AssemblyLine0, OpcodeClasses} import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext} import millfork.env.NormalFunction import millfork.error.Logger +import scala.annotation.tailrec import scala.util.control.TailCalls.{TailRec, done, tailcall} /** @@ -35,19 +36,19 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi override def name = "Changing index registers" override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = { + + val addressingModesUsingX = Set(AbsoluteX, ZeroPageX, IndexedX) + val addressingModesUsingY = Set(AbsoluteY, ZeroPageY, IndexedY, LongIndexedY, IndexedSY) val usesX = code.exists(l => OpcodeClasses.ReadsXAlways(l.opcode) || - OpcodeClasses.ReadsYAlways(l.opcode) || OpcodeClasses.ChangesX(l.opcode) || - OpcodeClasses.ChangesY(l.opcode) || - Set(AbsoluteX, AbsoluteY, ZeroPageY, ZeroPageX, IndexedX, IndexedY, LongIndexedY, IndexedSY)(l.addrMode) + addressingModesUsingX(l.addrMode) ) - val usesY = code.exists(l => - OpcodeClasses.ReadsXAlways(l.opcode) || - OpcodeClasses.ReadsYAlways(l.opcode) || - OpcodeClasses.ChangesX(l.opcode) || + val usesY = code.exists(l => { + OpcodeClasses.ReadsYAlways(l.opcode) || OpcodeClasses.ChangesY(l.opcode) || - Set(AbsoluteX, AbsoluteY, ZeroPageY, ZeroPageX, IndexedX, IndexedY, LongIndexedY, IndexedSY)(l.addrMode) + addressingModesUsingY(l.addrMode) + } ) if (!usesX && !usesY) { return code @@ -101,21 +102,25 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi } //noinspection OptionEqualsSome - private def canOptimize(code: List[AssemblyLine], dir: IndexDirection, loaded: Option[IndexReg]): Boolean = code match { + @tailrec + private def canOptimize(code: List[AssemblyLine], dir: IndexDirection, loaded: Option[IndexReg]): Boolean = { + val notX = loaded != Some(X) + val notY = loaded != Some(Y) + code match { case AssemblyLine0(INC | DEC | ASL | ROL | ROR | LSR | STZ | LDZ | BIT, AbsoluteX | ZeroPageX, _) :: xs if dir == X2Y => false case AssemblyLine0(LDY | STY, AbsoluteX | ZeroPageX, _) :: xs => false case AssemblyLine0(LDX | STX, AbsoluteY | ZeroPageY, _) :: xs => false - case AssemblyLine0(_, AbsoluteY, _) :: xs if loaded != Some(Y) => false - case AssemblyLine0(_, ZeroPageY, _) :: xs if loaded != Some(Y) => false - case AssemblyLine0(_, IndexedY, _) :: xs if dir == Y2X || loaded != Some(Y) => false - case AssemblyLine0(_, LongIndexedY, _) :: xs if dir == Y2X || loaded != Some(Y) => false - case AssemblyLine0(_, IndexedSY, _) :: xs if dir == Y2X || loaded != Some(Y) => false - case AssemblyLine0(_, AbsoluteX, _) :: xs if loaded != Some(X) => false - case AssemblyLine0(_, LongAbsoluteX, _) :: xs if loaded != Some(X) => false - case AssemblyLine0(_, ZeroPageX, _) :: xs if loaded != Some(X) => false - case AssemblyLine0(_, IndexedX, _) :: xs if dir == X2Y || loaded != Some(X) => false + case AssemblyLine0(_, AbsoluteY, _) :: xs if notY => false + case AssemblyLine0(_, ZeroPageY, _) :: xs if notY => false + case AssemblyLine0(_, IndexedY, _) :: xs if dir == Y2X || notY => false + case AssemblyLine0(_, LongIndexedY, _) :: xs if dir == Y2X || notY => false + case AssemblyLine0(_, IndexedSY, _) :: xs if dir == Y2X || notY => false + case AssemblyLine0(_, AbsoluteX, _) :: xs if notX => false + case AssemblyLine0(_, LongAbsoluteX, _) :: xs if notX => false + case AssemblyLine0(_, ZeroPageX, _) :: xs if notX => false + case AssemblyLine0(_, IndexedX | ImmediateWithAbsoluteX | ImmediateWithZeroPageX, _) :: xs if dir == X2Y || notX => false case AssemblyLine0(_, AbsoluteIndexedX, _) :: xs if dir == X2Y => false case AssemblyLine0(SHX | SHY | AHX | TAS | LAS, _, _) :: xs => false case AssemblyLine(TXY, _, _, e, _) :: xs => (e == Elidability.Elidable || e == Elidability.Volatile) && loaded == Some(X) && canOptimize(xs, dir, Some(Y)) @@ -155,11 +160,12 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi (e == Elidability.Elidable || e == Elidability.Volatile || dir == X2Y) && loaded == Some(Y) && canOptimize(xs, dir, Some(Y)) case AssemblyLine0(SAX | TXS | SBX, _, _) :: xs => dir == Y2X && loaded == Some(X) && canOptimize(xs, dir, Some(X)) - case AssemblyLine0(TSX, _, _) :: xs => dir == Y2X && loaded != Some(Y) && canOptimize(xs, dir, Some(X)) + case AssemblyLine0(TSX, _, _) :: xs => dir == Y2X && notY && canOptimize(xs, dir, Some(X)) case _ :: xs => canOptimize(xs, dir, loaded) case Nil => true + } } private def switchX2Y(code: List[AssemblyLine])(implicit log: Logger): TailRec[List[AssemblyLine]] = code match { diff --git a/src/main/scala/millfork/assembly/mos/opt/LaterOptimizations.scala b/src/main/scala/millfork/assembly/mos/opt/LaterOptimizations.scala index dece238b..48c3660e 100644 --- a/src/main/scala/millfork/assembly/mos/opt/LaterOptimizations.scala +++ b/src/main/scala/millfork/assembly/mos/opt/LaterOptimizations.scala @@ -145,8 +145,8 @@ object LaterOptimizations { //noinspection ZeroIndexToHead private def InterleavedLoads(load: Opcode.Value, store: Opcode.Value) = { (Elidable & HasOpcode(load) & MatchAddrMode(0) & MatchParameter(1)).capture(12) ~ - (Elidable & HasOpcode(store)).+.capture(10) ~ - (Elidable & HasOpcode(load) & MatchAddrMode(2) & MatchParameter(3) & DoesNotConcernMemoryAt(0, 1)).capture(13) ~ + (Elidable & HasOpcode(store) & MatchAddrMode(20) & MatchParameter(21)).+.capture(10) ~ + (Elidable & HasOpcode(load) & MatchAddrMode(2) & MatchParameter(3) & DoesNotConcernMemoryAt(0, 1) & DoesNotConcernMemoryAt(20, 21)).capture(13) ~ (Elidable & HasOpcode(store) & DoesNotConcernMemoryAt(0, 1) & DoesNotConcernMemoryAt(2, 3)).+.capture(11) ~ (Elidable & HasOpcode(load) & MatchAddrMode(0) & MatchParameter(1)) ~ WhereNoMemoryAccessOverlapBetweenTwoLineLists(10, 11) ~~> { (_, ctx) => diff --git a/src/main/scala/millfork/assembly/mos/opt/VariableLifetime.scala b/src/main/scala/millfork/assembly/mos/opt/VariableLifetime.scala index c58b8aa4..60b2f566 100644 --- a/src/main/scala/millfork/assembly/mos/opt/VariableLifetime.scala +++ b/src/main/scala/millfork/assembly/mos/opt/VariableLifetime.scala @@ -1,6 +1,6 @@ package millfork.assembly.mos.opt -import millfork.assembly.mos.{AssemblyLine, AssemblyLine0, OpcodeClasses, State} +import millfork.assembly.mos.{AddrMode, AssemblyLine, AssemblyLine0, OpcodeClasses, State} import millfork.env._ import millfork.error.ConsoleLogger import millfork.node.NiceFunctionProperty @@ -12,10 +12,10 @@ object VariableLifetime { // This only works for non-stack variables. def apply(variableName: String, code: List[AssemblyLine], expandToIncludeIndexing: Boolean = false, expandToIncludeUsesOfLoadedIndices: Option[Set[(NiceFunctionProperty, String)]] = None): Range = { - val flags = code.map(_.parameter match { + val flags = code.map(line => line.parameter match { case MemoryAddressConstant(MemoryVariable(n, _, _)) if n == variableName => true case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(MemoryVariable(n, _, _)), NumericConstant(_, 1)) if n == variableName => true - case _ => false + case p => line.addrMode == AddrMode.IndexedX && p.refersTo(variableName) }) if (flags.forall(!_)) return Range(0, 0) var min = flags.indexOf(true) diff --git a/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala b/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala index 80116b84..ffa12e49 100644 --- a/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala +++ b/src/main/scala/millfork/assembly/z80/opt/RuleBasedAssemblyOptimization.scala @@ -245,11 +245,11 @@ object HelperCheckers { case LD | LD_16 | ADD_16 | ADC_16 | SBC_16 => l.registers match { case TwoRegisters(MEM_HL | MEM_IX_D | MEM_IY_D | MEM_BC | MEM_DE, _) => true case TwoRegisters(_, MEM_HL | MEM_IX_D | MEM_IY_D | MEM_BC | MEM_DE) => true - case TwoRegisters(_, _) => false + case _ => false } case ADD | SUB | SBC | ADC | XOR | CP | OR | AND => l.registers match { case OneRegister(MEM_HL | MEM_IX_D | MEM_IY_D | MEM_BC | MEM_DE) => true - case OneRegister(_) => false + case _ => false } case CHANGED_MEM => true case POP | PUSH => false diff --git a/src/main/scala/millfork/assembly/z80/opt/WordVariableToRegisterOptimization.scala b/src/main/scala/millfork/assembly/z80/opt/WordVariableToRegisterOptimization.scala index 5e803ab9..accda855 100644 --- a/src/main/scala/millfork/assembly/z80/opt/WordVariableToRegisterOptimization.scala +++ b/src/main/scala/millfork/assembly/z80/opt/WordVariableToRegisterOptimization.scala @@ -565,7 +565,7 @@ object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] { } else if (de != "") { tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.register(PUSH, DE).pos(s) :: x :: ZLine.register(POP, DE).pos(s) :: _) } else { - throw new IllegalStateException() + tailcall(inlineVars(hl, bc, de, xs)).map(x :: _) }