1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-05-31 18:41:30 +00:00

Add local arrays

This commit is contained in:
Karol Stasiak 2019-06-24 22:32:29 +02:00
parent 96b5918728
commit 25c440f17d
6 changed files with 84 additions and 30 deletions

View File

@ -36,6 +36,8 @@
* Arrays can now be constant.
* Arrays can now be local.
* Added hint for identifiers with typos.
* Aliases now also support subfields.

View File

@ -104,6 +104,10 @@ This allows for overriding definitions of library functions by another library:
An array is a continuous sequence of bytes in memory.
An array declaration can happen at either top level of a file (*global* arrays),
or a top level of a function (*local* arrays).
Regardless of where they were declared, arrays are considered static.
Syntax:
`[segment(<segment>)] [const] array [(<element type>)] <name> [[<size>]] [align ( <alignment> )] [@<address>] [= <initial_values>]`
@ -133,7 +137,8 @@ If the declared size and the size deduced from the `<initial_values>` don't matc
* `<address>` is a constant expression that defines where in the memory the array is or will be located.
* `<initial_values>` is an array literal, see [Literals](./literals.md)
* `<initial_values>` is an array literal, see [Literals](./literals.md).
Local arrays can have initial values only if they're const.
TODO

View File

@ -375,7 +375,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
orElse(maybeGet[ThingInMemory](arrayName)).
orElse(maybeGet[ThingInMemory](arrayName + ".array")).
orElse(maybeGet[ConstantThing](arrayName)).
getOrElse(log.fatal(s"`$arrayName` is not an array or a pointer"))
getOrElse{
log.error(s"`$arrayName` is not an array or a pointer")
get[Thing]("nullptr")
}
}
def getPointy(name: String): Pointy = {
@ -1018,6 +1021,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
case Some(statements) =>
statements.foreach {
case v: VariableDeclarationStatement => env.registerVariable(v, options, pointies(v.name))
case a: ArrayDeclarationStatement => env.registerArray(a, options)
case _ => ()
}
val executableStatements = statements.flatMap {
@ -1293,6 +1297,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
if (options.flag(CompilationFlag.LUnixRelocatableCode) && stmt.alignment.exists(_.isMultiplePages)) {
log.error("Invalid alignment for LUnix code", stmt.position)
}
if (stmt.elements.isDefined && !stmt.const && parent.isDefined) {
log.error(s"Local array `${stmt.name}` cannot be initialized if it's not const", stmt.position)
}
val arrayName = prefix + stmt.name
val b = get[VariableType]("byte")
val w = get[VariableType]("word")
val p = get[Type]("pointer")
@ -1328,38 +1336,38 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
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,
case None => UninitializedArray(arrayName + ".array", length.toInt,
declaredBank = stmt.bank, indexType, e, stmt.const, alignment)
case Some(aa) => RelativeArray(stmt.name + ".array", aa, length.toInt,
case Some(aa) => RelativeArray(arrayName + ".array", aa, length.toInt,
declaredBank = stmt.bank, indexType, e, stmt.const)
}
addThing(array, stmt.position)
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, stmt.bank, alignment, isVolatile = false), stmt.position, options, Some(e))
registerAddressConstant(UninitializedMemoryVariable(arrayName, p, VariableAllocationMethod.None, stmt.bank, alignment, isVolatile = false), stmt.position, options, Some(e))
val a = address match {
case None => array.toAddress
case Some(aa) => aa
}
addThing(RelativeVariable(stmt.name + ".first", a, b, zeropage = false,
addThing(RelativeVariable(arrayName + ".first", a, b, zeropage = false,
declaredBank = stmt.bank, isVolatile = false), stmt.position)
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
val b = get[Type]("byte")
val w = get[Type]("word")
val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None, NoAlignment, isVolatile = false)
val relocatable = UninitializedMemoryVariable(arrayName, w, VariableAllocationMethod.Static, None, NoAlignment, isVolatile = false)
val addr = relocatable.toAddress
addThing(relocatable, stmt.position)
addThing(RelativeVariable(stmt.name + ".addr.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(stmt.name + ".addr.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(stmt.name + ".array.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(stmt.name + ".array.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(arrayName + ".addr.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(arrayName + ".addr.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(arrayName + ".array.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(arrayName + ".array.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
} else {
addThing(ConstantThing(stmt.name, a, p), stmt.position)
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(stmt.name + ".lo", a.loByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(stmt.name + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(stmt.name + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(arrayName, a, p), stmt.position)
addThing(ConstantThing(arrayName + ".hi", a.hiByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(arrayName + ".lo", a.loByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(arrayName + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(arrayName + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
}
if (length < 256) {
addThing(ConstantThing(stmt.name + ".length", lengthConst, b), stmt.position)
addThing(ConstantThing(arrayName + ".length", lengthConst, b), stmt.position)
}
case _ => log.error(s"Array `${stmt.name}` has weird length", stmt.position)
}
@ -1406,33 +1414,33 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
for (element <- contents) {
AbstractExpressionCompiler.checkAssignmentType(this, element, e)
}
val array = InitializedArray(stmt.name + ".array", address, contents, declaredBank = stmt.bank, indexType, e, readOnly = stmt.const, alignment)
val array = InitializedArray(arrayName + ".array", address, contents, declaredBank = stmt.bank, indexType, e, readOnly = stmt.const, alignment)
addThing(array, stmt.position)
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None,
registerAddressConstant(UninitializedMemoryVariable(arrayName, p, VariableAllocationMethod.None,
declaredBank = stmt.bank, alignment, isVolatile = false), stmt.position, options, Some(e))
val a = address match {
case None => array.toAddress
case Some(aa) => aa
}
addThing(RelativeVariable(stmt.name + ".first", a, e, zeropage = false,
addThing(RelativeVariable(arrayName + ".first", a, e, zeropage = false,
declaredBank = stmt.bank, isVolatile = false), stmt.position)
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
val b = get[Type]("byte")
val w = get[Type]("word")
val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None, NoAlignment, isVolatile = false)
val relocatable = UninitializedMemoryVariable(arrayName, w, VariableAllocationMethod.Static, None, NoAlignment, isVolatile = false)
val addr = relocatable.toAddress
addThing(relocatable, stmt.position)
addThing(RelativeVariable(stmt.name + ".array.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(stmt.name + ".array.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(arrayName + ".array.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(arrayName + ".array.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
} else {
addThing(ConstantThing(stmt.name, a, p), stmt.position)
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(stmt.name + ".lo", a.loByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(stmt.name + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(stmt.name + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(arrayName, a, p), stmt.position)
addThing(ConstantThing(arrayName + ".hi", a.hiByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(arrayName + ".lo", a.loByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(arrayName + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
addThing(ConstantThing(arrayName + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
}
if (length < 256) {
addThing(ConstantThing(stmt.name + ".length", NumericConstant(length, 1), b), stmt.position)
addThing(ConstantThing(arrayName + ".length", NumericConstant(length, 1), b), stmt.position)
}
}
}

View File

@ -21,6 +21,7 @@ object UnusedLocalVariables extends NodeOptimization {
def getAllLocalVariables(statements: List[Statement]): List[String] = statements.flatMap {
case v: VariableDeclarationStatement => List(v.name)
case v: ArrayDeclarationStatement => List(v.name)
case x: IfStatement => getAllLocalVariables(x.thenBranch) ++ getAllLocalVariables(x.elseBranch)
case x: WhileStatement => getAllLocalVariables(x.body)
case x: DoWhileStatement => getAllLocalVariables(x.body)
@ -32,6 +33,7 @@ object UnusedLocalVariables extends NodeOptimization {
case CompoundConstant(_, l, r) => getAllReadVariables(l) ++ getAllReadVariables(r)
case MemoryAddressConstant(th) => List(
th.name,
th.name.stripSuffix(".array"),
th.name.stripSuffix(".addr"),
th.name.stripSuffix(".hi"),
th.name.stripSuffix(".lo"),
@ -78,6 +80,8 @@ object UnusedLocalVariables extends NodeOptimization {
def removeVariables(statements: List[Statement], localsToRemove: Set[String]): List[Statement] = if (localsToRemove.isEmpty) statements else statements.flatMap {
case s: VariableDeclarationStatement =>
if (localsToRemove(s.name)) None else Some(s)
case s: ArrayDeclarationStatement =>
if (localsToRemove(s.name)) None else Some(s)
case s@ExpressionStatement(FunctionCallExpression(op, VariableExpression(n) :: params)) if op.endsWith("=") =>
if (localsToRemove(n)) {
params.flatMap {

View File

@ -378,7 +378,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
def asmStatement: P[ExecutableStatement]
def statement: P[Seq[Statement]] = (position() ~ P(keywordStatement | localVariableDefinition | expressionStatement)).map { case (p, s) => s.map(_.pos(p)) }
def statement: P[Seq[Statement]] = (position() ~ P(keywordStatement | arrayDefinition | localVariableDefinition | expressionStatement)).map { case (p, s) => s.map(_.pos(p)) }
def asmStatements: P[List[ExecutableStatement]] = ("{" ~/ AWS ~/ asmStatement.rep(sep = NoCut(EOL) ~ !"}" ~/ Pass) ~/ AWS ~/ "}" ~/ Pass).map(_.toList)

View File

@ -340,4 +340,39 @@ class ArraySuite extends FunSuite with Matchers {
m.readByte(0xc027) should equal(0)
}
}
test("Local arrays") {
EmuUnoptimizedRun(
"""
| byte output @$c000
| void main () {
| array square[5]
| square[0] = 1
| output = square[0]
| }
""".stripMargin).readByte(0xc000) should equal(1)
ShouldNotCompile(
"""
| void main () {
| array square = [0]
| }
""".stripMargin)
ShouldNotCompile(
"""
| void f() {
| square[1] = 1
| }
| void main () {
| array square = [0]
| }
""".stripMargin)
EmuUnoptimizedRun(
"""
| byte output @$c000
| void main () {
| const array square = [1]
| output = square[0]
| }
""".stripMargin).readByte(0xc000) should equal(1)
}
}