From 46ce602a3ea9d00730df5d0b5eee45cc9026c4f3 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Tue, 7 Aug 2018 23:55:08 +0200 Subject: [PATCH] Allow for function alignment --- src/main/scala/millfork/env/Environment.scala | 10 ++++- src/main/scala/millfork/env/Thing.scala | 5 +-- .../millfork/output/AbstractAssembler.scala | 14 +++---- .../millfork/output/CompiledFunction.scala | 2 +- .../scala/millfork/output/Deduplicate.scala | 42 +++++++++++-------- .../millfork/output/VariableAllocator.scala | 2 +- 6 files changed, 44 insertions(+), 31 deletions(-) diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index 009cc7f5..b456f8b6 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -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") diff --git a/src/main/scala/millfork/env/Thing.scala b/src/main/scala/millfork/env/Thing.scala index 753e0792..3a2aa47e 100644 --- a/src/main/scala/millfork/env/Thing.scala +++ b/src/main/scala/millfork/env/Thing.scala @@ -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 { diff --git a/src/main/scala/millfork/output/AbstractAssembler.scala b/src/main/scala/millfork/output/AbstractAssembler.scala index b16059f6..6d01cbbc 100644 --- a/src/main/scala/millfork/output/AbstractAssembler.scala +++ b/src/main/scala/millfork/output/AbstractAssembler.scala @@ -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") diff --git a/src/main/scala/millfork/output/CompiledFunction.scala b/src/main/scala/millfork/output/CompiledFunction.scala index 1ca46185..c2b4ab50 100644 --- a/src/main/scala/millfork/output/CompiledFunction.scala +++ b/src/main/scala/millfork/output/CompiledFunction.scala @@ -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) -> "" } diff --git a/src/main/scala/millfork/output/Deduplicate.scala b/src/main/scala/millfork/output/Deduplicate.scala index 7e19ed7c..f8b9b976 100644 --- a/src/main/scala/millfork/output/Deduplicate.scala +++ b/src/main/scala/millfork/output/Deduplicate.scala @@ -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 diff --git a/src/main/scala/millfork/output/VariableAllocator.scala b/src/main/scala/millfork/output/VariableAllocator.scala index 47dd243d..e5255d4d 100644 --- a/src/main/scala/millfork/output/VariableAllocator.scala +++ b/src/main/scala/millfork/output/VariableAllocator.scala @@ -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 })) {