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,
|
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")
|
||||||
|
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,
|
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 {
|
||||||
|
@ -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")
|
||||||
|
@ -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) -> ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
})) {
|
})) {
|
||||||
|
Loading…
Reference in New Issue
Block a user