1
0
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:
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, kernalInterrupt = stmt.kernalInterrupt,
reentrant = stmt.reentrant, reentrant = stmt.reentrant,
position = stmt.position, 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) addThing(mangled, stmt.position)
registerAddressConstant(mangled, stmt.position, options) registerAddressConstant(mangled, stmt.position, options)
@ -882,6 +883,13 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
else NoAlignment 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 = { def registerArray(stmt: ArrayDeclarationStatement, options: CompilationOptions): Unit = {
val b = get[VariableType]("byte") val b = get[VariableType]("byte")
val w = get[VariableType]("word") val w = get[VariableType]("word")

View File

@ -295,12 +295,11 @@ case class NormalFunction(name: String,
kernalInterrupt: Boolean, kernalInterrupt: Boolean,
reentrant: Boolean, reentrant: Boolean,
position: Option[Position], 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 shouldGenerate = true
override def zeropage: Boolean = false 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 { 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() compiledFunctions(f) = NonexistentFunction()
case None => case None =>
nonInlineableFunctions += function.name 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 optimizedCodeSize += code.map(_.sizeInBytes).sum
if (options.flag(CompilationFlag.InterproceduralOptimization)) { if (options.flag(CompilationFlag.InterproceduralOptimization)) {
gatherNiceFunctionProperties(niceFunctionProperties, f, code) gatherNiceFunctionProperties(niceFunctionProperties, f, code)
@ -257,7 +257,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
val bank0 = mem.banks(bank) val bank0 = mem.banks(bank)
val index = f.address.get.asInstanceOf[NumericConstant].value.toInt val index = f.address.get.asInstanceOf[NumericConstant].value.toInt
compiledFunctions(f.name) match { compiledFunctions(f.name) match {
case NormalCompiledFunction(_, code, _) => case NormalCompiledFunction(_, code, _, _) =>
labelMap(f.name) = index labelMap(f.name) = index
val end = outputFunction(bank, code, index, assembly, options) val end = outputFunction(bank, code, index, assembly, options)
for (i <- index until end) { 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) var justAfterCode = platform.codeAllocators.mapValues(a => a.startAt)
compiledFunctions.toList.sortBy{case (name, cf) => if (name == "main") 0 -> "" else cf.orderKey}.foreach { compiledFunctions.toList.sortBy{case (name, cf) => if (name == "main") 0 -> "" else cf.orderKey}.foreach {
case (_, NormalCompiledFunction(_, _, true)) => case (_, NormalCompiledFunction(_, _, true, _)) =>
// already done before // already done before
case (name, NormalCompiledFunction(bank, code, false)) => case (name, NormalCompiledFunction(bank, code, false, alignment)) =>
val size = code.map(_.sizeInBytes).sum 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 labelMap(name) = index
justAfterCode += bank -> outputFunction(bank, code, index, assembly, options) justAfterCode += bank -> outputFunction(bank, code, index, assembly, options)
case (_, NonexistentFunction()) => case (_, NonexistentFunction()) =>
@ -291,7 +291,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
env.allThings.things.foreach { env.allThings.things.foreach {
case (_, m@UninitializedMemoryVariable(name, typ, _, _, _)) if name.endsWith(".addr") || env.maybeGet[Thing](name + ".array").isDefined => case (_, m@UninitializedMemoryVariable(name, typ, _, _, _)) if name.endsWith(".addr") || env.maybeGet[Thing](name + ".array").isDefined =>
val isUsed = compiledFunctions.values.exists{ val isUsed = compiledFunctions.values.exists{
case NormalCompiledFunction(_, code, _) => code.exists(_.parameter.isRelatedTo(m)) case NormalCompiledFunction(_, code, _, _) => code.exists(_.parameter.isRelatedTo(m))
case _ => false case _ => false
} }
// println(m.name -> isUsed) // println(m.name -> isUsed)
@ -299,7 +299,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
val bank = m.bank(options) val bank = m.bank(options)
if (bank != "default") ??? if (bank != "default") ???
val bank0 = mem.banks(bank) 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 labelMap(name) = index + 1
val altName = m.name.stripPrefix(env.prefix) + "`" 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") 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) 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) -> "" 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]], 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 { bySegment(compiledFunctions).foreach {
case (segmentName, segContents) => case (segmentName, segContents) =>
function(segmentName, segContents).foreach { 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])]() var result = ListBuffer[(String, CompiledFunction[T])]()
val chunks = segContents.flatMap{ val chunks = segContents.flatMap{
case (_, Left(_)) => Nil case (_, Left(_)) => Nil
case (functionName, Right(code)) => case (functionName, Right(CodeAndAlignment(code, _))) =>
if (options.flag(CompilationFlag.OptimizeForSize)) { if (options.flag(CompilationFlag.OptimizeForSize)) {
getExtractableSnippets(functionName, code) getExtractableSnippets(functionName, code)
} else Nil } else Nil
@ -96,7 +96,7 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
} }
for((code, instances) <- best._2) { for((code, instances) <- best._2) {
val newName = env.nextLabel("xc") 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) { for(instance <- instances) {
toReplace(instance.functionName)(instance.offset) = newName toReplace(instance.functionName)(instance.offset) = newName
for (i <- instance.offset + 1 until instance.endOffset) { for (i <- instance.offset + 1 until instance.endOffset) {
@ -108,13 +108,14 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
result += functionName -> { result += functionName -> {
val linesToRemove = toRemove(functionName) val linesToRemove = toRemove(functionName)
val linesToReplace = toReplace(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) => case (line, i) =>
if (linesToRemove(i)) None if (linesToRemove(i)) None
else if (linesToReplace.contains(i)) Some(createCall(linesToReplace(i))) else if (linesToReplace.contains(i)) Some(createCall(linesToReplace(i)))
else Some(line) 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 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])]() var result = ListBuffer[(String, CompiledFunction[T])]()
val identicalFunctions = segContents.flatMap{ 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) }.groupBy(_._2).values.toSeq.map(_.keySet).filter(set => set.size > 1)
for(set <- identicalFunctions) { for(set <- identicalFunctions) {
val representative = if (set("main")) "main" else set.head 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) result += function -> RedirectedFunction(segmentName, representative, 0)
} else { } else {
segContents(function) match { segContents(function) match {
case Right(code) => case Right(CodeAndAlignment(code, alignment)) =>
result += function -> NormalCompiledFunction(segmentName, result += function -> NormalCompiledFunction(segmentName,
set.toList.map(name => createLabel(name)) ++ actualCode(function, code), set.toList.map(name => createLabel(name)) ++ actualCode(function, code),
hasFixedAddress = false) hasFixedAddress = false,
alignment = alignment)
case Left(_) => case Left(_) =>
} }
} }
@ -147,7 +149,7 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
result.toSeq 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 var result: String = to
val visited = mutable.Set[String]() val visited = mutable.Set[String]()
do { do {
@ -164,10 +166,10 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
None 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])]() var result = ListBuffer[(String, CompiledFunction[T])]()
val fallThroughList = segContents.flatMap { val fallThroughList = segContents.flatMap {
case (name, Right(code)) => case (name, Right(CodeAndAlignment(code, alignment))) =>
if (code.isEmpty) None if (code.isEmpty) None
else getJump(code.last) else getJump(code.last)
.filter(segContents.contains) .filter(segContents.contains)
@ -181,10 +183,12 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
fallthroughPredecessors.foreach { fallthroughPredecessors.foreach {
case (to, from) => case (to, from) =>
options.log.debug(s"Fallthrough from $from to $to") 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, result += from -> NormalCompiledFunction(segmentName,
init ++ segContents(to).right.get, init ++ segContents(to).right.get.code,
hasFixedAddress = false hasFixedAddress = false,
alignment = value.alignment
) )
val initSize = init.map(_.sizeInBytes).sum val initSize = init.map(_.sizeInBytes).sum
if (initSize <= 2) { if (initSize <= 2) {
@ -210,9 +214,9 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
def createLabel(name: String): T 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 { 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 (name, RedirectedFunction(segment, target, 0)) => Some((segment, name, Left(target))) // TODO
case _ => None case _ => None
}.groupBy(_._1).mapValues(_.map { case (_, name, code) => name -> code }.toMap) }.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]) { case class CodeChunk[T <: AbstractCode](functionName: String, offset: Int, endOffset: Int)(val code: List[T]) {
val codeSizeInBytes: Int = code.map(_.sizeInBytes).sum val codeSizeInBytes: Int = code.map(_.sizeInBytes).sum

View File

@ -37,7 +37,7 @@ sealed trait ByteAllocator {
if (occupied(i)) { if (occupied(i)) {
counter = 0 counter = 0
} else if (counter == 0 && (alignment match { } 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 DivisibleAlignment(divisor) => i % divisor != 0
case NoAlignment => false case NoAlignment => false
})) { })) {