mirror of https://github.com/KarolS/millfork.git
6502 and Z80: Optimize optimizations
This commit is contained in:
parent
e9cfec54b5
commit
29c1e3f2a6
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -35,4 +35,6 @@ object UnusedLabelRemoval extends AssemblyOptimization[AssemblyLine] {
|
|||
}
|
||||
|
||||
override def name = "Unused label removal"
|
||||
|
||||
override def minimumRequiredLines: Int = 2
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue