1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-05-31 18:41:30 +00:00
millfork/src/main/scala/millfork/assembly/mos/opt/SuperOptimizer.scala
2023-01-27 18:13:21 +01:00

113 lines
4.5 KiB
Scala

package millfork.assembly.mos.opt
import millfork.{CompilationFlag, CompilationOptions, OptimizationPresets}
import millfork.assembly.{AssemblyOptimization, OptimizationContext}
import millfork.assembly.mos.{AddrMode, AssemblyLine, Opcode}
import millfork.compiler.LabelGenerator
import millfork.env.NormalFunction
import millfork.error.{ConsoleLogger, ErrorsOnlyLogger}
import scala.collection.mutable
/**
* @author Karol Stasiak
*/
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
var allOptimizers = OptimizationPresets.Good ++ LaterOptimizations.All
if (options.flag(CompilationFlag.EmitIllegals)) {
allOptimizers ++= UndocumentedOptimizations.All
}
if (options.flag(CompilationFlag.EmitCmosOpcodes)) {
allOptimizers ++= CmosOptimizations.All
} else {
allOptimizers ++= NmosOptimizations.All
}
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) {
allOptimizers ++= SixteenOptimizations.AllForEmulation
}
if (options.flag(CompilationFlag.EmitNative65816Opcodes)) {
allOptimizers ++= SixteenOptimizations.AllForNative
}
if (options.flag(CompilationFlag.EmitHudsonOpcodes)) {
allOptimizers ++= HudsonOptimizations.All
}
if (options.flag(CompilationFlag.Emit65CE02Opcodes)) {
allOptimizers ++= CE02Optimizations.All
}
if (options.zpRegisterSize > 0) {
allOptimizers ++= ZeropageRegisterOptimizations.All
}
allOptimizers ++= List(
LocalVariableReadOptimization,
ChangeIndexRegisterOptimizationPreferringX2Y,
ChangeIndexRegisterOptimizationPreferringY2X)
val seenSoFar = mutable.Set[CodeView]()
val queue = mutable.Queue[(List[AssemblyOptimization[AssemblyLine]], List[AssemblyLine])]()
val leaves = mutable.ListBuffer[(List[AssemblyOptimization[AssemblyLine]], List[AssemblyLine])]()
val quickScrub = List(
UnusedLabelRemoval,
AlwaysGoodOptimizations.BranchInPlaceRemoval,
AlwaysGoodOptimizations.PointlessLoadBeforeReturn,
AlwaysGoodOptimizations.UnusedCodeRemoval
)
val optionsForMeasurements = options.copy(
commandLineFlags = options.commandLineFlags + (CompilationFlag.InternalCurrentlyOptimizingForMeasurement -> true),
jobContext = options.jobContext.copy(log = new ErrorsOnlyLogger(options.log))
)
val optimizationContextForMeasurements = optimizationContext.copy(options = optionsForMeasurements)
val quicklyCleanedCode = quickScrub.foldLeft(code)((c, o) => o.optimize(m, c, optimizationContextForMeasurements))
seenSoFar += viewCode(quicklyCleanedCode)
queue.enqueue(quickScrub.reverse -> quicklyCleanedCode)
while(queue.nonEmpty) {
val (optsSoFar, codeSoFar) = queue.dequeue()
var isLeaf = true
(if (optionsForMeasurements.flag(CompilationFlag.SingleThreaded)) allOptimizers
else allOptimizers.par).foreach { o =>
val optimized = o.optimize(m, codeSoFar, optimizationContextForMeasurements)
val view = viewCode(optimized)
seenSoFar.synchronized{
if (!seenSoFar(view)) {
isLeaf = false
seenSoFar += view
queue.enqueue((o :: optsSoFar) -> optimized)
}
}
}
if (isLeaf) {
// println(codeSoFar.map(_.sizeInBytes).sum + " B: " + optsSoFar.reverse.map(_.name).mkString(" -> "))
// val view = viewCode(codeSoFar)
// println(f"${view.hashCode}%08X ${view.content.map(_._1).mkString(" ")}%s")
leaves += optsSoFar -> codeSoFar
}
}
val result = leaves.minBy(_._2.map(_.cost).sum)
log.debug(s"Visited ${leaves.size} leaves")
log.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) =>
val n = opt.optimize(m, c, optimizationContext)
// println(c.mkString("","",""))
// println(n.mkString("","",""))
n
}
result._2
}
override val name = "Superoptimizer"
def viewCode(code: List[AssemblyLine]): CodeView = {
val indexedSeq = code.view.map(l => l.opcode -> l.addrMode).toIndexedSeq
CodeView(indexedSeq.hashCode(), indexedSeq)
}
}
case class CodeView(override val hashCode: Int, content: IndexedSeq[(Opcode.Value, AddrMode.Value)])