mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-23 23:30:22 +00:00
6502: Improve inlining of functions with lots of parameters.
This commit is contained in:
parent
fa7844e0b8
commit
457472080f
@ -28,6 +28,8 @@
|
||||
|
||||
* 6502: Few minor optimization improvements.
|
||||
|
||||
* 6502: Inlining improvements.
|
||||
|
||||
## 0.3.6
|
||||
|
||||
* **Breaking change!**
|
||||
|
8
src/main/scala/millfork/env/Thing.scala
vendored
8
src/main/scala/millfork/env/Thing.scala
vendored
@ -445,6 +445,8 @@ trait ParamSignature {
|
||||
def canBePointedTo: Boolean
|
||||
|
||||
def requireTrampoline(compilationOptions: CompilationOptions): Boolean
|
||||
|
||||
def paramThingNames: Set[String]
|
||||
}
|
||||
|
||||
case class NormalParamSignature(params: List[VariableInMemory]) extends ParamSignature {
|
||||
@ -459,6 +461,8 @@ case class NormalParamSignature(params: List[VariableInMemory]) extends ParamSig
|
||||
case _ => false
|
||||
}
|
||||
|
||||
def paramThingNames: Set[String] = params.map(_.name).toSet
|
||||
|
||||
}
|
||||
|
||||
sealed trait ParamPassingConvention {
|
||||
@ -525,6 +529,8 @@ case class AssemblyParamSignature(params: List[AssemblyParam]) extends ParamSign
|
||||
|
||||
override def requireTrampoline(compilationOptions: CompilationOptions): Boolean =
|
||||
false // all pointable functions with this kind of signature by definition use the pure register-cased parameter passing convention
|
||||
|
||||
def paramThingNames: Set[String] = params.map(_.variable.name).toSet
|
||||
}
|
||||
|
||||
case class EmptyFunctionParamSignature(paramType: Type) extends ParamSignature {
|
||||
@ -535,4 +541,6 @@ case class EmptyFunctionParamSignature(paramType: Type) extends ParamSignature {
|
||||
def canBePointedTo: Boolean = false
|
||||
|
||||
override def requireTrampoline(compilationOptions: CompilationOptions): Boolean = false
|
||||
|
||||
def paramThingNames: Set[String] = Set.empty
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
val code = compileFunction(function, optimizations, options, inlinedFunctions, labelMap.toMap, niceFunctionProperties.toSet)
|
||||
val strippedCodeForInlining = for {
|
||||
limit <- potentiallyInlineable.get(f)
|
||||
if code.map(_.sizeInBytes).sum <= limit
|
||||
if inliningCalculator.calculateExpectedSizeAfterInlining(options, function.params, code) <= limit
|
||||
s <- inliningCalculator.codeForInlining(f, functionsThatCanBeCalledFromInlinedFunctions, code)
|
||||
} yield s
|
||||
strippedCodeForInlining match {
|
||||
|
@ -1,10 +1,11 @@
|
||||
package millfork.output
|
||||
|
||||
import millfork.JobContext
|
||||
import millfork.assembly.{AbstractCode, Elidability}
|
||||
import millfork.assembly.m6809.MOpcode
|
||||
import millfork.assembly.mos.Opcode
|
||||
import millfork.assembly.z80.ZOpcode
|
||||
import millfork.compiler.AbstractCompiler
|
||||
import millfork.{CompilationOptions, JobContext}
|
||||
import millfork.assembly.{AbstractCode, Elidability}
|
||||
import millfork.env.ParamSignature
|
||||
import millfork.node._
|
||||
|
||||
import scala.collection.mutable
|
||||
@ -16,8 +17,10 @@ import scala.collection.mutable
|
||||
case class InliningResult(potentiallyInlineableFunctions: Map[String, Int], nonInlineableFunctions: Set[String])
|
||||
|
||||
abstract class AbstractInliningCalculator[T <: AbstractCode] {
|
||||
|
||||
def codeForInlining(fname: String, functionsThatCanBeCalledFromInlinedFunctions: Set[String], code: List[T]): Option[List[T]]
|
||||
def inline(code: List[T], inlinedFunctions: Map[String, List[T]], jobContext: JobContext): List[T]
|
||||
def calculateExpectedSizeAfterInlining(options: CompilationOptions, params: ParamSignature, code: List[T]): Int
|
||||
|
||||
private val sizes = Seq(64, 64, 8, 6, 5, 5, 4)
|
||||
|
||||
@ -74,6 +77,7 @@ abstract class AbstractInliningCalculator[T <: AbstractCode] {
|
||||
case Assignment(VariableExpression(_), expr) => getAllCalledFunctions(expr :: Nil)
|
||||
case MosAssemblyStatement(Opcode.JSR, _, VariableExpression(name), Elidability.Elidable) => (name -> false) :: Nil
|
||||
case Z80AssemblyStatement(ZOpcode.CALL, _, _, VariableExpression(name), Elidability.Elidable) => (name -> false) :: Nil
|
||||
case M6809AssemblyStatement(MOpcode.JSR, _, VariableExpression(name), Elidability.Elidable) => (name -> false) :: Nil
|
||||
case s: Statement => getAllCalledFunctions(s.getAllExpressions)
|
||||
case s: VariableExpression => Set(
|
||||
s.name,
|
||||
@ -94,4 +98,14 @@ abstract class AbstractInliningCalculator[T <: AbstractCode] {
|
||||
case SeparateBytesExpression(h, l) => getAllCalledFunctions(List(h, l))
|
||||
case _ => Nil
|
||||
}
|
||||
|
||||
protected def extractThingName(fullName: String): String = {
|
||||
var result = fullName.takeWhile(_ != '.')
|
||||
if (result.length == fullName.length) return result
|
||||
val suffix = fullName.drop(result.length)
|
||||
if (suffix == ".return" || suffix.startsWith(".return.")) {
|
||||
result += ".return"
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
package millfork.output
|
||||
|
||||
import millfork.JobContext
|
||||
import millfork.{CompilationOptions, JobContext}
|
||||
import millfork.assembly.m6809.MLine
|
||||
import millfork.env.ParamSignature
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
@ -9,6 +10,14 @@ import millfork.assembly.m6809.MLine
|
||||
object M6809InliningCalculator extends AbstractInliningCalculator[MLine] {
|
||||
override def codeForInlining(fname: String, functionsThatCanBeCalledFromInlinedFunctions: Set[String], code: List[MLine]): Option[List[MLine]] = None
|
||||
|
||||
override def calculateExpectedSizeAfterInlining(options: CompilationOptions, params: ParamSignature, code: List[MLine]): Int = {
|
||||
var sum = 0
|
||||
for (c <- code) {
|
||||
sum += c.sizeInBytes
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
override def inline(code: List[MLine], inlinedFunctions: Map[String, List[MLine]], jobContext: JobContext): List[MLine] = {
|
||||
if (inlinedFunctions.isEmpty) code
|
||||
else ???
|
||||
|
@ -1,6 +1,6 @@
|
||||
package millfork.output
|
||||
|
||||
import millfork.JobContext
|
||||
import millfork.{CompilationOptions, JobContext}
|
||||
import millfork.assembly.Elidability
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos.{AddrMode, _}
|
||||
@ -16,10 +16,38 @@ import scala.collection.mutable
|
||||
|
||||
object MosInliningCalculator extends AbstractInliningCalculator[AssemblyLine] {
|
||||
|
||||
private val sizes = Seq(64, 64, 8, 6, 5, 5, 4)
|
||||
|
||||
private val badOpcodes = Set(RTI, RTS, JSR, BRK, RTL, BSR, BYTE) ++ OpcodeClasses.ChangesStack
|
||||
private val jumpingRelatedOpcodes = Set(LABEL, JMP) ++ OpcodeClasses.ShortBranching
|
||||
def calculateExpectedSizeAfterInlining(options: CompilationOptions, params: ParamSignature, code: List[AssemblyLine]): Int = {
|
||||
var sum = 0
|
||||
var beforeParams = true
|
||||
val paramNames = params.paramThingNames
|
||||
for ((c, ix) <- code.zipWithIndex) {
|
||||
import Opcode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
sum += (c match {
|
||||
case AssemblyLine0(LABEL, _, _) if ix == 0 => 0
|
||||
|
||||
case AssemblyLine0(
|
||||
LDA | LDX | LDY | LDZ,
|
||||
Absolute | ZeroPage,
|
||||
MemoryAddressConstant(thing))
|
||||
if beforeParams && paramNames(extractThingName(thing.name))
|
||||
=> 1 // a guess of how likely parameter copying can be avoided
|
||||
|
||||
case AssemblyLine0(
|
||||
STA | STX | STY | STZ,
|
||||
Absolute | ZeroPage | LongAbsolute,
|
||||
_)
|
||||
=> c.sizeInBytes
|
||||
|
||||
case _ =>
|
||||
beforeParams = false
|
||||
c.sizeInBytes
|
||||
})
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
def codeForInlining(fname: String, functionsThatCanBeCalledFromInlinedFunctions: Set[String], code: List[AssemblyLine]): Option[List[AssemblyLine]] = {
|
||||
if (code.isEmpty) return None
|
||||
|
@ -1,9 +1,8 @@
|
||||
package millfork.output
|
||||
|
||||
import millfork.JobContext
|
||||
import millfork.{CompilationOptions, JobContext}
|
||||
import millfork.assembly.Elidability
|
||||
import millfork.assembly.z80._
|
||||
import millfork.compiler.AbstractCompiler
|
||||
import millfork.env._
|
||||
|
||||
import scala.collection.GenTraversableOnce
|
||||
@ -18,6 +17,14 @@ object Z80InliningCalculator extends AbstractInliningCalculator[ZLine] {
|
||||
private val badOpcodes = Set(RET, RETI, RETN, CALL, BYTE, POP, PUSH)
|
||||
private val jumpingRelatedOpcodes = Set(LABEL, JP, JR)
|
||||
|
||||
override def calculateExpectedSizeAfterInlining(options: CompilationOptions, params: ParamSignature, code: List[ZLine]): Int = {
|
||||
var sum = 0
|
||||
for (c <- code) {
|
||||
sum += c.sizeInBytes
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
override def codeForInlining(fname: String, functionsThatCanBeCalledFromInlinedFunctions: Set[String], code: List[ZLine]): Option[List[ZLine]] = {
|
||||
if (code.isEmpty) return None
|
||||
code.last match {
|
||||
|
Loading…
Reference in New Issue
Block a user