From e28ff16717ec66ac875238b70bf75686c5e647e8 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Wed, 20 Dec 2017 12:00:24 +0100 Subject: [PATCH] More optimizations, including optimizations with jump counting --- src/main/scala/millfork/Main.scala | 4 +- .../scala/millfork/OptimizationPresets.scala | 3 + .../millfork/assembly/AssemblyLine.scala | 3 +- src/main/scala/millfork/assembly/Chunk.scala | 2 +- .../opt/AlwaysGoodOptimizations.scala | 17 +++ .../millfork/assembly/opt/FlowAnalyzer.scala | 41 ++++-- .../assembly/opt/LaterOptimizations.scala | 127 +++++++++++++++++- .../opt/RuleBasedAssemblyOptimization.scala | 27 ++-- .../assembly/opt/SuperOptimizer.scala | 2 + .../scala/millfork/test/ComparisonSuite.scala | 4 +- .../test/emu/EmuOptimizedInlinedRun.scala | 6 +- .../millfork/test/emu/EmuOptimizedRun.scala | 6 +- .../test/emu/EmuQuantumOptimizedRun.scala | 6 +- .../test/emu/EmuUndocumentedRun.scala | 8 +- 14 files changed, 218 insertions(+), 38 deletions(-) diff --git a/src/main/scala/millfork/Main.scala b/src/main/scala/millfork/Main.scala index 9a5adbd3..88e985fb 100644 --- a/src/main/scala/millfork/Main.scala +++ b/src/main/scala/millfork/Main.scala @@ -4,7 +4,7 @@ import java.nio.charset.StandardCharsets import java.nio.file.{Files, Paths} import java.util.Locale -import millfork.assembly.opt.{CmosOptimizations, DangerousOptimizations, SuperOptimizer, UndocumentedOptimizations} +import millfork.assembly.opt._ import millfork.buildinfo.BuildInfo import millfork.cli.{CliParser, CliStatus} import millfork.env.Environment @@ -87,7 +87,7 @@ object Main { env.collectDeclarations(program, options) val extras = List( if (options.flag(CompilationFlag.EmitIllegals)) UndocumentedOptimizations.All else Nil, - if (options.flag(CompilationFlag.EmitCmosOpcodes)) CmosOptimizations.All else Nil, + if (options.flag(CompilationFlag.EmitCmosOpcodes)) CmosOptimizations.All else LaterOptimizations.Nmos, if (options.flag(CompilationFlag.DangerousOptimizations)) DangerousOptimizations.All else Nil, ).flatten val goodCycle = List.fill(optLevel - 1)(OptimizationPresets.Good ++ extras).flatten diff --git a/src/main/scala/millfork/OptimizationPresets.scala b/src/main/scala/millfork/OptimizationPresets.scala index 7b160e24..67edf76f 100644 --- a/src/main/scala/millfork/OptimizationPresets.scala +++ b/src/main/scala/millfork/OptimizationPresets.scala @@ -107,6 +107,7 @@ object OptimizationPresets { LaterOptimizations.UseXInsteadOfStack, LaterOptimizations.UseYInsteadOfStack, LaterOptimizations.IndexSwitchingOptimization, + LaterOptimizations.LoadingBranchesOptimization, ) val Good: List[AssemblyOptimization] = List[AssemblyOptimization]( @@ -114,6 +115,7 @@ object OptimizationPresets { AlwaysGoodOptimizations.CarryFlagConversion, DangerousOptimizations.ConstantIndexOffsetPropagation, AlwaysGoodOptimizations.BranchInPlaceRemoval, + AlwaysGoodOptimizations.CommonBranchBodyOptimization, AlwaysGoodOptimizations.ConstantFlowAnalysis, AlwaysGoodOptimizations.ConstantIndexPropagation, AlwaysGoodOptimizations.FlagFlowAnalysis, @@ -146,5 +148,6 @@ object OptimizationPresets { UnusedLabelRemoval, AlwaysGoodOptimizations.TailCallOptimization, AlwaysGoodOptimizations.UnusedCodeRemoval, + AlwaysGoodOptimizations.UnusedLabelRemoval, ) } diff --git a/src/main/scala/millfork/assembly/AssemblyLine.scala b/src/main/scala/millfork/assembly/AssemblyLine.scala index a34c3268..d9a70ad4 100644 --- a/src/main/scala/millfork/assembly/AssemblyLine.scala +++ b/src/main/scala/millfork/assembly/AssemblyLine.scala @@ -137,7 +137,8 @@ object OpcodeClasses { val SupportsZeroPageIndirect = Set(ORA, AND, EOR, ADC, STA, LDA, CMP, SBC) - val ShortBranching = Set(BEQ, BNE, BMI, BPL, BVC, BVS, BCC, BCS, BRA) + val ShortConditionalBranching = Set(BEQ, BNE, BMI, BPL, BVC, BVS, BCC, BCS) + val ShortBranching = ShortConditionalBranching + BRA val AllDirectJumps = ShortBranching + JMP val AllLinear = Set( ORA, AND, EOR, diff --git a/src/main/scala/millfork/assembly/Chunk.scala b/src/main/scala/millfork/assembly/Chunk.scala index 117587b1..a0178648 100644 --- a/src/main/scala/millfork/assembly/Chunk.scala +++ b/src/main/scala/millfork/assembly/Chunk.scala @@ -14,7 +14,7 @@ case object EmptyChunk extends Chunk { } case class LabelledChunk(label: String, chunk: Chunk) extends Chunk { - override def linearize: List[AssemblyLine] = AssemblyLine.label(Label(label)) :: chunk.linearize + override def linearize: List[AssemblyLine] = AssemblyLine.label(Label(label)).copy(elidable=false) :: chunk.linearize override def sizeInBytes: Int = chunk.sizeInBytes } diff --git a/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala b/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala index 898924b1..bb616219 100644 --- a/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala +++ b/src/main/scala/millfork/assembly/opt/AlwaysGoodOptimizations.scala @@ -846,5 +846,22 @@ object AlwaysGoodOptimizations { Where(ctx => ctx.get[Constant](1).quickSimplify.isLowestByteAlwaysEqual(ctx.get[Int](0)-1)) ~~> (_ => List(AssemblyLine.implied(DEX))), ) + val CommonBranchBodyOptimization = new RuleBasedAssemblyOptimization("Common branch body optimization", + needsFlowInfo = FlowInfoRequirement.JustLabels, + (Elidable & Linear & MatchOpcode(3) & MatchAddrMode(4) & MatchParameter(5)).capture(1) ~ + ( + (HasOpcode(JMP) & MatchParameter(2)) ~ Not(MatchOpcode(3)).* + ).capture(11) ~ + (Elidable & Linear & MatchOpcode(3) & MatchAddrMode(4) & MatchParameter(5)) ~ + (HasOpcode(LABEL) & MatchParameter(2) & HasCallerCount(1)).capture(12) ~~> { (code, ctx) => + ctx.get[List[AssemblyLine]](11) ++ ctx.get[List[AssemblyLine]](12) :+ code.head + } + ) + + + val UnusedLabelRemoval = new RuleBasedAssemblyOptimization("Unused label removal", + needsFlowInfo = FlowInfoRequirement.JustLabels, + (Elidable & HasOpcode(LABEL) & HasCallerCount(0)) ~~> (_ => Nil) + ) } diff --git a/src/main/scala/millfork/assembly/opt/FlowAnalyzer.scala b/src/main/scala/millfork/assembly/opt/FlowAnalyzer.scala index eaeb718f..a556df35 100644 --- a/src/main/scala/millfork/assembly/opt/FlowAnalyzer.scala +++ b/src/main/scala/millfork/assembly/opt/FlowAnalyzer.scala @@ -1,34 +1,53 @@ package millfork.assembly.opt import millfork.{CompilationFlag, CompilationOptions} -import millfork.assembly.{AssemblyLine, State} -import millfork.env.NormalFunction +import millfork.assembly.{AssemblyLine, Opcode, State} +import millfork.env.{Label, MemoryAddressConstant, NormalFunction} /** * @author Karol Stasiak */ -case class FlowInfo(statusBefore: CpuStatus, importanceAfter: CpuImportance) { +case class FlowInfo(statusBefore: CpuStatus, importanceAfter: CpuImportance, labelUseCountMap: Option[Map[String, Int]]) { def hasClear(state: State.Value): Boolean = statusBefore.hasClear(state) def hasSet(state: State.Value): Boolean = statusBefore.hasSet(state) def isUnimportant(state: State.Value): Boolean = importanceAfter.isUnimportant(state) + + def labelUseCount(label: String): Int = labelUseCountMap.map(_.getOrElse(label, 0)).getOrElse(-1) } object FlowInfo { - val Default = FlowInfo(CpuStatus(), CpuImportance()) + val Default = FlowInfo(CpuStatus(), CpuImportance(), None) } object FlowAnalyzer { - def analyze(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions): List[(FlowInfo, AssemblyLine)] = { - val forwardFlow = if (options.flag(CompilationFlag.DetailedFlowAnalysis)) { - QuantumFlowAnalyzer.analyze(f, code).map(_.collapse) - } else { - CoarseFlowAnalyzer.analyze(f, code) + def analyze(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions, req: FlowInfoRequirement.Value): List[(FlowInfo, AssemblyLine)] = { + val forwardFlow = req match { + case FlowInfoRequirement.BothFlows | FlowInfoRequirement.ForwardFlow => + if (options.flag(CompilationFlag.DetailedFlowAnalysis)) { + QuantumFlowAnalyzer.analyze(f, code).map(_.collapse) + } else { + CoarseFlowAnalyzer.analyze(f, code) + } + case FlowInfoRequirement.BackwardFlow | FlowInfoRequirement.JustLabels | FlowInfoRequirement.NoRequirement => + List.fill(code.size)(CpuStatus()) } - val reverseFlow = ReverseFlowAnalyzer.analyze(f, code) - forwardFlow.zip(reverseFlow).map{case (s,i) => FlowInfo(s,i)}.zip(code) + val reverseFlow = req match { + case FlowInfoRequirement.BothFlows | FlowInfoRequirement.BackwardFlow => + ReverseFlowAnalyzer.analyze(f, code) + case FlowInfoRequirement.ForwardFlow | FlowInfoRequirement.JustLabels | FlowInfoRequirement.NoRequirement => + List.fill(code.size)(CpuImportance()) + } + val labelMap = req match { + case FlowInfoRequirement.NoRequirement => None + case _ => Some(code.flatMap { + case AssemblyLine(op, _, MemoryAddressConstant(Label(l)), _) if op != Opcode.LABEL => Some(l) + case _ => None + }.groupBy(identity).mapValues(_.size)) + } + forwardFlow.zip(reverseFlow).map { case (s, i) => FlowInfo(s, i, labelMap) }.zip(code) } } diff --git a/src/main/scala/millfork/assembly/opt/LaterOptimizations.scala b/src/main/scala/millfork/assembly/opt/LaterOptimizations.scala index 3a1b2095..83fde035 100644 --- a/src/main/scala/millfork/assembly/opt/LaterOptimizations.scala +++ b/src/main/scala/millfork/assembly/opt/LaterOptimizations.scala @@ -1,6 +1,6 @@ package millfork.assembly.opt -import millfork.assembly.{AddrMode, AssemblyLine, Opcode, State} +import millfork.assembly._ import millfork.assembly.Opcode._ import millfork.assembly.AddrMode._ import millfork.assembly.OpcodeClasses._ @@ -228,15 +228,140 @@ object LaterOptimizations { ) + private val LdxAddrModes = Set(Immediate, Absolute, ZeroPage, ZeroPageY, AbsoluteY) + private val LdyAddrModes = Set(Immediate, Absolute, ZeroPage, ZeroPageX, AbsoluteX) + private val StxAddrModes = Set(Absolute, ZeroPage, ZeroPageY) + private val StyAddrModes = Set(Absolute, ZeroPage, ZeroPageX) + private val CpxyAddrModes = Set(Immediate, Absolute, ZeroPage) + + private def incDecThroughIndexRegister(amount: Int, dec: Boolean, carrySet: Boolean, useX: Boolean) = { + val ldAddrModes = if (useX) LdxAddrModes else LdyAddrModes + val stAddrModes = if (useX) StxAddrModes else StyAddrModes + val ldOp = if (useX) LDX else LDY + val stOp = if (useX) STX else STY + val changeOp = if (dec) if (useX) DEX else DEY else if (useX) INX else INY + val addOp = if (dec) SBC else ADC + val addParam = if (dec ^ carrySet) amount + 1 else amount + val indexState = if (useX) State.X else State.Y + val cState = if (carrySet) HasSet(State.C) else HasClear(State.C) + val carryOp = if (carrySet) SEC else CLC + + (Elidable & HasOpcode(LDA) & HasAddrModeIn(ldAddrModes)).capture(11) ~ + (Elidable & HasOpcode(carryOp)).? ~ + (Elidable & HasOpcode(addOp) & HasImmediate(addParam) & cState & HasClear(State.D)) ~ + (Elidable & HasOpcode(STA) & HasAddrModeIn(stAddrModes) & DoesntMatterWhatItDoesWith(State.A, State.C, State.V, indexState)).capture(12) ~~> { (_, ctx) => + ctx.get[List[AssemblyLine]](11).head.copy(opcode = ldOp) :: + (List.fill(amount)(AssemblyLine.implied(changeOp)) :+ + ctx.get[List[AssemblyLine]](12).head.copy(opcode = stOp)) + } + } + + val IncrementThroughIndexRegisters = new RuleBasedAssemblyOptimization("Increment through index registers", + needsFlowInfo = FlowInfoRequirement.BothFlows, + incDecThroughIndexRegister(1, dec = false, carrySet = false, useX = true), + incDecThroughIndexRegister(1, dec = false, carrySet = false, useX = false), + incDecThroughIndexRegister(1, dec = false, carrySet = true, useX = true), + incDecThroughIndexRegister(1, dec = false, carrySet = true, useX = false), + incDecThroughIndexRegister(1, dec = true, carrySet = true, useX = true), + incDecThroughIndexRegister(1, dec = true, carrySet = true, useX = false), + incDecThroughIndexRegister(2, dec = false, carrySet = false, useX = true), + incDecThroughIndexRegister(2, dec = false, carrySet = false, useX = false), + incDecThroughIndexRegister(2, dec = false, carrySet = true, useX = true), + incDecThroughIndexRegister(2, dec = false, carrySet = true, useX = false), + incDecThroughIndexRegister(2, dec = true, carrySet = true, useX = true), + incDecThroughIndexRegister(2, dec = true, carrySet = true, useX = false), + ) + + val LoadingBranchesOptimization = new RuleBasedAssemblyOptimization("Loading branches optimization", + needsFlowInfo = FlowInfoRequirement.BackwardFlow, + (Elidable & HasOpcode(LDA) & HasAddrModeIn(LdxAddrModes) & DoesntMatterWhatItDoesWith(State.X)) ~ + (Linear & Not(ConcernsX) & Not(ChangesA) & Not(HasOpcode(CMP)) & (Not(ReadsA) | Elidable & HasOpcode(STA) & HasAddrModeIn(StxAddrModes)) ).*.capture(39) ~ + (Elidable & HasOpcode(CMP) & HasAddrModeIn(CpxyAddrModes)).?.capture(40) ~ + (Elidable & HasOpcodeIn(OpcodeClasses.ShortConditionalBranching) & MatchParameter(22)).capture(41) ~ + (Elidable & HasOpcode(LDA)).capture(31) ~ + (Elidable & HasOpcodeIn(Set(JMP, BRA)) & MatchParameter(21)) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(22)).capture(42) ~ + (Elidable & HasOpcode(LDA)).capture(32) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(21) & HasCallerCount(1) & DoesntMatterWhatItDoesWith(State.A, State.X, State.N, State.Z)) ~~> { (code, ctx) => + val ldx = List(code.head.copy(opcode = LDX)) + val stx = ctx.get[List[AssemblyLine]](39).map(l => if (l.opcode == STA) l.copy(opcode = STX) else l ) + val cpx = ctx.get[List[AssemblyLine]](40).map(_.copy(opcode = CPX)) + val branch = ctx.get[List[AssemblyLine]](41) + val label = ctx.get[List[AssemblyLine]](42) + val loadIfJumped = ctx.get[List[AssemblyLine]](32) + val loadIfNotJumped = ctx.get[List[AssemblyLine]](31) + List(loadIfJumped, ldx, stx, cpx, branch, loadIfNotJumped, label).flatten + }, + (Elidable & HasOpcode(LDA) & HasAddrModeIn(LdyAddrModes) & DoesntMatterWhatItDoesWith(State.Y)) ~ + (Linear & Not(ConcernsY) & Not(ChangesA) & Not(HasOpcode(CMP)) & (Not(ReadsA) | Elidable & HasOpcode(STA) & HasAddrModeIn(StyAddrModes)) ).*.capture(39) ~ + (Elidable & HasOpcode(CMP) & HasAddrModeIn(CpxyAddrModes)).?.capture(40) ~ + (Elidable & HasOpcodeIn(OpcodeClasses.ShortConditionalBranching) & MatchParameter(22)).capture(41) ~ + (Elidable & HasOpcode(LDA)).capture(31) ~ + (Elidable & HasOpcodeIn(Set(JMP, BRA)) & MatchParameter(21)) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(22)).capture(42) ~ + (Elidable & HasOpcode(LDA)).capture(32) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(21) & HasCallerCount(1) & DoesntMatterWhatItDoesWith(State.A, State.Y, State.N, State.Z)) ~~> { (code, ctx) => + val ldy = List(code.head.copy(opcode = LDY)) + val sty = ctx.get[List[AssemblyLine]](39).map(l => if (l.opcode == STA) l.copy(opcode = STY) else l ) + val cpy = ctx.get[List[AssemblyLine]](40).map(_.copy(opcode = CPY)) + val branch = ctx.get[List[AssemblyLine]](41) + val label = ctx.get[List[AssemblyLine]](42) + val loadIfJumped = ctx.get[List[AssemblyLine]](32) + val loadIfNotJumped = ctx.get[List[AssemblyLine]](31) + List(loadIfJumped, ldy, sty, cpy, branch, loadIfNotJumped, label).flatten + }, + (HasOpcode(LDA) & DoesntMatterWhatItDoesWith(State.X)) ~ + (Linear & Not(ConcernsX)).*.capture(39) ~ + (Elidable & HasOpcodeIn(OpcodeClasses.ShortConditionalBranching) & MatchParameter(22)).capture(41) ~ + (Elidable & HasOpcode(LDA) & HasAddrModeIn(LdxAddrModes)).capture(31) ~ + (Elidable & HasOpcodeIn(Set(JMP, BRA)) & MatchParameter(21)) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(22)).capture(42) ~ + (Elidable & HasOpcode(LDA) & HasAddrModeIn(LdxAddrModes)).capture(32) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(21) & HasCallerCount(1)) ~ + (Elidable & HasOpcode(STA) & HasAddrModeIn(StxAddrModes) & DoesntMatterWhatItDoesWith(State.A, State.X, State.N, State.Z)).capture(33) ~~> { (code, ctx) => + val lda = List(code.head) + val cmp = ctx.get[List[AssemblyLine]](39) + val branch = ctx.get[List[AssemblyLine]](41) + val label = ctx.get[List[AssemblyLine]](42) + val loadIfJumped = ctx.get[List[AssemblyLine]](32).map(_.copy(opcode = LDX)) + val loadIfNotJumped = ctx.get[List[AssemblyLine]](31).map(_.copy(opcode = LDX)) + val stx = ctx.get[List[AssemblyLine]](33).map(_.copy(opcode = STX)) + List(loadIfJumped, lda, cmp, branch, loadIfNotJumped, label, stx).flatten + }, + (HasOpcode(LDA) & DoesntMatterWhatItDoesWith(State.Y)) ~ + (Linear & Not(ConcernsY)).*.capture(39) ~ + (Elidable & HasOpcodeIn(OpcodeClasses.ShortConditionalBranching) & MatchParameter(22)).capture(41) ~ + (Elidable & HasOpcode(LDA) & HasAddrModeIn(LdyAddrModes)).capture(31) ~ + (Elidable & HasOpcodeIn(Set(JMP, BRA)) & MatchParameter(21)) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(22)).capture(42) ~ + (Elidable & HasOpcode(LDA) & HasAddrModeIn(LdyAddrModes)).capture(32) ~ + (Elidable & HasOpcode(LABEL) & MatchParameter(21) & HasCallerCount(1)) ~ + (Elidable & HasOpcode(STA) & HasAddrModeIn(StyAddrModes) & DoesntMatterWhatItDoesWith(State.A, State.Y, State.N, State.Z)).capture(33) ~~> { (code, ctx) => + val lda = List(code.head) + val cmp = ctx.get[List[AssemblyLine]](39) + val branch = ctx.get[List[AssemblyLine]](41) + val label = ctx.get[List[AssemblyLine]](42) + val loadIfJumped = ctx.get[List[AssemblyLine]](32).map(_.copy(opcode = LDY)) + val loadIfNotJumped = ctx.get[List[AssemblyLine]](31).map(_.copy(opcode = LDY)) + val sty = ctx.get[List[AssemblyLine]](33).map(_.copy(opcode = STY)) + List(loadIfJumped, lda, cmp, branch, loadIfNotJumped, label, sty).flatten + }, + ) + val All = List( DoubleLoadToDifferentRegisters, DoubleLoadToTheSameRegister, IndexSwitchingOptimization, + LoadingBranchesOptimization, PointlessLoadAfterStore, PointessLoadingForShifting, LoadingAfterShifting, UseXInsteadOfStack, UseYInsteadOfStack, UseZeropageAddressingMode) + + val Nmos = List( + IncrementThroughIndexRegisters + ) } diff --git a/src/main/scala/millfork/assembly/opt/RuleBasedAssemblyOptimization.scala b/src/main/scala/millfork/assembly/opt/RuleBasedAssemblyOptimization.scala index 48cb84e0..97878ef1 100644 --- a/src/main/scala/millfork/assembly/opt/RuleBasedAssemblyOptimization.scala +++ b/src/main/scala/millfork/assembly/opt/RuleBasedAssemblyOptimization.scala @@ -13,16 +13,16 @@ import scala.collection.mutable object FlowInfoRequirement extends Enumeration { - val NoRequirement, BothFlows, ForwardFlow, BackwardFlow = Value + val NoRequirement, JustLabels, BothFlows, ForwardFlow, BackwardFlow = Value def assertForward(x: FlowInfoRequirement.Value): Unit = x match { case BothFlows | ForwardFlow => () - case NoRequirement | BackwardFlow => ErrorReporting.fatal("Forward flow info required") + case NoRequirement | JustLabels | BackwardFlow => ErrorReporting.fatal("Forward flow info required") } def assertBackward(x: FlowInfoRequirement.Value): Unit = x match { case BothFlows | BackwardFlow => () - case NoRequirement | ForwardFlow => ErrorReporting.fatal("Backward flow info required") + case NoRequirement | JustLabels | ForwardFlow => ErrorReporting.fatal("Backward flow info required") } } @@ -32,18 +32,7 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf override def optimize(f: NormalFunction, code: List[AssemblyLine], options: CompilationOptions): List[AssemblyLine] = { val effectiveCode = code.map(a => a.copy(parameter = a.parameter.quickSimplify)) - val taggedCode = needsFlowInfo match { - case FlowInfoRequirement.NoRequirement => effectiveCode.map(FlowInfo.Default -> _) - case FlowInfoRequirement.BothFlows => FlowAnalyzer.analyze(f, effectiveCode, options) - case FlowInfoRequirement.ForwardFlow => - if (options.flag(CompilationFlag.DetailedFlowAnalysis)) { - QuantumFlowAnalyzer.analyze(f, code).map(s => FlowInfo(s.collapse, CpuImportance())).zip(code) - } else { - CoarseFlowAnalyzer.analyze(f, code).map(s => FlowInfo(s, CpuImportance())).zip(code) - } - case FlowInfoRequirement.BackwardFlow => - ReverseFlowAnalyzer.analyze(f, code).map(i => FlowInfo(CpuStatus(), i)).zip(code) - } + val taggedCode = FlowAnalyzer.analyze(f, effectiveCode, options, needsFlowInfo) optimizeImpl(f, taggedCode, options) } @@ -754,4 +743,12 @@ case class Before(pattern: AssemblyPattern) extends AssemblyLinePattern { } override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = ??? +} + +case class HasCallerCount(count: Int) extends AssemblyLinePattern { + override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = + line match { + case AssemblyLine(Opcode.LABEL, _, MemoryAddressConstant(Label(l)), _) => flowInfo.labelUseCount(l) == count + case _ => false + } } \ No newline at end of file diff --git a/src/main/scala/millfork/assembly/opt/SuperOptimizer.scala b/src/main/scala/millfork/assembly/opt/SuperOptimizer.scala index 6213d13d..c50b268d 100644 --- a/src/main/scala/millfork/assembly/opt/SuperOptimizer.scala +++ b/src/main/scala/millfork/assembly/opt/SuperOptimizer.scala @@ -21,6 +21,8 @@ object SuperOptimizer extends AssemblyOptimization { } if (options.flag(CompilationFlag.EmitCmosOpcodes)) { allOptimizers ++= CmosOptimizations.All + } else { + allOptimizers ++= LaterOptimizations.Nmos } allOptimizers ++= List( VariableToRegisterOptimization, diff --git a/src/test/scala/millfork/test/ComparisonSuite.scala b/src/test/scala/millfork/test/ComparisonSuite.scala index 5b849d3b..c2651f07 100644 --- a/src/test/scala/millfork/test/ComparisonSuite.scala +++ b/src/test/scala/millfork/test/ComparisonSuite.scala @@ -1,6 +1,6 @@ package millfork.test -import millfork.test.emu.EmuBenchmarkRun +import millfork.test.emu.{EmuBenchmarkRun, EmuSuperOptimizedRun, EmuUltraBenchmarkRun} import org.scalatest.{FunSuite, Matchers} /** @@ -55,7 +55,7 @@ class ComparisonSuite extends FunSuite with Matchers { } test("Carry flag optimization test") { - EmuBenchmarkRun( + EmuUltraBenchmarkRun( """ | byte output @$c000 | void main () { diff --git a/src/test/scala/millfork/test/emu/EmuOptimizedInlinedRun.scala b/src/test/scala/millfork/test/emu/EmuOptimizedInlinedRun.scala index 8506cfb0..eca25e65 100644 --- a/src/test/scala/millfork/test/emu/EmuOptimizedInlinedRun.scala +++ b/src/test/scala/millfork/test/emu/EmuOptimizedInlinedRun.scala @@ -1,5 +1,6 @@ package millfork.test.emu +import millfork.assembly.opt.LaterOptimizations import millfork.{Cpu, OptimizationPresets} /** @@ -8,7 +9,10 @@ import millfork.{Cpu, OptimizationPresets} object EmuOptimizedInlinedRun extends EmuRun( Cpu.StrictMos, OptimizationPresets.NodeOpt, - OptimizationPresets.AssOpt ++ OptimizationPresets.Good ++ OptimizationPresets.Good ++ OptimizationPresets.Good, + OptimizationPresets.AssOpt ++ + OptimizationPresets.Good ++ LaterOptimizations.Nmos ++ + OptimizationPresets.Good ++ LaterOptimizations.Nmos ++ + OptimizationPresets.Good, false) diff --git a/src/test/scala/millfork/test/emu/EmuOptimizedRun.scala b/src/test/scala/millfork/test/emu/EmuOptimizedRun.scala index 13d252b0..3c6f8dc0 100644 --- a/src/test/scala/millfork/test/emu/EmuOptimizedRun.scala +++ b/src/test/scala/millfork/test/emu/EmuOptimizedRun.scala @@ -1,5 +1,6 @@ package millfork.test.emu +import millfork.assembly.opt.LaterOptimizations import millfork.{Cpu, OptimizationPresets} /** @@ -8,7 +9,10 @@ import millfork.{Cpu, OptimizationPresets} object EmuOptimizedRun extends EmuRun( Cpu.StrictMos, OptimizationPresets.NodeOpt, - OptimizationPresets.AssOpt ++ OptimizationPresets.Good ++ OptimizationPresets.Good ++ OptimizationPresets.Good, + OptimizationPresets.AssOpt ++ + OptimizationPresets.Good ++ LaterOptimizations.Nmos ++ + OptimizationPresets.Good ++ LaterOptimizations.Nmos ++ + OptimizationPresets.Good, false) diff --git a/src/test/scala/millfork/test/emu/EmuQuantumOptimizedRun.scala b/src/test/scala/millfork/test/emu/EmuQuantumOptimizedRun.scala index 9a85ac7c..dbdf4007 100644 --- a/src/test/scala/millfork/test/emu/EmuQuantumOptimizedRun.scala +++ b/src/test/scala/millfork/test/emu/EmuQuantumOptimizedRun.scala @@ -1,5 +1,6 @@ package millfork.test.emu +import millfork.assembly.opt.LaterOptimizations import millfork.{Cpu, OptimizationPresets} /** @@ -8,7 +9,10 @@ import millfork.{Cpu, OptimizationPresets} object EmuQuantumOptimizedRun extends EmuRun( Cpu.StrictMos, OptimizationPresets.NodeOpt, - OptimizationPresets.AssOpt ++ OptimizationPresets.Good ++ OptimizationPresets.Good ++ OptimizationPresets.Good, + OptimizationPresets.AssOpt ++ + OptimizationPresets.Good ++ LaterOptimizations.Nmos ++ + OptimizationPresets.Good ++ LaterOptimizations.Nmos ++ + OptimizationPresets.Good, true) diff --git a/src/test/scala/millfork/test/emu/EmuUndocumentedRun.scala b/src/test/scala/millfork/test/emu/EmuUndocumentedRun.scala index 16f9d1ae..c68d37a3 100644 --- a/src/test/scala/millfork/test/emu/EmuUndocumentedRun.scala +++ b/src/test/scala/millfork/test/emu/EmuUndocumentedRun.scala @@ -1,6 +1,6 @@ package millfork.test.emu -import millfork.assembly.opt.UndocumentedOptimizations +import millfork.assembly.opt.{LaterOptimizations, UndocumentedOptimizations} import millfork.{Cpu, OptimizationPresets} /** @@ -9,7 +9,11 @@ import millfork.{Cpu, OptimizationPresets} object EmuUndocumentedRun extends EmuRun( Cpu.Ricoh, // not Cpu.Mos, because I haven't found an emulator that supports both illegals and decimal mode yet OptimizationPresets.NodeOpt, - OptimizationPresets.AssOpt ++ UndocumentedOptimizations.All ++ OptimizationPresets.Good ++ UndocumentedOptimizations.All ++ OptimizationPresets.Good, + OptimizationPresets.AssOpt ++ LaterOptimizations.Nmos ++ + UndocumentedOptimizations.All ++ + OptimizationPresets.Good ++ LaterOptimizations.Nmos ++ + UndocumentedOptimizations.All ++ + OptimizationPresets.Good, false) { override def emitIllegals = true