Various 6502 optimization fixes

This commit is contained in:
Karol Stasiak 2023-01-27 17:34:09 +01:00
parent 1bc1ab3539
commit e9cfec54b5
5 changed files with 34 additions and 28 deletions

View File

@ -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 {

View File

@ -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) =>

View File

@ -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)

View File

@ -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

View File

@ -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 :: _)
}