From d4d9dafbbeebb999e797f09ab4bd973b80969bc1 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Tue, 6 Mar 2018 23:28:03 +0100 Subject: [PATCH] Inlining improvements --- .../scala/millfork/output/Assembler.scala | 9 +++++--- .../millfork/output/InliningCalculator.scala | 21 ++++++++++++------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/main/scala/millfork/output/Assembler.scala b/src/main/scala/millfork/output/Assembler.scala index f222264c..ca3bf335 100644 --- a/src/main/scala/millfork/output/Assembler.scala +++ b/src/main/scala/millfork/output/Assembler.scala @@ -160,8 +160,7 @@ class Assembler(private val program: Program, private val rootEnv: Environment) val assembly = mutable.ArrayBuffer[String]() - val potentiallyInlineable: Map[String, Int] = - InliningCalculator.getPotentiallyInlineableFunctions( + val inliningResult = InliningCalculator.calculate( program, options.flags(CompilationFlag.InlineFunctions) || options.flags(CompilationFlag.OptimizeForSonicSpeed), if (options.flags(CompilationFlag.OptimizeForSonicSpeed)) 4.0 @@ -171,6 +170,9 @@ class Assembler(private val program: Program, private val rootEnv: Environment) else if (options.flags(CompilationFlag.OptimizeForSpeed)) 8.0 else 1.2) + val potentiallyInlineable: Map[String, Int] = inliningResult.potentiallyInlineableFunctions + var nonInlineableFunctions: Set[String] = inliningResult.nonInlineableFunctions + var inlinedFunctions = Map[String, List[AssemblyLine]]() val compiledFunctions = mutable.Map[String, List[AssemblyLine]]() val recommendedCompilationOrder = callGraph.recommendedCompilationOrder @@ -180,7 +182,7 @@ class Assembler(private val program: Program, private val rootEnv: Environment) val strippedCodeForInlining = for { limit <- potentiallyInlineable.get(f) if code.map(_.sizeInBytes).sum <= limit - s <- InliningCalculator.codeForInlining(f, code) + s <- InliningCalculator.codeForInlining(f, nonInlineableFunctions, code) } yield s strippedCodeForInlining match { case Some(c) => @@ -188,6 +190,7 @@ class Assembler(private val program: Program, private val rootEnv: Environment) inlinedFunctions += f -> c compiledFunctions(f) = Nil case None => + nonInlineableFunctions += function.name compiledFunctions(f) = code optimizedCodeSize += code.map(_.sizeInBytes).sum } diff --git a/src/main/scala/millfork/output/InliningCalculator.scala b/src/main/scala/millfork/output/InliningCalculator.scala index 4b37f75c..9b776183 100644 --- a/src/main/scala/millfork/output/InliningCalculator.scala +++ b/src/main/scala/millfork/output/InliningCalculator.scala @@ -11,14 +11,17 @@ import scala.collection.mutable /** * @author Karol Stasiak */ + +case class InliningResult(potentiallyInlineableFunctions: Map[String, Int], nonInlineableFunctions: Set[String]) + object InliningCalculator { private val sizes = Seq(64, 64, 8, 6, 5, 5, 4) - def getPotentiallyInlineableFunctions(program: Program, - inlineByDefault: Boolean, - aggressivenessForNormal: Double, - aggressivenessForRecommended: Double): Map[String, Int] = { + def calculate(program: Program, + inlineByDefault: Boolean, + aggressivenessForNormal: Double, + aggressivenessForRecommended: Double): InliningResult = { val callCount = mutable.Map[String, Int]().withDefaultValue(0) val allFunctions = mutable.Set[String]() val badFunctions = mutable.Set[String]() @@ -44,11 +47,12 @@ object InliningCalculator { } allFunctions --= badFunctions recommendedFunctions --= badFunctions - (if (inlineByDefault) allFunctions else recommendedFunctions).map(f => f -> { + val map = (if (inlineByDefault) allFunctions else recommendedFunctions).map(f => f -> { val size = sizes(callCount(f) min (sizes.size - 1)) val aggressiveness = if (recommendedFunctions(f)) aggressivenessForRecommended else aggressivenessForNormal (size * aggressiveness).floor.toInt }).toMap + InliningResult(map, badFunctions.toSet) } private def getAllCalledFunctions(expressions: List[Node]): List[(String, Boolean)] = expressions.flatMap { @@ -79,7 +83,7 @@ object InliningCalculator { private val badOpcodes = Set(RTI, RTS, JSR, BRK, RTL, BSR) ++ OpcodeClasses.ChangesStack private val jumpingRelatedOpcodes = Set(LABEL, JMP) ++ OpcodeClasses.ShortBranching - def codeForInlining(fname: String, code: List[AssemblyLine]): Option[List[AssemblyLine]] = { + def codeForInlining(fname: String, functionsAlreadyKnownToBeNonInlineable: Set[String], code: List[AssemblyLine]): Option[List[AssemblyLine]] = { if (code.isEmpty) return None val lastOpcode = code.last.opcode if (lastOpcode != RTS && lastOpcode != RTL) return None @@ -89,8 +93,11 @@ object InliningCalculator { } if (result.head.opcode == LABEL && result.head.parameter == Label(fname).toAddress) result = result.tail if (result.exists{ - case AssemblyLine(op, AddrMode.Absolute | AddrMode.Relative, MemoryAddressConstant(Label(l)), _) if jumpingRelatedOpcodes(op) => + case AssemblyLine(op, AddrMode.Absolute | AddrMode.Relative | AddrMode.DoesNotExist, MemoryAddressConstant(Label(l)), _) if jumpingRelatedOpcodes(op) => !l.startsWith(".") + case AssemblyLine(JSR, AddrMode.Absolute, MemoryAddressConstant(th:ExternFunction), _) => false + case AssemblyLine(JSR, AddrMode.Absolute, MemoryAddressConstant(th:NormalFunction), _) => + !functionsAlreadyKnownToBeNonInlineable(th.name) case AssemblyLine(op, _, _, _) if jumpingRelatedOpcodes(op) || badOpcodes(op) => true case _ => false }) return None