1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-10-12 09:23:45 +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"Unoptimized code size: ${assembler.unoptimizedCodeSize}%5d B")
ErrorReporting.debug(f"Optimized code size: ${assembler.optimizedCodeSize}%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"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) { if (c.outputAssembly) {
val path = Paths.get(assOutput) val path = Paths.get(assOutput)

View File

@ -54,9 +54,10 @@ class Environment(val parent: Option[Environment], val prefix: String) {
case _ => None case _ => None
}.toList }.toList
def allPreallocatables: List[PrellocableThing] = things.values.flatMap { def allPreallocatables: List[PreallocableThing] = things.values.flatMap {
case m: NormalFunction => Some(m) case m: NormalFunction => Some(m)
case m: InitializedArray => Some(m) case m: InitializedArray => Some(m)
case m: InitializedMemoryVariable => Some(m)
case _ => None case _ => None
}.toList }.toList
@ -413,7 +414,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
stmt.assemblyParamPassingConvention match { stmt.assemblyParamPassingConvention match {
case ByVariable(name) => case ByVariable(name) =>
val zp = typ.name == "pointer" // TODO 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) addThing(v, stmt.position)
registerAddressConstant(v, stmt.position) registerAddressConstant(v, stmt.position)
if (typ.size == 2) { 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) case Some(aa) => RelativeArray(stmt.name + ".array", aa, length.toInt)
} }
addThing(array, stmt.position) 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 { val a = address match {
case None => array.toAddress case None => array.toAddress
case Some(aa) => aa 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 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) val array = InitializedArray(stmt.name + ".array", address, data)
addThing(array, stmt.position) 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 { val a = address match {
case None => array.toAddress case None => array.toAddress
case Some(aa) => aa case Some(aa) => aa
@ -538,7 +539,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
} }
} else { } else {
if (stmt.stack && stmt.global) ErrorReporting.error(s"`$name` is static or global and cannot be on stack", position) 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) { if (stmt.stack) {
val v = StackVariable(prefix + name, typ, this.baseStackOffset) val v = StackVariable(prefix + name, typ, this.baseStackOffset)
baseStackOffset += typ.size baseStackOffset += typ.size
@ -550,7 +551,16 @@ class Environment(val parent: Option[Environment], val prefix: String) {
} else { } else {
val (v, addr) = stmt.address.fold[(VariableInMemory, Constant)]({ 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 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) registerAddressConstant(v, stmt.position)
(v, v.toAddress) (v, v.toAddress)
})(a => { })(a => {

View File

@ -75,7 +75,7 @@ sealed trait ThingInMemory extends Thing {
def toAddress: Constant def toAddress: Constant
} }
sealed trait PrellocableThing extends ThingInMemory { sealed trait PreallocableThing extends ThingInMemory {
def shouldGenerate: Boolean def shouldGenerate: Boolean
def address: Option[Constant] def address: Option[Constant]
@ -118,7 +118,15 @@ case class StackVariable(name: String, typ: Type, baseOffset: Int) extends Varia
def sizeInBytes: Int = typ.size 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 sizeInBytes: Int = typ.size
override def zeropage: Boolean = alloc == VariableAllocationMethod.Zeropage 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) 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 trait MlArray extends ThingInMemory
case class UninitializedArray(name: String, sizeInBytes: Int) extends MlArray with UninitializedMemory { 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 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 override def shouldGenerate = true
} }
@ -196,7 +214,7 @@ case class NormalFunction(name: String,
code: List[ExecutableStatement], code: List[ExecutableStatement],
interrupt: Boolean, interrupt: Boolean,
reentrant: Boolean, reentrant: Boolean,
position: Option[Position]) extends FunctionInMemory with PrellocableThing { position: Option[Position]) extends FunctionInMemory with PreallocableThing {
override def shouldGenerate = true override def shouldGenerate = true
} }

View File

@ -21,7 +21,7 @@ class Assembler(private val rootEnv: Environment) {
var env = rootEnv.allThings var env = rootEnv.allThings
var unoptimizedCodeSize = 0 var unoptimizedCodeSize = 0
var optimizedCodeSize = 0 var optimizedCodeSize = 0
var initializedArraysSize = 0 var initializedVariablesSize = 0
val mem = new CompiledMemory val mem = new CompiledMemory
val labelMap = mutable.Map[String, Int]() val labelMap = mutable.Map[String, Int]()
@ -154,7 +154,7 @@ class Assembler(private val rootEnv: Environment) {
mem.banks(0).writeable(index) = true mem.banks(0).writeable(index) = true
index += 1 index += 1
} }
initializedArraysSize += items.length initializedVariablesSize += items.length
case InitializedArray(name, Some(_), items) => ??? case InitializedArray(name, Some(_), items) => ???
case f: NormalFunction if f.address.isDefined => case f: NormalFunction if f.address.isDefined =>
var index = f.address.get.asInstanceOf[NumericConstant].value.toInt 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 mem.banks(0).writeable(index) = true
index += 1 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 _ => case _ =>
} }
val allocator = platform.allocator val allocator = platform.allocator

View File

@ -25,4 +25,31 @@ class BasicSymonTest extends FunSuite with Matchers {
| } | }
""".stripMargin).readByte(0xc000) should equal(1) """.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 fastparse.core.Parsed.{Failure, Success}
import millfork.assembly.opt.AssemblyOptimization import millfork.assembly.opt.AssemblyOptimization
import millfork.compiler.{CompilationContext, MlCompiler} import millfork.compiler.{CompilationContext, MlCompiler}
import millfork.env.{Environment, InitializedArray, NormalFunction} import millfork.env.{Environment, InitializedArray, InitializedMemoryVariable, NormalFunction}
import millfork.error.ErrorReporting import millfork.error.ErrorReporting
import millfork.node.StandardCallGraph import millfork.node.StandardCallGraph
import millfork.node.opt.NodeOptimization 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)) d.contents.foreach(c => println(" !byte " + c))
unoptimizedSize += d.contents.length unoptimizedSize += d.contents.length
optimizedSize += 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") ErrorReporting.assertNoErrors("Compile failed")