6502 and Z80: Optimize optimizations

This commit is contained in:
Karol Stasiak 2023-01-27 18:11:38 +01:00
parent e9cfec54b5
commit 29c1e3f2a6
23 changed files with 180 additions and 9 deletions

View File

@ -25,4 +25,6 @@ trait AssemblyOptimization[T <: AbstractCode] {
def optimize(f: NormalFunction, code: List[T], context: OptimizationContext): List[T]
def requiredFlags: Set[CompilationFlag.Value] = Set.empty
def minimumRequiredLines: Int
}

View File

@ -37,6 +37,8 @@ object FlowInfoRequirement extends Enumeration {
trait AssemblyRuleSet{
def flatten: Seq[AssemblyRule]
def minimumRequiredLines: Int
}
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRuleSet*) extends AssemblyOptimization[MLine] {
@ -45,6 +47,9 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
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)
@ -56,7 +61,11 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
taggedCode match {
case Nil => code
case head :: tail =>
for ((rule, index) <- actualRulesWithIndex) {
val codeLength = code.length
for {
(rule, index) <- actualRulesWithIndex
if codeLength >= rule.minimumRequiredLines
} {
val ctx = new AssemblyMatchingContext(
optimizationContext.options,
optimizationContext.labelMap,
@ -218,10 +227,14 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
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 {
@ -242,6 +255,8 @@ trait AssemblyPattern {
def captureLength(i: Int) = CaptureLength(i, this)
def minimumRequiredLines: Int
}
object HelperCheckers {
private def badAddrModes(am: MAddrMode): Boolean = am match {
@ -344,6 +359,8 @@ case class Capture(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
}
override def toString: String = s"(?<$i>$pattern)"
override def minimumRequiredLines: Int = 0
}
case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
@ -356,6 +373,8 @@ case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPatte
}
override def toString: String = s"(?<$i>$pattern)"
override def minimumRequiredLines: Int = 0
}
@ -365,6 +384,8 @@ case class Where(predicate: AssemblyMatchingContext => Boolean) extends Assembly
}
override def toString: String = "Where(...)"
override def minimumRequiredLines: Int = 0
}
case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends AssemblyPattern {
@ -387,6 +408,8 @@ case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends Assembl
case (_: Both, _) => s"($l) · $r"
case _ => s"$l · $r"
}
override val minimumRequiredLines: Int = l.minimumRequiredLines + r.minimumRequiredLines
}
case class Many(rule: MLinePattern) extends AssemblyPattern {
@ -412,6 +435,8 @@ case class Many(rule: MLinePattern) extends AssemblyPattern {
}
override def toString: String = s"[$rule]*"
override def minimumRequiredLines: Int = 0
}
case class ManyWhereAtLeastOne(rule: MLinePattern, atLeastOneIsThis: MLinePattern) extends AssemblyPattern {
@ -446,6 +471,8 @@ case class ManyWhereAtLeastOne(rule: MLinePattern, atLeastOneIsThis: MLinePatter
}
override def toString: String = s"[∃$atLeastOneIsThis:$rule]*"
override def minimumRequiredLines: Int = 1
}
case class Opt(rule: MLinePattern) extends AssemblyPattern {
@ -468,6 +495,8 @@ case class Opt(rule: MLinePattern) extends AssemblyPattern {
}
override def toString: String = s"[$rule]?"
override def minimumRequiredLines: Int = 0
}
trait MLinePattern extends AssemblyPattern {
@ -495,6 +524,8 @@ trait MLinePattern extends AssemblyPattern {
else Both(x, this)
def hitRate: Double
override def minimumRequiredLines: Int = 1
}
//noinspection ScalaUnnecessaryParentheses
@ -516,6 +547,8 @@ case class WhereNoMemoryAccessOverlapBetweenTwoLineLists(ix1: Int, ix2: Int) ext
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 {
@ -761,6 +794,8 @@ case object DebugMatching extends AssemblyPattern {
code.foreach(println)
Some(code)
}
override def minimumRequiredLines: Int = 0
}
case object Linear extends MLinePattern {
@ -1281,6 +1316,8 @@ case class MatchElidableCopyOf(i: Int, firstLinePattern: MLinePattern, lastLineP
}
Some(after)
}
override def minimumRequiredLines: Int = 0
}
case object IsNotALabelUsedManyTimes extends MLinePattern {

View File

@ -35,6 +35,8 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
override def name = "Changing index registers"
override def minimumRequiredLines: Int = 2
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
val addressingModesUsingX = Set(AbsoluteX, ZeroPageX, IndexedX)

View File

@ -56,7 +56,8 @@ object CoarseFlowAnalyzer {
changed = false
var currentStatus: CpuStatus = functionStartStatus
var staSpWasLast = false
for (i <- codeArray.indices) {
var i = 0
while (i < codeArray.length) {
import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.AddrMode._
import millfork.node.MosNiceFunctionProperty._
@ -184,6 +185,7 @@ object CoarseFlowAnalyzer {
}
}
tFlag = codeArray(i).opcode == SET
i += 1;
}
// flagArray.zip(codeArray).foreach{
// case (fl, y) => if (y.isPrintable) println(f"$fl%-32s $y%-32s")

View File

@ -16,6 +16,8 @@ import scala.collection.{immutable, mutable}
object EmptyMemoryStoreRemoval extends AssemblyOptimization[AssemblyLine] {
override def name = "Removing pointless stores to automatic variables"
override def minimumRequiredLines: Int = 2
private val storeAddrModes = Set(Absolute, ZeroPage, AbsoluteX, AbsoluteY, ZeroPageX, ZeroPageY)
private val directStorageOpcodes = Set(
STA, STX, STY, SAX, STZ, SHX, SHY, AHX,

View File

@ -16,6 +16,8 @@ import scala.collection.mutable
object EmptyParameterStoreRemoval extends AssemblyOptimization[AssemblyLine] {
override def name = "Removing pointless stores to foreign variables"
override def minimumRequiredLines: Int = 2
private val storeInstructions = Set(STA, STX, STY, SAX, STZ, STA_W, STX_W, STY_W, STZ_W)
private val storeAddrModes = Set(Absolute, ZeroPage, AbsoluteX, AbsoluteY, ZeroPageX, ZeroPageY, LongAbsolute, LongAbsoluteX)

View File

@ -17,6 +17,8 @@ object LocalVariableReadOptimization extends AssemblyOptimization[AssemblyLine]
override def name: String = "Local variable read optimization"
override def minimumRequiredLines: Int = 2
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
val stillUsedVariables = code.flatMap {

View File

@ -18,6 +18,8 @@ import scala.util.control.TailCalls.tailcall
case class RepeatedIndexCalculationOptimization(forX: Boolean) extends AssemblyOptimization[AssemblyLine] {
override def name: String = "Repeated index calculation into " + (if (forX) "X" else "Y")
override def minimumRequiredLines: Int = 4
override def optimize(f: NormalFunction, code: List[AssemblyLine], context: OptimizationContext): List[AssemblyLine] = {
val log = context.log
val allRuns = findAllRuns(code, 0, None).result

View File

@ -37,6 +37,8 @@ object FlowInfoRequirement extends Enumeration {
trait AssemblyRuleSet{
def flatten: Seq[AssemblyRule]
def minimumRequiredLines: Int
}
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRuleSet*) extends AssemblyOptimization[AssemblyLine] {
@ -45,6 +47,9 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
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)
@ -56,11 +61,16 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
taggedCode match {
case Nil => code
case head :: tail =>
for ((rule, index) <- actualRulesWithIndex) {
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(_)
)
@ -138,7 +148,17 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
def functionReadsMemory(name: String): Boolean = !niceFunctionProperties(NiceFunctionProperty.DoesntReadMemory -> name)
private val map = new mutable.HashMap[Int, Any]()
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(", ")
@ -242,10 +262,14 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
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 {
@ -266,6 +290,8 @@ trait AssemblyPattern {
def captureLength(i: Int) = CaptureLength(i, this)
def minimumRequiredLines: Int
}
object HelperCheckers {
import AddrMode._
@ -374,6 +400,8 @@ case class Capture(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
}
override def toString: String = s"(?<$i>$pattern)"
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
}
case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
@ -386,6 +414,8 @@ case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPatte
}
override def toString: String = s"(?<$i>$pattern)"
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
}
@ -395,6 +425,8 @@ case class Where(predicate: AssemblyMatchingContext => Boolean) extends Assembly
}
override def toString: String = "Where(...)"
override def minimumRequiredLines: Int = 0
}
case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends AssemblyPattern {
@ -417,6 +449,8 @@ case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends Assembl
case (_: Both, _) => s"($l) · $r"
case _ => s"$l · $r"
}
override val minimumRequiredLines: Int = l.minimumRequiredLines + r.minimumRequiredLines
}
case class Many(rule: AssemblyLinePattern) extends AssemblyPattern {
@ -442,6 +476,8 @@ case class Many(rule: AssemblyLinePattern) extends AssemblyPattern {
}
override def toString: String = s"[$rule]*"
override def minimumRequiredLines: Int = 0
}
case class ManyWhereAtLeastOne(rule: AssemblyLinePattern, atLeastOneIsThis: AssemblyLinePattern) extends AssemblyPattern {
@ -476,6 +512,8 @@ case class ManyWhereAtLeastOne(rule: AssemblyLinePattern, atLeastOneIsThis: Asse
}
override def toString: String = s"[∃$atLeastOneIsThis:$rule]*"
override def minimumRequiredLines: Int = rule.minimumRequiredLines
}
case class Opt(rule: AssemblyLinePattern) extends AssemblyPattern {
@ -498,6 +536,8 @@ case class Opt(rule: AssemblyLinePattern) extends AssemblyPattern {
}
override def toString: String = s"[$rule]?"
override def minimumRequiredLines: Int = 0
}
trait AssemblyLinePattern extends AssemblyPattern {
@ -525,6 +565,8 @@ trait AssemblyLinePattern extends AssemblyPattern {
else Both(x, this)
def hitRate: Double
override def minimumRequiredLines: Int = 1
}
//noinspection ScalaUnnecessaryParentheses
@ -538,6 +580,8 @@ case class Match(predicate: AssemblyMatchingContext => Boolean) extends Assembly
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 {
@ -546,6 +590,8 @@ case class WhereNoMemoryAccessOverlapBetweenTwoLineLists(ix1: Int, ix2: Int) ext
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 {
@ -866,6 +912,8 @@ case object DebugMatching extends AssemblyPattern {
code.foreach(println)
Some(code)
}
override def minimumRequiredLines: Int = 0
}
case object Linear extends AssemblyLinePattern {
@ -1608,6 +1656,8 @@ case class MatchElidableCopyOf(i: Int, firstLinePattern: AssemblyLinePattern, la
}
Some(after)
}
override def minimumRequiredLines: Int = 0
}
case object IsZeroPage extends AssemblyLinePattern {

View File

@ -15,6 +15,8 @@ import scala.collection.mutable.ListBuffer
*/
object SingleAssignmentVariableOptimization extends AssemblyOptimization[AssemblyLine] {
override def minimumRequiredLines: Int = 2
private val BadOpcodes = Set(RTS, JSR, JMP, RTI)
private val GoodOpcodes = Set(
LDA, LDX, LAX, LDY, EOR, AND, ORA, ADC, SBC, CMP, CPX, CPY, BIT,

View File

@ -14,6 +14,8 @@ import scala.collection.mutable
*/
object SuperOptimizer extends AssemblyOptimization[AssemblyLine] {
override def minimumRequiredLines: Int = 2
override def optimize(m: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
val options = optimizationContext.options
val log = optimizationContext.log

View File

@ -17,6 +17,8 @@ import scala.util.control.TailCalls.{TailRec, done, tailcall}
*/
object TwoVariablesToIndexRegistersOptimization extends AssemblyOptimization[AssemblyLine] {
override def minimumRequiredLines: Int = 2
override def requiredFlags: Set[CompilationFlag.Value] = Set(CompilationFlag.RegisterVariables)
object CyclesAndBytes {

View File

@ -35,4 +35,6 @@ object UnusedLabelRemoval extends AssemblyOptimization[AssemblyLine] {
}
override def name = "Unused label removal"
override def minimumRequiredLines: Int = 2
}

View File

@ -16,6 +16,8 @@ object UseAccumulatorInsteadOfXRegister extends UseAccumulatorInsteadOfIndexRegi
class UseAccumulatorInsteadOfIndexRegister(doY: Boolean) extends AssemblyOptimization[AssemblyLine] {
override def name: String = if (doY) "Use accumulator instead of the Y register" else "Use accumulator instead of the X register"
override def minimumRequiredLines: Int = 2
override def optimize(f: NormalFunction, code: List[AssemblyLine], context: OptimizationContext): List[AssemblyLine] = {
val log = context.log
if (f.params.length == 1) return code

View File

@ -18,6 +18,8 @@ import scala.util.control.TailCalls.{TailRec, done, tailcall}
*/
object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine] {
override def minimumRequiredLines: Int = 2
override def requiredFlags: Set[CompilationFlag.Value] = Set(CompilationFlag.RegisterVariables)
object CyclesAndBytes {

View File

@ -17,6 +17,8 @@ object ByteVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
override def name = "Allocating variables to single registers"
override def minimumRequiredLines: Int = 3
object CyclesAndBytes {
val Zero = CyclesAndBytes(0, 0)
}

View File

@ -17,6 +17,8 @@ class ChangeRegisterPair(preferBC2DE: Boolean) extends AssemblyOptimization[ZLin
import millfork.node.ZRegister._
override def minimumRequiredLines: Int = 3
case class Loaded(b: Boolean = false, c: Boolean = false, d: Boolean = false, e: Boolean = false) {
def load(register: ZRegister.Value): Loaded = register match {
case B => copy(b = true, d = false, e = false)

View File

@ -12,6 +12,8 @@ import millfork.node.ZRegister
object CompactStackFrame extends AssemblyOptimization[ZLine] {
override def name: String = "Compacting the stack frame"
override def minimumRequiredLines: Int = 9
override def optimize(f: NormalFunction, code: List[ZLine], context: OptimizationContext): List[ZLine] = {
val register =
if (context.options.flag(CompilationFlag.UseIxForStack)) ZRegister.IX

View File

@ -14,6 +14,8 @@ import scala.collection.mutable
object EmptyMemoryStoreRemoval extends AssemblyOptimization[ZLine] {
override def name = "Removing pointless stores to automatic variables"
override def minimumRequiredLines: Int = 2
override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = {
val vs = VariableStatus(f, code, optimizationContext, _ => true, allowParams = true).getOrElse(return code)
if (vs.localVariables.isEmpty) {

View File

@ -12,6 +12,8 @@ import millfork.env._
object EmptyParameterStoreRemoval extends AssemblyOptimization[ZLine] {
override def name = "Removing pointless stores to foreign variables"
override def minimumRequiredLines: Int = 2
override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = {
val usedFunctions = code.flatMap {
case ZLine0(CALL | JP | JR, _, MemoryAddressConstant(th)) => Some(th.name)

View File

@ -37,6 +37,8 @@ object FlowInfoRequirement extends Enumeration {
trait AssemblyRuleSet{
def flatten: Seq[AssemblyRule]
def minimumRequiredLines: Int
}
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRuleSet*) extends AssemblyOptimization[ZLine]{
@ -45,6 +47,10 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
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[ZLine], optimizationContext: OptimizationContext): List[ZLine] = {
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo)
optimizeImpl(f, code, taggedCode, optimizationContext)
@ -55,7 +61,11 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
taggedCode match {
case Nil => code
case head :: tail =>
for ((rule, index) <- actualRulesWithIndex) {
val codeLength = code.length
for {
(rule, index) <- actualRulesWithIndex
if codeLength >= rule.minimumRequiredLines
} {
val ctx = new AssemblyMatchingContext(optimizationContext.options)
rule.pattern.matchTo(ctx, taggedCode) match {
case Some(rest: List[(FlowInfo, ZLine)]) =>
@ -293,10 +303,14 @@ object HelperCheckers {
}
case class AssemblyRule(pattern: AssemblyPattern, result: (List[ZLine], AssemblyMatchingContext) => List[ZLine]) 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 {
@ -317,6 +331,7 @@ trait AssemblyPattern {
def captureLength(i: Int) = CaptureLength(i, this)
def minimumRequiredLines: Int
}
case class Capture(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
@ -329,6 +344,8 @@ case class Capture(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
}
override def toString: String = s"(?<$i>$pattern)"
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
}
case class CaptureLine(i: Int, pattern: AssemblyLinePattern) extends AssemblyLinePattern {
@ -340,6 +357,8 @@ case class CaptureLine(i: Int, pattern: AssemblyLinePattern) extends AssemblyLin
}
override def hitRate: Double = 0.025
override def minimumRequiredLines: Int = 0
}
case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
@ -352,6 +371,8 @@ case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPatte
}
override def toString: String = s"(?<$i>$pattern)"
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
}
@ -361,6 +382,8 @@ case class Where(predicate: (AssemblyMatchingContext => Boolean)) extends Assemb
}
override def toString: String = "Where(...)"
override def minimumRequiredLines: Int = 0
}
case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends AssemblyPattern {
@ -383,6 +406,8 @@ case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends Assembl
case (_: Both, _) => s"($l) · $r"
case _ => s"$l · $r"
}
override val minimumRequiredLines: Int = l.minimumRequiredLines + r.minimumRequiredLines
}
case class Many(rule: AssemblyLinePattern) extends AssemblyPattern {
@ -408,6 +433,8 @@ case class Many(rule: AssemblyLinePattern) extends AssemblyPattern {
}
override def toString: String = s"[$rule]*"
override def minimumRequiredLines: Int = 0
}
case class ManyWhereAtLeastOne(rule: AssemblyLinePattern, atLeastOneIsThis: AssemblyLinePattern) extends AssemblyPattern {
@ -442,6 +469,8 @@ case class ManyWhereAtLeastOne(rule: AssemblyLinePattern, atLeastOneIsThis: Asse
}
override def toString: String = s"[∃$atLeastOneIsThis:$rule]*"
override def minimumRequiredLines: Int = rule.minimumRequiredLines
}
case class Opt(rule: AssemblyLinePattern) extends AssemblyPattern {
@ -464,6 +493,8 @@ case class Opt(rule: AssemblyLinePattern) extends AssemblyPattern {
}
override def toString: String = s"[$rule]?"
override def minimumRequiredLines: Int = 0
}
trait AssemblyLinePattern extends AssemblyPattern {
@ -493,6 +524,8 @@ trait AssemblyLinePattern extends AssemblyPattern {
else Both(x, this)
def hitRate: Double
override def minimumRequiredLines: Int = 1
}
trait TrivialAssemblyLinePattern extends AssemblyLinePattern with (ZLine => Boolean) {
@ -931,6 +964,8 @@ case object DebugMatching extends AssemblyPattern {
code.foreach(println)
Some(code)
}
override def minimumRequiredLines: Int = 0
}
case object Linear extends AssemblyLinePattern {
@ -1342,6 +1377,8 @@ case class MatchElidableCopyOf(i: Int, firstLinePattern: AssemblyLinePattern, la
}
Some(after)
}
override def minimumRequiredLines: Int = 0
}
case object IsNotALabelUsedManyTimes extends AssemblyLinePattern {

View File

@ -19,6 +19,8 @@ object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
override def name = "Allocating variables to register pairs"
override def minimumRequiredLines: Int = 3
object CyclesAndBytes {
val Zero = CyclesAndBytes(0, 0)
}

View File

@ -328,8 +328,12 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
val labelMapImm = labelMap.toMap
val niceFunctionPropertiesImm = niceFunctionProperties.toSet
val extraOptimizedCode = veryLateOptimizations(thisFunctionNiceProperties, options).foldLeft(code) { (c, opt) =>
val ocode = opt.optimize(function, c, OptimizationContext(options, labelMapImm, env.maybeGet[ThingInMemory]("__reg"), niceFunctionPropertiesImm))
if (ocode eq c) code else quickSimplify(ocode)
if (c.length < opt.minimumRequiredLines) {
c
} else {
val ocode = opt.optimize(function, c, OptimizationContext(options, labelMapImm, env.maybeGet[ThingInMemory]("__reg"), env.identityPage, niceFunctionPropertiesImm))
if (ocode eq c) c else quickSimplify(ocode)
}
}
compiledFunctions(f) = NormalCompiledFunction(
function.declaredBank.getOrElse(platform.defaultCodeBank),
@ -663,6 +667,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
val defaultBank = mem.banks("default").index
if (platform.freeZpBytes.nonEmpty) {
// TODO: reimplement indexOf/LastIndexOf for speed
val zpUsageOffset = platform.freeZpBytes.min
val zeropageOccupation = zpOccupied.slice(zpUsageOffset, platform.freeZpBytes.max + 1)
unimportantLabelMap += "__zeropage_usage" -> ("default", zeropageOccupation.lastIndexOf(true) - zeropageOccupation.indexOf(true) + 1)
@ -841,8 +846,12 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
unoptimizedCodeSize += unoptimized.map(_.sizeInBytes).sum
// unoptimized.foreach(l => log.trace(l.toString))
val code = optimizations.foldLeft(quickSimplify(unoptimized)) { (c, opt) =>
val ocode = opt.optimize(f, c, OptimizationContext(options, labelMap, env.maybeGet[ThingInMemory]("__reg"), niceFunctionProperties))
if (ocode eq c) ocode else quickSimplify(ocode)
if (c.length < opt.minimumRequiredLines) {
c
} else {
val ocode = opt.optimize(f, c, OptimizationContext(options, labelMap, env.maybeGet[ThingInMemory]("__reg"), env.identityPage, niceFunctionProperties))
if (ocode eq c) ocode else quickSimplify(ocode)
}
}
performFinalOptimizationPass(f, optimizations.nonEmpty, options, code)
}