1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-05-31 18:41:30 +00:00
millfork/src/main/scala/millfork/assembly/m6809/opt/RuleBasedAssemblyOptimization.scala
2023-01-27 18:13:21 +01:00

1348 lines
47 KiB
Scala
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package millfork.assembly.m6809.opt
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly._
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, Indexed, Inherent, InherentA, InherentB, LongRelative, MAddrMode, MLine, MLine0, MOpcode, MState, NonExistent, PostIncremented, PreDecremented, Relative, TwoRegisters}
import millfork.assembly.opt.SingleStatus
import millfork.compiler.LabelGenerator
import millfork.env._
import millfork.error.{FatalErrorReporting, Logger}
import millfork.node.{M6809NiceFunctionProperty, M6809Register, NiceFunctionProperty}
import scala.collection.mutable
/**
* @author Karol Stasiak
*/
object FlowInfoRequirement extends Enumeration {
val NoRequirement, JustLabels, BothFlows, ForwardFlow, BackwardFlow = Value
def assertForward(x: FlowInfoRequirement.Value): Unit = x match {
case BothFlows | ForwardFlow => ()
case NoRequirement | JustLabels | BackwardFlow => FatalErrorReporting.reportFlyingPig("Forward flow info required")
}
def assertBackward(x: FlowInfoRequirement.Value): Unit = x match {
case BothFlows | BackwardFlow => ()
case NoRequirement | JustLabels | ForwardFlow => FatalErrorReporting.reportFlyingPig("Backward flow info required")
}
def assertLabels(x: FlowInfoRequirement.Value): Unit = x match {
case NoRequirement => FatalErrorReporting.reportFlyingPig("Label info required")
case _ => ()
}
}
trait AssemblyRuleSet{
def flatten: Seq[AssemblyRule]
def minimumRequiredLines: Int
}
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRuleSet*) extends AssemblyOptimization[MLine] {
private val actualRules = rules.flatMap(_.flatten)
actualRules.foreach(_.pattern.validate(needsFlowInfo))
private val actualRulesWithIndex = actualRules.zipWithIndex
override val minimumRequiredLines: Int = rules.map(_.minimumRequiredLines).min
override def toString: String = name
override def optimize(f: NormalFunction, code: List[MLine], optimizationContext: OptimizationContext): List[MLine] = {
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo)
optimizeImpl(f, code, taggedCode, optimizationContext)
}
final def optimizeImpl(f: NormalFunction, code: List[MLine], taggedCode: List[(FlowInfo, MLine)], optimizationContext: OptimizationContext): List[MLine] = {
val log = optimizationContext.log
taggedCode match {
case Nil => code
case head :: tail =>
val codeLength = code.length
for {
(rule, index) <- actualRulesWithIndex
if codeLength >= rule.minimumRequiredLines
} {
val ctx = new AssemblyMatchingContext(
optimizationContext.options,
optimizationContext.labelMap,
optimizationContext.niceFunctionProperties,
head._1.labelUseCount(_)
)
rule.pattern.matchTo(ctx, taggedCode) match {
case Some(rest: List[(FlowInfo, MLine)]) =>
val optimizedChunkLengthBefore = taggedCode.length - rest.length
val (matchedChunkToOptimize, restOfCode) = code.splitAt(optimizedChunkLengthBefore)
val optimizedChunk: List[MLine] = rule.result(matchedChunkToOptimize, ctx)
val optimizedChunkWithSource =
if (!ctx.compilationOptions.flag(CompilationFlag.LineNumbersInAssembly)) optimizedChunk
else if (optimizedChunk.isEmpty) optimizedChunk
else if (matchedChunkToOptimize.size == 1) optimizedChunk.map(_.pos(matchedChunkToOptimize.head.source))
else if (optimizedChunk.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
else if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
else optimizedChunk
if (log.debugEnabled) {
log.debug(s"Applied $name ($index)")
}
if (log.traceEnabled) {
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
val before = head._1.statusBefore
val after = taggedCode(matchedChunkToOptimize.length - 1)._1.importanceAfter
log.trace(s"Before: $before")
log.trace(s"After: $after")
}
matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString))
log.trace(" ↓")
optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString))
}
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
return optimizedChunkWithSource ++ optimizeImpl(f, restOfCode, rest, optimizationContext)
} else {
return optimize(f, optimizedChunkWithSource ++ restOfCode, optimizationContext)
}
case None => ()
}
}
val optimizedTail = optimizeImpl(f, code.tail, tail, optimizationContext)
if (optimizedTail eq code.tail) {
code
} else {
code.head :: optimizedTail
}
}
}
}
class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
val labelMap: Map[String, (String, Int)],
val niceFunctionProperties: Set[(NiceFunctionProperty, String)],
val labelUseCount: String => Int) {
@inline
def log: Logger = compilationOptions.log
@inline
def nextLabel: LabelGenerator = compilationOptions.nextLabel
def functionChangesA(name: String): Boolean = !niceFunctionProperties(M6809NiceFunctionProperty.DoesntChangeA -> name)
def functionChangesX(name: String): Boolean = !niceFunctionProperties(M6809NiceFunctionProperty.DoesntChangeX -> name)
def functionChangesY(name: String): Boolean = !niceFunctionProperties(M6809NiceFunctionProperty.DoesntChangeY -> name)
def functionChangesU(name: String): Boolean = !niceFunctionProperties(M6809NiceFunctionProperty.DoesntChangeU -> name)
def functionChangesB(name: String): Boolean = !niceFunctionProperties(M6809NiceFunctionProperty.DoesntChangeB -> name)
def functionChangesCF(name: String): Boolean = !niceFunctionProperties(M6809NiceFunctionProperty.DoesntChangeCF -> name)
def functionChangesMemory(name: String): Boolean = !niceFunctionProperties(NiceFunctionProperty.DoesntWriteMemory -> name)
def functionReadsMemory(name: String): Boolean = !niceFunctionProperties(NiceFunctionProperty.DoesntReadMemory -> name)
private val map = new mutable.HashMap[Int, Any]()
override def toString: String = if (map.isEmpty) "<empty context>" else map.mkString(", ")
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
}
}
private def getImpl[T: Manifest](i: Int): AnyRef = {
if (!map.contains(i)) return null
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[AnyRef]
} else {
if (t.asInstanceOf[AnyRef] eq null) {
log.fatal(s"Value at index $i is null")
} else {
log.fatal(s"Value at index $i is a ${t.getClass.getSimpleName}, not a ${clazz.getSimpleName}")
}
}
}
def get[T: Manifest](i: Int): T = {
val v = getImpl[T](i)
if (v eq null) {
log.fatal(s"Value at index $i is null")
}
v.asInstanceOf[T]
}
def getOrDefault[T: Manifest](i: Int, defau: T): T = {
val v = getImpl[T](i)
if (v eq null) {
defau
} else {
v.asInstanceOf[T]
}
}
def isExternallyLinearBlock(i: Int): Boolean = {
val labels = mutable.Set[String]()
val jumps = mutable.Set[String]()
get[List[MLine]](i).foreach {
// JSR and BSR are allowed
case MLine0(MOpcode.RTS | MOpcode.RTI | MOpcode.SWI | MOpcode.SWI2 | MOpcode.SWI3, _, _) =>
return false
case MLine0(MOpcode.JMP, Absolute(false), MemoryAddressConstant(Label(l))) =>
jumps += l
case MLine0(MOpcode.JMP, a, _) =>
return false
case MLine0(MOpcode.LABEL, _, MemoryAddressConstant(Label(l))) =>
labels += l
case MLine0(b, _, MemoryAddressConstant(Label(l))) if MOpcode.Branching(b)=>
jumps += l
case MLine0(b, _, _) if MOpcode.Branching(b) =>
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 && labels.forall(l => labelUseCount(l) <= 1)
}
}
case class AssemblyRule(pattern: AssemblyPattern, result: (List[MLine], AssemblyMatchingContext) => List[MLine]) extends AssemblyRuleSet {
override def flatten: Seq[AssemblyRule] = List(this)
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
}
case class MultipleAssemblyRules(list: Seq[AssemblyRuleSet]) extends AssemblyRuleSet {
override def flatten: Seq[AssemblyRule] = list.flatMap(_.flatten)
override val minimumRequiredLines: Int = flatten.map(_.minimumRequiredLines).sum
}
trait AssemblyPattern {
def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = ()
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, MLine)]): Option[List[(FlowInfo, MLine)]]
def ~(x: AssemblyPattern) = Concatenation(this, x)
def ~(x: MLinePattern) = Concatenation(this, x)
def ~~>(result: (List[MLine], AssemblyMatchingContext) => List[MLine]) = AssemblyRule(this, result)
def ~~>(result: List[MLine] => List[MLine]) = AssemblyRule(this, (code, _) => result(code))
def capture(i: Int) = Capture(i, this)
def captureLength(i: Int) = CaptureLength(i, this)
def minimumRequiredLines: Int
}
object HelperCheckers {
private def badAddrModes(am: MAddrMode): Boolean = am match {
case Inherent | Immediate | InherentA | InherentB | Relative | LongRelative => false
case Absolute(b) => b
case _ => true
}
private val goodAddrModes: Set[MAddrMode] = Set(Inherent, Immediate, InherentA, InherentB, Relative, LongRelative)
def memoryAccessDoesntOverlap(l1: MLine, l2: MLine, assumeSameIndices: Boolean = false): Boolean = {
val a1 = l1.addrMode
val a2 = l2.addrMode
if (goodAddrModes(a1) || goodAddrModes(a2)) return true
if (badAddrModes(a1) || badAddrModes(a2)) return false
if (l1.opcode == MOpcode.CHANGED_MEM || l2.opcode == MOpcode.CHANGED_MEM) return false
// if ((a1 == IndexedSY) != (a2 == IndexedSY)) return true // bold assertion, but usually true
val p1 = l1.parameter
val p2 = l2.parameter
val w1 = MOpcode.AccessesWordInMemory(l1.opcode)
val w2 = MOpcode.AccessesWordInMemory(l2.opcode)
def distinctThings(a: String, b: String): Boolean = {
if (a == "__reg") return b != "__reg"
if (b == "__reg") return a != "__reg"
if (a == "__sp") return b != "__sp"
if (b == "__sp") return a != "__sp"
a.takeWhile(_ != '.') != b.takeWhile(_ != '.')
}
def handleKnownDistance(distance: Short): Boolean = {
if (w1 || w2) distance >= 2 || distance <= -2
else distance != 0
}
(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(_: ThingInMemory), NumericConstant(_, _)) =>
true // TODO: ???
case (NumericConstant(_, _), MemoryAddressConstant(_: ThingInMemory)) =>
true // TODO: ???
case (CompoundConstant(MathOperator.Plus | MathOperator.Minus, MemoryAddressConstant(a: ThingInMemory), NumericConstant(_, _)), NumericConstant(_, _)) =>
true // TODO: ???
case (NumericConstant(_, _), CompoundConstant(MathOperator.Plus | MathOperator.Minus, MemoryAddressConstant(a: ThingInMemory), NumericConstant(_, _))) =>
true // TODO: ???
case (MemoryAddressConstant(a: ThingInMemory), MemoryAddressConstant(b: ThingInMemory)) =>
distinctThings(a.name, b.name) // TODO: ???
case (CompoundConstant(op@(MathOperator.Plus | MathOperator.Minus), MemoryAddressConstant(a: ThingInMemory), NumericConstant(offset, _)),
MemoryAddressConstant(b: ThingInMemory)) =>
if (a.name == b.name) {
if (op == MathOperator.Plus) {
handleKnownDistance((-offset).toShort)
} else {
handleKnownDistance(offset.toShort)
}
} else {
distinctThings(a.name, b.name) // TODO: ???
}
case (MemoryAddressConstant(a: ThingInMemory),
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 {
distinctThings(a.name, b.name) // 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 {
distinctThings(a.name, b.name) // TODO: ???
}
case _ =>
false
}
}
}
case class Capture(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, MLine)]): Option[List[(FlowInfo, MLine)]] =
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)"
override def minimumRequiredLines: Int = 0
}
case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, MLine)]): Option[List[(FlowInfo, MLine)]] =
for {
rest <- pattern.matchTo(ctx, code)
} yield {
ctx.addObject(i, code.length - rest.length)
rest
}
override def toString: String = s"(?<$i>$pattern)"
override def minimumRequiredLines: Int = 0
}
case class Where(predicate: AssemblyMatchingContext => Boolean) extends AssemblyPattern {
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, MLine)]): Option[List[(FlowInfo, MLine)]] = {
if (predicate(ctx)) Some(code) else None
}
override def toString: String = "Where(...)"
override def minimumRequiredLines: Int = 0
}
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, MLine)]): Option[List[(FlowInfo, MLine)]] = {
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"
}
override val minimumRequiredLines: Int = l.minimumRequiredLines + r.minimumRequiredLines
}
case class Many(rule: MLinePattern) extends AssemblyPattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
rule.validate(needsFlowInfo)
}
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, MLine)]): Option[List[(FlowInfo, MLine)]] = {
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]*"
override def minimumRequiredLines: Int = 0
}
case class ManyWhereAtLeastOne(rule: MLinePattern, atLeastOneIsThis: MLinePattern) extends AssemblyPattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
rule.validate(needsFlowInfo)
}
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, MLine)]): Option[List[(FlowInfo, MLine)]] = {
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]*"
override def minimumRequiredLines: Int = 1
}
case class Opt(rule: MLinePattern) extends AssemblyPattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
rule.validate(needsFlowInfo)
}
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, MLine)]): Option[List[(FlowInfo, MLine)]] = {
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]?"
override def minimumRequiredLines: Int = 0
}
trait MLinePattern extends AssemblyPattern {
def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, MLine)]): Option[List[(FlowInfo, MLine)]] = 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: MLine): Boolean
def unary_! : MLinePattern = Not(this)
def ? : AssemblyPattern = Opt(this)
def * : AssemblyPattern = Many(this)
def + : AssemblyPattern = this ~ Many(this)
def |(x: MLinePattern): MLinePattern =
if (this.hitRate >= x.hitRate) EitherPattern(this, x)
else EitherPattern(x, this)
def &(x: MLinePattern): MLinePattern =
if (this.hitRate <= x.hitRate) Both(this, x)
else Both(x, this)
def hitRate: Double
override def minimumRequiredLines: Int = 1
}
//noinspection ScalaUnnecessaryParentheses
trait TrivialMLinePattern extends MLinePattern with (MLine => Boolean) {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = this (line)
}
case class Match(predicate: AssemblyMatchingContext => Boolean) extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = predicate(ctx)
override def toString: String = "Match(...)"
override def hitRate: Double = 0.5 // ?
}
case class WhereNoMemoryAccessOverlapBetweenTwoLineLists(ix1: Int, ix2: Int) extends AssemblyPattern {
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, MLine)]): Option[List[(FlowInfo, MLine)]] = {
val s1s = ctx.get[List[MLine]](ix1)
val s2s = ctx.get[List[MLine]](ix2)
if (s1s.forall(s1 => s2s.forall(s2 => HelperCheckers.memoryAccessDoesntOverlap(s1, s2)))) Some(code) else None
}
override def minimumRequiredLines: Int = 0
}
case class MatchA(i: Int) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
flowInfo.statusBefore.a match {
case SingleStatus(value) => ctx.addObject(i, value)
case _ => false
}
override def hitRate: Double = 0.42
}
case class MatchStoredRegister(i: Int) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = {
import MOpcode._
line.opcode match {
case STA =>
flowInfo.statusBefore.a match {
case SingleStatus(value) => ctx.addObject(i, value)
case _ => false
}
case STX =>
flowInfo.statusBefore.x match {
case SingleStatus(value) => ctx.addObject(i, value)
case _ => false
}
case STY =>
flowInfo.statusBefore.y match {
case SingleStatus(value) => ctx.addObject(i, value)
case _ => false
}
case _ => false
}
}
override def hitRate: Double = 0.42
}
case class MatchX(i: Int) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
flowInfo.statusBefore.x match {
case SingleStatus(value) => ctx.addObject(i, value)
case _ => false
}
override def hitRate: Double = 0.077
}
case class MatchY(i: Int) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
flowInfo.statusBefore.y match {
case SingleStatus(value) => ctx.addObject(i, value)
case _ => false
}
override def hitRate: Double = 0.074
}
case class HasA(value: Int) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
flowInfo.statusBefore.a.contains(value)
override def hitRate: Double = 0.08
}
case class HasB(value: Int) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
flowInfo.statusBefore.b.contains(value)
override def hitRate: Double = 0.08
}
case class HasD(value: Int) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
flowInfo.statusBefore.d.contains(value)
override def hitRate: Double = 0.08
}
case class HasX(value: Int) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
flowInfo.statusBefore.x.contains(value)
override def hitRate: Double = 0.018
}
case class HasY(value: Int) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
flowInfo.statusBefore.y.contains(value)
override def hitRate: Double = 0.011
}
case class DoesntMatterWhatItDoesWith(states: MState.Value*) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertBackward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
states.forall(state => flowInfo.importanceAfter.isUnimportant(state))
override def toString: String = states.mkString("[¯\\_(ツ)_/¯:", ",", "]")
override def hitRate: Double = 0.688
}
case class HasSet(state: MState.Value) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
flowInfo.hasSet(state)
override def hitRate: Double = 0.026
}
//case class HasSourceOfNZ(state: MState.Value) extends MLinePattern {
// override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
// FlowInfoRequirement.assertForward(needsFlowInfo)
//
// override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
// flowInfo.statusBefore.src.exists(s => s.matches(state))
//
// override def hitRate: Double = 0.2
//}
case class HasClear(state: MState.Value) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
flowInfo.hasClear(state)
override def hitRate: Double = 0.48
}
case object HasClearBitB0 extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
flowInfo.statusBefore.b0.contains(false)
override def hitRate: Double = 0.30
}
case object Anything extends TrivialMLinePattern {
override def apply(line: MLine): Boolean = true
override def hitRate: Double = 1
}
case class Not(inner: MLinePattern) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = inner.validate(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
!inner.matchLineTo(ctx, flowInfo, line)
override def toString: String = "¬" + inner
override def hitRate: Double = 1 - inner.hitRate
}
case class Both(l: MLinePattern, r: MLinePattern) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
l.validate(needsFlowInfo)
r.validate(needsFlowInfo)
}
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
l.matchLineTo(ctx, flowInfo, line) && r.matchLineTo(ctx, flowInfo, line)
override def toString: String = l + " ∧ " + r
override def &(x: MLinePattern): MLinePattern =
if (x.hitRate < l.hitRate) Both(x, this)
else Both(l, r & x)
override def hitRate: Double = l.hitRate min r.hitRate
}
case class EitherPattern(l: MLinePattern, r: MLinePattern) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
l.validate(needsFlowInfo)
r.validate(needsFlowInfo)
}
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
l.matchLineTo(ctx, flowInfo, line) || r.matchLineTo(ctx, flowInfo, line)
override def toString: String = s"($l $r)"
override def |(x: MLinePattern): MLinePattern =
if (x.hitRate > l.hitRate) EitherPattern(x, this)
else EitherPattern(l, r | x)
override def hitRate: Double = l.hitRate max r.hitRate
}
case object Elidable extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
line.elidable
override def hitRate: Double = 0.937
}
case object NotFixed extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
line.notFixed
override def hitRate: Double = 0.926
}
case object DebugMatching extends AssemblyPattern {
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, MLine)]): Option[List[(FlowInfo, MLine)]] = {
println(ctx)
code.foreach(println)
Some(code)
}
override def minimumRequiredLines: Int = 0
}
case object Linear extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
MOpcode.AllLinear(line.opcode)
override def hitRate: Double = 0.89
}
case object LinearOrBranch extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
MOpcode.AllLinear(line.opcode) || MOpcode.Branching(line.opcode)
override def hitRate: Double = 0.887
}
case object LinearOrLabel extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
line.opcode == MOpcode.LABEL || MOpcode.AllLinear(line.opcode)
override def hitRate: Double = 0.899
}
case object ReadsA extends TrivialMLinePattern {
override def apply(line: MLine): Boolean = line.readsRegister(M6809Register.A)
override def hitRate: Double = 0.58
}
case object ReadsB extends TrivialMLinePattern {
override def apply(line: MLine): Boolean = line.readsRegister(M6809Register.B)
override def hitRate: Double = 0.5 // ?
}
case object ReadsMemory extends TrivialMLinePattern {
override def apply(line: MLine): Boolean = line.readsMemory()
override def hitRate: Double = 0.33
}
case object ReadsX extends TrivialMLinePattern {
override def apply(line: MLine): Boolean = line.readsRegister(M6809Register.X)
override def hitRate: Double = 0.025
}
case object ReadsY extends TrivialMLinePattern {
override def apply(line: MLine): Boolean = line.readsRegister(M6809Register.Y)
override def hitRate: Double = 0.0249
}
case object ConcernsC extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
MOpcode.ReadsC(line.opcode) || MOpcode.ChangesC(line.opcode)
override def hitRate: Double = 0.378
}
case object ConcernsA extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
line.readsRegister(M6809Register.A) || line.changesRegister(M6809Register.A)
override def hitRate: Double = 0.5
}
case object ConcernsAH extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
line.readsRegister(M6809Register.B) || line.changesRegister(M6809Register.B)
override def hitRate: Double = 0.75 // ?
}
case object ConcernsX extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
line.readsRegister(M6809Register.X) || line.changesRegister(M6809Register.X)
override def hitRate: Double = 0.072
}
case object ConcernsS extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
line.readsRegister(M6809Register.S) || line.changesRegister(M6809Register.S)
override def hitRate: Double = 0.15
}
case object ConcernsY extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
line.readsRegister(M6809Register.Y) || line.changesRegister(M6809Register.Y)
override def hitRate: Double = 0.077
}
case object ChangesA extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = {
import MOpcode._
line match {
case MLine0(JSR, Absolute(false), MemoryAddressConstant(th)) => ctx.functionChangesA(th.name)
case _ => line.changesRegister(M6809Register.A)
}
}
override def hitRate: Double = 0.145
}
case object ChangesB extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = {
import MOpcode._
line match {
case MLine0(JSR, Absolute(false), MemoryAddressConstant(th)) => ctx.functionChangesB(th.name)
case _ => line.changesRegister(M6809Register.B)
}
}
override def hitRate: Double = 0.345
}
case object ChangesX extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = {
import MOpcode._
line match {
case MLine0(JSR, Absolute(false), MemoryAddressConstant(th)) => ctx.functionChangesX(th.name)
case _ => line.changesRegister(M6809Register.X)
}
}
override def hitRate: Double = 0.072
}
case object ChangesY extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = {
import MOpcode._
line match {
case MLine0(JSR, Absolute(false), MemoryAddressConstant(th)) => ctx.functionChangesY(th.name)
case _ => line.changesRegister(M6809Register.Y)
}
}
override def hitRate: Double = 0.094
}
case object ReadsC extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = {
import MOpcode._
line match {
// case MLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionReadsC(th.name)
case _ => MOpcode.ReadsC(line.opcode)
}
}
override def hitRate: Double = 0.19
}
case object ReadsN extends HasOpcodeIn(MOpcode.ReadsN)
case object ReadsZ extends HasOpcodeIn(MOpcode.ReadsZ)
case object ReadsV extends HasOpcodeIn(MOpcode.ReadsV)
case object ReadsH extends HasOpcodeIn(MOpcode.ReadsH)
//case object ChangesNAndZ extends HasOpcodeIn(MOpcode.ChangesNAndZ)
case object ChangesC extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = {
import MOpcode._
line match {
// case MLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionChangesC(th.name)
case MLine0(EXG, TwoRegisters(M6809Register.CC, _), _) => true
case MLine0(EXG, TwoRegisters(_, M6809Register.CC), _) => true
case MLine0(TFR, TwoRegisters(_, M6809Register.CC), _) => true
case _ => MOpcode.ChangesC(line.opcode)
}
}
override def hitRate: Double = 0.0317
}
case object NoopDiscards extends HasOpcodeIn(MOpcode.NoopDiscard)
case object NoopDiscardsOrLabel extends HasOpcodeIn(MOpcode.NoopDiscard + MOpcode.LABEL)
case object ChangesMemory extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = {
import MOpcode._
line match {
case MLine0(JSR, Absolute(false), MemoryAddressConstant(th)) => ctx.functionChangesMemory(th.name)
case _ => line.changesMemory()
}
}
override def hitRate: Double = 0.66
}
case class DoesntChangeMemoryAt(addrMode1: Int, param1: Int, opcode: MOpcode.Value = MOpcode.NOP) extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = {
import MOpcode._
line match {
case MLine0(JSR, Absolute(false), MemoryAddressConstant(th)) => !ctx.functionChangesMemory(th.name)
case _ =>
val p1 = ctx.get[Constant](param1)
val a1 = ctx.get[MAddrMode](addrMode1)
val changesSomeMemory = line.changesMemory()
// TODO: NOP
// this will break if the actual instruction was 16-bit
!changesSomeMemory || HelperCheckers.memoryAccessDoesntOverlap(MLine(opcode, a1, p1), line)
}
}
override def hitRate: Double = 0.966
}
case class DoesntChangeMemoryAtAssumingNonchangingIndices(addrMode1: Int, param1: Int, opcode: MOpcode.Value = MOpcode.NOP) extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = {
import MOpcode._
line match {
case MLine0(JSR, Absolute(false), MemoryAddressConstant(th)) => !ctx.functionChangesMemory(th.name)
case _ =>
val p1 = ctx.get[Constant](param1)
val a1 = ctx.get[MAddrMode](addrMode1)
val changesSomeMemory = line.changesMemory()
// TODO: NOP
// this will break if the actual instruction was 16-bit
!changesSomeMemory || HelperCheckers.memoryAccessDoesntOverlap(MLine(opcode, a1, p1), line, assumeSameIndices = true)
}
}
override def hitRate: Double = 0.973
}
case class DoesntChangeIndexingInAddrMode(i: Int) extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = {
import M6809Register._
ctx.get[MAddrMode](i) match {
case Indexed(S, false) => !ConcernsS.matchLineTo(ctx, flowInfo, line)
case Indexed(X, false) => !ChangesX.matchLineTo(ctx, flowInfo, line)
case Indexed(Y, false) => !ChangesY.matchLineTo(ctx, flowInfo, line)
case Absolute(true) => !ChangesMemory.matchLineTo(ctx, flowInfo, line)
case Absolute(false) | Inherent | InherentA | InherentB | Immediate => true
case _ => false // let's ignore rarer addressing modes
}
}
override def toString: String = s"¬(?<$i>AddrMode)"
override def hitRate: Double = 0.99
}
case object ConcernsMemory extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
ReadsMemory.matchLineTo(ctx, flowInfo, line) || ChangesMemory.matchLineTo(ctx, flowInfo, line)
override def hitRate: Double = 0.662
}
case class DoesNotConcernMemoryAt(addrMode1: Int, param1: Int) extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = {
{
import MOpcode._
line match {
case MLine0(JSR, Absolute(false), MemoryAddressConstant(th)) => !ctx.functionReadsMemory(th.name) && !ctx.functionChangesMemory(th.name)
case _ =>
val p1 = ctx.get[Constant](param1)
val a1 = ctx.get[MAddrMode](addrMode1)
// TODO: NOP
// this will break if the actual instruction was 16-bit
HelperCheckers.memoryAccessDoesntOverlap(MLine(MOpcode.NOP, a1, p1), line)
}
}
}
override def hitRate: Double = 0.968
}
case class HasOpcode(op: MOpcode.Value) extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
line.opcode == op
override def toString: String = op.toString
override def hitRate: Double = 0.071
}
class HasOpcodeIn(val ops: Set[MOpcode.Value]) extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
ops(line.opcode)
override def toString: String = ops.mkString("{", ",", "}")
def |(that: HasOpcodeIn): HasOpcodeIn = new HasOpcodeIn(ops ++ that.ops)
def --(that: HasOpcodeIn): HasOpcodeIn = new HasOpcodeIn(ops -- that.ops)
override def hitRate: Double = 0.1312
}
object HasOpcodeIn {
def apply(ops: MOpcode.Value*): HasOpcodeIn = new HasOpcodeIn(ops.toSet)
def apply(ops: Set[MOpcode.Value]): HasOpcodeIn = new HasOpcodeIn(ops)
}
case class HasAddrMode(am: MAddrMode) extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
line.addrMode == am
override def toString: String = am.toString
override def hitRate: Double = 0.295
}
case class IsTfr(s: M6809Register.Value, t: M6809Register.Value) extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
line.opcode == MOpcode.TFR && line.addrMode == TwoRegisters(s, t)
override def toString: String = s"(TFR $s,$t)"
override def hitRate: Double = 0.006
}
case class IsTfrFrom(s: M6809Register.Value) extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
line.opcode == MOpcode.TFR && (line.addrMode match {
case TwoRegisters(s1, _) => s == s1
case _ => false
})
override def toString: String = s"(TFR $s,_)"
override def hitRate: Double = 0.006
}
case class IsTfrTo(t: M6809Register.Value) extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
line.opcode == MOpcode.TFR && (line.addrMode match {
case TwoRegisters(_, t1) => t == t1
case _ => false
})
override def toString: String = s"(TFR _,$t)"
override def hitRate: Double = 0.006
}
case class HasAddrModeIn(ams: Set[MAddrMode]) extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
ams(line.addrMode)
override def toString: String = ams.mkString("{", ",", "}")
def |(that: HasAddrModeIn): HasAddrModeIn = HasAddrModeIn(ams ++ that.ams)
def --(that: HasAddrModeIn): HasAddrModeIn = HasAddrModeIn(ams -- that.ams)
override def hitRate: Double = 0.574
}
object HasAddrModeIn {
def apply(ams: MAddrMode*): HasAddrModeIn = HasAddrModeIn(ams.toSet)
}
case class HasImmediate(i: Int) extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
line.addrMode == Immediate && (line.parameter.quickSimplify match {
case NumericConstant(j, _) => (i & 0xffff) == (j & 0xffff)
case _ => false
})
override def toString: String = "#" + i
override def hitRate: Double = 0.039
}
case class HasByteImmediate(i: Int) extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
line.addrMode == Immediate && (line.parameter.quickSimplify match {
case NumericConstant(j, _) => (i & 0xff) == (j & 0xff)
case _ => false
})
override def toString: String = "#" + i
override def hitRate: Double = 0.039
}
case class HasImmediateWhere(predicate: Int => Boolean) extends TrivialMLinePattern {
override def apply(line: MLine): Boolean =
line.addrMode == Immediate && (line.parameter.quickSimplify match {
case NumericConstant(j, _) => predicate(j.toInt & 0xffff)
case _ => false
})
override def hitRate: Double = 0.556
}
case class HasParameterWhere(predicate: Constant => Boolean) extends TrivialMLinePattern {
override def apply(line: MLine): Boolean = predicate(line.parameter)
override def hitRate: Double = 0.332
}
case class MatchObject(i: Int, f: Function[MLine, Any]) extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
ctx.addObject(i, f(line))
override def toString: String = s"(?<$i>...)"
override def hitRate: Double = 0.8 // ?
}
case class MatchParameter(i: Int) extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
ctx.addObject(i, line.parameter.quickSimplify)
override def toString: String = s"(?<$i>Param)"
override def hitRate: Double = 0.947
}
case class DontMatchParameter(i: Int) extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
ctx.dontMatch(i, line.parameter.quickSimplify)
override def toString: String = s"¬(?<$i>Param)"
override def hitRate: Double = 0.7 // ?
}
case class MatchAddrMode(i: Int) extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
ctx.addObject(i, line.addrMode)
override def toString: String = s"¬(?<$i>AddrMode)"
override def hitRate: Double = 0.964
}
case class MatchOpcode(i: Int) extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
ctx.addObject(i, line.opcode)
override def toString: String = s"¬(?<$i>Op)"
override def hitRate: Double = 0.953
}
case class MatchImmediate(i: Int) extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
if (line.addrMode == Immediate) {
ctx.addObject(i, line.parameter.quickSimplify)
} else false
override def toString: String = s"(?<$i>#)"
override def hitRate: Double = 0.664
}
case class MatchNumericImmediate(i: Int) extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
if (line.addrMode == Immediate) {
line.parameter.quickSimplify match {
case NumericConstant(value, _) => ctx.addObject(i, value.toInt & 0xffff)
case _ => false
}
} else false
override def toString: String = s"(?<$i>#)"
override def hitRate: Double = 0.65
}
case class Before(pattern: AssemblyPattern) extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = {
pattern.validate(needsFlowInfo)
}
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, MLine)]): Option[List[(FlowInfo, MLine)]] = 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: MLine): Boolean = ???
override def hitRate: Double = 0.5 // ?
}
case class HasCallerCount(count: Int) extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
line match {
case MLine0(MOpcode.LABEL, _, MemoryAddressConstant(Label(l))) => flowInfo.labelUseCount(l) == count
case _ => false
}
override def hitRate: Double = 0.31
}
case class MatchElidableCopyOf(i: Int, firstLinePattern: MLinePattern, lastLinePattern: MLinePattern) extends AssemblyPattern {
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, MLine)]): Option[List[(FlowInfo, MLine)]] = {
val pattern = ctx.get[List[MLine]](i)
if (code.length < pattern.length) return None
val (before, after) = code.splitAt(pattern.length)
val lastIndex = code.length - 1
for (((a, (f, b)), ix) <- pattern.zip(before).zipWithIndex) {
if (!b.elidable) return None
if (a.opcode != b.opcode) return None
if (a.addrMode != b.addrMode) return None
if (a.parameter.quickSimplify != b.parameter.quickSimplify) return None
if (ix == 0 && !firstLinePattern.matchLineTo(ctx, f, b)) return None
if (ix == lastIndex && !lastLinePattern.matchLineTo(ctx, f, b)) return None
}
Some(after)
}
override def minimumRequiredLines: Int = 0
}
case object IsNotALabelUsedManyTimes extends MLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = FlowInfoRequirement.assertLabels(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean = line.opcode match {
case MOpcode.LABEL => line.parameter match {
case MemoryAddressConstant(Label(l)) => flowInfo.labelUseCount(l) <= 1
case _ => false
}
case _ => true
}
override def hitRate: Double = 0.92 // ?
}
object ParameterIsLocalLabel extends MLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: MLine): Boolean =
line match {
case MLine0(MOpcode.LABEL, _, MemoryAddressConstant(Label(l))) => l.startsWith(".")
case _ => false
}
override def hitRate: Double = 0.056
}