1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-11-04 09:04:33 +00:00

Few optimization improvements for 6502

This commit is contained in:
Karol Stasiak 2018-07-16 22:59:08 +02:00
parent b2fa129482
commit 0efd9ada3e
6 changed files with 65 additions and 19 deletions

View File

@ -2,6 +2,7 @@ package millfork.assembly.mos.opt
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import millfork.CompilationFlag
import millfork.assembly.mos.Opcode._ import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.OpcodeClasses._ import millfork.assembly.mos.OpcodeClasses._
import millfork.assembly.mos.{AddrMode, opt, _} import millfork.assembly.mos.{AddrMode, opt, _}
@ -132,6 +133,12 @@ object AlwaysGoodOptimizations {
HasClear(State.D) & HasSet(State.C) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~> { (code, ctx) => HasClear(State.D) & HasSet(State.C) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~> { (code, ctx) =>
AssemblyLine.immediate(LDA, (ctx.get[Constant](1) + (ctx.get[Int](0) + 1)).quickSimplify.loByte) :: Nil AssemblyLine.immediate(LDA, (ctx.get[Constant](1) + (ctx.get[Int](0) + 1)).quickSimplify.loByte) :: Nil
}, },
(Elidable &
MatchA(0) & MatchParameter(1) &
HasOpcode(ADC) & HasAddrMode(Immediate) &
HasSet(State.D) & HasClear(State.C) & DoesntMatterWhatItDoesWith(State.C, State.V)) ~~> { (code, ctx) =>
AssemblyLine.immediate(LDA, CompoundConstant(MathOperator.DecimalPlus, ctx.get[Constant](1), NumericConstant(ctx.get[Int](0), 1)).quickSimplify.loByte) :: Nil
},
(Elidable & (Elidable &
MatchA(0) & MatchParameter(1) & MatchA(0) & MatchParameter(1) &
HasOpcode(SBC) & HasAddrMode(Immediate) & HasOpcode(SBC) & HasAddrMode(Immediate) &
@ -2333,8 +2340,8 @@ object AlwaysGoodOptimizations {
ctx.addObject(3, ZeroPage) ctx.addObject(3, ZeroPage)
true true
}) ~ }) ~
(Linear & DoesNotConcernMemoryAt(3,4) & DoesNotConcernMemoryAt(3,5)).* ~ (Linear & DoesNotConcernMemoryAt(3, 4) & DoesNotConcernMemoryAt(3, 5)).* ~
(HasOpcode(STA) & MatchA(1) & HasAddrModeIn(Absolute, ZeroPage) & MatchParameter(5)) ~ (HasOpcode(STA) & HasAddrModeIn(Absolute, ZeroPage) & MatchA(1) & MatchParameter(5)) ~
Where(ctx => { Where(ctx => {
val lo = ctx.get[Int](0) & 0xff val lo = ctx.get[Int](0) & 0xff
val hi = ctx.get[Int](1) & 0xff val hi = ctx.get[Int](1) & 0xff
@ -2342,7 +2349,10 @@ object AlwaysGoodOptimizations {
true true
}) ~ }) ~
(Linear & DoesNotConcernMemoryAt(3,4) & DoesNotConcernMemoryAt(3,5)).* ~ (Linear & DoesNotConcernMemoryAt(3,4) & DoesNotConcernMemoryAt(3,5)).* ~
(Elidable & MatchParameter(6) & HasAddrModeIn(IndexedZ, IndexedY)) ~~> { (code, ctx) => (Elidable & MatchParameter(4) & HasAddrModeIn(IndexedZ, IndexedY) & MatchAddrMode(9)) ~
Where(ctx => {
ctx.get[AddrMode.Value](9) == IndexedY || !ctx.compilationOptions.flag(CompilationFlag.Emit65CE02Opcodes)
}) ~~> { (code, ctx) =>
val addr = ctx.get[Int](2) val addr = ctx.get[Int](2)
val last = code.last val last = code.last
code.init :+ last.copy(parameter = NumericConstant(addr, 2), addrMode = if (last.addrMode == IndexedZ) Absolute else AbsoluteY) code.init :+ last.copy(parameter = NumericConstant(addr, 2), addrMode = if (last.addrMode == IndexedZ) Absolute else AbsoluteY)
@ -2363,8 +2373,11 @@ object AlwaysGoodOptimizations {
ctx.addObject(2, hi * 256 + lo) ctx.addObject(2, hi * 256 + lo)
true true
}) ~ }) ~
(Linear & DoesNotConcernMemoryAt(3,4) & DoesNotConcernMemoryAt(3,5)).* ~ (Linear & DoesNotConcernMemoryAt(3, 4) & DoesNotConcernMemoryAt(3, 5)).* ~
(Elidable & MatchParameter(6) & HasAddrModeIn(IndexedZ, IndexedY)) ~~> { (code, ctx) => (Elidable & MatchParameter(4) & HasAddrModeIn(IndexedZ, IndexedY) & MatchAddrMode(9)) ~
Where(ctx => {
ctx.get[AddrMode.Value](9) == IndexedY || !ctx.compilationOptions.flag(CompilationFlag.Emit65CE02Opcodes)
}) ~~> { (code, ctx) =>
val addr = ctx.get[Int](2) val addr = ctx.get[Int](2)
val last = code.last val last = code.last
code.init :+ last.copy(parameter = NumericConstant(addr, 2), addrMode = if (last.addrMode == IndexedZ) Absolute else AbsoluteY) code.init :+ last.copy(parameter = NumericConstant(addr, 2), addrMode = if (last.addrMode == IndexedZ) Absolute else AbsoluteY)

View File

@ -1,8 +1,7 @@
package millfork.assembly.mos.opt package millfork.assembly.mos.opt
import millfork.CompilationOptions
import millfork.assembly.mos.{AssemblyLine, OpcodeClasses} import millfork.assembly.mos.{AssemblyLine, OpcodeClasses}
import millfork.assembly.AssemblyOptimization import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.env.NormalFunction import millfork.env.NormalFunction
import millfork.error.ErrorReporting import millfork.error.ErrorReporting
@ -33,7 +32,7 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
override def name = "Changing index registers" override def name = "Changing index registers"
override def optimize(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions): List[AssemblyLine] = { override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
val usesX = code.exists(l => val usesX = code.exists(l =>
OpcodeClasses.ReadsXAlways(l.opcode) || OpcodeClasses.ReadsXAlways(l.opcode) ||
OpcodeClasses.ReadsYAlways(l.opcode) || OpcodeClasses.ReadsYAlways(l.opcode) ||

View File

@ -29,9 +29,15 @@ object FlowInfoRequirement extends Enumeration {
} }
} }
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRule*) extends AssemblyOptimization[AssemblyLine] { trait AssemblyRuleSet{
def flatten: Seq[AssemblyRule]
}
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRuleSet*) extends AssemblyOptimization[AssemblyLine] {
private val actualRules = rules.flatMap(_.flatten)
actualRules.foreach(_.pattern.validate(needsFlowInfo))
rules.foreach(_.pattern.validate(needsFlowInfo))
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = { override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
val effectiveCode = code.map(a => a.copy(parameter = a.parameter.quickSimplify)) val effectiveCode = code.map(a => a.copy(parameter = a.parameter.quickSimplify))
@ -43,8 +49,8 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
code match { code match {
case Nil => Nil case Nil => Nil
case head :: tail => case head :: tail =>
for ((rule, index) <- rules.zipWithIndex) { for ((rule, index) <- actualRules.zipWithIndex) {
val ctx = new AssemblyMatchingContext(optimizationContext.options, optimizationContext.labelMap, optimizationContext.niceFunctionProperties) val ctx = new AssemblyMatchingContext(optimizationContext.options, optimizationContext.labelMap, optimizationContext.zreg, optimizationContext.niceFunctionProperties)
rule.pattern.matchTo(ctx, code) match { rule.pattern.matchTo(ctx, code) match {
case Some(rest: List[(FlowInfo, AssemblyLine)]) => case Some(rest: List[(FlowInfo, AssemblyLine)]) =>
val matchedChunkToOptimize: List[AssemblyLine] = code.take(code.length - rest.length).map(_._2) val matchedChunkToOptimize: List[AssemblyLine] = code.take(code.length - rest.length).map(_._2)
@ -74,6 +80,7 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
class AssemblyMatchingContext(val compilationOptions: CompilationOptions, class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
val labelMap: Map[String, Int], val labelMap: Map[String, Int],
val zeropageRegister: Option[ThingInMemory],
val niceFunctionProperties: Set[(NiceFunctionProperty, String)]) { val niceFunctionProperties: Set[(NiceFunctionProperty, String)]) {
def functionChangesA(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntChangeA -> name) def functionChangesA(name: String): Boolean = !niceFunctionProperties(MosNiceFunctionProperty.DoesntChangeA -> name)
@ -189,10 +196,18 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
jumps.isEmpty jumps.isEmpty
} }
def zreg(i: Int): Constant = {
MemoryAddressConstant(zeropageRegister.get) + i
}
} }
case class AssemblyRule(pattern: AssemblyPattern, result: (List[AssemblyLine], AssemblyMatchingContext) => List[AssemblyLine]) { case class AssemblyRule(pattern: AssemblyPattern, result: (List[AssemblyLine], AssemblyMatchingContext) => List[AssemblyLine]) extends AssemblyRuleSet {
override def flatten: Seq[AssemblyRule] = List(this)
}
case class MultipleAssemblyRules(list: Seq[AssemblyRuleSet]) extends AssemblyRuleSet {
override def flatten: Seq[AssemblyRule] = list.flatMap(_.flatten)
} }
trait AssemblyPattern { trait AssemblyPattern {

View File

@ -57,7 +57,7 @@ object SuperOptimizer extends AssemblyOptimization[AssemblyLine] {
) )
val optionsForMeasurements = options.copy(commandLineFlags = options.commandLineFlags + (CompilationFlag.InternalCurrentlyOptimizingForMeasurement -> true)) val optionsForMeasurements = options.copy(commandLineFlags = options.commandLineFlags + (CompilationFlag.InternalCurrentlyOptimizingForMeasurement -> true))
val optimizationContextForMeasurements = optimizationContext.copy(options = optionsForMeasurements) val optimizationContextForMeasurements = optimizationContext.copy(options = optionsForMeasurements)
val quicklyCleanedCode = quickScrub.foldLeft(code)((c, o) => o.optimize(m, c, optionsForMeasurements)) val quicklyCleanedCode = quickScrub.foldLeft(code)((c, o) => o.optimize(m, c, optimizationContextForMeasurements))
seenSoFar += viewCode(quicklyCleanedCode) seenSoFar += viewCode(quicklyCleanedCode)
queue.enqueue(quickScrub.reverse -> quicklyCleanedCode) queue.enqueue(quickScrub.reverse -> quicklyCleanedCode)
@ -91,7 +91,7 @@ object SuperOptimizer extends AssemblyOptimization[AssemblyLine] {
ErrorReporting.debug(s"Visited ${leaves.size} leaves") ErrorReporting.debug(s"Visited ${leaves.size} leaves")
ErrorReporting.debug(s"${code.map(_.sizeInBytes).sum} B -> ${result._2.map(_.sizeInBytes).sum} B: ${result._1.reverse.map(_.name).mkString(" -> ")}") ErrorReporting.debug(s"${code.map(_.sizeInBytes).sum} B -> ${result._2.map(_.sizeInBytes).sum} B: ${result._1.reverse.map(_.name).mkString(" -> ")}")
result._1.reverse.foldLeft(code){(c, opt) => result._1.reverse.foldLeft(code){(c, opt) =>
val n = opt.optimize(m, c, options) val n = opt.optimize(m, c, optimizationContext)
// println(c.mkString("","","")) // println(c.mkString("","",""))
// println(n.mkString("","","")) // println(n.mkString("","",""))
n n

View File

@ -1,9 +1,8 @@
package millfork.assembly.mos.opt package millfork.assembly.mos.opt
import millfork.CompilationOptions
import millfork.assembly.mos.AssemblyLine import millfork.assembly.mos.AssemblyLine
import millfork.assembly.mos.Opcode._ import millfork.assembly.mos.Opcode._
import millfork.assembly.AssemblyOptimization import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.env._ import millfork.env._
import millfork.error.ErrorReporting import millfork.error.ErrorReporting
@ -12,7 +11,7 @@ import millfork.error.ErrorReporting
*/ */
object UnusedLabelRemoval extends AssemblyOptimization[AssemblyLine] { object UnusedLabelRemoval extends AssemblyOptimization[AssemblyLine] {
override def optimize(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions): List[AssemblyLine] = { override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
val usedLabels = code.flatMap { val usedLabels = code.flatMap {
case AssemblyLine(LABEL, _, _, _) => None case AssemblyLine(LABEL, _, _, _) => None
case AssemblyLine(_, _, MemoryAddressConstant(Label(l)), _) => Some(l) case AssemblyLine(_, _, MemoryAddressConstant(Label(l)), _) => Some(l)

View File

@ -3,7 +3,7 @@ package millfork.assembly.mos.opt
import millfork.assembly.mos.Opcode._ import millfork.assembly.mos.Opcode._
import millfork.assembly.mos.AddrMode._ import millfork.assembly.mos.AddrMode._
import millfork.assembly.AssemblyOptimization import millfork.assembly.AssemblyOptimization
import millfork.assembly.mos.{AssemblyLine, State} import millfork.assembly.mos.{AssemblyLine, Opcode, State}
import millfork.env.{CompoundConstant, Constant, MathOperator} import millfork.env.{CompoundConstant, Constant, MathOperator}
/** /**
@ -84,10 +84,30 @@ object ZeropageRegisterOptimizations {
) )
val StashInRegInsteadOfStack = new RuleBasedAssemblyOptimization("Stashing in zeropage register instead of stack",
needsFlowInfo = FlowInfoRequirement.BothFlows,
MultipleAssemblyRules((0 to 1).map{ zregIndex =>
(Elidable & HasOpcode(PHA) & DoesntMatterWhatItDoesWithReg(zregIndex)) ~
(Linear & Not(ConcernsS) & Not(RefersToOrUses("__reg", zregIndex))).*.capture(21) ~
(Elidable & HasOpcode(TSX)) ~
HasOpcodeIn(CLC, SED, CLD, SEC).*.capture(22) ~
(Elidable & MatchOpcode(1) & HasAddrMode(AbsoluteX) & HasParameterWhere(p => p.isProvably(0x101))).* ~
(Elidable & HasOpcode(INX)) ~
(Elidable & HasOpcode(TXS) & DoesntMatterWhatItDoesWith(State.Z, State.N)) ~~> { (code, ctx) =>
AssemblyLine.zeropage(STA, ctx.zreg(zregIndex)) :: (
ctx.get[List[AssemblyLine]](21) ++
ctx.get[List[AssemblyLine]](22) ++ List(
AssemblyLine.zeropage(ctx.get[Opcode.Value](1), ctx.zreg(zregIndex)),
AssemblyLine.implied(TSX)))
}
})
)
val All: List[AssemblyOptimization[AssemblyLine]] = List( val All: List[AssemblyOptimization[AssemblyLine]] = List(
ConstantMultiplication, ConstantMultiplication,
DeadRegStore, DeadRegStore,
DeadRegStoreFromFlow, DeadRegStoreFromFlow,
StashInRegInsteadOfStack,
) )
} }