1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-11 15:29:34 +00:00
millfork/src/main/scala/millfork/assembly/opt/RuleBasedAssemblyOptimization.scala

757 lines
30 KiB
Scala
Raw Normal View History

2017-12-06 23:23:30 +00:00
package millfork.assembly.opt
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly._
import millfork.env._
import millfork.error.ErrorReporting
import scala.collection.mutable
/**
* @author Karol Stasiak
*/
object FlowInfoRequirement extends Enumeration {
val NoRequirement, BothFlows, ForwardFlow, BackwardFlow = Value
def assertForward(x: FlowInfoRequirement.Value): Unit = x match {
case BothFlows | ForwardFlow => ()
case NoRequirement | BackwardFlow => ErrorReporting.fatal("Forward flow info required")
}
def assertBackward(x: FlowInfoRequirement.Value): Unit = x match {
case BothFlows | BackwardFlow => ()
case NoRequirement | ForwardFlow => ErrorReporting.fatal("Backward flow info required")
}
}
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRule*) extends AssemblyOptimization {
rules.foreach(_.pattern.validate(needsFlowInfo))
override def optimize(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions): List[AssemblyLine] = {
val effectiveCode = code.map(a => a.copy(parameter = a.parameter.quickSimplify))
val taggedCode = needsFlowInfo match {
case FlowInfoRequirement.NoRequirement => effectiveCode.map(FlowInfo.Default -> _)
case FlowInfoRequirement.BothFlows => FlowAnalyzer.analyze(f, effectiveCode, options)
case FlowInfoRequirement.ForwardFlow =>
if (options.flag(CompilationFlag.DetailedFlowAnalysis)) {
QuantumFlowAnalyzer.analyze(f, code).map(s => FlowInfo(s.collapse, CpuImportance())).zip(code)
} else {
CoarseFlowAnalyzer.analyze(f, code).map(s => FlowInfo(s, CpuImportance())).zip(code)
}
case FlowInfoRequirement.BackwardFlow =>
ReverseFlowAnalyzer.analyze(f, code).map(i => FlowInfo(CpuStatus(), i)).zip(code)
}
optimizeImpl(f, taggedCode, options)
}
def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, AssemblyLine)], options: CompilationOptions): List[AssemblyLine] = {
code match {
case Nil => Nil
case head :: tail =>
for ((rule, index) <- rules.zipWithIndex) {
val ctx = new AssemblyMatchingContext
rule.pattern.matchTo(ctx, code) match {
case Some(rest: List[(FlowInfo, AssemblyLine)]) =>
val matchedChunkToOptimize: List[AssemblyLine] = code.take(code.length - rest.length).map(_._2)
val optimizedChunk: List[AssemblyLine] = rule.result(matchedChunkToOptimize, ctx)
ErrorReporting.debug(s"Applied $name ($index)")
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
val before = code.head._1.statusBefore
val after = code(matchedChunkToOptimize.length - 1)._1.importanceAfter
ErrorReporting.trace(s"Before: $before")
ErrorReporting.trace(s"After: $after")
}
matchedChunkToOptimize.filter(_.isPrintable).foreach(l => ErrorReporting.trace(l.toString))
ErrorReporting.trace(" ↓")
optimizedChunk.filter(_.isPrintable).foreach(l => ErrorReporting.trace(l.toString))
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
return optimizedChunk ++ optimizeImpl(f, rest, options)
} else {
return optimize(f, optimizedChunk ++ rest.map(_._2), options)
}
case None => ()
}
}
head._2 :: optimizeImpl(f, tail, options)
}
}
}
class AssemblyMatchingContext {
private val map = mutable.Map[Int, Any]()
def addObject(i: Int, o: Any): Boolean = {
if (map.contains(i)) {
map(i) == o
} else {
map(i) = o
true
}
}
def dontMatch(i: Int, o: Any): Boolean = {
if (map.contains(i)) {
map(i) != o
} else {
false
}
}
def get[T: Manifest](i: Int): T = {
val t = map(i)
val clazz = implicitly[Manifest[T]].runtimeClass match {
case java.lang.Integer.TYPE => classOf[java.lang.Integer]
case java.lang.Boolean.TYPE => classOf[java.lang.Boolean]
// TODO
case x => x
}
if (clazz.isInstance(t)) {
t.asInstanceOf[T]
} else {
if (i eq null) {
ErrorReporting.fatal(s"Value at index $i is null")
} else {
ErrorReporting.fatal(s"Value at index $i is a ${t.getClass.getSimpleName}, not a ${clazz.getSimpleName}")
}
}
}
def isExternallyLinearBlock(i: Int): Boolean = {
val labels = mutable.Set[String]()
val jumps = mutable.Set[String]()
get[List[AssemblyLine]](i).foreach {
case AssemblyLine(Opcode.RTS | Opcode.RTI | Opcode.BRK, _, _, _) =>
return false
case AssemblyLine(Opcode.JMP, AddrMode.Indirect, _, _) =>
return false
case AssemblyLine(Opcode.LABEL, _, MemoryAddressConstant(Label(l)), _) =>
labels += l
case AssemblyLine(Opcode.JMP, AddrMode.Absolute, MemoryAddressConstant(Label(l)), _) =>
jumps += l
case AssemblyLine(Opcode.JMP, AddrMode.Absolute, _, _) =>
return false
case AssemblyLine(_, AddrMode.Relative, MemoryAddressConstant(Label(l)), _) =>
jumps += l
case AssemblyLine(br, _, _, _) if OpcodeClasses.ShortBranching(br) =>
return false
case _ => ()
}
// if a jump leads inside the block, then it's internal
// if a jump leads outside the block, then it's external
jumps --= labels
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]) {
}
trait AssemblyPattern {
def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = ()
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]]
def ~(x: AssemblyPattern) = Concatenation(this, x)
def ~(x: AssemblyLinePattern) = Concatenation(this, x)
def ~~>(result: (List[AssemblyLine], AssemblyMatchingContext) => List[AssemblyLine]) = AssemblyRule(this, result)
def ~~>(result: List[AssemblyLine] => List[AssemblyLine]) = AssemblyRule(this, (code, _) => result(code))
def capture(i: Int) = Capture(i, this)
def captureLength(i: Int) = CaptureLength(i, this)
}
case class Capture(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] =
for {
rest <- pattern.matchTo(ctx, code)
} yield {
ctx.addObject(i, code.take(code.length - rest.length).map(_._2))
rest
}
override def toString: String = s"(?<$i>$pattern)"
}
case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] =
for {
rest <- pattern.matchTo(ctx, code)
} yield {
ctx.addObject(i, code.length - rest.length)
rest
}
override def toString: String = s"(?<$i>$pattern)"
}
case class Where(predicate: (AssemblyMatchingContext => Boolean)) extends AssemblyPattern {
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = {
if (predicate(ctx)) Some(code) else None
}
override def toString: String = "Where(...)"
}
case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends AssemblyPattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
l.validate(needsFlowInfo)
r.validate(needsFlowInfo)
}
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = {
for {
middle <- l.matchTo(ctx, code)
end <- r.matchTo(ctx, middle)
} yield end
}
override def toString: String = (l, r) match {
case (_: Both, _: Both) => s"($l) · ($r)"
case (_, _: Both) => s"$l · ($r)"
case (_: Both, _) => s"($l) · $r"
case _ => s"$l · $r"
}
}
case class Many(rule: AssemblyLinePattern) extends AssemblyPattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
rule.validate(needsFlowInfo)
}
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = {
var c = code
while (true) {
c match {
case Nil =>
return Some(Nil)
case x :: xs =>
if (rule.matchLineTo(ctx, x._1, x._2)) {
c = xs
} else {
return Some(c)
}
}
}
None
}
override def toString: String = s"[$rule]*"
}
case class ManyWhereAtLeastOne(rule: AssemblyLinePattern, atLeastOneIsThis: AssemblyLinePattern) extends AssemblyPattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
rule.validate(needsFlowInfo)
}
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = {
var c = code
var oneFound = false
while (true) {
c match {
case Nil =>
return Some(Nil)
case x :: xs =>
if (atLeastOneIsThis.matchLineTo(ctx, x._1, x._2)) {
oneFound = true
}
if (rule.matchLineTo(ctx, x._1, x._2)) {
c = xs
} else {
if (oneFound) {
return Some(c)
} else {
return None
}
}
}
}
None
}
override def toString: String = s"[∃$atLeastOneIsThis:$rule]*"
}
case class Opt(rule: AssemblyLinePattern) extends AssemblyPattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
rule.validate(needsFlowInfo)
}
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = {
code match {
case Nil =>
Some(Nil)
case x :: xs =>
if (rule.matchLineTo(ctx, x._1, x._2)) {
Some(xs)
} else {
Some(code)
}
}
}
override def toString: String = s"[$rule]?"
}
trait AssemblyLinePattern extends AssemblyPattern {
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = code match {
case Nil => None
case x :: xs => if (matchLineTo(ctx, x._1, x._2)) Some(xs) else None
}
def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean
def unary_! : AssemblyLinePattern = Not(this)
def ? : AssemblyPattern = Opt(this)
def * : AssemblyPattern = Many(this)
def + : AssemblyPattern = this ~ Many(this)
def |(x: AssemblyLinePattern): AssemblyLinePattern = Either(this, x)
def &(x: AssemblyLinePattern): AssemblyLinePattern = Both(this, x)
protected def memoryAccessDoesntOverlap(a1: AddrMode.Value, p1: Constant, a2: AddrMode.Value, p2: Constant): Boolean = {
import AddrMode._
val badAddrModes = Set(IndexedX, IndexedY, ZeroPageIndirect, AbsoluteIndexedX)
if (badAddrModes(a1) || badAddrModes(a2)) return false
val goodAddrModes = Set(Implied, Immediate, Relative)
if (goodAddrModes(a1) || goodAddrModes(a2)) return true
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.quickSimplify, p2.quickSimplify) 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(a: ThingInMemory), MemoryAddressConstant(b:ThingInMemory)) =>
a.name.takeWhile(_ != '.') != b.name.takeWhile(_ != '.') // TODO: ???
case (CompoundConstant(MathOperator.Plus | MathOperator.Minus, MemoryAddressConstant(a: ThingInMemory), NumericConstant(_, _)),
MemoryAddressConstant(b: ThingInMemory)) =>
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: ???
case _ =>
false
}
}
}
//noinspection LanguageFeature
object AssemblyLinePattern {
implicit def __implicitOpcodeIn(ops: Set[Opcode.Value]): AssemblyLinePattern = HasOpcodeIn(ops)
}
case class MatchA(i: Int) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
flowInfo.statusBefore.a match {
case SingleStatus(value) => ctx.addObject(i, value)
case _ => false
}
}
case class MatchX(i: Int) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
flowInfo.statusBefore.x match {
case SingleStatus(value) => ctx.addObject(i, value)
case _ => false
}
}
case class MatchY(i: Int) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
flowInfo.statusBefore.y match {
case SingleStatus(value) => ctx.addObject(i, value)
case _ => false
}
}
case class HasA(value: Int) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
flowInfo.statusBefore.a.contains(value)
}
case class HasX(value: Int) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
flowInfo.statusBefore.x.contains(value)
}
case class HasY(value: Int) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
flowInfo.statusBefore.y.contains(value)
}
case class DoesntMatterWhatItDoesWith(states: State.Value*) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertBackward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
states.forall(state => flowInfo.importanceAfter.isUnimportant(state))
override def toString: String = states.mkString("[¯\\_(ツ)_/¯:", ",", "]")
}
case class HasSet(state: State.Value) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
flowInfo.hasSet(state)
}
case class HasClear(state: State.Value) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
flowInfo.hasClear(state)
}
case object Anything extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
true
}
case class Not(inner: AssemblyLinePattern) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = inner.validate(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
!inner.matchLineTo(ctx, flowInfo, line)
override def toString: String = "¬" + inner
}
case class Both(l: AssemblyLinePattern, r: AssemblyLinePattern) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
l.validate(needsFlowInfo)
r.validate(needsFlowInfo)
}
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
l.matchLineTo(ctx, flowInfo, line) && r.matchLineTo(ctx, flowInfo, line)
override def toString: String = l + " ∧ " + r
}
case class Either(l: AssemblyLinePattern, r: AssemblyLinePattern) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
l.validate(needsFlowInfo)
r.validate(needsFlowInfo)
}
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
l.matchLineTo(ctx, flowInfo, line) || r.matchLineTo(ctx, flowInfo, line)
override def toString: String = s"($l $r)"
}
case object Elidable extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
line.elidable
}
case object Linear extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
OpcodeClasses.AllLinear(line.opcode)
}
case object LinearOrBranch extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
OpcodeClasses.AllLinear(line.opcode) || OpcodeClasses.ShortBranching(line.opcode)
}
case object LinearOrLabel extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
line.opcode == Opcode.LABEL || OpcodeClasses.AllLinear(line.opcode)
}
case object ReadsA extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
OpcodeClasses.ReadsAAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ReadsAIfImplied(line.opcode)
}
case object ReadsMemory extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
line.addrMode match {
case AddrMode.Indirect => true
case AddrMode.Implied | AddrMode.Immediate => false
case _ =>
OpcodeClasses.ReadsMemoryIfNotImpliedOrImmediate(line.opcode)
}
}
case object ReadsX extends AssemblyLinePattern {
val XAddrModes = Set(AddrMode.AbsoluteX, AddrMode.IndexedX, AddrMode.ZeroPageX, AddrMode.AbsoluteIndexedX)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
OpcodeClasses.ReadsXAlways(line.opcode) || XAddrModes(line.addrMode)
}
case object ReadsY extends AssemblyLinePattern {
val YAddrModes = Set(AddrMode.AbsoluteY, AddrMode.IndexedY, AddrMode.ZeroPageY)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
OpcodeClasses.ReadsYAlways(line.opcode) || YAddrModes(line.addrMode)
}
case object ConcernsC extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
OpcodeClasses.ReadsC(line.opcode) && OpcodeClasses.ChangesC(line.opcode)
}
case object ConcernsA extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
OpcodeClasses.ConcernsAAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ConcernsAIfImplied(line.opcode)
}
case object ConcernsX extends AssemblyLinePattern {
val XAddrModes = Set(AddrMode.AbsoluteX, AddrMode.IndexedX, AddrMode.ZeroPageX)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
OpcodeClasses.ConcernsXAlways(line.opcode) || XAddrModes(line.addrMode)
}
case object ConcernsY extends AssemblyLinePattern {
val YAddrModes = Set(AddrMode.AbsoluteY, AddrMode.IndexedY, AddrMode.ZeroPageY)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
OpcodeClasses.ConcernsYAlways(line.opcode) || YAddrModes(line.addrMode)
}
case object ChangesA extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
OpcodeClasses.ChangesAAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ChangesAIfImplied(line.opcode)
}
case object ChangesMemory extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
OpcodeClasses.ChangesMemoryAlways(line.opcode) || line.addrMode != AddrMode.Implied && OpcodeClasses.ChangesMemoryIfNotImplied(line.opcode)
}
case class DoesntChangeMemoryAt(addrMode1: Int, param1: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
val p1 = ctx.get[Constant](param1)
val p2 = line.parameter
val a1 = ctx.get[AddrMode.Value](addrMode1)
val a2 = line.addrMode
val changesSomeMemory = OpcodeClasses.ChangesMemoryAlways(line.opcode) || line.addrMode != AddrMode.Implied && OpcodeClasses.ChangesMemoryIfNotImplied(line.opcode)
!changesSomeMemory || memoryAccessDoesntOverlap(a1, p1, a2, p2)
}
}
case object ConcernsMemory extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
ReadsMemory.matchLineTo(ctx, flowInfo, line) && ChangesMemory.matchLineTo(ctx, flowInfo, line)
}
case class DoesNotConcernMemoryAt(addrMode1: Int, param1: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
val p1 = ctx.get[Constant](param1)
val p2 = line.parameter
val a1 = ctx.get[AddrMode.Value](addrMode1)
val a2 = line.addrMode
memoryAccessDoesntOverlap(a1, p1, a2, p2)
}
}
case class HasOpcode(op: Opcode.Value) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
line.opcode == op
override def toString: String = op.toString
}
case class HasOpcodeIn(ops: Set[Opcode.Value]) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
ops(line.opcode)
override def toString: String = ops.mkString("{", ",", "}")
}
case class HasAddrMode(am: AddrMode.Value) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
line.addrMode == am
override def toString: String = am.toString
}
case class HasAddrModeIn(ams: Set[AddrMode.Value]) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
ams(line.addrMode)
override def toString: String = ams.mkString("{", ",", "}")
}
case class HasImmediate(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
line.addrMode == AddrMode.Immediate && (line.parameter.quickSimplify match {
case NumericConstant(j, _) => (i & 0xff) == (j & 0xff)
case _ => false
})
override def toString: String = "#" + i
}
case class MatchObject(i: Int, f: Function[AssemblyLine, Any]) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
ctx.addObject(i, f(line))
override def toString: String = s"(?<$i>...)"
}
case class MatchParameter(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
ctx.addObject(i, line.parameter.quickSimplify)
override def toString: String = s"(?<$i>Param)"
}
case class DontMatchParameter(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
ctx.dontMatch(i, line.parameter.quickSimplify)
override def toString: String = s"¬(?<$i>Param)"
}
case class MatchAddrMode(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
ctx.addObject(i, line.addrMode)
override def toString: String = s"¬(?<$i>AddrMode)"
}
case class MatchOpcode(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
ctx.addObject(i, line.opcode)
override def toString: String = s"¬(?<$i>Op)"
}
case class MatchImmediate(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
if (line.addrMode == AddrMode.Immediate) {
ctx.addObject(i, line.parameter.quickSimplify)
} else false
override def toString: String = s"(?<$i>#)"
}
case class DoesntChangeIndexingInAddrMode(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
ctx.get[AddrMode.Value](i) match {
case AddrMode.ZeroPageX | AddrMode.AbsoluteX | AddrMode.IndexedX | AddrMode.AbsoluteIndexedX => !OpcodeClasses.ChangesX.contains(line.opcode)
case AddrMode.ZeroPageY | AddrMode.AbsoluteY | AddrMode.IndexedY => !OpcodeClasses.ChangesY.contains(line.opcode)
case _ => true
}
override def toString: String = s"¬(?<$i>AddrMode)"
}
case class Before(pattern: AssemblyPattern) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
pattern.validate(needsFlowInfo)
}
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = code match {
case Nil => None
case x :: xs => pattern.matchTo(ctx, xs) match {
case Some(m) => Some(xs)
case None => None
}
}
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = ???
}