mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-10 01:36:59 +00:00
More optimizations
This commit is contained in:
parent
54e7139e84
commit
d6f38ba87b
@ -14,6 +14,7 @@ object OptimizationPresets {
|
||||
UnusedGlobalVariables,
|
||||
)
|
||||
val AssOpt: List[AssemblyOptimization] = List[AssemblyOptimization](
|
||||
AlwaysGoodOptimizations.NonetAddition,
|
||||
AlwaysGoodOptimizations.PointlessSignCheck,
|
||||
AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad,
|
||||
AlwaysGoodOptimizations.PointlessLoadAfterLoadOrStore,
|
||||
@ -31,6 +32,7 @@ object OptimizationPresets {
|
||||
AlwaysGoodOptimizations.PointlessLoadBeforeTransfer,
|
||||
VariableToRegisterOptimization,
|
||||
AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad,
|
||||
AlwaysGoodOptimizations.CommonIndexSubexpressionElimination,
|
||||
AlwaysGoodOptimizations.PointlessOperationPairRemoval,
|
||||
AlwaysGoodOptimizations.PointlessOperationPairRemoval2,
|
||||
AlwaysGoodOptimizations.PoinlessLoadBeforeAnotherLoad,
|
||||
@ -125,6 +127,7 @@ object OptimizationPresets {
|
||||
DangerousOptimizations.ConstantIndexOffsetPropagation,
|
||||
AlwaysGoodOptimizations.CommonBranchBodyOptimization,
|
||||
AlwaysGoodOptimizations.CommonExpressionInConditional,
|
||||
AlwaysGoodOptimizations.CommonIndexSubexpressionElimination,
|
||||
AlwaysGoodOptimizations.ConstantFlowAnalysis,
|
||||
AlwaysGoodOptimizations.ConstantIndexPropagation,
|
||||
AlwaysGoodOptimizations.DoubleJumpSimplification,
|
||||
@ -137,6 +140,7 @@ object OptimizationPresets {
|
||||
AlwaysGoodOptimizations.IndexSequenceOptimization,
|
||||
AlwaysGoodOptimizations.MathOperationOnTwoIdenticalMemoryOperands,
|
||||
AlwaysGoodOptimizations.ModificationOfJustWrittenValue,
|
||||
AlwaysGoodOptimizations.NonetAddition,
|
||||
AlwaysGoodOptimizations.OperationsAroundShifting,
|
||||
AlwaysGoodOptimizations.PoinlessFlagChange,
|
||||
AlwaysGoodOptimizations.PointlessLoadAfterLoadOrStore,
|
||||
|
@ -1220,4 +1220,93 @@ object AlwaysGoodOptimizations {
|
||||
(Elidable & HasOpcode(BPL)) ~~> { code => code.init :+ code.last.copy(opcode = JMP, addrMode = Absolute) },
|
||||
)
|
||||
}
|
||||
|
||||
val NonetAddition = new RuleBasedAssemblyOptimization("Nonet addition",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
(Elidable & HasOpcode(LDX) & HasImmediate(0) & HasClear(State.D)) ~
|
||||
(Elidable & HasOpcode(BCC) & MatchParameter(14)) ~
|
||||
(Elidable & HasOpcode(INX)) ~
|
||||
(Elidable & HasOpcode(LABEL) & MatchParameter(14) & HasCallerCount(1)) ~
|
||||
(Elidable & HasOpcode(CLC)) ~
|
||||
(Elidable & HasOpcode(ADC) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsX)) ~
|
||||
(Elidable & HasOpcode(STA) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsX)) ~
|
||||
(Elidable & HasOpcode(TXA)) ~
|
||||
(Elidable & HasOpcode(ADC) & MatchAddrMode(2) & MatchParameter(3) & Not(ConcernsX) & DoesNotConcernMemoryAt(0, 1)) ~
|
||||
(Elidable & HasOpcode(STA) & MatchAddrMode(2) & MatchParameter(3) & Not(ConcernsX) & DoesntMatterWhatItDoesWith(State.C, State.N, State.V, State.Z)) ~~> { (code, ctx) =>
|
||||
val label = getNextLabel("in")
|
||||
List(
|
||||
code(1), // BCC
|
||||
code(8).copy(opcode = INC),
|
||||
code(3), //LABEL
|
||||
code(4), //CLC
|
||||
code(5), //ADC
|
||||
code(6), //STA
|
||||
AssemblyLine.relative(BCC, label),
|
||||
code(8).copy(opcode = INC),
|
||||
AssemblyLine.label(label))
|
||||
}
|
||||
)
|
||||
|
||||
val CommonIndexSubexpressionElimination: RuleBasedAssemblyOptimization = {
|
||||
def eliminate(firstLda: Boolean, targetY: Boolean): AssemblyRule = {
|
||||
val firstLoad = HasClear(State.D) & (
|
||||
if (firstLda) HasOpcodeIn(Set(LDA, STA, LAX)) & HasAddrModeIn(Set(Absolute, ZeroPage, Immediate)) & MatchParameter(1)
|
||||
else if (targetY) HasOpcodeIn(Set(TXA, TAX))
|
||||
else HasOpcodeIn(Set(TAY, TYA))
|
||||
)
|
||||
val secondLoad = Elidable & (
|
||||
if (firstLda) HasOpcode(LDA) & HasAddrModeIn(Set(Absolute, ZeroPage, Immediate)) & MatchParameter(1)
|
||||
else if (targetY) HasOpcode(TXA)
|
||||
else HasOpcode(TYA)
|
||||
)
|
||||
val firstTransfer = if (targetY) HasOpcode(TAY) else HasOpcode(TAX)
|
||||
val secondTransfer = Elidable & (
|
||||
if (targetY) HasOpcode(TAY)
|
||||
else HasOpcode(TAX)
|
||||
) & DoesntMatterWhatItDoesWith(State.A, State.Z, State.N, State.C)
|
||||
val fillerLine =
|
||||
HasAddrMode(Implied) & HasOpcodeIn(Set(ASL, CLC, CLD, SEC, SED, LSR, INC, DEC)) |
|
||||
HasOpcodeIn(Set(ADC, ORA, EOR, AND, SBC)) & HasAddrModeIn(Set(Absolute, ZeroPage, Immediate))
|
||||
val firstFiller = fillerLine.*
|
||||
val secondFiller = (Elidable & fillerLine).*
|
||||
val betweenLines = (Linear & Not(secondLoad)).+
|
||||
(firstLoad ~ firstFiller ~ firstTransfer).capture(91) ~ betweenLines.capture(95) ~ (secondLoad ~ secondFiller ~ secondTransfer).capture(92) ~ Where(ctx => {
|
||||
val first = ctx.get[List[AssemblyLine]](91)
|
||||
val second = ctx.get[List[AssemblyLine]](92)
|
||||
val between = ctx.get[List[AssemblyLine]](95)
|
||||
var currentD = false
|
||||
var currentCDefined = false
|
||||
first.length == second.length &&
|
||||
first.head.parameter == second.head.parameter &&
|
||||
(first.head.addrMode == Immediate) == (second.head.addrMode == Immediate) && first.tail.zip(second.tail).forall(p => {
|
||||
p._1.opcode == p._2.opcode && p._1.parameter.quickSimplify == p._2.parameter.quickSimplify && (p._1.addrMode == Immediate) == (p._2.addrMode == Immediate)
|
||||
}) && (for (s1 <- first; s2 <- between) yield HelperCheckers.memoryAccessDoesntOverlap(s1.addrMode, s1.parameter, s2.addrMode, s2.parameter)).forall(identity) && {
|
||||
var currentD = false
|
||||
var currentCDefined = false
|
||||
var noAdditionDependency = true
|
||||
first.tail.map(_.opcode).foreach{
|
||||
case SED => currentD = true
|
||||
case CLD => currentD = false
|
||||
case CLC | SEC => currentCDefined = true
|
||||
case ADC | SBC => noAdditionDependency = currentCDefined
|
||||
case _ => ()
|
||||
}
|
||||
noAdditionDependency && !currentD
|
||||
}
|
||||
|
||||
}) ~~> { (code, ctx) =>
|
||||
val first = ctx.get[List[AssemblyLine]](91)
|
||||
val between = ctx.get[List[AssemblyLine]](95)
|
||||
first ++ between
|
||||
}
|
||||
}
|
||||
new RuleBasedAssemblyOptimization("Common index subexpression elimination",
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
eliminate(firstLda = true, targetY = false),
|
||||
eliminate(firstLda = false, targetY = false),
|
||||
eliminate(firstLda = false, targetY = true),
|
||||
eliminate(firstLda = true, targetY = true),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -170,13 +170,14 @@ trait AssemblyPattern {
|
||||
|
||||
def captureLength(i: Int) = CaptureLength(i, this)
|
||||
|
||||
protected def memoryAccessDoesntOverlap(a1: AddrMode.Value, p1: Constant, a2: AddrMode.Value, p2: Constant): Boolean = {
|
||||
import AddrMode._
|
||||
val badAddrModes = Set(IndexedX, IndexedY, ZeroPageIndirect, AbsoluteIndexedX)
|
||||
}
|
||||
object HelperCheckers {
|
||||
import AddrMode._
|
||||
private val badAddrModes = Set(IndexedX, IndexedY, ZeroPageIndirect, AbsoluteIndexedX)
|
||||
private val goodAddrModes = Set(Implied, Immediate, Relative)
|
||||
def memoryAccessDoesntOverlap(a1: AddrMode.Value, p1: Constant, a2: AddrMode.Value, p2: Constant): Boolean = {
|
||||
if (badAddrModes(a1) || badAddrModes(a2)) return false
|
||||
val goodAddrModes = Set(Implied, Immediate, Relative)
|
||||
if (goodAddrModes(a1) || goodAddrModes(a2)) return true
|
||||
|
||||
def handleKnownDistance(distance: Short): Boolean = {
|
||||
val indexingAddrModes = Set(AbsoluteIndexedX, AbsoluteX, ZeroPageX, AbsoluteY, ZeroPageY)
|
||||
val a1Indexing = indexingAddrModes(a1)
|
||||
@ -412,7 +413,7 @@ case class WhereNoMemoryAccessOverlapBetweenTwoLineLists(ix1: Int, ix2: Int) ext
|
||||
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = {
|
||||
val s1s = ctx.get[List[AssemblyLine]](ix1)
|
||||
val s2s = ctx.get[List[AssemblyLine]](ix2)
|
||||
if (s1s.forall(s1 => s2s.forall(s2 => memoryAccessDoesntOverlap(s1.addrMode, s1.parameter, s2.addrMode, s2.parameter)))) Some(code) else None
|
||||
if (s1s.forall(s1 => s2s.forall(s2 => HelperCheckers.memoryAccessDoesntOverlap(s1.addrMode, s1.parameter, s2.addrMode, s2.parameter)))) Some(code) else None
|
||||
}
|
||||
}
|
||||
|
||||
@ -639,7 +640,7 @@ case class DoesntChangeMemoryAt(addrMode1: Int, param1: Int) extends AssemblyLin
|
||||
val a1 = ctx.get[AddrMode.Value](addrMode1)
|
||||
val a2 = line.addrMode
|
||||
val changesSomeMemory = OpcodeClasses.ChangesMemoryAlways(line.opcode) || line.addrMode != AddrMode.Implied && OpcodeClasses.ChangesMemoryIfNotImplied(line.opcode)
|
||||
!changesSomeMemory || memoryAccessDoesntOverlap(a1, p1, a2, p2)
|
||||
!changesSomeMemory || HelperCheckers.memoryAccessDoesntOverlap(a1, p1, a2, p2)
|
||||
}
|
||||
}
|
||||
|
||||
@ -654,7 +655,7 @@ case class DoesNotConcernMemoryAt(addrMode1: Int, param1: Int) extends AssemblyL
|
||||
val p2 = line.parameter
|
||||
val a1 = ctx.get[AddrMode.Value](addrMode1)
|
||||
val a2 = line.addrMode
|
||||
memoryAccessDoesntOverlap(a1, p1, a2, p2)
|
||||
HelperCheckers.memoryAccessDoesntOverlap(a1, p1, a2, p2)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,4 +302,42 @@ class AssemblyOptimizationSuite extends FunSuite with Matchers {
|
||||
|
|
||||
""".stripMargin)
|
||||
}
|
||||
|
||||
test("Adding a nonet") {
|
||||
EmuBenchmarkRun(
|
||||
"""
|
||||
| word output @$C000
|
||||
| byte source @$C002
|
||||
| void main () {
|
||||
| init()
|
||||
| output += source <<<< 1
|
||||
| }
|
||||
| void init() {
|
||||
| output = 0
|
||||
| source = $FF
|
||||
| }
|
||||
|
|
||||
""".stripMargin){m =>
|
||||
m.readWord(0xc000) should equal(0x1FE)
|
||||
}
|
||||
}
|
||||
|
||||
test("Common indexing subexpression elimination") {
|
||||
EmuBenchmarkRun(
|
||||
"""
|
||||
| array output [55] @$C000
|
||||
| array input = [0,1,2,3,4,5,6,7,8,9,10]
|
||||
| void main () {
|
||||
| byte indexer
|
||||
| indexer = init()
|
||||
| output[(indexer + 1) & 7] = input[(indexer + 1) & 7]
|
||||
| }
|
||||
| byte init() {
|
||||
| return 2
|
||||
| }
|
||||
|
|
||||
""".stripMargin){m =>
|
||||
m.readWord(0xc003) should equal(3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user