1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-12 06:29:34 +00:00

Allow for preinitialized global variables

This commit is contained in:
Karol Stasiak 2017-12-19 18:58:33 +01:00
parent 115f040fe2
commit 86ef4fcaf4
6 changed files with 91 additions and 15 deletions

View File

@ -102,7 +102,7 @@ object Main {
ErrorReporting.debug(f"Unoptimized code size: ${assembler.unoptimizedCodeSize}%5d B")
ErrorReporting.debug(f"Optimized code size: ${assembler.optimizedCodeSize}%5d B")
ErrorReporting.debug(f"Gain: ${(100L * (assembler.unoptimizedCodeSize - assembler.optimizedCodeSize) / assembler.unoptimizedCodeSize.toDouble).round}%5d%%")
ErrorReporting.debug(f"Initialized arrays: ${assembler.initializedArraysSize}%5d B")
ErrorReporting.debug(f"Initialized variables: ${assembler.initializedVariablesSize}%5d B")
if (c.outputAssembly) {
val path = Paths.get(assOutput)

View File

@ -54,9 +54,10 @@ class Environment(val parent: Option[Environment], val prefix: String) {
case _ => None
}.toList
def allPreallocatables: List[PrellocableThing] = things.values.flatMap {
def allPreallocatables: List[PreallocableThing] = things.values.flatMap {
case m: NormalFunction => Some(m)
case m: InitializedArray => Some(m)
case m: InitializedMemoryVariable => Some(m)
case _ => None
}.toList
@ -413,7 +414,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
stmt.assemblyParamPassingConvention match {
case ByVariable(name) =>
val zp = typ.name == "pointer" // TODO
val v = MemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto)
val v = UninitializedMemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto)
addThing(v, stmt.position)
registerAddressConstant(v, stmt.position)
if (typ.size == 2) {
@ -452,7 +453,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
case Some(aa) => RelativeArray(stmt.name + ".array", aa, length.toInt)
}
addThing(array, stmt.position)
registerAddressConstant(MemoryVariable(stmt.name, p, VariableAllocationMethod.None), stmt.position)
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None), stmt.position)
val a = address match {
case None => array.toAddress
case Some(aa) => aa
@ -486,7 +487,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
val data = contents.map(x => eval(x).getOrElse(Constant.error(s"Array `${stmt.name}` has non-constant contents", stmt.position)))
val array = InitializedArray(stmt.name + ".array", address, data)
addThing(array, stmt.position)
registerAddressConstant(MemoryVariable(stmt.name, p, VariableAllocationMethod.None), stmt.position)
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None), stmt.position)
val a = address match {
case None => array.toAddress
case Some(aa) => aa
@ -538,7 +539,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
}
} else {
if (stmt.stack && stmt.global) ErrorReporting.error(s"`$name` is static or global and cannot be on stack", position)
if (stmt.initialValue.isDefined) ErrorReporting.error(s"`$name` is not a constant and cannot have a value", position)
if (stmt.initialValue.isDefined && parent.isDefined) ErrorReporting.error(s"`$name` is local and not a constant and therefore cannot have a value", position)
if (stmt.stack) {
val v = StackVariable(prefix + name, typ, this.baseStackOffset)
baseStackOffset += typ.size
@ -550,7 +551,16 @@ class Environment(val parent: Option[Environment], val prefix: String) {
} else {
val (v, addr) = stmt.address.fold[(VariableInMemory, Constant)]({
val alloc = if (typ.name == "pointer") VariableAllocationMethod.Zeropage else if (stmt.global) VariableAllocationMethod.Static else VariableAllocationMethod.Auto
val v = MemoryVariable(prefix + name, typ, alloc)
if (alloc != VariableAllocationMethod.Static && stmt.initialValue.isDefined) {
ErrorReporting.error(s"`$name` cannot be preinitialized`", position)
}
val v = stmt.initialValue.fold[MemoryVariable](UninitializedMemoryVariable(prefix + name, typ, alloc)){ive =>
if (options.flags(CompilationFlag.ReadOnlyArrays)) {
ErrorReporting.warn("Initialized variable in read-only segment", options, position)
}
val ivc = eval(ive).getOrElse(Constant.error(s"Initial value of `$name` is not a constant", position))
InitializedMemoryVariable(name, None, typ, ivc)
}
registerAddressConstant(v, stmt.position)
(v, v.toAddress)
})(a => {

View File

@ -75,7 +75,7 @@ sealed trait ThingInMemory extends Thing {
def toAddress: Constant
}
sealed trait PrellocableThing extends ThingInMemory {
sealed trait PreallocableThing extends ThingInMemory {
def shouldGenerate: Boolean
def address: Option[Constant]
@ -118,7 +118,15 @@ case class StackVariable(name: String, typ: Type, baseOffset: Int) extends Varia
def sizeInBytes: Int = typ.size
}
case class MemoryVariable(name: String, typ: Type, alloc: VariableAllocationMethod.Value) extends VariableInMemory with UninitializedMemory {
object MemoryVariable {
def unapply(v: MemoryVariable) = Some((v.name, v.typ, v.alloc))
}
abstract class MemoryVariable extends VariableInMemory {
def alloc: VariableAllocationMethod.Value
}
case class UninitializedMemoryVariable(name: String, typ: Type, alloc: VariableAllocationMethod.Value) extends MemoryVariable with UninitializedMemory {
override def sizeInBytes: Int = typ.size
override def zeropage: Boolean = alloc == VariableAllocationMethod.Zeropage
@ -126,6 +134,16 @@ case class MemoryVariable(name: String, typ: Type, alloc: VariableAllocationMeth
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
}
case class InitializedMemoryVariable(name: String, address: Option[Constant], typ: Type, initialValue: Constant) extends MemoryVariable with PreallocableThing {
override def zeropage: Boolean = false
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
override def shouldGenerate: Boolean = true
override def alloc: VariableAllocationMethod.Value = VariableAllocationMethod.Static
}
trait MlArray extends ThingInMemory
case class UninitializedArray(name: String, sizeInBytes: Int) extends MlArray with UninitializedMemory {
@ -138,7 +156,7 @@ case class RelativeArray(name: String, address: Constant, sizeInBytes: Int) exte
override def toAddress: Constant = address
}
case class InitializedArray(name: String, address: Option[Constant], contents: List[Constant]) extends MlArray with PrellocableThing {
case class InitializedArray(name: String, address: Option[Constant], contents: List[Constant]) extends MlArray with PreallocableThing {
override def shouldGenerate = true
}
@ -196,7 +214,7 @@ case class NormalFunction(name: String,
code: List[ExecutableStatement],
interrupt: Boolean,
reentrant: Boolean,
position: Option[Position]) extends FunctionInMemory with PrellocableThing {
position: Option[Position]) extends FunctionInMemory with PreallocableThing {
override def shouldGenerate = true
}

View File

@ -21,7 +21,7 @@ class Assembler(private val rootEnv: Environment) {
var env = rootEnv.allThings
var unoptimizedCodeSize = 0
var optimizedCodeSize = 0
var initializedArraysSize = 0
var initializedVariablesSize = 0
val mem = new CompiledMemory
val labelMap = mutable.Map[String, Int]()
@ -154,7 +154,7 @@ class Assembler(private val rootEnv: Environment) {
mem.banks(0).writeable(index) = true
index += 1
}
initializedArraysSize += items.length
initializedVariablesSize += items.length
case InitializedArray(name, Some(_), items) => ???
case f: NormalFunction if f.address.isDefined =>
var index = f.address.get.asInstanceOf[NumericConstant].value.toInt
@ -187,7 +187,23 @@ class Assembler(private val rootEnv: Environment) {
mem.banks(0).writeable(index) = true
index += 1
}
initializedArraysSize += items.length
initializedVariablesSize += items.length
case m@InitializedMemoryVariable(name, None, typ, value) =>
if (options.flags(CompilationFlag.PreventJmpIndirectBug) && (index & 0xff) + typ.size > 0x100) {
index = (index & 0xffff00) + 0x100
}
labelMap(name) = index
val altName = m.name.stripPrefix(env.prefix) + "`"
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
assembly.append("* = $" + index.toHexString)
assembly.append(name)
for(i <- 0 until typ.size) {
writeByte(0, index, value.subbyte(i))
assembly.append(" !byte " + value.subbyte(i).quickSimplify)
mem.banks(0).writeable(index) = true
index += 1
}
initializedVariablesSize += typ.size
case _ =>
}
val allocator = platform.allocator

View File

@ -25,4 +25,31 @@ class BasicSymonTest extends FunSuite with Matchers {
| }
""".stripMargin).readByte(0xc000) should equal(1)
}
test("Preallocated variables") {
val m = EmuUnoptimizedRun(
"""
| array output [2] @$c000
| byte number = 4
| void main () {
| output[0] = number
| number += 1
| output[1] = number
| }
""".stripMargin)
m.readByte(0xc000) should equal(4)
m.readByte(0xc001) should equal(5)
}
test("Preallocated variables 2") {
val m = EmuUnoptimizedRun(
"""
| word output @$c000
| word number = 344
| void main () {
| output = number
| }
""".stripMargin)
m.readWord(0xc000) should equal(344)
}
}

View File

@ -6,7 +6,7 @@ import com.loomcom.symon.{Bus, Cpu, CpuState}
import fastparse.core.Parsed.{Failure, Success}
import millfork.assembly.opt.AssemblyOptimization
import millfork.compiler.{CompilationContext, MlCompiler}
import millfork.env.{Environment, InitializedArray, NormalFunction}
import millfork.env.{Environment, InitializedArray, InitializedMemoryVariable, NormalFunction}
import millfork.error.ErrorReporting
import millfork.node.StandardCallGraph
import millfork.node.opt.NodeOptimization
@ -134,6 +134,11 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
d.contents.foreach(c => println(" !byte " + c))
unoptimizedSize += d.contents.length
optimizedSize += d.contents.length
case d: InitializedMemoryVariable =>
println(d.name)
0.until(d.typ.size).foreach(c => println(" !byte " + d.initialValue.subbyte(c)))
unoptimizedSize += d.typ.size
optimizedSize += d.typ.size
}
ErrorReporting.assertNoErrors("Compile failed")