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/mos/opt/RuleBasedAssemblyOptimization.scala
2023-01-27 18:14:50 +01:00

1739 lines
67 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.mos.opt
import millfork.{CompilationFlag, CompilationOptions}
import millfork.assembly._
import millfork.assembly.mos.{AddrMode, _}
import millfork.assembly.opt.SingleStatus
import millfork.compiler.LabelGenerator
import millfork.env._
import millfork.error.{FatalErrorReporting, Logger}
import millfork.node.{MosNiceFunctionProperty, 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[AssemblyLine] {
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[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo)
optimizeImpl(f, code, taggedCode, optimizationContext)
}
final def optimizeImpl(f: NormalFunction, code: List[AssemblyLine], taggedCode: List[(FlowInfo, AssemblyLine)], optimizationContext: OptimizationContext): List[AssemblyLine] = {
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.zreg,
optimizationContext.identityPage,
optimizationContext.niceFunctionProperties,
head._1.labelUseCount(_)
)
rule.pattern.matchTo(ctx, taggedCode) match {
case Some(rest: List[(FlowInfo, AssemblyLine)]) =>
val optimizedChunkLengthBefore = taggedCode.length - rest.length
val (matchedChunkToOptimize, restOfCode) = code.splitAt(optimizedChunkLengthBefore)
val optimizedChunk: List[AssemblyLine] = 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 zeropageRegister: Option[ThingInMemory],
val identityPage: Constant,
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(MosNiceFunctionProperty.DoesntChangeA -> name)
def functionChangesX(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntChangeX -> name)
def functionChangesY(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntChangeY -> name)
def functionChangesIZ(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntChangeIZ -> name)
def functionChangesAH(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntChangeAH -> name)
def functionChangesC(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntChangeC -> name)
def functionReadsD(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntConcernD -> name)
def functionReadsC(name: String): Boolean = ReverseFlowAnalyzer.functionsThatReadC(name)
def functionChangesMemory(name: String): Boolean = !niceFunctionProperties(NiceFunctionProperty.DoesntWriteMemory -> name)
def functionReadsMemory(name: String): Boolean = !niceFunctionProperties(NiceFunctionProperty.DoesntReadMemory -> name)
private var m_map: mutable.HashMap[Int, Any] = _
@inline
private def map = {
var m = m_map
if (m eq null) {
m = new mutable.HashMap[Int, Any]()
m_map = m
}
m
}
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 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
} 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[AssemblyLine]](i).foreach {
// JSR and BSR are allowed
case AssemblyLine0(Opcode.RTS | Opcode.RTI | Opcode.RTL | Opcode.BRK, _, _) =>
return false
case AssemblyLine0(Opcode.JMP, AddrMode.Indirect | AddrMode.AbsoluteIndexedX | AddrMode.LongIndirect, _) =>
return false
case AssemblyLine0(Opcode.LABEL, _, MemoryAddressConstant(Label(l))) =>
labels += l
case AssemblyLine0(Opcode.JMP, AddrMode.Absolute, MemoryAddressConstant(Label(l))) =>
jumps += l
case AssemblyLine0(Opcode.JMP, AddrMode.Absolute | AddrMode.LongAbsolute, _) =>
return false
case AssemblyLine0(_, AddrMode.Relative, MemoryAddressConstant(Label(l))) =>
jumps += l
case AssemblyLine0(_, AddrMode.ZeroPageWithRelative, StructureConstant(_, List(_, MemoryAddressConstant(Label(l))))) =>
jumps += l
case AssemblyLine0(br, _, _) if OpcodeClasses.ShortBranching(br) || OpcodeClasses.SingleBitBranch(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 && labels.forall(l => labelUseCount(l) <= 1)
}
def zreg(i: Int): Constant = {
MemoryAddressConstant(zeropageRegister.get) + i
}
}
case class AssemblyRule(pattern: AssemblyPattern, result: (List[AssemblyLine], AssemblyMatchingContext) => List[AssemblyLine]) 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).min
}
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)
def minimumRequiredLines: Int
}
object HelperCheckers {
import AddrMode._
private val badAddrModes = Set(IndexedX, IndexedY, IndexedZ, LongIndexedY, LongIndexedZ, IndexedSY, Indirect, TripleAbsolute, Stack)
private val goodAddrModes = Set(Implied, Immediate, WordImmediate, Relative, LongRelative)
def memoryAccessDoesntOverlap(l1: AssemblyLine, l2: AssemblyLine, 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 == Opcode.CHANGED_MEM || l2.opcode == Opcode.CHANGED_MEM) return false
if ((a1 == IndexedSY) != (a2 == IndexedSY)) return true // bold assertion, but usually true
val p1 = if(a1 == ZeroPageWithRelative) l1.parameter.asInstanceOf[StructureConstant].fields.head else l1.parameter
val p2 = if(a2 == ZeroPageWithRelative) l2.parameter.asInstanceOf[StructureConstant].fields.head else l2.parameter
val w1 = OpcodeClasses.AccessesWordInMemory(l1.opcode)
val w2 = OpcodeClasses.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 = {
// `distance` is the distance between the first byte that can be addressed by l1 (b1) and the first byte that can be addressed by l2 (b2): (b2-b1)
val indexingAddrModes = Set(AbsoluteIndexedX, AbsoluteX, ZeroPageX, AbsoluteY, ZeroPageY, LongAbsoluteX)
val indicesCancelOut = assumeSameIndices && a1 == a2
val a1Indexing = indexingAddrModes(a1) && !indicesCancelOut
val a2Indexing = indexingAddrModes(a2) && !indicesCancelOut
(a1Indexing, a2Indexing) match {
case (false, false) => distance != 0 && (distance != 1 || !w1) && (distance != -1 || !w2)
case (true, false) => distance > 255 || distance < 0 && (distance != 256 || !w1) && (distance != -1 || !w2)
case (false, true) => distance > 0 || distance < -255 && (distance != 1 || !w1) && (distance != -256 || !w2)
case (true, true) => distance > 255 || distance < -255 && (distance != 265 || !w1) && (distance != -256 || !w2)
}
}
(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, 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)"
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
}
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)"
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
}
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(...)"
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, 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"
}
override val minimumRequiredLines: Int = l.minimumRequiredLines + r.minimumRequiredLines
}
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]*"
override def minimumRequiredLines: Int = 0
}
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]*"
override def minimumRequiredLines: Int = rule.minimumRequiredLines
}
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]?"
override def minimumRequiredLines: Int = 0
}
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 =
if (this.hitRate >= x.hitRate) EitherPattern(this, x)
else EitherPattern(x, this)
def &(x: AssemblyLinePattern): AssemblyLinePattern =
if (this.hitRate <= x.hitRate) Both(this, x)
else Both(x, this)
def hitRate: Double
override def minimumRequiredLines: Int = 1
}
//noinspection ScalaUnnecessaryParentheses
trait TrivialAssemblyLinePattern extends AssemblyLinePattern with (AssemblyLine => Boolean) {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = this (line)
}
case class Match(predicate: AssemblyMatchingContext => Boolean) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = predicate(ctx)
override def toString: String = "Match(...)"
override def hitRate: Double = 0.5 // ?
override def minimumRequiredLines: Int = 0
}
case class WhereNoMemoryAccessOverlapBetweenTwoLineLists(ix1: Int, ix2: Int) extends AssemblyPattern {
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = {
val s1s = ctx.get[List[AssemblyLine]](ix1)
val s2s = ctx.get[List[AssemblyLine]](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 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
}
override def hitRate: Double = 0.42
}
case class MatchStoredRegister(i: Int) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
import Opcode._
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 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
}
override def hitRate: Double = 0.077
}
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
}
override def hitRate: Double = 0.074
}
case class MatchZpReg(i: Int, registerIndex: 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.reg(registerIndex) match {
case SingleStatus(value) => ctx.addObject(i, value)
case _ => false
}
override def hitRate: Double = 0.003
}
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)
override def hitRate: Double = 0.08
}
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)
override def hitRate: Double = 0.018
}
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)
override def hitRate: Double = 0.011
}
case class HasZ(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.iz.contains(value)
override def hitRate: Double = 0.005
}
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("[¯\\_(ツ)_/¯:", ",", "]")
override def hitRate: Double = 0.688
}
case class DoesntMatterWhatItDoesWithReg(index: Int) extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertBackward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
flowInfo.importanceAfter.isPseudoregisterUnimportant(index)
override def toString: String = s"\\_(ツ)_/¯: __reg+$index]"
override def hitRate: Double = 0.45
}
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)
override def hitRate: Double = 0.026
}
case object XContainsHardwareStackPointer extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
flowInfo.statusBefore.eqSX
override def hitRate: Double = 0.046
}
case object XContainsSoftwareStackPointer extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
flowInfo.statusBefore.eqSpX
override def hitRate: Double = 0.046 // ?
}
case class HasSourceOfNZ(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.statusBefore.src.exists(s => s.matches(state))
override def hitRate: Double = 0.2
}
object HasAccu8 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.M)
override def hitRate: Double = 0.5 // ?
}
object HasAccu16 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.M)
override def hitRate: Double = 0.5 // ?
}
object HasIndex8 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.W)
override def hitRate: Double = 0.5 // ?
}
object HasIndex16 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.W)
override def hitRate: Double = 0.5 // ?
}
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)
override def hitRate: Double = 0.48
}
case object HasClearBitA0 extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit =
FlowInfoRequirement.assertForward(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
flowInfo.statusBefore.a0.contains(false)
override def hitRate: Double = 0.30
}
case object Anything extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = true
override def hitRate: Double = 1
}
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
override def hitRate: Double = 1 - inner.hitRate
}
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
override def &(x: AssemblyLinePattern): AssemblyLinePattern =
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: 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)"
override def |(x: AssemblyLinePattern): AssemblyLinePattern =
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 AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
line.elidable
override def hitRate: Double = 0.937
}
case object NotFixed extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
line.notFixed
override def hitRate: Double = 0.926
}
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)
}
override def minimumRequiredLines: Int = 0
}
case object Linear extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
if (line.opcode == Opcode.JSR) line.parameter match {
case MemoryAddressConstant(f: FunctionInMemory) => f.hasOptimizationHints
case _ => false
} else OpcodeClasses.AllLinear(line.opcode)
}
override def hitRate: Double = 0.89
}
case object LinearOrBranch extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean =
if (line.opcode == Opcode.JSR) line.parameter match {
case MemoryAddressConstant(f: FunctionInMemory) => f.hasOptimizationHints
case _ => false
} else OpcodeClasses.AllLinear(line.opcode) || OpcodeClasses.ShortBranching(line.opcode)
override def hitRate: Double = 0.887
}
case object LinearOrLabel extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean =
if (line.opcode == Opcode.JSR) line.parameter match {
case MemoryAddressConstant(f: FunctionInMemory) => f.hasOptimizationHints
case _ => false
} else line.opcode == Opcode.LABEL || OpcodeClasses.AllLinear(line.opcode)
override def hitRate: Double = 0.899
}
case object ReadsA extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean =
OpcodeClasses.ReadsAAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ReadsAIfImplied(line.opcode)
override def hitRate: Double = 0.58
}
case object ReadsAH extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean =
OpcodeClasses.ReadsAHAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ReadsAHIfImplied(line.opcode)
override def hitRate: Double = 0.5 // ?
}
case object ReadsMemory extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean =
line.addrMode match {
case AddrMode.Indirect => true
case AddrMode.TripleAbsolute => true
case AddrMode.Implied | AddrMode.Immediate => false
case _ =>
OpcodeClasses.ReadsMemoryIfNotImpliedOrImmediate(line.opcode)
}
override def hitRate: Double = 0.33
}
case object IsNonvolatile extends TrivialAssemblyLinePattern {
// TODO: this is a big hack
override def apply(line: AssemblyLine): Boolean =
line.addrMode match {
case AddrMode.Implied | AddrMode.Immediate => true
case _ => line.parameter match {
case MemoryAddressConstant(_) => true
case CompoundConstant(_, MemoryAddressConstant(_), _) => true
case _ => false
}
}
override def hitRate: Double = 0.858
}
case object ReadsX extends TrivialAssemblyLinePattern {
val XAddrModes: Set[AddrMode.Value] = Set(AddrMode.AbsoluteX, AddrMode.IndexedX, AddrMode.ZeroPageX, AddrMode.AbsoluteIndexedX, AddrMode.ImmediateWithAbsoluteX, AddrMode.ImmediateWithZeroPageX)
override def apply(line: AssemblyLine): Boolean =
OpcodeClasses.ReadsXAlways(line.opcode) || XAddrModes(line.addrMode)
override def hitRate: Double = 0.025
}
case object ReadsY extends TrivialAssemblyLinePattern {
val YAddrModes: Set[AddrMode.Value] = Set(AddrMode.AbsoluteY, AddrMode.IndexedY, AddrMode.ZeroPageY)
override def apply(line: AssemblyLine): Boolean =
OpcodeClasses.ReadsYAlways(line.opcode) || YAddrModes(line.addrMode)
override def hitRate: Double = 0.0249
}
case object ConcernsC extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean =
OpcodeClasses.ReadsC(line.opcode) || OpcodeClasses.ChangesC(line.opcode)
override def hitRate: Double = 0.378
}
case object ConcernsA extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean =
OpcodeClasses.ConcernsAAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ConcernsAIfImplied(line.opcode)
override def hitRate: Double = 0.75
}
case object ConcernsAH extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean =
OpcodeClasses.ConcernsAHAlways(line.opcode) || line.addrMode == AddrMode.Implied && OpcodeClasses.ConcernsAHIfImplied(line.opcode)
override def hitRate: Double = 0.5 // ?
}
case object ConcernsX extends TrivialAssemblyLinePattern {
val XAddrModes: Set[AddrMode.Value] = Set(AddrMode.AbsoluteX, AddrMode.AbsoluteIndexedX, AddrMode.LongAbsoluteX, AddrMode.IndexedX, AddrMode.ZeroPageX)
override def apply(line: AssemblyLine): Boolean =
OpcodeClasses.ConcernsXAlways(line.opcode) || XAddrModes(line.addrMode)
override def hitRate: Double = 0.072
}
case object ConcernsS extends TrivialAssemblyLinePattern {
val SAddrModes: Set[AddrMode.Value] = Set(AddrMode.Stack, AddrMode.IndexedSY)
override def apply(line: AssemblyLine): Boolean =
OpcodeClasses.ConcernsSAlways(line.opcode) || SAddrModes(line.addrMode)
override def hitRate: Double = 0.15
}
case object ConcernsY extends TrivialAssemblyLinePattern {
val YAddrModes: Set[AddrMode.Value] = Set(AddrMode.AbsoluteY, AddrMode.IndexedSY, AddrMode.IndexedY, AddrMode.LongIndexedY, AddrMode.ZeroPageY)
override def apply(line: AssemblyLine): Boolean =
OpcodeClasses.ConcernsYAlways(line.opcode) || YAddrModes(line.addrMode)
override def hitRate: Double = 0.077
}
case object ConcernsStack extends TrivialAssemblyLinePattern {
val SAddrModes: Set[AddrMode.Value] = Set(AddrMode.IndexedSY, AddrMode.Stack)
override def apply(line: AssemblyLine): Boolean =
OpcodeClasses.ConcernsStackAlways(line.opcode) || SAddrModes(line.addrMode)
override def hitRate: Double = 0.218
}
case object ConcernsIZ extends TrivialAssemblyLinePattern {
val IZAddrModes: Set[AddrMode.Value] = Set(AddrMode.IndexedZ, AddrMode.LongIndexedZ)
override def apply(line: AssemblyLine): Boolean =
OpcodeClasses.ConcernsIZAlways(line.opcode) || IZAddrModes(line.addrMode)
override def hitRate: Double = 0.1 // ?
}
case object ChangesA extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
import Opcode._
import AddrMode._
line match {
case AssemblyLine0(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionChangesA(th.name)
case AssemblyLine0(_, Implied, _) => OpcodeClasses.ChangesAIfImplied(line.opcode) || OpcodeClasses.ChangesAAlways(line.opcode)
case _ => OpcodeClasses.ChangesAAlways(line.opcode)
}
}
override def hitRate: Double = 0.345
}
case object ChangesX extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
import Opcode._
import AddrMode._
line match {
case AssemblyLine0(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionChangesX(th.name)
case _ => OpcodeClasses.ChangesX(line.opcode)
}
}
override def hitRate: Double = 0.072
}
case object ChangesY extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
import Opcode._
import AddrMode._
line match {
case AssemblyLine0(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionChangesY(th.name)
case _ => OpcodeClasses.ChangesY(line.opcode)
}
}
override def hitRate: Double = 0.094
}
case object ReadsNOrZ extends HasOpcodeIn(OpcodeClasses.ReadsNOrZ)
case object ReadsC extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
import Opcode._
import AddrMode._
line match {
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionReadsC(th.name)
case _ => OpcodeClasses.ReadsC(line.opcode)
}
}
override def hitRate: Double = 0.19
}
case object ReadsD extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
import Opcode._
import AddrMode._
line match {
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionReadsD(th.name)
case _ => OpcodeClasses.ReadsD(line.opcode)
}
}
override def hitRate: Double = 0.0277
}
case object ReadsV extends HasOpcodeIn(OpcodeClasses.ReadsV)
case object ChangesNAndZ extends HasOpcodeIn(OpcodeClasses.ChangesNAndZ)
case object ChangesC extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
import Opcode._
import AddrMode._
line match {
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionChangesC(th.name)
case _ => OpcodeClasses.ChangesC(line.opcode)
}
}
override def hitRate: Double = 0.0317
}
case object ChangesV extends HasOpcodeIn(OpcodeClasses.ChangesV)
case object ChangesStack extends HasOpcodeIn(OpcodeClasses.ChangesStack)
case object ChangesIZ extends HasOpcodeIn(OpcodeClasses.ChangesIZ)
case object ChangesS extends HasOpcodeIn(OpcodeClasses.ChangesS)
case object SupportsAbsolute extends HasOpcodeIn(OpcodeClasses.SupportsAbsolute)
case object SupportsAbsoluteX extends HasOpcodeIn(OpcodeClasses.SupportsAbsoluteX)
case object SupportsAbsoluteY extends HasOpcodeIn(OpcodeClasses.SupportsAbsoluteY)
case object ShortConditionalBranching extends HasOpcodeIn(OpcodeClasses.ShortConditionalBranching)
case object ShortBranching extends HasOpcodeIn(OpcodeClasses.ShortBranching)
case object OverwritesA extends HasOpcodeIn(OpcodeClasses.OverwritesA)
case object OverwritesX extends HasOpcodeIn(OpcodeClasses.OverwritesX)
case object OverwritesY extends HasOpcodeIn(OpcodeClasses.OverwritesY)
case object NoopDiscardsFlags extends HasOpcodeIn(OpcodeClasses.NoopDiscardsFlags)
case object NoopDiscardsFlagsOrLabel extends HasOpcodeIn(OpcodeClasses.NoopDiscardsFlags + Opcode.LABEL)
case object ChangesAH extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
import Opcode._
import AddrMode._
line match {
case AssemblyLine0(JSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionChangesAH(th.name)
case AssemblyLine0(_, Implied, _) => OpcodeClasses.ChangesAHIfImplied(line.opcode) || OpcodeClasses.ChangesAHAlways(line.opcode)
case _ => OpcodeClasses.ChangesAHAlways(line.opcode)
}
}
override def hitRate: Double = 0.1 // ?
}
case object ChangesM extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = line match {
case AssemblyLine0(Opcode.SEP | Opcode.REP, AddrMode.Immediate, NumericConstant(n, _)) => (n & 0x20) != 0
case AssemblyLine0(Opcode.SEP | Opcode.REP | Opcode.PLP | Opcode.XCE, _, _) => true
case _ => false
}
override def hitRate: Double = 0.02 // ?
}
case object ChangesW extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = line match {
case AssemblyLine0(Opcode.SEP | Opcode.REP, AddrMode.Immediate, NumericConstant(n, _)) => (n & 0x10) != 0
case AssemblyLine0(Opcode.SEP | Opcode.REP | Opcode.PLP | Opcode.XCE, _, _) => true
case _ => false
}
override def hitRate: Double = 0.02 // ?
}
case object ChangesMemory extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
import AddrMode._
import Opcode._
line match {
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => ctx.functionChangesMemory(th.name)
case AssemblyLine0(op, Implied, _) => OpcodeClasses.ChangesMemoryAlways(op)
case AssemblyLine0(op, TripleAbsolute, _) => true
case AssemblyLine0(op, _, _) => OpcodeClasses.ChangesMemoryAlways(op) || OpcodeClasses.ChangesMemoryIfNotImplied(op)
case _ => false
}
}
override def hitRate: Double = 0.66
}
case class DoesntChangeMemoryAt(addrMode1: Int, param1: Int, opcode: Opcode.Value = Opcode.NOP) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
import AddrMode._
import Opcode._
line match {
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => !ctx.functionChangesMemory(th.name)
case _ =>
val p1 = ctx.get[Constant](param1)
val a1 = ctx.get[AddrMode.Value](addrMode1)
val changesSomeMemory = OpcodeClasses.ChangesMemoryAlways(line.opcode) || line.addrMode != AddrMode.Implied && OpcodeClasses.ChangesMemoryIfNotImplied(line.opcode)
// TODO: NOP
// this will break if the actual instruction was 16-bit
!changesSomeMemory || HelperCheckers.memoryAccessDoesntOverlap(AssemblyLine(opcode, a1, p1), line)
}
}
override def hitRate: Double = 0.966
}
case class DoesntChangeMemoryAtAssumingNonchangingIndices(addrMode1: Int, param1: Int, opcode: Opcode.Value = Opcode.NOP) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
import AddrMode._
import Opcode._
line match {
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => !ctx.functionChangesMemory(th.name)
case _ =>
val p1 = ctx.get[Constant](param1)
val a1 = ctx.get[AddrMode.Value](addrMode1)
val changesSomeMemory = OpcodeClasses.ChangesMemoryAlways(line.opcode) || line.addrMode != AddrMode.Implied && OpcodeClasses.ChangesMemoryIfNotImplied(line.opcode)
// TODO: NOP
// this will break if the actual instruction was 16-bit
!changesSomeMemory || HelperCheckers.memoryAccessDoesntOverlap(AssemblyLine(opcode, a1, p1), line, assumeSameIndices = true)
}
}
override def hitRate: Double = 0.973
}
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)
override def hitRate: Double = 0.662
}
case class DoesNotConcernMemoryAt(addrMode1: Int, param1: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
{
import AddrMode._
import Opcode._
line match {
case AssemblyLine0(JSR | BSR, Absolute | LongAbsolute, MemoryAddressConstant(th)) => !ctx.functionReadsMemory(th.name) && !ctx.functionChangesMemory(th.name)
case _ =>
val p1 = ctx.get[Constant](param1)
val a1 = ctx.get[AddrMode.Value](addrMode1)
// TODO: NOP
// this will break if the actual instruction was 16-bit
HelperCheckers.memoryAccessDoesntOverlap(AssemblyLine(Opcode.NOP, a1, p1), line)
}
}
}
override def hitRate: Double = 0.968
}
case class HasOpcode(op: Opcode.Value) extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean =
line.opcode == op
override def toString: String = op.toString
override def hitRate: Double = 0.071
}
case class RefersTo(identifier: String, offset: Int = 999) extends TrivialAssemblyLinePattern {
def check(constant: Constant): Boolean = {
constant match {
case SubbyteConstant(base, 0) =>
check(base)
case MemoryAddressConstant(th) =>
(offset == 999 || offset == 0) && th.name == identifier
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(nn, _)) =>
(offset == 999 || offset == nn) && th.name == identifier
case CompoundConstant(MathOperator.Plus, NumericConstant(nn, _), MemoryAddressConstant(th)) =>
(offset == 999 || offset == nn) && th.name == identifier
case StructureConstant(_, list) =>
list.exists(check)
case _ => false
}
}
override def apply(line: AssemblyLine): Boolean = {
(line.addrMode == AddrMode.ZeroPage || line.addrMode == AddrMode.Absolute || line.addrMode == AddrMode.LongAbsolute) && check(line.parameter)
}
override def toString: String = s"<$identifier+$offset>"
override def hitRate: Double = 0.013
}
case class RefersToOrUses(identifier: String, offset: Int = 999) extends TrivialAssemblyLinePattern {
def check(parameter: Constant, addrMode: AddrMode.Value): Boolean = {
addrMode match {
case AddrMode.ZeroPage | AddrMode.Absolute | AddrMode.LongAbsolute | AddrMode.Indirect => parameter match {
case SubbyteConstant(base, 0) =>
check(base, addrMode)
case MemoryAddressConstant(th) =>
(offset == 999 || offset == 0) && th.name == identifier
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(nn, _)) =>
(offset == 999 || offset == nn) && th.name == identifier
case CompoundConstant(MathOperator.Plus, NumericConstant(nn, _), MemoryAddressConstant(th)) =>
(offset == 999 || offset == nn) && th.name == identifier
case CompoundConstant(MathOperator.Minus, MemoryAddressConstant(th), NumericConstant(nn, _)) =>
(offset == 999 || offset == -nn) && th.name == identifier
case _ => false
}
case AddrMode.IndexedY | AddrMode.LongIndexedY | AddrMode.IndexedZ | AddrMode.LongIndexedZ => parameter match {
case SubbyteConstant(base, 0) =>
check(base, addrMode)
case MemoryAddressConstant(th) =>
(offset == 999 || offset == 0 || offset == 1) && th.name == identifier
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(nn, _)) =>
(offset == 999 || offset == nn || offset == nn + 1) && th.name == identifier
case CompoundConstant(MathOperator.Plus, NumericConstant(nn, _), MemoryAddressConstant(th)) =>
(offset == 999 || offset == nn || offset == nn + 1) && th.name == identifier
case CompoundConstant(MathOperator.Minus, MemoryAddressConstant(th), NumericConstant(nn, _)) =>
(offset == 999 || offset == -nn || offset == -nn + 1) && th.name == identifier
case _ => false
}
case AddrMode.AbsoluteX | AddrMode.AbsoluteY | AddrMode.AbsoluteIndexedX => parameter match {
case SubbyteConstant(base, 0) =>
check(base, addrMode)
case MemoryAddressConstant(th) =>
(offset == 999 || offset >= 0 && offset <= 255) && th.name == identifier
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(nn, _)) =>
(offset == 999 || offset >= nn && offset <= nn + 255) && th.name == identifier
case CompoundConstant(MathOperator.Plus, NumericConstant(nn, _), MemoryAddressConstant(th)) =>
(offset == 999 || offset >= nn && offset <= nn + 255) && th.name == identifier
case CompoundConstant(MathOperator.Minus, MemoryAddressConstant(th), NumericConstant(nn, _)) =>
(offset == 999 || offset >= -nn && offset <= -nn + 255) && th.name == identifier
case _ => false
}
case AddrMode.IndexedX => parameter match {
case SubbyteConstant(base, 0) => check(base, addrMode)
case MemoryAddressConstant(th) => th.name == identifier
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(nn, _)) => th.name == identifier
case CompoundConstant(MathOperator.Plus, NumericConstant(nn, _), MemoryAddressConstant(th)) => th.name == identifier
case CompoundConstant(MathOperator.Minus, MemoryAddressConstant(th), NumericConstant(nn, _)) => th.name == identifier
case _ => false
}
case AddrMode.ZeroPageWithRelative => parameter match {
case StructureConstant(_, params) => params.exists(x => check(x, AddrMode.ZeroPage))
case _ => false
}
case AddrMode.TripleAbsolute => parameter match {
case StructureConstant(_, params) => params.exists(x => check(x, AddrMode.Absolute))
case _ => false // TODO: ???
}
case AddrMode.ImmediateWithAbsolute | AddrMode.ImmediateWithZeroPage => parameter match {
case StructureConstant(_, params) => params.exists(x => check(x, AddrMode.Absolute))
case _ => false // TODO: ???
}
case AddrMode.ImmediateWithAbsoluteX | AddrMode.ImmediateWithZeroPageX => parameter match {
case StructureConstant(_, params) => params.exists(x => check(x, AddrMode.AbsoluteX))
case _ => false // TODO: ???
}
case _ => false
}
}
override def apply(line: AssemblyLine): Boolean = check(line.parameter, line.addrMode)
override def toString: String = s"<$identifier+$offset>"
override def hitRate: Double = 0.014
}
case class CallsAnyOf(identifiers: Set[String]) extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = {
(line.addrMode == AddrMode.Absolute ||
line.addrMode == AddrMode.LongAbsolute ||
line.addrMode == AddrMode.LongRelative) && (line.parameter match {
case MemoryAddressConstant(th) => identifiers(th.name)
case _ => false
})
}
override def toString: String = identifiers.mkString("(JSR {", ",", "})")
override def hitRate: Double = 0.03 // ?
}
case class CallsAnyExcept(identifiers: Set[String]) extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = {
(line.addrMode == AddrMode.Absolute ||
line.addrMode == AddrMode.LongAbsolute ||
line.addrMode == AddrMode.LongRelative) && (line.parameter match {
case MemoryAddressConstant(th) => th.name.head != '.' && !identifiers(th.name)
case _ => false
})
}
override def toString: String = identifiers.mkString("(JSR ¬{", ",", "})")
override def hitRate: Double = 0.056
}
class HasOpcodeIn(val ops: Set[Opcode.Value]) extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): 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: Opcode.Value*): HasOpcodeIn = new HasOpcodeIn(ops.toSet)
def apply(ops: Set[Opcode.Value]): HasOpcodeIn = new HasOpcodeIn(ops)
}
case class HasAddrMode(am: AddrMode.Value) extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean =
line.addrMode == am
override def toString: String = am.toString
override def hitRate: Double = 0.295
}
case class HasAddrModeIn(ams: Set[AddrMode.Value]) extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): 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: AddrMode.Value*): HasAddrModeIn = HasAddrModeIn(ams.toSet)
}
case class HasImmediate(i: Int) extends TrivialAssemblyLinePattern {
override def apply(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
override def hitRate: Double = 0.039
}
case class HasImmediateWhere(predicate: Int => Boolean) extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean =
line.addrMode == AddrMode.Immediate && (line.parameter.quickSimplify match {
case NumericConstant(j, _) => predicate(j.toInt & 0xff)
case _ => false
})
override def hitRate: Double = 0.556
}
case class HasParameterWhere(predicate: Constant => Boolean) extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = predicate(line.parameter)
override def hitRate: Double = 0.332
}
case object HasSmallParameter extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = line.parameter match {
case MemoryAddressConstant(th : VariableInMemory) => th.typ.size < 255
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th : VariableInMemory), NumericConstant(_, 1)) => th.typ.size < 255
case MemoryAddressConstant(th : MfArray) => th.sizeInBytes < 255
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th : MfArray), NumericConstant(_, 1)) => th.sizeInBytes < 255
case _ => false
}
override def hitRate: Double = 0.332
}
case object HasIdentityPageParameter extends TrivialAssemblyLinePattern {
override def apply(line: AssemblyLine): Boolean = line.parameter match {
case MemoryAddressConstant(th) => th.name == "identity$"
case _ => false
}
override def hitRate: Double = 0.04
}
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>...)"
override def hitRate: Double = 0.8 // ?
}
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)"
override def hitRate: Double = 0.947
}
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)"
override def hitRate: Double = 0.7 // ?
}
case class MatchAddrMode(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
ctx.addAddrModeLoosely(i, line.addrMode)
override def toString: String = s"¬(?<$i>AddrMode)"
override def hitRate: Double = 0.964
}
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)"
override def hitRate: Double = 0.953
}
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>#)"
override def hitRate: Double = 0.664
}
case class MatchNumericImmediate(i: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
if (line.addrMode == AddrMode.Immediate) {
line.parameter.quickSimplify match {
case NumericConstant(value, _) => ctx.addObject(i, value.toInt & 0xff)
case _ => false
}
} else false
override def toString: String = s"(?<$i>#)"
override def hitRate: Double = 0.65
}
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.LongAbsoluteX | AddrMode.IndexedX | AddrMode.AbsoluteIndexedX => !ChangesX.matchLineTo(ctx, flowInfo, line)
case AddrMode.ZeroPageY | AddrMode.AbsoluteY | AddrMode.IndexedY | AddrMode.LongIndexedY => !ChangesY.matchLineTo(ctx, flowInfo, line)
case AddrMode.IndexedZ | AddrMode.LongIndexedZ => !ChangesIZ.matchLineTo(ctx, flowInfo, line)
case AddrMode.Stack => !OpcodeClasses.ChangesS.contains(line.opcode)
case AddrMode.IndexedSY => !OpcodeClasses.ChangesS.contains(line.opcode) && !ChangesY.matchLineTo(ctx, flowInfo, line)
case AddrMode.Indirect | AddrMode.LongIndirect => ChangesMemory.matchLineTo(ctx, flowInfo, line)
case _ => true
}
override def toString: String = s"¬(?<$i>AddrMode)"
override def hitRate: Double = 0.99
}
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 = ???
override def hitRate: Double = 0.5 // ?
}
case class HasCallerCount(count: Int) extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
line match {
case AssemblyLine0(Opcode.LABEL, _, MemoryAddressConstant(Label(l))) => flowInfo.labelUseCount(l) == count
case _ => false
}
override def hitRate: Double = 0.31
}
object ParameterIsLocalLabel extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
line match {
case AssemblyLine0(Opcode.LABEL, _, MemoryAddressConstant(Label(l))) => l.startsWith(".")
case _ => false
}
override def hitRate: Double = 0.056
}
case class MatchElidableCopyOf(i: Int, firstLinePattern: AssemblyLinePattern, lastLinePattern: AssemblyLinePattern) extends AssemblyPattern {
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = {
val pattern = ctx.get[List[AssemblyLine]](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 IsZeroPage extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = {
import Opcode._
line match {
case AssemblyLine0(_, AddrMode.ZeroPage, _) => true
case l@AssemblyLine(LDA | STA | CMP |
LDX | STX | CPX |
LDY | STY | CPY |
LDZ | STZ | CPZ |
BIT |
ADC | SBC | AND | ORA | EOR |
INC | DEC | ROL | ROR | ASL | LSR |
ISC | DCP | LAX | SAX | RLA | RRA | SLO | SRE, AddrMode.Absolute, p, Elidability.Elidable, _) =>
p match {
case NumericConstant(n, _) => n <= 255
case MemoryAddressConstant(th) => ctx.labelMap.getOrElse(th.name, 0 -> 0x800)._2 < 0x100
case CompoundConstant(MathOperator.Plus,
MemoryAddressConstant(th),
NumericConstant(n, _)) => ctx.labelMap.getOrElse(th.name, 0 -> 0x800)._2 + n < 0x100
case _ => false
}
case _ => false
}
}
override def hitRate: Double = 0.24
}
case object IsNotALabelUsedManyTimes extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = FlowInfoRequirement.assertLabels(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = line.opcode match {
case Opcode.LABEL => line.parameter match {
case MemoryAddressConstant(Label(l)) => flowInfo.labelUseCount(l) <= 1
case _ => false
}
case _ => true
}
override def hitRate: Double = 0.92 // ?
}
case object ParameterIsLabel extends AssemblyLinePattern {
override def validate(needsFlowInfo: FlowInfoRequirement.Value): Unit = FlowInfoRequirement.assertLabels(needsFlowInfo)
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = line.parameter match {
case MemoryAddressConstant(Label(l)) => true
case _ => false
}
override def hitRate: Double = 0.09 // ?
}