mirror of
https://github.com/KarolS/millfork.git
synced 2024-10-31 14:04:58 +00:00
Compare commits
12 Commits
6f294d6dec
...
d8c11a9c50
Author | SHA1 | Date | |
---|---|---|---|
|
d8c11a9c50 | ||
|
ee47ccef5a | ||
|
24de2f7530 | ||
|
1beb695151 | ||
|
9229092309 | ||
|
ca7166d1ae | ||
|
1594d63a9a | ||
|
75cc34663c | ||
|
f4d2fdd370 | ||
|
29c1e3f2a6 | ||
|
e9cfec54b5 | ||
|
1bc1ab3539 |
@ -575,7 +575,7 @@ object CompilationFlag extends Enumeration {
|
||||
EmitIllegals, DecimalMode, LenientTextEncoding, LineNumbersInAssembly, SourceInAssembly, EnableBreakpoints,
|
||||
// compilation options for MOS:
|
||||
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes, EmitWdcOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
||||
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack,
|
||||
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack, IdentityPage,
|
||||
// compilation options for I80
|
||||
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitR800Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
|
||||
UseShadowRegistersForInterrupts,
|
||||
@ -654,6 +654,7 @@ object CompilationFlag extends Enumeration {
|
||||
"u_stack" -> UseUForStack,
|
||||
"y_stack" -> UseYForStack,
|
||||
"software_stack" -> SoftwareStack,
|
||||
"identity_page" -> IdentityPage,
|
||||
"use_shadow_registers_for_irq" -> UseShadowRegistersForInterrupts,
|
||||
"output_intel_syntax" -> UseIntelSyntaxForOutput,
|
||||
"input_intel_syntax" -> UseIntelSyntaxForInput,
|
||||
|
@ -175,7 +175,7 @@ object Main {
|
||||
f"${path.getFileName}%s ${start}%04X ${start}%04X ${codeLength}%04X".getBytes(StandardCharsets.UTF_8))
|
||||
}
|
||||
}
|
||||
errorReporting.debug(s"Total time: ${Math.round((System.nanoTime() - startTime)/1e6)} ms")
|
||||
errorReporting.info(s"Total time: ${Math.round((System.nanoTime() - startTime)/1e6)} ms")
|
||||
c.runFileName.foreach{ program =>
|
||||
if (File.separatorChar == '\\') {
|
||||
if (!new File(program).exists() && !new File(program + ".exe").exists()) {
|
||||
@ -293,6 +293,7 @@ object Main {
|
||||
if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.All else Nil,
|
||||
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) SixteenOptimizations.AllForEmulation else Nil,
|
||||
if (options.flag(CompilationFlag.EmitNative65816Opcodes)) SixteenOptimizations.AllForNative else Nil,
|
||||
if (options.flag(CompilationFlag.IdentityPage)) IdentityPageOptimizations.All else Nil,
|
||||
if (options.flag(CompilationFlag.DangerousOptimizations)) DangerousOptimizations.All else Nil
|
||||
).flatten
|
||||
val goodCycle = List.fill(optLevel - 2)(OptimizationPresets.Good ++ goodExtras).flatten
|
||||
@ -757,6 +758,9 @@ object Main {
|
||||
boolean("-fregister-variables", "-fno-register-variables").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.RegisterVariables, v)
|
||||
}.description("Allow moving local variables into CPU registers. Enabled by default.")
|
||||
boolean("-fidentity-page", "-fno-identity-page").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.IdentityPage, v)
|
||||
}.description("Whether should use an identity page to optimize certain operations.")
|
||||
flag("-Os", "--size").repeatable().action { c =>
|
||||
c.changeFlag(CompilationFlag.OptimizeForSize, true).
|
||||
changeFlag(CompilationFlag.OptimizeForSpeed, false).
|
||||
@ -769,6 +773,7 @@ object Main {
|
||||
}.description("Prefer faster code even if it is slightly bigger (experimental). Implies -finline.")
|
||||
flag("-Ob", "--blast-processing").repeatable().action { c =>
|
||||
c.changeFlag(CompilationFlag.OptimizeForSize, false).
|
||||
changeFlag(CompilationFlag.IdentityPage, true).
|
||||
changeFlag(CompilationFlag.OptimizeForSpeed, true).
|
||||
changeFlag(CompilationFlag.OptimizeForSonicSpeed, true)
|
||||
}.description("Prefer faster code even if it is much bigger (experimental). Implies -finline.")
|
||||
|
@ -2,7 +2,7 @@ package millfork.assembly
|
||||
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.compiler.LabelGenerator
|
||||
import millfork.env.{NormalFunction, ThingInMemory}
|
||||
import millfork.env.{Constant, NormalFunction, ThingInMemory}
|
||||
import millfork.error.Logger
|
||||
import millfork.node.NiceFunctionProperty
|
||||
|
||||
@ -12,6 +12,7 @@ import millfork.node.NiceFunctionProperty
|
||||
case class OptimizationContext(options: CompilationOptions,
|
||||
labelMap: Map[String, (String, Int)],
|
||||
zreg: Option[ThingInMemory],
|
||||
identityPage: Constant,
|
||||
niceFunctionProperties: Set[(NiceFunctionProperty, String)]) {
|
||||
@inline
|
||||
def log: Logger = options.log
|
||||
@ -25,4 +26,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 {
|
||||
|
@ -995,16 +995,10 @@ object AlwaysGoodOptimizations {
|
||||
|
||||
val ConstantFlowAnalysis = new RuleBasedAssemblyOptimization("Constant flow analysis",
|
||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||
(MatchX(0) & HasAddrMode(AbsoluteX) & SupportsAbsolute & Elidable & HasParameterWhere({
|
||||
case MemoryAddressConstant(th) => th.name == "identity$"
|
||||
case _ => false
|
||||
})) ~~> { (code, ctx) =>
|
||||
(MatchX(0) & HasAddrMode(AbsoluteX) & SupportsAbsolute & Not(HasOpcode(BIT)) & Elidable & HasIdentityPageParameter) ~~> { (code, ctx) =>
|
||||
code.map(l => l.copy(addrMode = Immediate, parameter = NumericConstant(ctx.get[Int](0), 1)))
|
||||
},
|
||||
(MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable & HasParameterWhere({
|
||||
case MemoryAddressConstant(th) => th.name == "identity$"
|
||||
case _ => false
|
||||
})) ~~> { (code, ctx) =>
|
||||
(MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable & HasIdentityPageParameter) ~~> { (code, ctx) =>
|
||||
code.map(l => l.copy(addrMode = Immediate, parameter = NumericConstant(ctx.get[Int](0), 1)))
|
||||
},
|
||||
(MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable) ~~> { (code, ctx) =>
|
||||
|
@ -1,10 +1,11 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.mos.{AssemblyLine, AssemblyLine0, OpcodeClasses}
|
||||
import millfork.assembly.mos.{AddrMode, AssemblyLine, AssemblyLine0, OpcodeClasses}
|
||||
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
|
||||
import millfork.env.NormalFunction
|
||||
import millfork.error.Logger
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.util.control.TailCalls.{TailRec, done, tailcall}
|
||||
|
||||
/**
|
||||
@ -34,20 +35,22 @@ 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)
|
||||
val addressingModesUsingY = Set(AbsoluteY, ZeroPageY, IndexedY, LongIndexedY, IndexedSY)
|
||||
val usesX = code.exists(l =>
|
||||
OpcodeClasses.ReadsXAlways(l.opcode) ||
|
||||
OpcodeClasses.ReadsYAlways(l.opcode) ||
|
||||
OpcodeClasses.ChangesX(l.opcode) ||
|
||||
OpcodeClasses.ChangesY(l.opcode) ||
|
||||
Set(AbsoluteX, AbsoluteY, ZeroPageY, ZeroPageX, IndexedX, IndexedY, LongIndexedY, IndexedSY)(l.addrMode)
|
||||
addressingModesUsingX(l.addrMode)
|
||||
)
|
||||
val usesY = code.exists(l =>
|
||||
OpcodeClasses.ReadsXAlways(l.opcode) ||
|
||||
OpcodeClasses.ReadsYAlways(l.opcode) ||
|
||||
OpcodeClasses.ChangesX(l.opcode) ||
|
||||
val usesY = code.exists(l => {
|
||||
OpcodeClasses.ReadsYAlways(l.opcode) ||
|
||||
OpcodeClasses.ChangesY(l.opcode) ||
|
||||
Set(AbsoluteX, AbsoluteY, ZeroPageY, ZeroPageX, IndexedX, IndexedY, LongIndexedY, IndexedSY)(l.addrMode)
|
||||
addressingModesUsingY(l.addrMode)
|
||||
}
|
||||
)
|
||||
if (!usesX && !usesY) {
|
||||
return code
|
||||
@ -101,21 +104,25 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
|
||||
}
|
||||
|
||||
//noinspection OptionEqualsSome
|
||||
private def canOptimize(code: List[AssemblyLine], dir: IndexDirection, loaded: Option[IndexReg]): Boolean = code match {
|
||||
@tailrec
|
||||
private def canOptimize(code: List[AssemblyLine], dir: IndexDirection, loaded: Option[IndexReg]): Boolean = {
|
||||
val notX = loaded != Some(X)
|
||||
val notY = loaded != Some(Y)
|
||||
code match {
|
||||
|
||||
case AssemblyLine0(INC | DEC | ASL | ROL | ROR | LSR | STZ | LDZ | BIT, AbsoluteX | ZeroPageX, _) :: xs if dir == X2Y => false
|
||||
case AssemblyLine0(LDY | STY, AbsoluteX | ZeroPageX, _) :: xs => false
|
||||
case AssemblyLine0(LDX | STX, AbsoluteY | ZeroPageY, _) :: xs => false
|
||||
|
||||
case AssemblyLine0(_, AbsoluteY, _) :: xs if loaded != Some(Y) => false
|
||||
case AssemblyLine0(_, ZeroPageY, _) :: xs if loaded != Some(Y) => false
|
||||
case AssemblyLine0(_, IndexedY, _) :: xs if dir == Y2X || loaded != Some(Y) => false
|
||||
case AssemblyLine0(_, LongIndexedY, _) :: xs if dir == Y2X || loaded != Some(Y) => false
|
||||
case AssemblyLine0(_, IndexedSY, _) :: xs if dir == Y2X || loaded != Some(Y) => false
|
||||
case AssemblyLine0(_, AbsoluteX, _) :: xs if loaded != Some(X) => false
|
||||
case AssemblyLine0(_, LongAbsoluteX, _) :: xs if loaded != Some(X) => false
|
||||
case AssemblyLine0(_, ZeroPageX, _) :: xs if loaded != Some(X) => false
|
||||
case AssemblyLine0(_, IndexedX, _) :: xs if dir == X2Y || loaded != Some(X) => false
|
||||
case AssemblyLine0(_, AbsoluteY, _) :: xs if notY => false
|
||||
case AssemblyLine0(_, ZeroPageY, _) :: xs if notY => false
|
||||
case AssemblyLine0(_, IndexedY, _) :: xs if dir == Y2X || notY => false
|
||||
case AssemblyLine0(_, LongIndexedY, _) :: xs if dir == Y2X || notY => false
|
||||
case AssemblyLine0(_, IndexedSY, _) :: xs if dir == Y2X || notY => false
|
||||
case AssemblyLine0(_, AbsoluteX, _) :: xs if notX => false
|
||||
case AssemblyLine0(_, LongAbsoluteX, _) :: xs if notX => false
|
||||
case AssemblyLine0(_, ZeroPageX, _) :: xs if notX => false
|
||||
case AssemblyLine0(_, IndexedX | ImmediateWithAbsoluteX | ImmediateWithZeroPageX, _) :: xs if dir == X2Y || notX => false
|
||||
case AssemblyLine0(_, AbsoluteIndexedX, _) :: xs if dir == X2Y => false
|
||||
case AssemblyLine0(SHX | SHY | AHX | TAS | LAS, _, _) :: xs => false
|
||||
case AssemblyLine(TXY, _, _, e, _) :: xs => (e == Elidability.Elidable || e == Elidability.Volatile) && loaded == Some(X) && canOptimize(xs, dir, Some(Y))
|
||||
@ -155,11 +162,12 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
|
||||
(e == Elidability.Elidable || e == Elidability.Volatile || dir == X2Y) && loaded == Some(Y) && canOptimize(xs, dir, Some(Y))
|
||||
|
||||
case AssemblyLine0(SAX | TXS | SBX, _, _) :: xs => dir == Y2X && loaded == Some(X) && canOptimize(xs, dir, Some(X))
|
||||
case AssemblyLine0(TSX, _, _) :: xs => dir == Y2X && loaded != Some(Y) && canOptimize(xs, dir, Some(X))
|
||||
case AssemblyLine0(TSX, _, _) :: xs => dir == Y2X && notY && canOptimize(xs, dir, Some(X))
|
||||
|
||||
case _ :: xs => canOptimize(xs, dir, loaded)
|
||||
|
||||
case Nil => true
|
||||
}
|
||||
}
|
||||
|
||||
private def switchX2Y(code: List[AssemblyLine])(implicit log: Logger): TailRec[List[AssemblyLine]] = code match {
|
||||
|
@ -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")
|
||||
|
@ -160,11 +160,6 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
||||
}
|
||||
|
||||
def hasSet(state: State.Value): Boolean = state match {
|
||||
case State.A => false
|
||||
case State.AH => false
|
||||
case State.X => false
|
||||
case State.Y => false
|
||||
case State.IZ => false
|
||||
case State.Z => z.contains(true)
|
||||
case State.N => n.contains(true)
|
||||
case State.C => c.contains(true)
|
||||
|
@ -20,7 +20,7 @@ object DangerousOptimizations {
|
||||
(HasOpcode(TAY) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.A)) ~
|
||||
(Linear & Not(ConcernsY)).*
|
||||
).capture(1) ~
|
||||
(Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y)) ~~> { (code, ctx) =>
|
||||
(Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y) & HasSmallParameter) ~~> { (code, ctx) =>
|
||||
val last = code.last
|
||||
ctx.get[List[AssemblyLine]](1) :+ last.copy(parameter = last.parameter.+(ctx.get[Constant](0)).quickSimplify)
|
||||
},
|
||||
@ -30,27 +30,27 @@ object DangerousOptimizations {
|
||||
(HasOpcode(TAX) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.A)) ~
|
||||
(Linear & Not(ConcernsX)).*
|
||||
).capture(1) ~
|
||||
(Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X)) ~~> { (code, ctx) =>
|
||||
(Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X) & HasSmallParameter) ~~> { (code, ctx) =>
|
||||
val last = code.last
|
||||
ctx.get[List[AssemblyLine]](1) :+ last.copy(parameter = last.parameter.+(ctx.get[Constant](0)).quickSimplify)
|
||||
},
|
||||
(Elidable & HasOpcode(INY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y)) ~~> { (code, ctx) =>
|
||||
(Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y) & HasSmallParameter) ~~> { (code, ctx) =>
|
||||
val last = code.last
|
||||
List(last.copy(parameter = last.parameter.+(1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(DEY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y)) ~~> { (code, ctx) =>
|
||||
(Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y) & HasSmallParameter) ~~> { (code, ctx) =>
|
||||
val last = code.last
|
||||
List(last.copy(parameter = last.parameter.+(-1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(INX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X)) ~~> { (code, ctx) =>
|
||||
(Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X) & HasSmallParameter) ~~> { (code, ctx) =>
|
||||
val last = code.last
|
||||
List(last.copy(parameter = last.parameter.+(1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(DEX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X)) ~~> { (code, ctx) =>
|
||||
(Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X) & HasSmallParameter) ~~> { (code, ctx) =>
|
||||
val last = code.last
|
||||
List(last.copy(parameter = last.parameter.+(-1).quickSimplify))
|
||||
},
|
||||
|
@ -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)
|
||||
|
||||
|
@ -21,12 +21,16 @@ case class FlowInfo(holder: FlowHolder, index: Int, _labelUseCountMap: () => Opt
|
||||
lazy val importanceAfter: CpuImportance = holder.importanceAfter(index)
|
||||
lazy val labelUseCountMap: Option[Map[String, Int]] = _labelUseCountMap()
|
||||
|
||||
@inline
|
||||
def hasClear(state: State.Value): Boolean = statusBefore.hasClear(state)
|
||||
|
||||
@inline
|
||||
def hasSet(state: State.Value): Boolean = statusBefore.hasSet(state)
|
||||
|
||||
@inline
|
||||
def isUnimportant(state: State.Value): Boolean = importanceAfter.isUnimportant(state)
|
||||
|
||||
@inline
|
||||
def labelUseCount(label: String): Int = labelUseCountMap.map(_.getOrElse(label, 0)).getOrElse(-1)
|
||||
|
||||
override def toString: String = holder.toString(index)
|
||||
|
@ -0,0 +1,50 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.{AssemblyLine, State}
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.env.NumericConstant
|
||||
|
||||
object IdentityPageOptimizations {
|
||||
|
||||
val SimplifiableAccess = new RuleBasedAssemblyOptimization("Simplifiable identity page access",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
(Elidable & HasOpcode(LDA) & HasAddrMode(AbsoluteX) & HasIdentityPageParameter) ~~> { c =>
|
||||
List(AssemblyLine.implied(TXA))
|
||||
},
|
||||
(Elidable & HasOpcode(LDA) & HasAddrMode(AbsoluteY) & HasIdentityPageParameter) ~~> { c =>
|
||||
List(AssemblyLine.implied(TYA))
|
||||
},
|
||||
)
|
||||
|
||||
val UseInsteadOfStack = new RuleBasedAssemblyOptimization("Use identity page instead of stack",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(Elidable & HasOpcode(PHA)) ~
|
||||
(Not(ConcernsX) & Not(ConcernsStack)).*.capture(0) ~
|
||||
(Elidable & HasOpcode(TSX) & HasAddrMode(AbsoluteX) & HasIdentityPageParameter) ~
|
||||
(Elidable & HasAddrMode(AbsoluteX) & HasParameterWhere{
|
||||
case NumericConstant(0x101, _) => true
|
||||
case _ => false
|
||||
}).capture(1) ~
|
||||
(Elidable & HasOpcode(INX)) ~
|
||||
(Elidable & HasOpcode(TXS) & DoesntMatterWhatItDoesWith(State.X)) ~~> { (code, ctx) =>
|
||||
List(AssemblyLine.implied(TAX)) ++ ctx.get[List[AssemblyLine]](0) ++ List(ctx.get[AssemblyLine](1).copy(parameter = ctx.identityPage))
|
||||
},
|
||||
(Elidable & HasOpcode(PHA)) ~
|
||||
(Not(ConcernsY) & Not(ConcernsStack)).*.capture(0) ~
|
||||
(Elidable & HasOpcode(TSX) & HasAddrMode(AbsoluteX) & HasIdentityPageParameter) ~
|
||||
(Elidable & HasAddrMode(AbsoluteX) & HasParameterWhere{
|
||||
case NumericConstant(0x101, _) => true
|
||||
case _ => false
|
||||
}).capture(1) ~
|
||||
(Elidable & HasOpcode(INX)) ~
|
||||
(Elidable & HasOpcode(TXS) & DoesntMatterWhatItDoesWith(State.Y)) ~~> { (code, ctx) =>
|
||||
List(AssemblyLine.implied(TAY)) ++ ctx.get[List[AssemblyLine]](0) ++ List(ctx.get[AssemblyLine](1).copy(parameter = ctx.identityPage, addrMode = AbsoluteY))
|
||||
},
|
||||
)
|
||||
|
||||
val All: List[RuleBasedAssemblyOptimization] = List(
|
||||
SimplifiableAccess,
|
||||
UseInsteadOfStack)
|
||||
|
||||
}
|
@ -145,8 +145,8 @@ object LaterOptimizations {
|
||||
//noinspection ZeroIndexToHead
|
||||
private def InterleavedLoads(load: Opcode.Value, store: Opcode.Value) = {
|
||||
(Elidable & HasOpcode(load) & MatchAddrMode(0) & MatchParameter(1)).capture(12) ~
|
||||
(Elidable & HasOpcode(store)).+.capture(10) ~
|
||||
(Elidable & HasOpcode(load) & MatchAddrMode(2) & MatchParameter(3) & DoesNotConcernMemoryAt(0, 1)).capture(13) ~
|
||||
(Elidable & HasOpcode(store) & MatchAddrMode(20) & MatchParameter(21)).+.capture(10) ~
|
||||
(Elidable & HasOpcode(load) & MatchAddrMode(2) & MatchParameter(3) & DoesNotConcernMemoryAt(0, 1) & DoesNotConcernMemoryAt(20, 21)).capture(13) ~
|
||||
(Elidable & HasOpcode(store) & DoesNotConcernMemoryAt(0, 1) & DoesNotConcernMemoryAt(2, 3)).+.capture(11) ~
|
||||
(Elidable & HasOpcode(load) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
WhereNoMemoryAccessOverlapBetweenTwoLineLists(10, 11) ~~> { (_, ctx) =>
|
||||
|
@ -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(_)
|
||||
)
|
||||
@ -111,6 +121,7 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
|
||||
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
|
||||
@ -138,7 +149,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 +263,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 +291,8 @@ trait AssemblyPattern {
|
||||
|
||||
def captureLength(i: Int) = CaptureLength(i, this)
|
||||
|
||||
def minimumRequiredLines: Int
|
||||
|
||||
}
|
||||
object HelperCheckers {
|
||||
import AddrMode._
|
||||
@ -374,6 +401,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 +415,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 +426,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 +450,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 +477,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 +513,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 +537,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 +566,8 @@ trait AssemblyLinePattern extends AssemblyPattern {
|
||||
else Both(x, this)
|
||||
|
||||
def hitRate: Double
|
||||
|
||||
override def minimumRequiredLines: Int = 1
|
||||
}
|
||||
|
||||
//noinspection ScalaUnnecessaryParentheses
|
||||
@ -538,6 +581,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 +591,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 +913,8 @@ case object DebugMatching extends AssemblyPattern {
|
||||
code.foreach(println)
|
||||
Some(code)
|
||||
}
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case object Linear extends AssemblyLinePattern {
|
||||
@ -1466,6 +1515,27 @@ case class HasParameterWhere(predicate: Constant => Boolean) extends TrivialAsse
|
||||
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))
|
||||
@ -1608,6 +1678,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
|
||||
|
@ -1,6 +1,6 @@
|
||||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.mos.{AssemblyLine, AssemblyLine0, OpcodeClasses, State}
|
||||
import millfork.assembly.mos.{AddrMode, AssemblyLine, AssemblyLine0, OpcodeClasses, State}
|
||||
import millfork.env._
|
||||
import millfork.error.ConsoleLogger
|
||||
import millfork.node.NiceFunctionProperty
|
||||
@ -12,10 +12,10 @@ object VariableLifetime {
|
||||
|
||||
// This only works for non-stack variables.
|
||||
def apply(variableName: String, code: List[AssemblyLine], expandToIncludeIndexing: Boolean = false, expandToIncludeUsesOfLoadedIndices: Option[Set[(NiceFunctionProperty, String)]] = None): Range = {
|
||||
val flags = code.map(_.parameter match {
|
||||
val flags = code.map(line => line.parameter match {
|
||||
case MemoryAddressConstant(MemoryVariable(n, _, _)) if n == variableName => true
|
||||
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(MemoryVariable(n, _, _)), NumericConstant(_, 1)) if n == variableName => true
|
||||
case _ => false
|
||||
case p => line.addrMode == AddrMode.IndexedX && p.refersTo(variableName)
|
||||
})
|
||||
if (flags.forall(!_)) return Range(0, 0)
|
||||
var min = flags.indexOf(true)
|
||||
|
@ -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 {
|
||||
@ -28,14 +30,14 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
}
|
||||
|
||||
case class FeaturesForIndexRegisters(
|
||||
blastProcessing: Boolean,
|
||||
izIsAlwaysZero: Boolean,
|
||||
indexRegisterTransfers: Boolean,
|
||||
functionsSafeForX: Set[String],
|
||||
functionsSafeForY: Set[String],
|
||||
functionsSafeForZ: Set[String],
|
||||
identityArray: Constant,
|
||||
log: Logger)
|
||||
useIdentityPage: Boolean,
|
||||
izIsAlwaysZero: Boolean,
|
||||
indexRegisterTransfers: Boolean,
|
||||
functionsSafeForX: Set[String],
|
||||
functionsSafeForY: Set[String],
|
||||
functionsSafeForZ: Set[String],
|
||||
identityArray: Constant,
|
||||
log: Logger)
|
||||
|
||||
case class FeaturesForAccumulator(
|
||||
cmos: Boolean,
|
||||
@ -176,11 +178,11 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
val removeVariablesForReal = !options.flag(CompilationFlag.InternalCurrentlyOptimizingForMeasurement)
|
||||
val costFunction: CyclesAndBytes => Int = if (options.flag(CompilationFlag.OptimizeForSpeed)) _.cycles else _.bytes
|
||||
val importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext)
|
||||
val blastProcessing = options.flag(CompilationFlag.OptimizeForSonicSpeed)
|
||||
val identityArray = f.environment.maybeGet[ThingInMemory]("identity$").map(MemoryAddressConstant).getOrElse(Constant.Zero)
|
||||
val useIdentityPage = options.flag(CompilationFlag.IdentityPage)
|
||||
val identityArray = f.environment.identityPage
|
||||
val izIsAlwaysZero = !options.flag(CompilationFlag.Emit65CE02Opcodes)
|
||||
val featuresForIndices = FeaturesForIndexRegisters(
|
||||
blastProcessing = blastProcessing,
|
||||
useIdentityPage = useIdentityPage,
|
||||
izIsAlwaysZero = izIsAlwaysZero,
|
||||
indexRegisterTransfers = options.flag(CompilationFlag.EmitEmulation65816Opcodes),
|
||||
functionsSafeForX = optimizationContext.niceFunctionProperties.filter(x => x._1 == MosNiceFunctionProperty.DoesntChangeX).map(_._2),
|
||||
@ -383,14 +385,26 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
val vx = xCandidate.getOrElse("-")
|
||||
val vy = yCandidate.getOrElse("-")
|
||||
val vz = zCandidate.getOrElse("-")
|
||||
val accessingX = lines match {
|
||||
case (AssemblyLine0(_, _, MemoryAddressConstant(th)), _) :: _ => th.name == vx
|
||||
case _ => false
|
||||
}
|
||||
val accessingY = lines match {
|
||||
case (AssemblyLine0(_, _, MemoryAddressConstant(th)), _) :: _ => th.name == vy
|
||||
case _ => false
|
||||
}
|
||||
val accessingZ = lines match {
|
||||
case (AssemblyLine0(_, _, MemoryAddressConstant(th)), _) :: _ => th.name == vz
|
||||
case _ => false
|
||||
}
|
||||
lines match {
|
||||
case (AssemblyLine0(_, Immediate, MemoryAddressConstant(th)), _) :: xs
|
||||
if th.name == vx || th.name == vy || th.name == vz =>
|
||||
if accessingX || accessingY || accessingZ =>
|
||||
// if an address of a variable is used, then that variable cannot be assigned to a register
|
||||
None
|
||||
|
||||
case (AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)), _) :: xs
|
||||
if th.name == vx || th.name == vy || th.name == vz =>
|
||||
if accessingX || accessingY || accessingZ =>
|
||||
// if an address of a variable is used, then that variable cannot be assigned to a register
|
||||
None
|
||||
|
||||
@ -410,7 +424,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
Indirect | LongIndirect |
|
||||
AbsoluteIndexedX, MemoryAddressConstant(th)), _) :: xs =>
|
||||
// if a variable is used as an array or a pointer, then it cannot be assigned to a register
|
||||
if (th.name == vx || th.name == vy || th.name == vz) {
|
||||
if (accessingX || accessingY || accessingZ) {
|
||||
None
|
||||
} else {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
@ -422,56 +436,56 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
|
||||
case (AssemblyLine0(SEP | REP, _, _), _) :: xs => None
|
||||
|
||||
case (AssemblyLine0(STY | LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if th.name == vx && (features.indexRegisterTransfers) =>
|
||||
case (AssemblyLine0(STY | LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if accessingX && (features.indexRegisterTransfers) =>
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
|
||||
case (AssemblyLine0(STX | LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if th.name == vy && (features.indexRegisterTransfers) =>
|
||||
case (AssemblyLine0(STX | LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if accessingY && (features.indexRegisterTransfers) =>
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
|
||||
case (AssemblyLine0(LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteY, _), f) :: xs if th.name == vx && f.y == Unimportant =>
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteY, _), f) :: xs if accessingX && f.y == Unimportant =>
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
|
||||
case (AssemblyLine0(LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteX, _), f) :: xs if th.name == vy && f.x == Unimportant =>
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteX, _), f) :: xs if accessingY && f.x == Unimportant =>
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
|
||||
case (AssemblyLine0(LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(AssemblyLine0(SEC | CLC, _, _), _) ::
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteY, _), f) :: xs if th.name == vx && f.y == Unimportant =>
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteY, _), f) :: xs if accessingX && f.y == Unimportant =>
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
|
||||
case (AssemblyLine0(LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(AssemblyLine0(SEC | CLC, _, _), _) ::
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteX, _), f) :: xs if th.name == vy && f.x == Unimportant =>
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteX, _), f) :: xs if accessingY && f.x == Unimportant =>
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
|
||||
case (AssemblyLine0(STY | LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if th.name == vx => None
|
||||
case (AssemblyLine0(STY | LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if accessingX => None
|
||||
|
||||
case (AssemblyLine0(STX | LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if th.name == vy => None
|
||||
case (AssemblyLine0(STX | LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if accessingY => None
|
||||
|
||||
case (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs
|
||||
if opcodesIdentityTable(op) && features.blastProcessing =>
|
||||
if (th.name == vx || th.name == vy) {
|
||||
if opcodesIdentityTable(op) && features.useIdentityPage =>
|
||||
if (accessingX || accessingY) {
|
||||
if (elidability == Elidability.Elidable) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 0, cycles = -1))
|
||||
else None
|
||||
} else {
|
||||
if (th.name == vz) None
|
||||
if (accessingZ) None
|
||||
else canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
}
|
||||
|
||||
case (AssemblyLine0(opcode, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
|
||||
if th.name == vx && (opcode == LDY || opcode == LDZ || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
if accessingX && (opcode == LDY || opcode == LDZ || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
// if a variable is used by some opcodes, then it cannot be assigned to a register
|
||||
None
|
||||
|
||||
case (AssemblyLine0(opcode, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
|
||||
if th.name == vy && (opcode == LDX || opcode == LAX || opcode == LDZ || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
if accessingY && (opcode == LDX || opcode == LAX || opcode == LDZ || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
// if a variable is used by some opcodes, then it cannot be assigned to a register
|
||||
None
|
||||
|
||||
case (AssemblyLine0(opcode, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
|
||||
if th.name == vz && (opcode == LDX || opcode == LDY || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
if accessingZ && (opcode == LDX || opcode == LDY || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
// if a variable is used by some opcodes, then it cannot be assigned to a register
|
||||
None
|
||||
|
||||
@ -479,7 +493,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
if xCandidate.isDefined =>
|
||||
// if a register is populated with a different variable, then this variable cannot be assigned to that register
|
||||
// removing LDX saves 3 cycles
|
||||
if (elidability == Elidability.Elidable && th.name == vx) {
|
||||
if (elidability == Elidability.Elidable && accessingX) {
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
} else {
|
||||
@ -493,7 +507,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
if xCandidate.isDefined =>
|
||||
// LAX = LDX-LDA, and since LDX simplifies to nothing and LDA simplifies to TXA,
|
||||
// LAX simplifies to TXA, saving two bytes
|
||||
if (elidability == Elidability.Elidable && th.name == vx) {
|
||||
if (elidability == Elidability.Elidable && accessingX) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
} else {
|
||||
None
|
||||
@ -503,7 +517,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
// if a register is populated with a different variable, then this variable cannot be assigned to that register
|
||||
// removing LDX saves 3 bytes
|
||||
// sometimes that LDX has to be converted into CPX#0
|
||||
if (elidability == Elidability.Elidable && th.name == vy) {
|
||||
if (elidability == Elidability.Elidable && accessingY) {
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
} else {
|
||||
@ -514,7 +528,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
}
|
||||
|
||||
case (AssemblyLine(LDZ, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), imp) :: xs if zCandidate.isDefined =>
|
||||
if (elidability == Elidability.Elidable && th.name == vz) {
|
||||
if (elidability == Elidability.Elidable && accessingZ) {
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
} else {
|
||||
@ -553,7 +567,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
else None
|
||||
} else {
|
||||
if (th.name == vz) None
|
||||
if (accessingZ) None
|
||||
else canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
}
|
||||
|
||||
@ -561,7 +575,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
if xCandidate.isDefined =>
|
||||
// a variable cannot be inlined if there is TAX not after LDA of that variable
|
||||
// but LDA-TAX can be simplified to TXA
|
||||
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && th.name == vx) {
|
||||
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && accessingX) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
} else {
|
||||
None
|
||||
@ -571,7 +585,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
if yCandidate.isDefined =>
|
||||
// a variable cannot be inlined if there is TAY not after LDA of that variable
|
||||
// but LDA-TAY can be simplified to TYA
|
||||
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && th.name == vy) {
|
||||
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && accessingY) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
} else {
|
||||
None
|
||||
@ -581,7 +595,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
if zCandidate.isDefined =>
|
||||
// a variable cannot be inlined if there is TAZ not after LDA of that variable
|
||||
// but LDA-TAZ can be simplified to TZA
|
||||
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && th.name == vy) {
|
||||
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && accessingY) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
} else {
|
||||
None
|
||||
@ -589,7 +603,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
|
||||
case (AssemblyLine(LDA | STA, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs =>
|
||||
// changing LDA->TXA, STA->TAX, INC->INX, DEC->DEX saves 2 bytes
|
||||
if (th.name == vy || th.name == vx || th.name == vz) {
|
||||
if (accessingY || accessingX || accessingZ) {
|
||||
if (elidability == Elidability.Elidable) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
} else {
|
||||
@ -601,7 +615,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
|
||||
case (AssemblyLine(INC | DEC, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs =>
|
||||
// changing LDA->TXA, STA->TAX, INC->INX, DEC->DEX saves 2 bytes
|
||||
if (th.name == vy || th.name == vx || th.name == vz) {
|
||||
if (accessingY || accessingX || accessingZ) {
|
||||
if (elidability == Elidability.Elidable) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 4))
|
||||
} else {
|
||||
@ -613,7 +627,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
|
||||
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs =>
|
||||
// changing STZ->LDX saves 1 byte
|
||||
if (th.name == vy || th.name == vx) {
|
||||
if (accessingY || accessingX) {
|
||||
if (elidability == Elidability.Elidable && features.izIsAlwaysZero) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 1, cycles = 2))
|
||||
} else {
|
||||
@ -888,37 +902,53 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
val vy = yCandidate.getOrElse("-")
|
||||
val vz = zCandidate.getOrElse("-")
|
||||
val va = aCandidate.getOrElse("-")
|
||||
val accessingX = lines match {
|
||||
case (AssemblyLine0(_, _, MemoryAddressConstant(th)), _) :: _ => th.name == vx
|
||||
case _ => false
|
||||
}
|
||||
val accessingY = lines match {
|
||||
case (AssemblyLine0(_, _, MemoryAddressConstant(th)), _) :: _ => th.name == vy
|
||||
case _ => false
|
||||
}
|
||||
val accessingZ = lines match {
|
||||
case (AssemblyLine0(_, _, MemoryAddressConstant(th)), _) :: _ => th.name == vz
|
||||
case _ => false
|
||||
}
|
||||
val accessingA = lines match {
|
||||
case (AssemblyLine0(_, _, MemoryAddressConstant(th)), _) :: _ => th.name == va
|
||||
case _ => false
|
||||
}
|
||||
lines match {
|
||||
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map( AssemblyLine.implied(INX).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(INY).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vz =>
|
||||
if accessingZ =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(INZ).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(DEX).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(DEY).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vz =>
|
||||
if accessingZ =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(DEZ).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(opcode@(DEC | INC | ROL | ROR | ASL | LSR), Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(opcode).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), imp) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs))
|
||||
} else {
|
||||
@ -926,22 +956,22 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
}
|
||||
|
||||
case (AssemblyLine(LAX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s) :: _)
|
||||
|
||||
case (l@AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
|
||||
if opcodesIdentityTable(op) && th.name == vx =>
|
||||
if opcodesIdentityTable(op) && accessingX =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l.copy(addrMode = AbsoluteX, parameter = features.identityArray) :: _)
|
||||
|
||||
case (l@AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
|
||||
if opcodesIdentityTable(op) && th.name == vy =>
|
||||
if opcodesIdentityTable(op) && accessingY =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l.copy(addrMode = AbsoluteY, parameter = features.identityArray) :: _)
|
||||
|
||||
case (l@AssemblyLine0(LDA | TYA | TXA | TZA | CLA, _, _), _) :: xs if aCandidate.isDefined && isReturn(xs) =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l :: _)
|
||||
|
||||
case (l@AssemblyLine0(LDA, _, _), _) :: (AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
|
||||
if opcodesCommutative(op) && th.name == va =>
|
||||
if opcodesCommutative(op) && accessingA =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l.copy(opcode = op) :: _)
|
||||
|
||||
case (l@AssemblyLine0(LDA, _, _), _) :: (clc@AssemblyLine0(CLC, _, _), _) :: (AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
|
||||
@ -965,7 +995,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s) :: clc :: l.copy(opcode = op) :: _)
|
||||
|
||||
case (AssemblyLine(LDA | STA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), imp) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
} else {
|
||||
@ -973,11 +1003,11 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
}
|
||||
|
||||
case (AssemblyLine(LAX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), imp) :: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
} else {
|
||||
@ -985,7 +1015,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
}
|
||||
|
||||
case (AssemblyLine(LDZ, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), imp) :: xs
|
||||
if th.name == vz =>
|
||||
if accessingZ =>
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
} else {
|
||||
@ -993,64 +1023,64 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
}
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s), _) :: (AssemblyLine(TAX, _, _, Elidability.Elidable, _), _) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
// these TXA's may get optimized away by a different optimization
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s), _) :: (AssemblyLine(TAY, _, _, Elidability.Elidable, _), _) :: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
// these TYA's may get optimized away by a different optimization
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s), _) :: (AssemblyLine(TAZ, _, _, Elidability.Elidable, _), _) :: xs
|
||||
if th.name == vz =>
|
||||
if accessingZ =>
|
||||
// these TZA's may get optimized away by a different optimization
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s), _) :: (AssemblyLine(TXA, _, _, Elidability.Elidable, _), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
// these TAX's may get optimized away by a different optimization
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, _), _) :: (AssemblyLine(TYA, _, _, Elidability.Elidable, _), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
// these TAY's may get optimized away by a different optimization
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAY) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s1), _) :: (AssemblyLine(CMP, am, param, Elidability.Elidable, s2), _) :: xs
|
||||
if th.name == vx && CpxyzAddrModes(am) && isNot(vx, param) =>
|
||||
if accessingX && CpxyzAddrModes(am) && isNot(vx, param) =>
|
||||
// ditto
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s1) :: AssemblyLine(CPX, am, param).pos(s2) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s1), _) :: (AssemblyLine(CMP, am, param, Elidability.Elidable, s2), _) :: xs
|
||||
if th.name == vy && CpxyzAddrModes(am) && isNot(vx, param) =>
|
||||
if accessingY && CpxyzAddrModes(am) && isNot(vx, param) =>
|
||||
// ditto
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s1) :: AssemblyLine(CPY, am, param).pos(s2) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s1), _) :: (AssemblyLine(CMP, am, param, Elidability.Elidable, s2), _) :: xs
|
||||
if th.name == vy && CpxyzAddrModes(am) && isNot(vx, param) =>
|
||||
if accessingY && CpxyzAddrModes(am) && isNot(vx, param) =>
|
||||
// ditto
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA).pos(s1) :: AssemblyLine(CPZ, am, param).pos(s2) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx && features.indexRegisterTransfers =>
|
||||
if accessingX && features.indexRegisterTransfers =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXY).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy && features.indexRegisterTransfers =>
|
||||
if accessingY && features.indexRegisterTransfers =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYX).pos(s) :: _)
|
||||
|
||||
case (l0@AssemblyLine0(LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(l1@AssemblyLine0(LDA | STA | ADC | SBC | AND | ORA | EOR | CMP, AbsoluteY, _), f):: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
if (l1.opcode != STA || f.n == Unimportant && f.z == Unimportant) {
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l1.copy(addrMode = AbsoluteX) :: _)
|
||||
} else if (f.c == Unimportant) {
|
||||
@ -1062,7 +1092,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
|
||||
case (l0@AssemblyLine0(LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(l1@AssemblyLine0(LDA | STA | ADC | SBC | AND | ORA | EOR | CMP, AbsoluteX, _), f):: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
if (l1.opcode != STA || f.n == Unimportant && f.z == Unimportant) {
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l1.copy(addrMode = AbsoluteY) :: _)
|
||||
} else if (f.c == Unimportant) {
|
||||
@ -1074,31 +1104,31 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
case (l0@AssemblyLine0(LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(l5@AssemblyLine0(SEC | CLC, _, _), _) ::
|
||||
(l1@AssemblyLine0(LDA | STA | ADC | SBC | AND | ORA | EOR | CMP, AbsoluteY, _), _):: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l5 :: l1.copy(addrMode = AbsoluteX) :: _)
|
||||
|
||||
case (l0@AssemblyLine0(LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(l5@AssemblyLine0(SEC | CLC, _, _), _) ::
|
||||
(l1@AssemblyLine0(LDA | STA | ADC | SBC | AND | ORA | EOR | CMP, AbsoluteX, _), _):: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l5 :: l1.copy(addrMode = AbsoluteY) :: _)
|
||||
|
||||
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx => features.log.fatal("Unexpected LDY")
|
||||
if accessingX => features.log.fatal("Unexpected LDY")
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy => features.log.fatal("Unexpected LDX")
|
||||
if accessingY => features.log.fatal("Unexpected LDX")
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vz =>
|
||||
if accessingZ =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAY).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, am, param, Elidability.Elidable, s1), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s2), _) :: xs
|
||||
@ -1117,51 +1147,51 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine(LDZ, am, param).pos(s1, s2) :: AssemblyLine.implied(TZA).pos(s1, s2) :: _)
|
||||
|
||||
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAY).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vz =>
|
||||
if accessingZ =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAZ).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(STX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(STY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(STX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy && features.indexRegisterTransfers =>
|
||||
if accessingY && features.indexRegisterTransfers =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXY).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(STY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx && features.indexRegisterTransfers =>
|
||||
if accessingX && features.indexRegisterTransfers =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYX).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(STX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy => features.log.fatal("Unexpected STX")
|
||||
if accessingY => features.log.fatal("Unexpected STX")
|
||||
|
||||
case (AssemblyLine(STY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx => features.log.fatal("Unexpected STY")
|
||||
if accessingX => features.log.fatal("Unexpected STY")
|
||||
|
||||
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
if (features.izIsAlwaysZero) tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(LDX, 0).pos(s) :: _)
|
||||
else features.log.fatal("Unexpected STZ")
|
||||
|
||||
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
if (features.izIsAlwaysZero) tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(LDY, 0).pos(s) :: _)
|
||||
else features.log.fatal("Unexpected STZ")
|
||||
|
||||
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
if (features.izIsAlwaysZero) tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(LDA, 0).pos(s) :: _)
|
||||
else tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA).pos(s) :: _)
|
||||
|
||||
|
@ -953,7 +953,7 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
||||
if (result.contains("???")) s" ??? (${this.toString.stripPrefix(" ")})" else result
|
||||
}
|
||||
|
||||
def readsRegister(r: ZRegister.Value): Boolean = {
|
||||
def readsRegister(r: ZRegister.Value): Boolean = { // TODO: optimize
|
||||
import ZOpcode._
|
||||
import ZRegister._
|
||||
r match {
|
||||
|
@ -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)
|
||||
@ -172,6 +174,11 @@ class ChangeRegisterPair(preferBC2DE: Boolean) extends AssemblyOptimization[ZLin
|
||||
case (x@ZLine0(_, TwoRegisters(r, BC), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(r, DE)) :: switchBC2DE(xs)
|
||||
|
||||
case (x@ZLine0(_, TwoRegisters(MEM_BC, r), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(MEM_DE, r)) :: switchBC2DE(xs)
|
||||
case (x@ZLine0(_, TwoRegisters(r, MEM_BC), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(r, MEM_DE)) :: switchBC2DE(xs)
|
||||
|
||||
case (x@ZLine0(_, TwoRegisters(B, r), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(D, r)) :: switchBC2DE(xs)
|
||||
case (x@ZLine0(_, TwoRegisters(r, B), _)) :: xs =>
|
||||
@ -220,6 +227,11 @@ class ChangeRegisterPair(preferBC2DE: Boolean) extends AssemblyOptimization[ZLin
|
||||
case (x@ZLine0(_, TwoRegisters(r, DE), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(r, BC)) :: switchDE2BC(xs)
|
||||
|
||||
case (x@ZLine0(_, TwoRegisters(MEM_DE, r), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(MEM_BC, r)) :: switchDE2BC(xs)
|
||||
case (x@ZLine0(_, TwoRegisters(r, MEM_DE), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(r, MEM_BC)) :: switchDE2BC(xs)
|
||||
|
||||
case (x@ZLine0(_, TwoRegisters(D, r), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(B, r)) :: switchDE2BC(xs)
|
||||
case (x@ZLine0(_, TwoRegisters(r, D), _)) :: xs =>
|
||||
|
@ -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)]) =>
|
||||
@ -245,11 +255,11 @@ object HelperCheckers {
|
||||
case LD | LD_16 | ADD_16 | ADC_16 | SBC_16 => l.registers match {
|
||||
case TwoRegisters(MEM_HL | MEM_IX_D | MEM_IY_D | MEM_BC | MEM_DE, _) => true
|
||||
case TwoRegisters(_, MEM_HL | MEM_IX_D | MEM_IY_D | MEM_BC | MEM_DE) => true
|
||||
case TwoRegisters(_, _) => false
|
||||
case _ => false
|
||||
}
|
||||
case ADD | SUB | SBC | ADC | XOR | CP | OR | AND => l.registers match {
|
||||
case OneRegister(MEM_HL | MEM_IX_D | MEM_IY_D | MEM_BC | MEM_DE) => true
|
||||
case OneRegister(_) => false
|
||||
case _ => false
|
||||
}
|
||||
case CHANGED_MEM => true
|
||||
case POP | PUSH => false
|
||||
@ -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)
|
||||
}
|
||||
@ -565,7 +567,7 @@ object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
|
||||
} else if (de != "") {
|
||||
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.register(PUSH, DE).pos(s) :: x :: ZLine.register(POP, DE).pos(s) :: _)
|
||||
} else {
|
||||
throw new IllegalStateException()
|
||||
tailcall(inlineVars(hl, bc, de, xs)).map(x :: _)
|
||||
}
|
||||
|
||||
|
||||
|
@ -74,11 +74,17 @@ object BuiltIns {
|
||||
case FunctionCallExpression(name, List(param)) if env.maybeGet[Type](name).isDefined =>
|
||||
return simpleOperation(opcode, ctx, param, indexChoice, preserveA, commutative, decimal)
|
||||
case _: FunctionCallExpression | _: SumExpression if commutative =>
|
||||
val code = MosExpressionCompiler.compileToA(ctx, source)
|
||||
if (ctx.options.flags(CompilationFlag.IdentityPage)
|
||||
&& !code.exists(_.concernsX)
|
||||
&& AssemblyLine.treatment(code, State.X) == Treatment.Unchanged) {
|
||||
return List(AssemblyLine.implied(TAX)) ++ code ++ wrapInSedCldIfNeeded(decimal, List(AssemblyLine.absoluteX(opcode, env.identityPage)))
|
||||
}
|
||||
// TODO: is it ok?
|
||||
if (ctx.options.zpRegisterSize >= 1) {
|
||||
val reg = ctx.env.get[ThingInMemory]("__reg")
|
||||
return List(AssemblyLine.implied(PHA)) ++
|
||||
MosExpressionCompiler.compileToA(ctx, source) ++
|
||||
MosExpressionCompiler.fixTsx(code) ++
|
||||
List(AssemblyLine.zeropage(STA, reg), AssemblyLine.implied(PLA)) ++
|
||||
wrapInSedCldIfNeeded(decimal, List(AssemblyLine.zeropage(opcode, reg)))
|
||||
} else if (ctx.options.flag(CompilationFlag.EmitEmulation65816Opcodes)) {
|
||||
|
@ -72,6 +72,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
case 2 => if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
AssemblyLine.accu16 :: AssemblyLine(LDA_W, WordImmediate, expr) :: (AssemblyLine.variable(ctx, STA_W, m) :+ AssemblyLine.accu8)
|
||||
} else AssemblyLine.tsx(ctx) ++ List(
|
||||
// TODO: ???
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.immediate(LDA, expr.loByte),
|
||||
AssemblyLine.dataStackX(ctx, STA, offset),
|
||||
@ -155,10 +156,10 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
}
|
||||
|
||||
val cmos = ctx.options.flag(CompilationFlag.EmitCmosOpcodes)
|
||||
if (register == MosRegister.AX && !code.exists(_.concernsX)) {
|
||||
if (register == MosRegister.AX && !code.exists(_.concernsX) && code.forall(_.treatment(State.X).==(Treatment.Unchanged))) {
|
||||
return preserveRegisterIfNeeded(ctx, MosRegister.A, code)
|
||||
}
|
||||
if (register == MosRegister.AY && !code.exists(_.concernsY)) {
|
||||
if (register == MosRegister.AY && !code.exists(_.concernsY) && code.forall(_.treatment(State.Y).==(Treatment.Unchanged))) {
|
||||
return preserveRegisterIfNeeded(ctx, MosRegister.A, code)
|
||||
}
|
||||
if (states.exists(state => AssemblyLine.treatment(code, state) != Treatment.Unchanged)) {
|
||||
@ -672,7 +673,11 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
}
|
||||
case RegisterVariable(MosRegister.Y, _) => actualOffset match {
|
||||
case 0 => List(tsx)
|
||||
case 1 => List(tsx, AssemblyLine.implied(TXA), AssemblyLine.implied(TAY), AssemblyLine.implied(INY))
|
||||
case 1 =>
|
||||
if (ctx.options.flag(CompilationFlag.IdentityPage))
|
||||
List(tsx, AssemblyLine.absoluteX(LDY, ctx.env.identityPage), AssemblyLine.implied(INY))
|
||||
else
|
||||
List(tsx, AssemblyLine.implied(TXA), AssemblyLine.implied(TAY), AssemblyLine.implied(INY))
|
||||
case _ => List(tsx, AssemblyLine.implied(TXA), AssemblyLine.implied(CLC), AssemblyLine.immediate(ADC, actualOffset), AssemblyLine.implied(TAY))
|
||||
}
|
||||
case _ =>
|
||||
|
@ -241,7 +241,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
val registers = (reg, offset) match {
|
||||
case (OneRegister(r), Some(o)) => env.evalForAsm(expression) match {
|
||||
case (OneRegister(r), Some(o)) => env.evalForAsm(o) match {
|
||||
case Some(NumericConstant(v, _)) => OneRegisterOffset(r, v.toInt)
|
||||
case Some(_) =>
|
||||
ctx.log.error("Non-numeric constant", o.position)
|
||||
@ -250,7 +250,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
||||
ctx.log.error("Inlining failed due to non-constant things", o.position)
|
||||
reg
|
||||
}
|
||||
case (TwoRegisters(t, s), Some(o)) => env.evalForAsm(expression) match {
|
||||
case (TwoRegisters(t, s), Some(o)) => env.evalForAsm(o) match {
|
||||
case Some(NumericConstant(v, _)) => TwoRegistersOffset(t, s, v.toInt)
|
||||
case Some(_) =>
|
||||
ctx.log.error("Non-numeric constant", o.position)
|
||||
|
15
src/main/scala/millfork/env/Environment.scala
vendored
15
src/main/scala/millfork/env/Environment.scala
vendored
@ -295,18 +295,20 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
|
||||
private def removeVariableImpl(str: String): Unit = {
|
||||
def extractThingName(fullName: String): String = {
|
||||
var result = fullName.takeWhile(_ != '.')
|
||||
if (result.length == fullName.length) return result
|
||||
val suffix = fullName.drop(result.length)
|
||||
val ix = fullName.indexOf('.')
|
||||
if (ix < 0) return fullName
|
||||
var result = fullName.substring(0, ix)
|
||||
val suffix = fullName.substring(ix)
|
||||
if (suffix == ".return" || suffix.startsWith(".return.")) {
|
||||
result += ".return"
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
val strWithoutPrefix = str.stripPrefix(prefix)
|
||||
val toRemove = things.keys.filter { n =>
|
||||
val baseName = extractThingName(n)
|
||||
baseName == str || baseName == str.stripPrefix(prefix)
|
||||
baseName == str || baseName == strWithoutPrefix
|
||||
}.toSet
|
||||
removedThings ++= toRemove.map(_.stripPrefix(prefix))
|
||||
things --= toRemove
|
||||
@ -393,6 +395,9 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
}
|
||||
}
|
||||
|
||||
@inline
|
||||
final def identityPage: Constant = maybeGet[MfArray]("identity$").fold(Constant.Zero)(_.toAddress)
|
||||
|
||||
def getPointy(name: String): Pointy = {
|
||||
InitializedMemoryVariable
|
||||
UninitializedMemoryVariable
|
||||
@ -2618,7 +2623,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
def collectDeclarations(program: Program, options: CompilationOptions): Unit = {
|
||||
val b = get[VariableType]("byte")
|
||||
val v = get[Type]("void")
|
||||
if (options.flag(CompilationFlag.OptimizeForSonicSpeed)) {
|
||||
if (options.flag(CompilationFlag.IdentityPage)) {
|
||||
addThing(InitializedArray("identity$", None, IndexedSeq.tabulate(256)(n => LiteralExpression(n, 1)), declaredBank = None, b, b, readOnly = true, Set.empty, defaultArrayAlignment(options, 256)), None)
|
||||
}
|
||||
program.declarations.foreach {
|
||||
|
@ -240,8 +240,8 @@ object MosNiceFunctionProperty {
|
||||
case object DoesntConcernD extends NiceFunctionProperty("D")
|
||||
case object DoesntChangeZpRegister extends NiceFunctionProperty("reg")
|
||||
case class SetsSourceOfNZ(sourceOfNZ: SourceOfNZ) extends NiceFunctionProperty(sourceOfNZ + "NZ")
|
||||
case class SetsXTo(value: Int) extends NiceFunctionProperty("Y=" + value)
|
||||
case class SetsYTo(value: Int) extends NiceFunctionProperty("Z=" + value)
|
||||
case class SetsXTo(value: Int) extends NiceFunctionProperty("X=" + value)
|
||||
case class SetsYTo(value: Int) extends NiceFunctionProperty("Y=" + value)
|
||||
case class SetsATo(value: Int) extends NiceFunctionProperty("A=" + value)
|
||||
case class Bit0OfA(value: Boolean) extends NiceFunctionProperty("A0=" + value)
|
||||
case class Bit7OfA(value: Boolean) extends NiceFunctionProperty("A7=" + value)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ class MemoryBank(val index: Int, val isBigEndian: Boolean) {
|
||||
|
||||
def readWord(addrHi: Int, addrLo: Int): Int = readByte(addrLo) + (readByte(addrHi) << 8)
|
||||
|
||||
// TODO: use new:
|
||||
val output: Array[Byte] = Array.fill[Byte](1 << 16)(0)
|
||||
val occupied: Array[Boolean] = Array.fill(1 << 16)(false)
|
||||
val initialized: Array[Boolean] = Array.fill(1 << 16)(false)
|
||||
|
@ -22,7 +22,7 @@ class MosAssembler(program: Program,
|
||||
|
||||
|
||||
override def performFinalOptimizationPass(f: NormalFunction, actuallyOptimize: Boolean, options: CompilationOptions, code: List[AssemblyLine]):List[AssemblyLine] = {
|
||||
val optimizationContext = OptimizationContext(options, Map(), f.environment.maybeGet[ThingInMemory]("__reg"), Set())
|
||||
val optimizationContext = OptimizationContext(options, Map(), f.environment.maybeGet[ThingInMemory]("__reg"), f.environment.identityPage, Set())
|
||||
if (actuallyOptimize) {
|
||||
val finalCode = if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.removeLoadZero(f, code, optimizationContext) else code
|
||||
JumpShortening(f, JumpShortening(f, JumpFixing(f, JumpFollowing(options, finalCode), options), optimizationContext), optimizationContext)
|
||||
@ -167,7 +167,7 @@ class MosAssembler(program: Program,
|
||||
case AssemblyLine0(BRK | RTI, _, _) => true
|
||||
case _ => false
|
||||
}) return
|
||||
val optimizationContext = OptimizationContext(options, Map(), function.environment.maybeGet[ThingInMemory]("__reg"), Set())
|
||||
val optimizationContext = OptimizationContext(options, Map(), function.environment.maybeGet[ThingInMemory]("__reg"), function.environment.identityPage, Set())
|
||||
val flow = CoarseFlowAnalyzer.analyze(function, code, optimizationContext)
|
||||
def rtsPropertyScan[T](extractor: CpuStatus => Status[T])(niceFunctionProperty: Status[T] => Option[NiceFunctionProperty]): Unit = {
|
||||
val statuses = code.zipWithIndex.flatMap{
|
||||
|
@ -865,7 +865,7 @@ class Z80Assembler(program: Program,
|
||||
case ZLine0(RST, _, _) => true
|
||||
case _ => false
|
||||
}) return
|
||||
val optimizationContext = OptimizationContext(options, Map(), None, Set())
|
||||
val optimizationContext = OptimizationContext(options, Map(), None, Constant.Zero, Set())
|
||||
val flow = CoarseFlowAnalyzer.analyze(function, code, optimizationContext)
|
||||
def retPropertyScan[T](extractor: CpuStatus => Status[T])(niceFunctionProperty: Status[T] => Option[NiceFunctionProperty]): Unit = {
|
||||
val statuses = code.zipWithIndex.flatMap{
|
||||
|
@ -714,4 +714,21 @@ class ArraySuite extends FunSuite with Matchers with AppendedClues {
|
||||
m.readByte(0x4008) should equal('9'.toInt)
|
||||
}
|
||||
}
|
||||
|
||||
test("Arrays of pointers") {
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos)(
|
||||
"""
|
||||
|const array arr1 = [1, 2, 3]
|
||||
|const array arr2 = [101, 102, 103]
|
||||
|array(pointer) arrs = [ arr1, arr2 ]
|
||||
|byte output @$c000
|
||||
|void main() {
|
||||
| pointer p
|
||||
| p = arrs[1]
|
||||
| output = p[0]
|
||||
|}
|
||||
""".stripMargin) { m =>
|
||||
m.readByte(0xc000) should equal(101)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -362,4 +362,28 @@ class StructSuite extends FunSuite with Matchers {
|
||||
| }
|
||||
|""".stripMargin){m => }
|
||||
}
|
||||
|
||||
test("Assigning struct fields via pointers") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8086, Cpu.Motorola6809)("""
|
||||
struct STRUCT1 { word a }
|
||||
struct STRUCT2 { word b }
|
||||
pointer.STRUCT1 pS1
|
||||
pointer.STRUCT2 pS2
|
||||
STRUCT1 s1 @$c000
|
||||
STRUCT2 s2
|
||||
|
||||
noinline void f() {
|
||||
pS1->a = pS2->b
|
||||
}
|
||||
|
||||
void main() {
|
||||
s2.b = $405
|
||||
pS1 = s1.pointer
|
||||
pS2 = s2.pointer
|
||||
f()
|
||||
}
|
||||
"""){m =>
|
||||
m.readWord(0xc000) should equal(0x405)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -728,9 +728,9 @@ class Z80AssemblySuite extends FunSuite with Matchers {
|
||||
"""
|
||||
| asm void main () {
|
||||
| ret
|
||||
| add a,ix(0)
|
||||
| adc a,ix(0)
|
||||
| sub ix(0)
|
||||
| add a,ix(1)
|
||||
| adc a,ix(2)
|
||||
| sub ix(3)
|
||||
| sbc a,ix(0)
|
||||
| and ix(0)
|
||||
| xor ix(0)
|
||||
@ -760,8 +760,8 @@ class Z80AssemblySuite extends FunSuite with Matchers {
|
||||
| ex (sp),ix
|
||||
| jp (ix)
|
||||
| ld sp,ix
|
||||
| ld a,ix(0)
|
||||
| ld ix(0),a
|
||||
| ld a,ix(2)
|
||||
| ld ix(2),a
|
||||
|
|
||||
| ret
|
||||
| }
|
||||
@ -773,9 +773,9 @@ class Z80AssemblySuite extends FunSuite with Matchers {
|
||||
"""
|
||||
| asm void main () {
|
||||
| ret
|
||||
| add a,iy(0)
|
||||
| adc a,iy(0)
|
||||
| sub iy(0)
|
||||
| add a,iy(1)
|
||||
| adc a,iy(2)
|
||||
| sub iy(3)
|
||||
| sbc a,iy(0)
|
||||
| and iy(0)
|
||||
| xor iy(0)
|
||||
@ -805,8 +805,8 @@ class Z80AssemblySuite extends FunSuite with Matchers {
|
||||
| ex (sp),iy
|
||||
| jp (iy)
|
||||
| ld sp,iy
|
||||
| ld a,iy(0)
|
||||
| ld iy(0),a
|
||||
| ld a,iy(2)
|
||||
| ld iy(2),a
|
||||
|
|
||||
| ret
|
||||
| }
|
||||
|
@ -149,6 +149,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
CompilationFlag.InterproceduralOptimization -> true,
|
||||
CompilationFlag.CompactReturnDispatchParams -> true,
|
||||
CompilationFlag.UseOptimizationHints -> true,
|
||||
CompilationFlag.IdentityPage -> blastProcessing, // TODO
|
||||
CompilationFlag.SoftwareStack -> softwareStack,
|
||||
CompilationFlag.EmitCmosOpcodes -> millfork.Cpu.CmosCompatible.contains(platform.cpu),
|
||||
CompilationFlag.EmitSC02Opcodes -> millfork.Cpu.CmosCompatible.contains(platform.cpu),
|
||||
|
Loading…
Reference in New Issue
Block a user