mirror of
https://github.com/KarolS/millfork.git
synced 2024-09-10 11:54:30 +00:00
Allow for function alignment
This commit is contained in:
parent
46df8a6f21
commit
46ce602a3e
10
src/main/scala/millfork/env/Environment.scala
vendored
10
src/main/scala/millfork/env/Environment.scala
vendored
@ -774,7 +774,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
kernalInterrupt = stmt.kernalInterrupt,
|
||||
reentrant = stmt.reentrant,
|
||||
position = stmt.position,
|
||||
declaredBank = stmt.bank
|
||||
declaredBank = stmt.bank,
|
||||
alignment = defaultFunctionAlignment(options, hot = true) // TODO: decide actual hotness in a smarter way
|
||||
)
|
||||
addThing(mangled, stmt.position)
|
||||
registerAddressConstant(mangled, stmt.position, options)
|
||||
@ -882,6 +883,13 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
else NoAlignment
|
||||
}
|
||||
|
||||
private def defaultFunctionAlignment(options: CompilationOptions, hot: Boolean): MemoryAlignment = {
|
||||
// TODO:
|
||||
if (hot && options.platform.cpuFamily == CpuFamily.M6502 &&
|
||||
options.flag(CompilationFlag.OptimizeForSonicSpeed)) WithinPageAlignment
|
||||
else NoAlignment
|
||||
}
|
||||
|
||||
def registerArray(stmt: ArrayDeclarationStatement, options: CompilationOptions): Unit = {
|
||||
val b = get[VariableType]("byte")
|
||||
val w = get[VariableType]("word")
|
||||
|
5
src/main/scala/millfork/env/Thing.scala
vendored
5
src/main/scala/millfork/env/Thing.scala
vendored
@ -295,12 +295,11 @@ case class NormalFunction(name: String,
|
||||
kernalInterrupt: Boolean,
|
||||
reentrant: Boolean,
|
||||
position: Option[Position],
|
||||
declaredBank: Option[String]) extends FunctionInMemory with PreallocableThing {
|
||||
declaredBank: Option[String],
|
||||
override val alignment: MemoryAlignment) extends FunctionInMemory with PreallocableThing {
|
||||
override def shouldGenerate = true
|
||||
|
||||
override def zeropage: Boolean = false
|
||||
|
||||
override def alignment: MemoryAlignment = NoAlignment
|
||||
}
|
||||
|
||||
case class ConstantThing(name: String, value: Constant, typ: Type) extends TypedThing with VariableLikeThing with IndexableThing {
|
||||
|
@ -204,7 +204,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
compiledFunctions(f) = NonexistentFunction()
|
||||
case None =>
|
||||
nonInlineableFunctions += function.name
|
||||
compiledFunctions(f) = NormalCompiledFunction(function.declaredBank.getOrElse(platform.defaultCodeBank), code, function.address.isDefined)
|
||||
compiledFunctions(f) = NormalCompiledFunction(function.declaredBank.getOrElse(platform.defaultCodeBank), code, function.address.isDefined, function.alignment)
|
||||
optimizedCodeSize += code.map(_.sizeInBytes).sum
|
||||
if (options.flag(CompilationFlag.InterproceduralOptimization)) {
|
||||
gatherNiceFunctionProperties(niceFunctionProperties, f, code)
|
||||
@ -257,7 +257,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
val bank0 = mem.banks(bank)
|
||||
val index = f.address.get.asInstanceOf[NumericConstant].value.toInt
|
||||
compiledFunctions(f.name) match {
|
||||
case NormalCompiledFunction(_, code, _) =>
|
||||
case NormalCompiledFunction(_, code, _, _) =>
|
||||
labelMap(f.name) = index
|
||||
val end = outputFunction(bank, code, index, assembly, options)
|
||||
for (i <- index until end) {
|
||||
@ -275,11 +275,11 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
var justAfterCode = platform.codeAllocators.mapValues(a => a.startAt)
|
||||
|
||||
compiledFunctions.toList.sortBy{case (name, cf) => if (name == "main") 0 -> "" else cf.orderKey}.foreach {
|
||||
case (_, NormalCompiledFunction(_, _, true)) =>
|
||||
case (_, NormalCompiledFunction(_, _, true, _)) =>
|
||||
// already done before
|
||||
case (name, NormalCompiledFunction(bank, code, false)) =>
|
||||
case (name, NormalCompiledFunction(bank, code, false, alignment)) =>
|
||||
val size = code.map(_.sizeInBytes).sum
|
||||
val index = codeAllocators(bank).allocateBytes(mem.banks(bank), options, size, initialized = true, writeable = false, location = AllocationLocation.High, alignment = NoAlignment)
|
||||
val index = codeAllocators(bank).allocateBytes(mem.banks(bank), options, size, initialized = true, writeable = false, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = index
|
||||
justAfterCode += bank -> outputFunction(bank, code, index, assembly, options)
|
||||
case (_, NonexistentFunction()) =>
|
||||
@ -291,7 +291,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
env.allThings.things.foreach {
|
||||
case (_, m@UninitializedMemoryVariable(name, typ, _, _, _)) if name.endsWith(".addr") || env.maybeGet[Thing](name + ".array").isDefined =>
|
||||
val isUsed = compiledFunctions.values.exists{
|
||||
case NormalCompiledFunction(_, code, _) => code.exists(_.parameter.isRelatedTo(m))
|
||||
case NormalCompiledFunction(_, code, _, _) => code.exists(_.parameter.isRelatedTo(m))
|
||||
case _ => false
|
||||
}
|
||||
// println(m.name -> isUsed)
|
||||
@ -299,7 +299,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
val bank = m.bank(options)
|
||||
if (bank != "default") ???
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size + 1, initialized = true, writeable = false, location = AllocationLocation.High, alignment = NoAlignment)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size + 1, initialized = true, writeable = false, location = AllocationLocation.High, alignment = m.alignment)
|
||||
labelMap(name) = index + 1
|
||||
val altName = m.name.stripPrefix(env.prefix) + "`"
|
||||
val thing = if (name.endsWith(".addr")) env.get[ThingInMemory](name.stripSuffix(".addr")) else env.get[ThingInMemory](name + ".array")
|
||||
|
@ -9,7 +9,7 @@ sealed trait CompiledFunction[T <: AbstractCode] {
|
||||
def orderKey : (Int, String)
|
||||
}
|
||||
|
||||
case class NormalCompiledFunction[T <: AbstractCode](segment: String, code: List[T], hasFixedAddress: Boolean) extends CompiledFunction[T] {
|
||||
case class NormalCompiledFunction[T <: AbstractCode](segment: String, code: List[T], hasFixedAddress: Boolean, alignment: MemoryAlignment) extends CompiledFunction[T] {
|
||||
override def orderKey: (Int, String) = (if (hasFixedAddress) 1 else 2) -> ""
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
|
||||
}
|
||||
|
||||
def runStage(compiledFunctions: mutable.Map[String, CompiledFunction[T]],
|
||||
function: (String, Map[String, Either[String, List[T]]]) => Seq[(String, CompiledFunction[T])]): Unit = {
|
||||
function: (String, Map[String, Either[String, CodeAndAlignment[T]]]) => Seq[(String, CompiledFunction[T])]): Unit = {
|
||||
bySegment(compiledFunctions).foreach {
|
||||
case (segmentName, segContents) =>
|
||||
function(segmentName, segContents).foreach {
|
||||
@ -30,11 +30,11 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
|
||||
}
|
||||
}
|
||||
|
||||
def extractCommonCode(segmentName: String, segContents: Map[String, Either[String, List[T]]]): Seq[(String, CompiledFunction[T])] = {
|
||||
def extractCommonCode(segmentName: String, segContents: Map[String, Either[String, CodeAndAlignment[T]]]): Seq[(String, CompiledFunction[T])] = {
|
||||
var result = ListBuffer[(String, CompiledFunction[T])]()
|
||||
val chunks = segContents.flatMap{
|
||||
case (_, Left(_)) => Nil
|
||||
case (functionName, Right(code)) =>
|
||||
case (functionName, Right(CodeAndAlignment(code, _))) =>
|
||||
if (options.flag(CompilationFlag.OptimizeForSize)) {
|
||||
getExtractableSnippets(functionName, code)
|
||||
} else Nil
|
||||
@ -96,7 +96,7 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
|
||||
}
|
||||
for((code, instances) <- best._2) {
|
||||
val newName = env.nextLabel("xc")
|
||||
result += newName -> NormalCompiledFunction(segmentName, createLabel(newName) :: tco(code :+ createReturn), hasFixedAddress = false)
|
||||
result += newName -> NormalCompiledFunction(segmentName, createLabel(newName) :: tco(code :+ createReturn), hasFixedAddress = false, alignment = NoAlignment)
|
||||
for(instance <- instances) {
|
||||
toReplace(instance.functionName)(instance.offset) = newName
|
||||
for (i <- instance.offset + 1 until instance.endOffset) {
|
||||
@ -108,13 +108,14 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
|
||||
result += functionName -> {
|
||||
val linesToRemove = toRemove(functionName)
|
||||
val linesToReplace = toReplace(functionName)
|
||||
val newCode = segContents(functionName).right.get.zipWithIndex.flatMap{
|
||||
val value = segContents(functionName).right.get
|
||||
val newCode = value.code.zipWithIndex.flatMap{
|
||||
case (line, i) =>
|
||||
if (linesToRemove(i)) None
|
||||
else if (linesToReplace.contains(i)) Some(createCall(linesToReplace(i)))
|
||||
else Some(line)
|
||||
}
|
||||
NormalCompiledFunction(segmentName, tco(newCode), hasFixedAddress = false)
|
||||
NormalCompiledFunction(segmentName, tco(newCode), hasFixedAddress = false, alignment = value.alignment)
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,10 +123,10 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
|
||||
result.toSeq
|
||||
}
|
||||
|
||||
def deduplicateIdenticalFunctions(segmentName: String, segContents: Map[String, Either[String, List[T]]]): Seq[(String, CompiledFunction[T])] = {
|
||||
def deduplicateIdenticalFunctions(segmentName: String, segContents: Map[String, Either[String, CodeAndAlignment[T]]]): Seq[(String, CompiledFunction[T])] = {
|
||||
var result = ListBuffer[(String, CompiledFunction[T])]()
|
||||
val identicalFunctions = segContents.flatMap{
|
||||
case (name, code) => code.toOption.map(c => name -> actualCode(name, c))
|
||||
case (name, code) => code.toOption.map(c => name -> actualCode(name, c.code))
|
||||
}.groupBy(_._2).values.toSeq.map(_.keySet).filter(set => set.size > 1)
|
||||
for(set <- identicalFunctions) {
|
||||
val representative = if (set("main")) "main" else set.head
|
||||
@ -135,10 +136,11 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
|
||||
result += function -> RedirectedFunction(segmentName, representative, 0)
|
||||
} else {
|
||||
segContents(function) match {
|
||||
case Right(code) =>
|
||||
case Right(CodeAndAlignment(code, alignment)) =>
|
||||
result += function -> NormalCompiledFunction(segmentName,
|
||||
set.toList.map(name => createLabel(name)) ++ actualCode(function, code),
|
||||
hasFixedAddress = false)
|
||||
hasFixedAddress = false,
|
||||
alignment = alignment)
|
||||
case Left(_) =>
|
||||
}
|
||||
}
|
||||
@ -147,7 +149,7 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
|
||||
result.toSeq
|
||||
}
|
||||
|
||||
private def follow(segContents: Map[String, Either[String, List[T]]], to: String): Option[String] = {
|
||||
private def follow(segContents: Map[String, Either[String, CodeAndAlignment[T]]], to: String): Option[String] = {
|
||||
var result: String = to
|
||||
val visited = mutable.Set[String]()
|
||||
do {
|
||||
@ -164,10 +166,10 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
|
||||
None
|
||||
}
|
||||
|
||||
def eliminateTailJumps(segmentName: String, segContents: Map[String, Either[String, List[T]]]): Seq[(String, CompiledFunction[T])] = {
|
||||
def eliminateTailJumps(segmentName: String, segContents: Map[String, Either[String, CodeAndAlignment[T]]]): Seq[(String, CompiledFunction[T])] = {
|
||||
var result = ListBuffer[(String, CompiledFunction[T])]()
|
||||
val fallThroughList = segContents.flatMap {
|
||||
case (name, Right(code)) =>
|
||||
case (name, Right(CodeAndAlignment(code, alignment))) =>
|
||||
if (code.isEmpty) None
|
||||
else getJump(code.last)
|
||||
.filter(segContents.contains)
|
||||
@ -181,10 +183,12 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
|
||||
fallthroughPredecessors.foreach {
|
||||
case (to, from) =>
|
||||
options.log.debug(s"Fallthrough from $from to $to")
|
||||
val init = segContents(from).right.get.init
|
||||
val value = segContents(from).right.get
|
||||
val init = value.code.init
|
||||
result += from -> NormalCompiledFunction(segmentName,
|
||||
init ++ segContents(to).right.get,
|
||||
hasFixedAddress = false
|
||||
init ++ segContents(to).right.get.code,
|
||||
hasFixedAddress = false,
|
||||
alignment = value.alignment
|
||||
)
|
||||
val initSize = init.map(_.sizeInBytes).sum
|
||||
if (initSize <= 2) {
|
||||
@ -210,9 +214,9 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
|
||||
|
||||
def createLabel(name: String): T
|
||||
|
||||
def bySegment(compiledFunctions: mutable.Map[String, CompiledFunction[T]]): Map[String, Map[String, Either[String, List[T]]]] = {
|
||||
def bySegment(compiledFunctions: mutable.Map[String, CompiledFunction[T]]): Map[String, Map[String, Either[String, CodeAndAlignment[T]]]] = {
|
||||
compiledFunctions.flatMap {
|
||||
case (name, NormalCompiledFunction(segment, code, false)) => Some((segment, name, Right(code))) // TODO
|
||||
case (name, NormalCompiledFunction(segment, code, false, alignment)) => Some((segment, name, Right(CodeAndAlignment(code, alignment)))) // TODO
|
||||
case (name, RedirectedFunction(segment, target, 0)) => Some((segment, name, Left(target))) // TODO
|
||||
case _ => None
|
||||
}.groupBy(_._1).mapValues(_.map { case (_, name, code) => name -> code }.toMap)
|
||||
@ -252,6 +256,8 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
|
||||
}
|
||||
}
|
||||
|
||||
case class CodeAndAlignment[T <: AbstractCode](code: List[T], alignment: MemoryAlignment)
|
||||
|
||||
case class CodeChunk[T <: AbstractCode](functionName: String, offset: Int, endOffset: Int)(val code: List[T]) {
|
||||
val codeSizeInBytes: Int = code.map(_.sizeInBytes).sum
|
||||
|
||||
|
@ -37,7 +37,7 @@ sealed trait ByteAllocator {
|
||||
if (occupied(i)) {
|
||||
counter = 0
|
||||
} else if (counter == 0 && (alignment match {
|
||||
case WithinPageAlignment => i.&(0xff00) != i.+(count - 1).&(0xff00)
|
||||
case WithinPageAlignment => count <= 256 && i.&(0xff00) != i.+(count - 1).&(0xff00)
|
||||
case DivisibleAlignment(divisor) => i % divisor != 0
|
||||
case NoAlignment => false
|
||||
})) {
|
||||
|
Loading…
Reference in New Issue
Block a user