1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-30 21:29:36 +00:00

Optimization bugfixes

This commit is contained in:
Karol Stasiak 2018-01-29 12:08:21 +01:00
parent 17920cee26
commit 7bbf655e6f
7 changed files with 161 additions and 87 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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