1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-07-05 09:28:54 +00:00

Allow for function alignment

This commit is contained in:
Karol Stasiak 2018-08-07 23:55:08 +02:00
parent 46df8a6f21
commit 46ce602a3e
6 changed files with 44 additions and 31 deletions

View File

@ -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")

View File

@ -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 {

View File

@ -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")

View File

@ -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) -> ""
}

View File

@ -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

View File

@ -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
})) {