mirror of
https://github.com/KarolS/millfork.git
synced 2024-06-30 21:29:36 +00:00
Optimization bugfixes
This commit is contained in:
parent
17920cee26
commit
7bbf655e6f
|
@ -103,6 +103,7 @@ object OptimizationPresets {
|
|||
AlwaysGoodOptimizations.PointlessMathFromFlow,
|
||||
AlwaysGoodOptimizations.PointlessMathFromFlow,
|
||||
AlwaysGoodOptimizations.PointlessMathFromFlow,
|
||||
AlwaysGoodOptimizations.SimplifiableCondition,
|
||||
AlwaysGoodOptimizations.IncrementingIndexRegistersAfterTransfer,
|
||||
AlwaysGoodOptimizations.MathOperationOnTwoIdenticalMemoryOperands,
|
||||
LaterOptimizations.UseZeropageAddressingMode,
|
||||
|
@ -154,6 +155,7 @@ object OptimizationPresets {
|
|||
AlwaysGoodOptimizations.RemoveNops,
|
||||
AlwaysGoodOptimizations.ReverseFlowAnalysis,
|
||||
AlwaysGoodOptimizations.SimplifiableBitOpsSequence,
|
||||
AlwaysGoodOptimizations.SimplifiableCondition,
|
||||
AlwaysGoodOptimizations.SmarterShiftingWords,
|
||||
AlwaysGoodOptimizations.UnconditionalJumpRemoval,
|
||||
UnusedLabelRemoval,
|
||||
|
|
|
@ -17,6 +17,8 @@ import millfork.env._
|
|||
object AlwaysGoodOptimizations {
|
||||
|
||||
val counter = new AtomicInteger(30000)
|
||||
val LdxAddrModes = Set(Immediate, Absolute, ZeroPage, ZeroPageY, AbsoluteY)
|
||||
val LdyAddrModes = Set(Immediate, Absolute, ZeroPage, ZeroPageX, AbsoluteX)
|
||||
|
||||
def getNextLabel(prefix: String) = f".$prefix%s__${counter.getAndIncrement()}%05d"
|
||||
|
||||
|
@ -340,15 +342,22 @@ object AlwaysGoodOptimizations {
|
|||
flagsToTrash: Seq[State.Value],
|
||||
fix: ((AssemblyMatchingContext, Int) => List[AssemblyLine]),
|
||||
alternateStore: Opcode.Value = LABEL) = {
|
||||
val actualFlagsToTrash = List(State.N, State.Z) ++ flagsToTrash
|
||||
val init = Elidable & HasOpcode(store) & HasAddrMode(addrMode) & MatchAddrMode(3) & MatchParameter(0) & DoesntMatterWhatItDoesWith(actualFlagsToTrash: _*) & initExtra
|
||||
val meantime = (Linear & Not(ConcernsMemory) & meantimeExtra).*
|
||||
val oneModification = Elidable & HasOpcode(modify) & HasAddrMode(addrMode) & MatchParameter(0) & DoesntMatterWhatItDoesWith(actualFlagsToTrash: _*)
|
||||
val actualFlagsToTrashForStore = List(State.N, State.Z) ++ flagsToTrash ++ (store match{
|
||||
case STA => List(State.A)
|
||||
case STY => List(State.Y)
|
||||
case STX => List(State.X)
|
||||
case SAX => List(State.A, State.X)
|
||||
case STZ => Nil
|
||||
})
|
||||
val actualFlagsToTrashForModify = List(State.N, State.Z) ++ flagsToTrash
|
||||
val init = Elidable & HasOpcode(store) & HasAddrMode(addrMode) & MatchAddrMode(3) & MatchParameter(0) & DoesntMatterWhatItDoesWith(actualFlagsToTrashForStore: _*) & initExtra
|
||||
val meantime = (Linear & DoesNotConcernMemoryAt(3, 0) & meantimeExtra).*
|
||||
val oneModification = Elidable & HasOpcode(modify) & HasAddrMode(addrMode) & MatchParameter(0) & DoesntMatterWhatItDoesWith(actualFlagsToTrashForModify: _*)
|
||||
val modifications = (if (atLeastTwo) oneModification ~ oneModification.+ else oneModification.+).captureLength(1)
|
||||
if (alternateStore == LABEL) {
|
||||
((init ~ meantime).capture(2) ~ modifications) ~~> ((code, ctx) => fix(ctx, ctx.get[Int](1)) ++ ctx.get[List[AssemblyLine]](2))
|
||||
} else {
|
||||
(init.capture(3) ~ meantime.capture(2) ~ modifications) ~~> { (code, ctx) =>
|
||||
(init ~ meantime.capture(2) ~ modifications) ~~> { (code, ctx) =>
|
||||
fix(ctx, ctx.get[Int](1)) ++
|
||||
List(AssemblyLine(alternateStore, ctx.get[AddrMode.Value](3), ctx.get[Constant](0))) ++
|
||||
ctx.get[List[AssemblyLine]](2)
|
||||
|
@ -356,8 +365,8 @@ object AlwaysGoodOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
val ModificationOfJustWrittenValue = new RuleBasedAssemblyOptimization("Modification of Just written value",
|
||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||
val ModificationOfJustWrittenValue = new RuleBasedAssemblyOptimization("Modification of just written value",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
modificationOfJustWrittenValue(STA, Absolute, MatchA(5), INC, Anything, atLeastTwo = false, Seq(), (c, i) => List(
|
||||
AssemblyLine.immediate(LDA, (c.get[Int](5) + i) & 0xff)
|
||||
)),
|
||||
|
@ -572,10 +581,10 @@ object AlwaysGoodOptimizations {
|
|||
|
||||
val PointlessLoadBeforeTransfer = new RuleBasedAssemblyOptimization("Pointless load before transfer",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
loadBeforeTransfer(LDX, LDA, ConcernsX, State.X, TXA, Set(Immediate, ZeroPage, Absolute, IndexedY, AbsoluteY)),
|
||||
loadBeforeTransfer(LDA, LDX, ConcernsA, State.A, TAX, Set(Immediate, ZeroPage, Absolute, IndexedY, AbsoluteY)),
|
||||
loadBeforeTransfer(LDY, LDA, ConcernsY, State.Y, TYA, Set(Immediate, ZeroPage, Absolute, ZeroPageX, IndexedX, AbsoluteX)),
|
||||
loadBeforeTransfer(LDA, LDY, ConcernsA, State.A, TAY, Set(Immediate, ZeroPage, Absolute, ZeroPageX, IndexedX, AbsoluteX)),
|
||||
loadBeforeTransfer(LDX, LDA, ConcernsX, State.X, TXA, LdxAddrModes),
|
||||
loadBeforeTransfer(LDA, LDX, ConcernsA, State.A, TAX, LdxAddrModes),
|
||||
loadBeforeTransfer(LDY, LDA, ConcernsY, State.Y, TYA, LdyAddrModes),
|
||||
loadBeforeTransfer(LDA, LDY, ConcernsA, State.A, TAY, LdyAddrModes),
|
||||
)
|
||||
|
||||
private def immediateLoadBeforeTwoTransfers(ld1: Opcode.Value, ld2: Opcode.Value, concerns1: AssemblyLinePattern, overwrites1: State.Value, t12: Opcode.Value, t21: Opcode.Value) =
|
||||
|
@ -698,17 +707,21 @@ object AlwaysGoodOptimizations {
|
|||
val stax = if (hiFromX) STX else STA
|
||||
val restriction = if (hiFromX) Not(ReadsX) else Anything
|
||||
val originalStart = if (hiFirst) {
|
||||
(Elidable & HasOpcode(LDA) & MatchParameter(0) & MatchAddrMode(1)) ~
|
||||
(Elidable & HasOpcode(STA) & MatchParameter(2) & MatchAddrMode(3) & restriction) ~
|
||||
(Elidable & HasOpcode(ldax) & HasImmediate(0)) ~
|
||||
(Elidable & HasOpcode(stax) & MatchParameter(4) & MatchAddrMode(5))
|
||||
} else {
|
||||
(Elidable & HasOpcode(ldax) & HasImmediate(0)) ~
|
||||
(HasOpcode(ldax) & HasImmediate(0)) ~
|
||||
(Elidable & HasOpcode(stax) & MatchParameter(4) & MatchAddrMode(5)) ~
|
||||
(Elidable & HasOpcode(LDA) & MatchParameter(0) & MatchAddrMode(1)) ~
|
||||
(Elidable & HasOpcode(STA) & MatchParameter(2) & MatchAddrMode(3) & restriction)
|
||||
(HasOpcode(LDA) & MatchParameter(0) & MatchAddrMode(1) & DoesNotConcernMemoryAt(5, 4)) ~
|
||||
(Elidable & HasOpcode(STA) & MatchParameter(2) & MatchAddrMode(3) & DoesntChangeMemoryAt(1, 0) & DoesntChangeMemoryAt(5, 4) & restriction)
|
||||
} else {
|
||||
(HasOpcode(LDA) & MatchParameter(0) & MatchAddrMode(1)) ~
|
||||
(Elidable & HasOpcode(STA) & MatchParameter(2) & MatchAddrMode(3) & DoesntChangeMemoryAt(1, 0) & restriction) ~
|
||||
(HasOpcode(ldax) & HasImmediate(0)) ~
|
||||
(Elidable & HasOpcode(stax) & MatchParameter(4) & MatchAddrMode(5) & DoesntChangeMemoryAt(1, 0) & DoesntChangeMemoryAt(3, 2))
|
||||
}
|
||||
val middle = (Linear & Not(ConcernsMemory) & DoesntChangeIndexingInAddrMode(3) & DoesntChangeIndexingInAddrMode(5)).*
|
||||
val middle = (Linear
|
||||
& DoesntChangeIndexingInAddrMode(3)
|
||||
& DoesntChangeIndexingInAddrMode(5)
|
||||
& DoesNotConcernMemoryAt(3, 2)
|
||||
& DoesNotConcernMemoryAt(5, 4)).*
|
||||
val singleOriginalShift =
|
||||
(Elidable & HasOpcode(ASL) & MatchParameter(2) & MatchAddrMode(3)) ~
|
||||
(Elidable & HasOpcode(ROL) & MatchParameter(4) & MatchAddrMode(5) & DoesntMatterWhatItDoesWith(State.C, State.N, State.V, State.Z))
|
||||
|
@ -1092,4 +1105,27 @@ object AlwaysGoodOptimizations {
|
|||
code.tail.init :+ code.head
|
||||
},
|
||||
)
|
||||
|
||||
private def remapZ2N(line: AssemblyLine) = line.opcode match {
|
||||
case BNE => line.copy(opcode = BMI)
|
||||
case BEQ => line.copy(opcode = BPL)
|
||||
}
|
||||
private def remapZ2V(line: AssemblyLine) = line.opcode match {
|
||||
case BNE => line.copy(opcode = BVS)
|
||||
case BEQ => line.copy(opcode = BVC)
|
||||
}
|
||||
|
||||
val SimplifiableCondition = new RuleBasedAssemblyOptimization("Simplifiable condition",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
HasOpcode(LDA) ~
|
||||
(Elidable & HasOpcode(AND) & HasImmediate(0x80)) ~
|
||||
(Elidable & HasOpcodeIn(Set(BNE, BEQ)) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> {code =>
|
||||
List(code(0), remapZ2N(code(2)))
|
||||
},
|
||||
(Elidable & HasOpcode(LDA) & HasImmediate(0x80)) ~
|
||||
(Elidable & HasOpcode(AND)) ~
|
||||
(Elidable & HasOpcodeIn(Set(BNE, BEQ)) & DoesntMatterWhatItDoesWith(State.A, State.N, State.Z)) ~~> {code =>
|
||||
List(code(1).copy(opcode = LDA), remapZ2N(code(2)))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -46,6 +46,18 @@ object Status {
|
|||
SingleStatus(y >= 0x80)
|
||||
case _ => AnyStatus()
|
||||
}
|
||||
|
||||
def adc(value: Int, carry: Status[Boolean], decimal: Status[Boolean]): Status[Int] = inner match {
|
||||
case SingleStatus(x) => decimal match {
|
||||
case SingleStatus(false) => carry match {
|
||||
case SingleStatus(true) => SingleStatus(x + value + 1)
|
||||
case SingleStatus(false) => SingleStatus(x + value)
|
||||
case _ => AnyStatus()
|
||||
}
|
||||
case _ => AnyStatus()
|
||||
}
|
||||
case _ => AnyStatus()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -195,6 +207,10 @@ object CoarseFlowAnalyzer {
|
|||
val n = nn.toInt
|
||||
currentStatus = currentStatus.nz(n).copy(a = SingleStatus(n), x = SingleStatus(n))
|
||||
|
||||
case AssemblyLine(ADC, Immediate, NumericConstant(nn, _), _) =>
|
||||
val n = nn.toInt
|
||||
val newA = currentStatus.a.adc(n, currentStatus.c, currentStatus.d)
|
||||
currentStatus = currentStatus.copy(n = newA.n(), z = newA.z(), a = newA, c = AnyStatus(), v = AnyStatus())
|
||||
case AssemblyLine(EOR, Immediate, NumericConstant(nn, _), _) =>
|
||||
val n = nn.toInt
|
||||
currentStatus = currentStatus.copy(n = currentStatus.a.n(_ ^ n), z = currentStatus.a.z(_ ^ n), a = currentStatus.a.map(_ ^ n))
|
||||
|
@ -236,6 +252,11 @@ object CoarseFlowAnalyzer {
|
|||
case AssemblyLine(TYA, _, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = currentStatus.y, n = currentStatus.y.n(), z = currentStatus.y.z())
|
||||
|
||||
case AssemblyLine(ASL, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = currentStatus.a.map(_ << 1), n = currentStatus.a.n(_ << 1), z = currentStatus.a.z(_ << 1),c = currentStatus.a.map(a => a.&(0xff).!=(0)))
|
||||
case AssemblyLine(LSR, Implied, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = currentStatus.a.map(a => a.>>(1).&(0x7f)), n = currentStatus.a.n(a => a.>>(1).&(0x7f)), z = currentStatus.a.z(a => a.>>(1).&(0x7f)),c = currentStatus.a.map(a => a.&(1).!=(0)))
|
||||
|
||||
case AssemblyLine(opcode, addrMode, parameter, _) =>
|
||||
if (OpcodeClasses.ChangesX(opcode)) currentStatus = currentStatus.copy(x = AnyStatus())
|
||||
if (OpcodeClasses.ChangesY(opcode)) currentStatus = currentStatus.copy(y = AnyStatus())
|
||||
|
|
|
@ -244,7 +244,7 @@ object LaterOptimizations {
|
|||
// TODO: make it more generic
|
||||
val IndexSwitchingOptimization = new RuleBasedAssemblyOptimization("Index switching optimization",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(Elidable & HasOpcode(LDY) & MatchAddrMode(2) & Not(ReadsX) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(LDY) & MatchAddrMode(2) & Not(ReadsX) & MatchParameter(0) & HasAddrModeIn(LdxAddrModes)) ~
|
||||
(Elidable & Linear & Not(ChangesY) & HasAddrMode(AbsoluteY) & SupportsAbsoluteX & Not(ConcernsX)) ~
|
||||
(HasOpcode(LDY) & Not(ConcernsX)) ~
|
||||
(Linear & Not(ChangesY) & Not(ConcernsX) & HasAddrModeIn(Set(AbsoluteY, IndexedY, ZeroPageY))) ~
|
||||
|
@ -257,7 +257,7 @@ object LaterOptimizations {
|
|||
code(3),
|
||||
code(5).copy(addrMode = AbsoluteX))
|
||||
},
|
||||
(Elidable & HasOpcode(LDX) & MatchAddrMode(2) & Not(ReadsY) & MatchParameter(0)) ~
|
||||
(Elidable & HasOpcode(LDX) & MatchAddrMode(2) & Not(ReadsY) & MatchParameter(0) & HasAddrModeIn(LdyAddrModes)) ~
|
||||
(Elidable & Linear & Not(ChangesX) & HasAddrMode(AbsoluteX) & SupportsAbsoluteY & Not(ConcernsY)) ~
|
||||
(HasOpcode(LDX) & Not(ConcernsY)) ~
|
||||
(Linear & Not(ChangesX) & Not(ConcernsY) & HasAddrModeIn(Set(AbsoluteX, IndexedX, ZeroPageX, AbsoluteIndexedX))) ~
|
||||
|
|
|
@ -55,14 +55,15 @@ case class CpuImportance(a: Importance = UnknownImportance,
|
|||
)
|
||||
|
||||
def isUnimportant(state: State.Value): Boolean = state match {
|
||||
case State.A => a == Unimportant
|
||||
case State.X => x == Unimportant
|
||||
case State.Y => y == Unimportant
|
||||
case State.Z => z == Unimportant
|
||||
case State.N => n == Unimportant
|
||||
case State.C => c == Unimportant
|
||||
case State.V => v == Unimportant
|
||||
case State.D => d == Unimportant
|
||||
// UnknownImportance is usually an effect of unreachable code
|
||||
case State.A => a != Important
|
||||
case State.X => x != Important
|
||||
case State.Y => y != Important
|
||||
case State.Z => z != Important
|
||||
case State.N => n != Important
|
||||
case State.C => c != Important
|
||||
case State.V => v != Important
|
||||
case State.D => d != Important
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,8 +98,16 @@ object ReverseFlowAnalyzer {
|
|||
case _ =>
|
||||
}
|
||||
codeArray(i) match {
|
||||
case AssemblyLine(JSR, _, MemoryAddressConstant(fun:FunctionInMemory), _) =>
|
||||
var result = new CpuImportance().copy(d = Important)
|
||||
case AssemblyLine(JSR | JMP, Absolute, MemoryAddressConstant(fun:FunctionInMemory), _) =>
|
||||
var result = new CpuImportance(
|
||||
a = Unimportant,
|
||||
x = Unimportant,
|
||||
y = Unimportant,
|
||||
z = Unimportant,
|
||||
n = Unimportant,
|
||||
c = Unimportant,
|
||||
v = Unimportant,
|
||||
d = Important)
|
||||
fun.params match {
|
||||
case AssemblyParamSignature(params) =>
|
||||
params.foreach(_.variable match {
|
||||
|
@ -119,16 +128,16 @@ object ReverseFlowAnalyzer {
|
|||
case _ =>
|
||||
}
|
||||
currentImportance = result
|
||||
case AssemblyLine(JSR, _, _, _) =>
|
||||
case AssemblyLine(JSR | BRK, _, _, _) =>
|
||||
currentImportance = finalImportance
|
||||
case AssemblyLine(JMP, Absolute, MemoryAddressConstant(Label(l)), _) =>
|
||||
case AssemblyLine(JMP | BRA, Absolute | Relative, MemoryAddressConstant(Label(l)), _) =>
|
||||
val L = l
|
||||
val labelIndex = codeArray.indexWhere {
|
||||
case AssemblyLine(LABEL, _, MemoryAddressConstant(Label(L)), _) => true
|
||||
case _ => false
|
||||
}
|
||||
currentImportance = if (labelIndex < 0) finalImportance else importanceArray(labelIndex)
|
||||
case AssemblyLine(JMP, Indirect, _, _) =>
|
||||
case AssemblyLine(JMP, Indirect | AbsoluteIndexedX, _, _) =>
|
||||
currentImportance = finalImportance
|
||||
case AssemblyLine(BNE | BEQ, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(z = Important)
|
||||
|
@ -136,7 +145,7 @@ object ReverseFlowAnalyzer {
|
|||
currentImportance = currentImportance.copy(n = Important)
|
||||
case AssemblyLine(SED | CLD, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(d = Unimportant)
|
||||
case AssemblyLine(RTS, _, _, _) =>
|
||||
case AssemblyLine(RTS | RTI, _, _, _) =>
|
||||
currentImportance = finalImportance
|
||||
case AssemblyLine(DISCARD_XF, _, _, _) =>
|
||||
currentImportance = currentImportance.copy(x = Unimportant, n = Unimportant, z = Unimportant, c = Unimportant, v = Unimportant)
|
||||
|
@ -158,8 +167,10 @@ object ReverseFlowAnalyzer {
|
|||
if (OpcodeClasses.ReadsYAlways(opcode)) currentImportance = currentImportance.copy(y = Important)
|
||||
if (OpcodeClasses.ReadsAAlways(opcode)) currentImportance = currentImportance.copy(a = Important)
|
||||
if (OpcodeClasses.ReadsAIfImplied(opcode) && addrMode == Implied) currentImportance = currentImportance.copy(a = Important)
|
||||
if (addrMode == AbsoluteX || addrMode == IndexedX || addrMode == ZeroPageX) currentImportance = currentImportance.copy(x = Important)
|
||||
if (addrMode == AbsoluteY || addrMode == IndexedY || addrMode == ZeroPageY) currentImportance = currentImportance.copy(y = Important)
|
||||
if (addrMode == AbsoluteX || addrMode == IndexedX || addrMode == ZeroPageX || addrMode == AbsoluteIndexedX)
|
||||
currentImportance = currentImportance.copy(x = Important)
|
||||
if (addrMode == AbsoluteY || addrMode == IndexedY || addrMode == ZeroPageY)
|
||||
currentImportance = currentImportance.copy(y = Important)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,6 +72,8 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
|
|||
class AssemblyMatchingContext {
|
||||
private val map = mutable.Map[Int, Any]()
|
||||
|
||||
override def toString: String = map.mkString(", ")
|
||||
|
||||
def addObject(i: Int, o: Any): Boolean = {
|
||||
if (map.contains(i)) {
|
||||
map(i) == o
|
||||
|
@ -81,6 +83,16 @@ class AssemblyMatchingContext {
|
|||
}
|
||||
}
|
||||
|
||||
def addAddrModeLoosely(i: Int, o: AddrMode.Value): Boolean = {
|
||||
if (map.contains(i)) {
|
||||
val a = map(i).asInstanceOf[AddrMode.Value]
|
||||
a == o || a == AddrMode.ZeroPage && o == AddrMode.Absolute || a == AddrMode.Absolute && o == AddrMode.ZeroPage
|
||||
} else {
|
||||
map(i) = o
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
def dontMatch(i: Int, o: Any): Boolean = {
|
||||
if (map.contains(i)) {
|
||||
map(i) != o
|
||||
|
@ -134,44 +146,6 @@ class AssemblyMatchingContext {
|
|||
jumps.isEmpty
|
||||
}
|
||||
|
||||
def areMemoryReferencesProvablyNonOverlapping(param1: Int, addrMode1: Int, param2: Int, addrMode2: Int): Boolean = {
|
||||
val p1 = get[Constant](param1).quickSimplify
|
||||
val a1 = get[AddrMode.Value](addrMode1)
|
||||
val p2 = get[Constant](param2).quickSimplify
|
||||
val a2 = get[AddrMode.Value](addrMode2)
|
||||
import AddrMode._
|
||||
val badAddrModes = Set(IndexedX, IndexedY, ZeroPageIndirect, AbsoluteIndexedX)
|
||||
if (badAddrModes(a1) || badAddrModes(a2)) return false
|
||||
|
||||
def handleKnownDistance(distance: Short): Boolean = {
|
||||
val indexingAddrModes = Set(AbsoluteIndexedX, AbsoluteX, ZeroPageX, AbsoluteY, ZeroPageY)
|
||||
val a1Indexing = indexingAddrModes(a1)
|
||||
val a2Indexing = indexingAddrModes(a2)
|
||||
(a1Indexing, a2Indexing) match {
|
||||
case (false, false) => distance != 0
|
||||
case (true, false) => distance > 255 || distance < 0
|
||||
case (false, true) => distance > 0 || distance < -255
|
||||
case (true, true) => distance > 255 || distance < -255
|
||||
}
|
||||
}
|
||||
|
||||
(p1, p2) match {
|
||||
case (NumericConstant(n1, _), NumericConstant(n2, _)) =>
|
||||
handleKnownDistance((n2 - n1).toShort)
|
||||
case (a, CompoundConstant(MathOperator.Plus, b, NumericConstant(distance, _))) if a.quickSimplify == b.quickSimplify =>
|
||||
handleKnownDistance(distance.toShort)
|
||||
case (CompoundConstant(MathOperator.Plus, a, NumericConstant(distance, _)), b) if a.quickSimplify == b.quickSimplify =>
|
||||
handleKnownDistance((-distance).toShort)
|
||||
case (a, CompoundConstant(MathOperator.Minus, b, NumericConstant(distance, _))) if a.quickSimplify == b.quickSimplify =>
|
||||
handleKnownDistance((-distance).toShort)
|
||||
case (CompoundConstant(MathOperator.Minus, a, NumericConstant(distance, _)), b) if a.quickSimplify == b.quickSimplify =>
|
||||
handleKnownDistance(distance.toShort)
|
||||
case (MemoryAddressConstant(MemoryVariable(a, _, _)), MemoryAddressConstant(MemoryVariable(b, _, _))) =>
|
||||
a.takeWhile(_ != '.') != a.takeWhile(_ != '.') // TODO: ???
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class AssemblyRule(pattern: AssemblyPattern, result: (List[AssemblyLine], AssemblyMatchingContext) => List[AssemblyLine]) {
|
||||
|
@ -236,15 +210,37 @@ trait AssemblyPattern {
|
|||
true // TODO: ???
|
||||
case (MemoryAddressConstant(a: ThingInMemory), MemoryAddressConstant(b: ThingInMemory)) =>
|
||||
a.name.takeWhile(_ != '.') != b.name.takeWhile(_ != '.') // TODO: ???
|
||||
case (CompoundConstant(MathOperator.Plus | MathOperator.Minus, MemoryAddressConstant(a: ThingInMemory), NumericConstant(_, _)),
|
||||
case (CompoundConstant(op@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(a: ThingInMemory), NumericConstant(offset, _)),
|
||||
MemoryAddressConstant(b: ThingInMemory)) =>
|
||||
a.name.takeWhile(_ != '.') != b.name.takeWhile(_ != '.') // TODO: ???
|
||||
if (a.name == b.name) {
|
||||
if (op == MathOperator.Plus) {
|
||||
handleKnownDistance((-offset).toShort)
|
||||
} else {
|
||||
handleKnownDistance(offset.toShort)
|
||||
}
|
||||
} else {
|
||||
a.name.takeWhile(_ != '.') != b.name.takeWhile(_ != '.') // TODO: ???
|
||||
}
|
||||
case (MemoryAddressConstant(a: ThingInMemory),
|
||||
CompoundConstant(MathOperator.Plus | MathOperator.Minus, MemoryAddressConstant(b: ThingInMemory), NumericConstant(_, _))) =>
|
||||
a.name.takeWhile(_ != '.') != b.name.takeWhile(_ != '.') // TODO: ???
|
||||
case (CompoundConstant(MathOperator.Plus | MathOperator.Minus, MemoryAddressConstant(a: ThingInMemory), NumericConstant(_, _)),
|
||||
CompoundConstant(MathOperator.Plus | MathOperator.Minus, MemoryAddressConstant(b: ThingInMemory), NumericConstant(_, _))) =>
|
||||
a.name.takeWhile(_ != '.') != b.name.takeWhile(_ != '.') // TODO: ???
|
||||
CompoundConstant(op@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(b: ThingInMemory), NumericConstant(offset, _))) =>
|
||||
if (a.name == b.name) {
|
||||
if (op == MathOperator.Minus) {
|
||||
handleKnownDistance((-offset).toShort)
|
||||
} else {
|
||||
handleKnownDistance(offset.toShort)
|
||||
}
|
||||
} else {
|
||||
a.name.takeWhile(_ != '.') != b.name.takeWhile(_ != '.') // TODO: ???
|
||||
}
|
||||
case (CompoundConstant(op1@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(a: ThingInMemory), NumericConstant(o1, _)),
|
||||
CompoundConstant(op2@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(b: ThingInMemory), NumericConstant(o2, _))) =>
|
||||
if (a.name == b.name) {
|
||||
val offset1 = if (op1==MathOperator.Plus) o1 else -o1
|
||||
val offset2 = if (op2==MathOperator.Plus) o2 else -o2
|
||||
handleKnownDistance((offset2 - offset1).toShort)
|
||||
} else {
|
||||
a.name.takeWhile(_ != '.') != b.name.takeWhile(_ != '.') // TODO: ???
|
||||
}
|
||||
case _ =>
|
||||
false
|
||||
}
|
||||
|
@ -550,6 +546,14 @@ case object Elidable extends AssemblyLinePattern {
|
|||
line.elidable
|
||||
}
|
||||
|
||||
case object DebugMatching extends AssemblyPattern {
|
||||
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = {
|
||||
println(ctx)
|
||||
code.foreach(println)
|
||||
Some(code)
|
||||
}
|
||||
}
|
||||
|
||||
case object Linear extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
|
||||
OpcodeClasses.AllLinear(line.opcode)
|
||||
|
@ -641,7 +645,7 @@ case class DoesntChangeMemoryAt(addrMode1: Int, param1: Int) extends AssemblyLin
|
|||
|
||||
case object ConcernsMemory extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean =
|
||||
ReadsMemory(line) && ChangesMemory(line)
|
||||
ReadsMemory(line) || ChangesMemory(line)
|
||||
}
|
||||
|
||||
case class DoesNotConcernMemoryAt(addrMode1: Int, param1: Int) extends AssemblyLinePattern {
|
||||
|
@ -715,7 +719,7 @@ case class DontMatchParameter(i: Int) extends AssemblyLinePattern {
|
|||
|
||||
case class MatchAddrMode(i: Int) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
|
||||
ctx.addObject(i, line.addrMode)
|
||||
ctx.addAddrModeLoosely(i, line.addrMode)
|
||||
|
||||
override def toString: String = s"¬(?<$i>AddrMode)"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package millfork.test
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuUnoptimizedRun}
|
||||
import millfork.test.emu.{EmuBenchmarkRun, EmuUltraBenchmarkRun, EmuUnoptimizedRun}
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
|
@ -42,7 +42,7 @@ class ShiftSuite extends FunSuite with Matchers {
|
|||
}
|
||||
|
||||
test("Long shifting left") {
|
||||
EmuBenchmarkRun("""
|
||||
EmuUltraBenchmarkRun("""
|
||||
| long output @$c000
|
||||
| void main () {
|
||||
| output = $1010301
|
||||
|
|
Loading…
Reference in New Issue
Block a user