1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-08-12 11:29:20 +00:00

More optimizations

This commit is contained in:
Karol Stasiak 2018-02-14 22:50:34 +01:00
parent 54e7139e84
commit d6f38ba87b
4 changed files with 140 additions and 8 deletions

View File

@ -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,

View File

@ -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),
)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}
}