From c846a19eef4575968d59ed4639f44163a2853af7 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Tue, 7 Aug 2018 17:31:41 +0200 Subject: [PATCH] Preliminary support for object alignment --- .../compiler/AbstractExpressionCompiler.scala | 5 +- .../compiler/AbstractReturnDispatch.scala | 3 +- .../AbstractStatementPreprocessor.scala | 4 +- .../compiler/mos/MosReturnDispatch.scala | 7 ++- .../compiler/z80/Z80ReturnDispatch.scala | 5 +- src/main/scala/millfork/env/Environment.scala | 57 ++++++++++++------- src/main/scala/millfork/env/Thing.scala | 22 ++++--- src/main/scala/millfork/node/Node.scala | 7 ++- .../millfork/output/AbstractAssembler.scala | 20 +++---- .../millfork/output/MemoryAlignment.scala | 10 ++++ .../millfork/output/VariableAllocator.scala | 30 ++++++---- src/main/scala/millfork/parser/MfParser.scala | 6 +- 12 files changed, 113 insertions(+), 63 deletions(-) create mode 100644 src/main/scala/millfork/output/MemoryAlignment.scala diff --git a/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala b/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala index d147e521..f4625e1e 100644 --- a/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala @@ -29,7 +29,10 @@ class AbstractExpressionCompiler[T <: AbstractCode] { def callingContext(ctx: CompilationContext, v: MemoryVariable): CompilationContext = { val result = new Environment(Some(ctx.env), "", ctx.options.platform.cpuFamily, ctx.jobContext) - result.registerVariable(VariableDeclarationStatement(v.name, v.typ.name, stack = false, global = false, constant = false, volatile = false, register = false, initialValue = None, address = None, bank = v.declaredBank), ctx.options) + result.registerVariable(VariableDeclarationStatement( + v.name, v.typ.name, + stack = false, global = false, constant = false, volatile = false, register = false, + initialValue = None, address = None, bank = v.declaredBank, alignment = None), ctx.options) ctx.copy(env = result) } diff --git a/src/main/scala/millfork/compiler/AbstractReturnDispatch.scala b/src/main/scala/millfork/compiler/AbstractReturnDispatch.scala index c228fe3c..0096d2d3 100644 --- a/src/main/scala/millfork/compiler/AbstractReturnDispatch.scala +++ b/src/main/scala/millfork/compiler/AbstractReturnDispatch.scala @@ -5,6 +5,7 @@ import millfork.assembly.AbstractCode import millfork.env._ import millfork.error.ConsoleLogger import millfork.node._ +import millfork.output.NoAlignment import scala.collection.mutable @@ -132,7 +133,7 @@ abstract class AbstractReturnDispatch[T <: AbstractCode] { val a = InitializedArray(label + "$" + ix + ".array", None, (paramMins(ix) to paramMaxes(ix)).map { key => map(key)._2.lift(ix).getOrElse(LiteralExpression(0, 1)) }.toList, - ctx.function.declaredBank, b, b) + ctx.function.declaredBank, b, b, NoAlignment) env.registerUnnamedArray(a) a } diff --git a/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala b/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala index 20fee2e8..ccd2379a 100644 --- a/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala +++ b/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala @@ -34,7 +34,7 @@ abstract class AbstractStatementPreprocessor(ctx: CompilationContext, statements } protected val reentrantVars: Set[String] = trackableVars.filter(v => env.get[Variable](v) match { case _: StackVariable => true - case UninitializedMemoryVariable(_, _, VariableAllocationMethod.Auto, _) => ctx.options.flag(CompilationFlag.DangerousOptimizations) + case UninitializedMemoryVariable(_, _, VariableAllocationMethod.Auto, _, _) => ctx.options.flag(CompilationFlag.DangerousOptimizations) case _ => false }) protected val nonreentrantVars: Set[String] = trackableVars -- reentrantVars @@ -172,7 +172,7 @@ abstract class AbstractStatementPreprocessor(ctx: CompilationContext, statements case TextLiteralExpression(characters) => val name = genName(characters) if (ctx.env.maybeGet[Thing](name).isEmpty) { - ctx.env.root.registerArray(ArrayDeclarationStatement(name, None, None, None, Some(LiteralContents(characters))).pos(pos), ctx.options) + ctx.env.root.registerArray(ArrayDeclarationStatement(name, None, None, None, Some(LiteralContents(characters)), None).pos(pos), ctx.options) } VariableExpression(name).pos(pos) case VariableExpression(v) if currentVarValues.contains(v) => diff --git a/src/main/scala/millfork/compiler/mos/MosReturnDispatch.scala b/src/main/scala/millfork/compiler/mos/MosReturnDispatch.scala index fa9d303b..f9345c20 100644 --- a/src/main/scala/millfork/compiler/mos/MosReturnDispatch.scala +++ b/src/main/scala/millfork/compiler/mos/MosReturnDispatch.scala @@ -7,6 +7,7 @@ import millfork.compiler.{AbstractReturnDispatch, BranchSpec, CompilationContext import millfork.env._ import millfork.error.ConsoleLogger import millfork.node._ +import millfork.output.NoAlignment import scala.collection.mutable @@ -40,7 +41,7 @@ object MosReturnDispatch extends AbstractReturnDispatch[AssemblyLine] { } if (useJmpaix) { - val jumpTable = InitializedArray(label + "$jt.array", None, (actualMin to actualMax).flatMap(i => List(lobyte0(map(i)._1), hibyte0(map(i)._1))).toList, ctx.function.declaredBank, b, b) + val jumpTable = InitializedArray(label + "$jt.array", None, (actualMin to actualMax).flatMap(i => List(lobyte0(map(i)._1), hibyte0(map(i)._1))).toList, ctx.function.declaredBank, b, b, NoAlignment) env.registerUnnamedArray(jumpTable) if (copyParams.isEmpty) { val loadIndex = MosExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(MosRegister.A, b)), BranchSpec.None) @@ -55,8 +56,8 @@ object MosReturnDispatch extends AbstractReturnDispatch[AssemblyLine] { } } else { val loadIndex = MosExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(MosRegister.X, b)), BranchSpec.None) - val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => lobyte1(map(i)._1)).toList, ctx.function.declaredBank, b, b) - val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => hibyte1(map(i)._1)).toList, ctx.function.declaredBank, b, b) + val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => lobyte1(map(i)._1)).toList, ctx.function.declaredBank, b, b, NoAlignment) + val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => hibyte1(map(i)._1)).toList, ctx.function.declaredBank, b, b, NoAlignment) env.registerUnnamedArray(jumpTableLo) env.registerUnnamedArray(jumpTableHi) val actualJump = if (ctx.options.flag(CompilationFlag.LUnixRelocatableCode)) { diff --git a/src/main/scala/millfork/compiler/z80/Z80ReturnDispatch.scala b/src/main/scala/millfork/compiler/z80/Z80ReturnDispatch.scala index a6fa8d53..bb4a8a09 100644 --- a/src/main/scala/millfork/compiler/z80/Z80ReturnDispatch.scala +++ b/src/main/scala/millfork/compiler/z80/Z80ReturnDispatch.scala @@ -5,6 +5,7 @@ import millfork.compiler.{AbstractReturnDispatch, CompilationContext} import millfork.env.{Constant, InitializedArray, ThingInMemory, VariableType} import millfork.error.ConsoleLogger import millfork.node.{Expression, ReturnDispatchStatement, ZRegister} +import millfork.output.NoAlignment import scala.collection.mutable @@ -50,8 +51,8 @@ object Z80ReturnDispatch extends AbstractReturnDispatch[ZLine] { } val copyParams = pair._2.reverse.flatten val offsetAfterParams = pair._1 - val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => lobyte0(map(i)._1)).toList, ctx.function.declaredBank, b, b) - val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => hibyte0(map(i)._1)).toList, ctx.function.declaredBank, b, b) + val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => lobyte0(map(i)._1)).toList, ctx.function.declaredBank, b, b, NoAlignment) + val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => hibyte0(map(i)._1)).toList, ctx.function.declaredBank, b, b, NoAlignment) env.registerUnnamedArray(jumpTableLo) env.registerUnnamedArray(jumpTableHi) diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index 94b10965..1bc9d556 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -7,7 +7,7 @@ import millfork.assembly.z80.{IfFlagClear, IfFlagSet, ZFlag} import millfork.compiler.LabelGenerator import millfork.error.Logger import millfork.node._ -import millfork.output.{AllocationLocation, CompiledMemory, VariableAllocator} +import millfork.output._ import scala.collection.mutable @@ -156,7 +156,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa case VariableAllocationMethod.Zeropage => if (forZpOnly || !options.platform.hasZeroPage) { val addr = - allocators(bank).allocateBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Zeropage) + allocators(bank).allocateBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Zeropage, alignment = m.alignment) onEachVariable(m.name, addr) List( ConstantThing(m.name.stripPrefix(prefix) + "`", NumericConstant(addr, 2), p) @@ -170,7 +170,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa val graveName = m.name.stripPrefix(prefix) + "`" if (forZpOnly) { if (bank == "default") { - allocators(bank).tryAllocateZeropageBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes) match { + allocators(bank).tryAllocateZeropageBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, alignment = m.alignment) match { case None => Nil case Some(addr) => onEachVariable(m.name, addr) @@ -182,7 +182,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa } else if (things.contains(graveName)) { Nil } else { - val addr = allocators(bank).allocateBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Either) + val addr = allocators(bank).allocateBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Either, alignment = m.alignment) onEachVariable(m.name, addr) List( ConstantThing(graveName, NumericConstant(addr, 2), p) @@ -294,8 +294,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa InitializedMemoryVariable UninitializedMemoryVariable getArrayOrPointer(name) match { - case th@InitializedArray(_, _, cs, _, i, e) => ConstantPointy(th.toAddress, Some(name), Some(cs.length), i, e) - case th@UninitializedArray(_, size, _, i, e) => ConstantPointy(th.toAddress, Some(name), Some(size), i, e) + case th@InitializedArray(_, _, cs, _, i, e, _) => ConstantPointy(th.toAddress, Some(name), Some(cs.length), i, e) + case th@UninitializedArray(_, size, _, i, e, _) => ConstantPointy(th.toAddress, Some(name), Some(size), i, e) case th@RelativeArray(_, _, size, _, i, e) => ConstantPointy(th.toAddress, Some(name), Some(size), i, e) case ConstantThing(_, value, typ) if typ.size <= 2 => val b = get[VariableType]("byte") @@ -713,7 +713,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa }) } if (resultType.size > Cpu.getMaxSizeReturnableViaRegisters(options.platform.cpu, options)) { - registerVariable(VariableDeclarationStatement(stmt.name + ".return", stmt.resultType, None, global = true, stack = false, constant = false, volatile = false, register = false, None, None), options) + registerVariable(VariableDeclarationStatement(stmt.name + ".return", stmt.resultType, None, global = true, stack = false, constant = false, volatile = false, register = false, None, None, None), options) } stmt.statements match { case None => @@ -786,7 +786,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa if (!thing.zeropage && options.flag(CompilationFlag.LUnixRelocatableCode)) { val b = get[Type]("byte") val w = get[Type]("word") - val relocatable = UninitializedMemoryVariable(thing.name + ".addr", w, VariableAllocationMethod.Static, None) + val relocatable = UninitializedMemoryVariable(thing.name + ".addr", w, VariableAllocationMethod.Static, None, defaultVariableAlignment(options, 2)) val addr = relocatable.toAddress addThing(relocatable, position) addThing(RelativeVariable(thing.name + ".addr.hi", addr + 1, b, zeropage = false, None), position) @@ -814,7 +814,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa stmt.assemblyParamPassingConvention match { case ByVariable(name) => val zp = typ.name == "pointer" // TODO - val v = UninitializedMemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto, None) + val v = UninitializedMemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto, None, defaultVariableAlignment(options, 2)) addThing(v, stmt.position) registerAddressConstant(v, stmt.position, options) val addr = v.toAddress @@ -870,6 +870,18 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa } } + private def defaultArrayAlignment(options: CompilationOptions, size: Long): MemoryAlignment = { + if (options.platform.cpuFamily == CpuFamily.M6502 && + options.flag(CompilationFlag.OptimizeForSpeed) && + size <= 256) WithinPageAlignment + else NoAlignment + } + + private def defaultVariableAlignment(options: CompilationOptions, size: Long): MemoryAlignment = { + if (options.flag(CompilationFlag.PreventJmpIndirectBug) && size == 2) WithinPageAlignment + else NoAlignment + } + def registerArray(stmt: ArrayDeclarationStatement, options: CompilationOptions): Unit = { val b = get[VariableType]("byte") val w = get[VariableType]("word") @@ -897,14 +909,15 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa lengthConst match { case NumericConstant(length, _) => if (length > 0xffff || length < 0) log.error(s"Array `${stmt.name}` has invalid length", stmt.position) + val alignment = stmt.alignment.getOrElse(defaultArrayAlignment(options, length)) val array = address match { case None => UninitializedArray(stmt.name + ".array", length.toInt, - declaredBank = stmt.bank, indexType, b) + declaredBank = stmt.bank, indexType, b, alignment) case Some(aa) => RelativeArray(stmt.name + ".array", aa, length.toInt, declaredBank = stmt.bank, indexType, b) } addThing(array, stmt.position) - registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, stmt.bank), stmt.position, options) + registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, stmt.bank, NoAlignment), stmt.position, options) val a = address match { case None => array.toAddress case Some(aa) => aa @@ -914,7 +927,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa if (options.flag(CompilationFlag.LUnixRelocatableCode)) { val b = get[Type]("byte") val w = get[Type]("word") - val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None) + val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None, NoAlignment) val addr = relocatable.toAddress addThing(relocatable, stmt.position) addThing(RelativeVariable(stmt.name + ".addr.hi", addr + 1, b, zeropage = false, None), stmt.position) @@ -968,11 +981,12 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa } val length = contents.length if (length > 0xffff || length < 0) log.error(s"Array `${stmt.name}` has invalid length", stmt.position) + val alignment = stmt.alignment.getOrElse(defaultArrayAlignment(options, length)) val address = stmt.address.map(a => eval(a).getOrElse(errorConstant(s"Array `${stmt.name}` has non-constant address", stmt.position))) - val array = InitializedArray(stmt.name + ".array", address, contents, declaredBank = stmt.bank, indexType, b) + val array = InitializedArray(stmt.name + ".array", address, contents, declaredBank = stmt.bank, indexType, b, alignment) addThing(array, stmt.position) registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, - declaredBank = stmt.bank), stmt.position, options) + declaredBank = stmt.bank, alignment), stmt.position, options) val a = address match { case None => array.toAddress case Some(aa) => aa @@ -982,7 +996,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa if (options.flag(CompilationFlag.LUnixRelocatableCode)) { val b = get[Type]("byte") val w = get[Type]("word") - val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None) + val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None, NoAlignment) val addr = relocatable.toAddress addThing(relocatable, stmt.position) addThing(RelativeVariable(stmt.name + ".array.hi", addr + 1, b, zeropage = false, None), stmt.position) @@ -1023,6 +1037,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa case _ => () } } + val alignment = stmt.alignment.getOrElse(defaultVariableAlignment(options, typ.size)) if (stmt.constant) { if (stmt.stack) log.error(s"`$name` is a constant and cannot be on stack", position) if (stmt.register) log.error(s"`$name` is a constant and cannot be in a register", position) @@ -1060,11 +1075,11 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa log.error(s"`$name` cannot be preinitialized`", position) } val v = stmt.initialValue.fold[MemoryVariable](UninitializedMemoryVariable(prefix + name, typ, alloc, - declaredBank = stmt.bank)){ive => + declaredBank = stmt.bank, alignment)){ive => if (options.flags(CompilationFlag.ReadOnlyArrays)) { log.warn("Initialized variable in read-only segment", position) } - InitializedMemoryVariable(name, None, typ, ive, declaredBank = stmt.bank) + InitializedMemoryVariable(name, None, typ, ive, declaredBank = stmt.bank, alignment) } registerAddressConstant(v, stmt.position, options) (v, v.toAddress) @@ -1096,6 +1111,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa if (typ.name == "__reg$type") { return (".lo", 0, b) :: (".hi", 1, b) :: + (".loword", 0, w) :: (".b2b3", 2, w) :: List.tabulate(typ.size) { i => (".b" + i, i, b) } } @@ -1161,7 +1177,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa def collectDeclarations(program: Program, options: CompilationOptions): Unit = { val b = get[VariableType]("byte") if (options.flag(CompilationFlag.OptimizeForSonicSpeed)) { - addThing(InitializedArray("identity$", None, List.tabulate(256)(n => LiteralExpression(n, 1)), declaredBank = None, b, b), None) + addThing(InitializedArray("identity$", None, List.tabulate(256)(n => LiteralExpression(n, 1)), declaredBank = None, b, b, defaultArrayAlignment(options, 256)), None) } program.declarations.foreach { case a: AliasDefinitionStatement => registerAlias(a) @@ -1189,11 +1205,12 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa volatile = false, register = false, initialValue = None, - address = None), options) + address = None, + alignment = None), options) } if (CpuFamily.forType(options.platform.cpu) == CpuFamily.M6502) { if (!things.contains("__constant8")) { - things("__constant8") = InitializedArray("__constant8", None, List(LiteralExpression(8, 1)), declaredBank = None, b, b) + things("__constant8") = InitializedArray("__constant8", None, List(LiteralExpression(8, 1)), declaredBank = None, b, b, NoAlignment) } } } diff --git a/src/main/scala/millfork/env/Thing.scala b/src/main/scala/millfork/env/Thing.scala index 39cbe774..4b39949f 100644 --- a/src/main/scala/millfork/env/Thing.scala +++ b/src/main/scala/millfork/env/Thing.scala @@ -1,9 +1,9 @@ package millfork.env import millfork.assembly.BranchingOpcodeMapping -import millfork.{CompilationFlag, CompilationOptions, CpuFamily} -import millfork.assembly.mos.Opcode +import millfork.{CompilationFlag, CompilationOptions} import millfork.node._ +import millfork.output.{MemoryAlignment, NoAlignment} sealed trait Thing { def name: String @@ -27,7 +27,7 @@ sealed trait Type extends CallableThing { def isCompatible(other: Type): Boolean = this == other - override def toString(): String = name + override def toString: String = name def isAssignableTo(targetType: Type): Boolean = isCompatible(targetType) @@ -110,6 +110,8 @@ sealed trait PreallocableThing extends ThingInMemory { def address: Option[Constant] + def alignment: MemoryAlignment + def toAddress: Constant = address.getOrElse(MemoryAddressConstant(this)) } @@ -152,6 +154,8 @@ sealed trait UninitializedMemory extends ThingInMemory { def sizeInBytes: Int def alloc: VariableAllocationMethod.Value + + def alignment: MemoryAlignment } object VariableAllocationMethod extends Enumeration { @@ -170,7 +174,7 @@ abstract class MemoryVariable extends VariableInMemory { def alloc: VariableAllocationMethod.Value } -case class UninitializedMemoryVariable(name: String, typ: Type, alloc: VariableAllocationMethod.Value, declaredBank: Option[String]) extends MemoryVariable with UninitializedMemory { +case class UninitializedMemoryVariable(name: String, typ: Type, alloc: VariableAllocationMethod.Value, declaredBank: Option[String], override val alignment: MemoryAlignment) extends MemoryVariable with UninitializedMemory { override def sizeInBytes: Int = typ.size override def zeropage: Boolean = alloc == VariableAllocationMethod.Zeropage @@ -178,7 +182,7 @@ case class UninitializedMemoryVariable(name: String, typ: Type, alloc: VariableA override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this) } -case class InitializedMemoryVariable(name: String, address: Option[Constant], typ: Type, initialValue: Expression, declaredBank: Option[String]) extends MemoryVariable with PreallocableThing { +case class InitializedMemoryVariable(name: String, address: Option[Constant], typ: Type, initialValue: Expression, declaredBank: Option[String], override val alignment: MemoryAlignment) extends MemoryVariable with PreallocableThing { override def zeropage: Boolean = false override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this) @@ -193,10 +197,10 @@ trait MfArray extends ThingInMemory with IndexableThing { def elementType: VariableType } -case class UninitializedArray(name: String, sizeInBytes: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType) extends MfArray with UninitializedMemory { +case class UninitializedArray(name: String, sizeInBytes: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val alignment: MemoryAlignment) extends MfArray with UninitializedMemory { override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this) - override def alloc = VariableAllocationMethod.Static + override def alloc: VariableAllocationMethod.Value = VariableAllocationMethod.Static override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false) @@ -215,7 +219,7 @@ case class RelativeArray(name: String, address: Constant, sizeInBytes: Int, decl override def zeropage: Boolean = false } -case class InitializedArray(name: String, address: Option[Constant], contents: List[Expression], declaredBank: Option[String], indexType: VariableType, elementType: VariableType) extends MfArray with PreallocableThing { +case class InitializedArray(name: String, address: Option[Constant], contents: List[Expression], declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val alignment: MemoryAlignment) extends MfArray with PreallocableThing { override def shouldGenerate = true override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false) @@ -293,6 +297,8 @@ case class NormalFunction(name: String, 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/node/Node.scala b/src/main/scala/millfork/node/Node.scala index 9010db35..9759534c 100644 --- a/src/main/scala/millfork/node/Node.scala +++ b/src/main/scala/millfork/node/Node.scala @@ -3,6 +3,7 @@ package millfork.node import millfork.assembly.mos.{AddrMode, Opcode} import millfork.assembly.z80.{ZOpcode, ZRegisters} import millfork.env.{Constant, ParamPassingConvention, Type} +import millfork.output.MemoryAlignment case class Position(moduleName: String, line: Int, column: Int, cursor: Int) @@ -203,7 +204,8 @@ case class VariableDeclarationStatement(name: String, volatile: Boolean, register: Boolean, initialValue: Option[Expression], - address: Option[Expression]) extends DeclarationStatement { + address: Option[Expression], + alignment: Option[MemoryAlignment]) extends DeclarationStatement { override def getAllExpressions: List[Expression] = List(initialValue, address).flatten } @@ -268,7 +270,8 @@ case class ArrayDeclarationStatement(name: String, bank: Option[String], length: Option[Expression], address: Option[Expression], - elements: Option[ArrayContents]) extends DeclarationStatement { + elements: Option[ArrayContents], + alignment: Option[MemoryAlignment]) extends DeclarationStatement { override def getAllExpressions: List[Expression] = List(length, address).flatten ++ elements.fold(List[Expression]())(_.getAllExpressions) } diff --git a/src/main/scala/millfork/output/AbstractAssembler.scala b/src/main/scala/millfork/output/AbstractAssembler.scala index 84cc1604..7f1c1052 100644 --- a/src/main/scala/millfork/output/AbstractAssembler.scala +++ b/src/main/scala/millfork/output/AbstractAssembler.scala @@ -227,7 +227,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program } env.allPreallocatables.foreach { - case thing@InitializedArray(name, Some(NumericConstant(address, _)), items, _, _, _) => + case thing@InitializedArray(name, Some(NumericConstant(address, _)), items, _, _, _, NoAlignment) => val bank = thing.bank(options) val bank0 = mem.banks(bank) var index = address.toInt @@ -251,7 +251,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program }).mkString(", ")) } initializedVariablesSize += items.length - case thing@InitializedArray(name, Some(_), items, _, _, _) => ??? + case thing@InitializedArray(name, Some(_), items, _, _, _, NoAlignment) => ??? case f: NormalFunction if f.address.isDefined => val bank = f.bank(options) val bank0 = mem.banks(bank) @@ -279,7 +279,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program // already done before case (name, NormalCompiledFunction(bank, code, false)) => val size = code.map(_.sizeInBytes).sum - val index = codeAllocators(bank).allocateBytes(mem.banks(bank), options, size, initialized = true, writeable = false, location = AllocationLocation.High) + val index = codeAllocators(bank).allocateBytes(mem.banks(bank), options, size, initialized = true, writeable = false, location = AllocationLocation.High, alignment = NoAlignment) labelMap(name) = index justAfterCode += bank -> outputFunction(bank, code, index, assembly, options) case (_, NonexistentFunction()) => @@ -289,7 +289,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program if (options.flag(CompilationFlag.LUnixRelocatableCode)) { env.allThings.things.foreach { - case (_, m@UninitializedMemoryVariable(name, typ, _, _)) if name.endsWith(".addr") || env.maybeGet[Thing](name + ".array").isDefined => + case (_, m@UninitializedMemoryVariable(name, typ, _, _, NoAlignment)) if name.endsWith(".addr") || env.maybeGet[Thing](name + ".array").isDefined => val isUsed = compiledFunctions.values.exists{ case NormalCompiledFunction(_, code, _) => code.exists(_.parameter.isRelatedTo(m)) case _ => false @@ -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) + var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size + 1, initialized = true, writeable = false, location = AllocationLocation.High, alignment = NoAlignment) 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") @@ -320,17 +320,17 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program } case _ => () } - val index = codeAllocators("default").allocateBytes(mem.banks("default"), options, 1, initialized = true, writeable = false, location = AllocationLocation.High) + val index = codeAllocators("default").allocateBytes(mem.banks("default"), options, 1, initialized = true, writeable = false, location = AllocationLocation.High, alignment = NoAlignment) writeByte("default", index, 2.toByte) // BIT abs assembly.append("* = $" + index.toHexString) assembly.append(" !byte 2 ;; end of LUnix relocatable segment") justAfterCode += "default" -> (index + 1) } env.allPreallocatables.foreach { - case thing@InitializedArray(name, None, items, _, _, _) => + case thing@InitializedArray(name, None, items, _, _, _, alignment) => val bank = thing.bank(options) val bank0 = mem.banks(bank) - var index = codeAllocators(bank).allocateBytes(bank0, options, items.size, initialized = true, writeable = true, location = AllocationLocation.High) + var index = codeAllocators(bank).allocateBytes(bank0, options, items.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment) labelMap(name) = index assembly.append("* = $" + index.toHexString) assembly.append(name) @@ -349,10 +349,10 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program } initializedVariablesSize += items.length justAfterCode += bank -> index - case m@InitializedMemoryVariable(name, None, typ, value, _) => + case m@InitializedMemoryVariable(name, None, typ, value, _, alignment) => val bank = m.bank(options) val bank0 = mem.banks(bank) - var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size, initialized = true, writeable = true, location = AllocationLocation.High) + var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment) labelMap(name) = index val altName = m.name.stripPrefix(env.prefix) + "`" env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer")) diff --git a/src/main/scala/millfork/output/MemoryAlignment.scala b/src/main/scala/millfork/output/MemoryAlignment.scala new file mode 100644 index 00000000..0caa7f4d --- /dev/null +++ b/src/main/scala/millfork/output/MemoryAlignment.scala @@ -0,0 +1,10 @@ +package millfork.output + +/** + * @author Karol Stasiak + */ +sealed trait MemoryAlignment + +case object NoAlignment extends MemoryAlignment +case object WithinPageAlignment extends MemoryAlignment +case class DivisibleAlignment(divisor: Int) extends MemoryAlignment diff --git a/src/main/scala/millfork/output/VariableAllocator.scala b/src/main/scala/millfork/output/VariableAllocator.scala index a1bcc52c..47dd243d 100644 --- a/src/main/scala/millfork/output/VariableAllocator.scala +++ b/src/main/scala/millfork/output/VariableAllocator.scala @@ -28,13 +28,19 @@ sealed trait ByteAllocator { def notifyAboutEndOfCode(org: Int): Unit - def findFreeBytes(mem: MemoryBank, count: Int, options: CompilationOptions): Int = { + def findFreeBytes(mem: MemoryBank, count: Int, options: CompilationOptions, alignment: MemoryAlignment): Int = { var lastFree = startAt var counter = 0 val occupied = mem.occupied var previous = -800 for(i <- preferredOrder.getOrElse(startAt until endBefore)) { - if (occupied(i) || counter == 0 && count == 2 && i.&(0xff) == 0xff && options.flags(CompilationFlag.PreventJmpIndirectBug)) { + if (occupied(i)) { + counter = 0 + } else if (counter == 0 && (alignment match { + case WithinPageAlignment => i.&(0xff00) != i.+(count - 1).&(0xff00) + case DivisibleAlignment(divisor) => i % divisor != 0 + case NoAlignment => false + })) { counter = 0 } else { if (previous != i - 1) { @@ -87,7 +93,7 @@ class VariableAllocator(pointers: List[Int], private val bytes: ByteAllocator) { var heapStart: Int = bytes.startAt - def allocateBytes(mem: MemoryBank, callGraph: CallGraph, p: VariableVertex, options: CompilationOptions, count: Int, initialized: Boolean, writeable: Boolean, location: AllocationLocation.Value): Int = { + def allocateBytes(mem: MemoryBank, callGraph: CallGraph, p: VariableVertex, options: CompilationOptions, count: Int, initialized: Boolean, writeable: Boolean, location: AllocationLocation.Value, alignment: MemoryAlignment): Int = { if (!variableMap.contains(count)) { variableMap(count) = mutable.Map() } @@ -97,12 +103,12 @@ class VariableAllocator(pointers: List[Int], private val bytes: ByteAllocator) { return a } } - val addr = allocateBytes(mem, options, count, initialized, writeable, location) + val addr = allocateBytes(mem, options, count, initialized, writeable, location, alignment) variableMap(count)(addr) = Set(p) addr } - def tryAllocateZeropageBytes(mem: MemoryBank, callGraph: CallGraph, p: VariableVertex, options: CompilationOptions, count: Int): Option[Int]={ + def tryAllocateZeropageBytes(mem: MemoryBank, callGraph: CallGraph, p: VariableVertex, options: CompilationOptions, count: Int, alignment: MemoryAlignment): Option[Int]={ if (!variableMap.contains(count)) { variableMap(count) = mutable.Map() } @@ -112,32 +118,32 @@ class VariableAllocator(pointers: List[Int], private val bytes: ByteAllocator) { return Some(a) } } - val addr = zeropage.findFreeBytes(mem, count, options) + val addr = zeropage.findFreeBytes(mem, count, options, alignment) if (addr < 0) None else { markBytes(options.log, mem, addr, count, initialized = false, writeable = true) Some(addr) } } - def allocateBytes(mem: MemoryBank, options: CompilationOptions, count: Int, initialized: Boolean, writeable: Boolean, location: AllocationLocation.Value): Int = { + def allocateBytes(mem: MemoryBank, options: CompilationOptions, count: Int, initialized: Boolean, writeable: Boolean, location: AllocationLocation.Value, alignment: MemoryAlignment): Int = { val addr = if (options.platform.hasZeroPage) { location match { case AllocationLocation.Zeropage => - val a = zeropage.findFreeBytes(mem, count, options) + val a = zeropage.findFreeBytes(mem, count, options, alignment) if (a < 0) { options.log.fatal("Out of zeropage memory") } a case AllocationLocation.High => - val a = bytes.findFreeBytes(mem, count, options) + val a = bytes.findFreeBytes(mem, count, options, alignment) if (a < 0) { options.log.fatal("Out of high memory") } a case AllocationLocation.Either => - var a = zeropage.findFreeBytes(mem, count, options) + var a = zeropage.findFreeBytes(mem, count, options, alignment) if (a < 0) { - a = bytes.findFreeBytes(mem, count, options) + a = bytes.findFreeBytes(mem, count, options, alignment) if (a < 0) { options.log.fatal("Out of high memory") } @@ -145,7 +151,7 @@ class VariableAllocator(pointers: List[Int], private val bytes: ByteAllocator) { a } } else { - val a = bytes.findFreeBytes(mem, count, options) + val a = bytes.findFreeBytes(mem, count, options, alignment) if (a < 0) { options.log.fatal("Out of high memory") } diff --git a/src/main/scala/millfork/parser/MfParser.scala b/src/main/scala/millfork/parser/MfParser.scala index 74c8423d..1f6d161d 100644 --- a/src/main/scala/millfork/parser/MfParser.scala +++ b/src/main/scala/millfork/parser/MfParser.scala @@ -107,6 +107,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri name <- identifier ~/ HWS ~/ Pass addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).?.opaque("
") ~ HWS initialValue <- ("=" ~/ HWS ~/ mfExpression(1, false)).? ~ HWS + alignment = None // TODO _ <- &(EOL) ~/ "" } yield { Seq(VariableDeclarationStatement(name, typ, @@ -116,7 +117,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri constant = flags("const"), volatile = flags("volatile"), register = flags("register"), - initialValue, addr).pos(p)) + initialValue, addr, alignment).pos(p)) } val paramDefinition: P[ParameterDeclaration] = for { @@ -216,8 +217,9 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri name <- "array" ~ !letterOrDigit ~/ SWS ~ identifier ~ HWS length <- ("[" ~/ AWS ~/ mfExpression(nonStatementLevel, false) ~ AWS ~ "]").? ~ HWS addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).? ~/ HWS + alignment = None // TODO contents <- ("=" ~/ HWS ~/ arrayContents).? ~/ HWS - } yield Seq(ArrayDeclarationStatement(name, bank, length, addr, contents).pos(p)) + } yield Seq(ArrayDeclarationStatement(name, bank, length, addr, contents, alignment).pos(p)) def tightMfExpression(allowIntelHex: Boolean): P[Expression] = { val a = if (allowIntelHex) atomWithIntel else atom