Skip to content

Commit

Permalink
prevent struct member vars from shuffling around, can take address of…
Browse files Browse the repository at this point in the history
… struct now
  • Loading branch information
irmen committed Jul 12, 2019
1 parent a089c48 commit 1f54200
Show file tree
Hide file tree
Showing 16 changed files with 226 additions and 153 deletions.
10 changes: 5 additions & 5 deletions compiler/src/prog8/ast/antlr/Antr2Kotlin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
return VarDecl(
VarDeclType.VAR,
vd.datatype()?.toAst() ?: DataType.STRUCT,
vd.ZEROPAGE() != null,
if(vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
vd.arrayindex()?.toAst(),
vd.varname.text,
vd.structname?.text,
Expand All @@ -83,7 +83,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
return VarDecl(
VarDeclType.CONST,
vd.datatype()?.toAst() ?: DataType.STRUCT,
vd.ZEROPAGE() != null,
if(vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
vd.arrayindex()?.toAst(),
vd.varname.text,
vd.structname?.text,
Expand All @@ -100,7 +100,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
return VarDecl(
VarDeclType.MEMORY,
vd.datatype()?.toAst() ?: DataType.STRUCT,
vd.ZEROPAGE() != null,
if(vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
vd.arrayindex()?.toAst(),
vd.varname.text,
vd.structname?.text,
Expand Down Expand Up @@ -519,7 +519,7 @@ private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
val loopregister = register()?.toAst()
val datatype = datatype()?.toAst()
val zeropage = ZEROPAGE()!=null
val zeropage = if(ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE
val loopvar = identifier()?.toAst()
val iterable = expression()!!.toAst()
val scope =
Expand Down Expand Up @@ -573,7 +573,7 @@ private fun prog8Parser.VardeclContext.toAst(): VarDecl {
return VarDecl(
VarDeclType.VAR,
datatype()?.toAst() ?: DataType.STRUCT,
ZEROPAGE() != null,
if(ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
arrayindex()?.toAst(),
varname.text,
structname?.text,
Expand Down
14 changes: 8 additions & 6 deletions compiler/src/prog8/ast/processing/AstChecker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ internal class AstChecker(private val program: Program,
if(variable==null)
checkResult.add(ExpressionError("pointer-of operand must be the name of a heap variable", addressOf.position))
else {
if(variable.datatype !in ArrayDatatypes && variable.datatype !in StringDatatypes)
if(variable.datatype !in ArrayDatatypes && variable.datatype !in StringDatatypes && variable.datatype!=DataType.STRUCT)
checkResult.add(ExpressionError("pointer-of operand must be the name of a string or array heap variable", addressOf.position))
}
if(addressOf.scopedname==null)
Expand Down Expand Up @@ -500,12 +500,14 @@ internal class AstChecker(private val program: Program,

when(decl.type) {
VarDeclType.VAR, VarDeclType.CONST -> {
if(decl.struct!=null || decl.datatype==DataType.STRUCT) {
if(decl.datatype!=DataType.STRUCT)
throw FatalAstException("struct vardecl should be of data type struct $decl")
if(decl.datatype==DataType.STRUCT) {
if(decl.struct==null)
throw FatalAstException("struct vardecl should be linked to its struct $decl")
if(decl.zeropage)
if(decl.zeropage==ZeropageWish.PREFER_ZEROPAGE || decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE)
err("struct can not be in zeropage")
}
if(decl.struct!=null) {
if(decl.zeropage==ZeropageWish.PREFER_ZEROPAGE || decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE)
err("struct can not be in zeropage")
}
if (decl.value == null) {
Expand Down Expand Up @@ -1284,7 +1286,7 @@ internal class AstChecker(private val program: Program,
if(decl==null)
checkResult.add(SyntaxError("struct can only contain variable declarations", structDecl.position))
else {
if(decl.zeropage)
if(decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE || decl.zeropage==ZeropageWish.PREFER_ZEROPAGE)
checkResult.add(SyntaxError("struct can not contain zeropage members", decl.position))
if(decl.datatype !in NumericDatatypes)
checkResult.add(SyntaxError("structs can only contain numerical types", decl.position))
Expand Down
10 changes: 5 additions & 5 deletions compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
VarDecl(
VarDeclType.VAR,
member.datatype,
false,
ZeropageWish.NOT_IN_ZEROPAGE,
member.arraysize,
mangledStructMemberName(decl.name, member.name),
null,
decl.struct!!.name,
initvalue,
member.isArray,
true,
Expand Down Expand Up @@ -131,7 +131,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
subroutine.parameters
.filter { it.name !in namesInSub }
.forEach {
val vardecl = VarDecl(VarDeclType.VAR, it.type, false, null, it.name, null, null,
val vardecl = VarDecl(VarDeclType.VAR, it.type, ZeropageWish.DONTCARE, null, it.name, null, null,
isArray = false, hiddenButDoNotRemove = true, position = subroutine.position)
vardecl.linkParents(subroutine)
subroutine.statements.add(0, vardecl)
Expand Down Expand Up @@ -183,7 +183,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(listOf(ForLoop.iteratorLoopcounterVarname), forLoop.body.statements.first())
if(existing==null) {
// create loop iteration counter variable (without value, to avoid an assignment)
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, true, null, ForLoop.iteratorLoopcounterVarname, null, null,
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE, null, ForLoop.iteratorLoopcounterVarname, null, null,
isArray = false, hiddenButDoNotRemove = true, position = forLoop.loopVar.position)
vardecl.linkParents(forLoop.body)
forLoop.body.statements.add(0, vardecl)
Expand Down Expand Up @@ -230,7 +230,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
val declaredType = if(literalValue.isArray) ArrayElementTypes.getValue(literalValue.type) else literalValue.type
val variable = VarDecl(VarDeclType.VAR,
declaredType,
false,
ZeropageWish.NOT_IN_ZEROPAGE,
null,
"$autoHeapValuePrefix${literalValue.heapId}",
null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
pointerExpr.linkParents(arglist[argparam.first.index].parent)
arglist[argparam.first.index] = pointerExpr
// add a vardecl so that the autovar can be resolved in later lookups
val variable = VarDecl(VarDeclType.VAR, strvalue.type, false, null, autoVarName, null, strvalue,
val variable = VarDecl(VarDeclType.VAR, strvalue.type, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, strvalue,
isArray = false, hiddenButDoNotRemove = false, position = strvalue.position)
addVarDecl(strvalue.definingScope(), variable)
}
Expand Down
12 changes: 10 additions & 2 deletions compiler/src/prog8/ast/statements/AstStatements.kt
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,17 @@ class Break(override val position: Position) : IStatement {
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
}


enum class ZeropageWish {
REQUIRE_ZEROPAGE,
PREFER_ZEROPAGE,
DONTCARE,
NOT_IN_ZEROPAGE
}

class VarDecl(val type: VarDeclType,
private val declaredDatatype: DataType,
val zeropage: Boolean,
val zeropage: ZeropageWish,
var arraysize: ArrayIndex?,
val name: String,
private val structName: String?,
Expand Down Expand Up @@ -617,7 +625,7 @@ class BranchStatement(var condition: BranchCondition,

class ForLoop(val loopRegister: Register?,
val decltype: DataType?,
val zeropage: Boolean,
val zeropage: ZeropageWish,
val loopVar: IdentifierReference?,
var iterable: IExpression,
var body: AnonymousScope,
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/prog8/compiler/AstToSourceCode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
if(decl.isArray)
output("]")

if(decl.zeropage)
if(decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE || decl.zeropage==ZeropageWish.PREFER_ZEROPAGE)
output(" @zp")
output(" ${decl.name} ")
if(decl.value!=null) {
Expand Down Expand Up @@ -305,7 +305,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
output("for ")
if(forLoop.decltype!=null) {
output(datatypeString(forLoop.decltype))
if (forLoop.zeropage)
if (forLoop.zeropage==ZeropageWish.REQUIRE_ZEROPAGE || forLoop.zeropage==ZeropageWish.PREFER_ZEROPAGE)
output(" @zp ")
else
output(" ")
Expand Down
19 changes: 19 additions & 0 deletions compiler/src/prog8/compiler/Compiler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1488,6 +1488,22 @@ internal class Compiler(private val program: Program) {
}
}

private fun pushStructAddress(value: IExpression) {
when (value) {
is LiteralValue -> throw CompilerException("can only push address of struct that is a variable on the heap")
is IdentifierReference -> {
// notice that the mangled name of the first struct member is the start address of this struct var
val vardecl = value.targetVarDecl(program.namespace)!!
val firstStructMember = (vardecl.struct!!.statements.first() as VarDecl).name
val firstVarName = listOf(vardecl.name, firstStructMember)
// find the flattened var that belongs to this first struct member
val firstVar = value.definingScope().lookup(firstVarName, value) as VarDecl
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = firstVar.scopedname) // TODO
}
else -> throw CompilerException("can only take address of a the float as constant literal or variable")
}
}

private fun popValueIntoTarget(assignTarget: AssignTarget, datatype: DataType) {
when {
assignTarget.identifier != null -> {
Expand Down Expand Up @@ -2066,6 +2082,9 @@ internal class Compiler(private val program: Program) {
else if(target.datatype== DataType.FLOAT) {
pushFloatAddress(addrof.identifier)
}
else if(target.datatype == DataType.STRUCT) {
pushStructAddress(addrof.identifier)
}
else
throw CompilerException("cannot take memory pointer $addrof")
}
Expand Down
34 changes: 22 additions & 12 deletions compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import prog8.ast.base.printWarning
import prog8.ast.expressions.LiteralValue
import prog8.ast.statements.StructDecl
import prog8.ast.statements.VarDecl
import prog8.ast.statements.ZeropageWish
import prog8.vm.RuntimeValue
import prog8.compiler.CompilerException
import prog8.compiler.HeapValues
Expand All @@ -17,10 +18,12 @@ import java.nio.file.Path

class IntermediateProgram(val name: String, var loadAddress: Int, val heap: HeapValues, val source: Path) {

data class VariableParameters (val zp: ZeropageWish, val memberOfStruct: StructDecl?)

class ProgramBlock(val name: String,
var address: Int?,
val instructions: MutableList<Instruction> = mutableListOf(),
val variables: MutableMap<String, RuntimeValue> = mutableMapOf(), // names are fully scoped
val variables: MutableList<Triple<String, RuntimeValue, VariableParameters>> = mutableListOf(), // names are fully scoped
val memoryPointers: MutableMap<String, Pair<Int, DataType>> = mutableMapOf(),
val labels: MutableMap<String, Instruction> = mutableMapOf(), // names are fully scoped
val force_output: Boolean)
Expand All @@ -29,7 +32,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
get() { return variables.size }
val numInstructions: Int
get() { return instructions.filter { it.opcode!= Opcode.LINE }.size }
val variablesMarkedForZeropage: MutableSet<String> = mutableSetOf()
val variablesMarkedForZeropage: MutableSet<String> = mutableSetOf() // TODO maybe this can be removed now we have ValueParameters
}

val allocatedZeropageVariables = mutableMapOf<String, Pair<Int, DataType>>()
Expand All @@ -46,14 +49,16 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
// allocates all @zp marked variables on the zeropage (for all blocks, as long as there is space in the ZP)
var notAllocated = 0
for(block in blocks) {
val zpVariables = block.variables.filter { it.key in block.variablesMarkedForZeropage }
val zpVariables = block.variables.filter { it.first in block.variablesMarkedForZeropage }
if (zpVariables.isNotEmpty()) {
for (variable in zpVariables) {
for ((varname, value, varparams) in zpVariables) {
if(varparams.zp==ZeropageWish.NOT_IN_ZEROPAGE || varparams.memberOfStruct!=null)
throw CompilerException("zp conflict")
try {
val address = zeropage.allocate(variable.key, variable.value.type, null)
allocatedZeropageVariables[variable.key] = Pair(address, variable.value.type)
val address = zeropage.allocate(varname, value.type, null)
allocatedZeropageVariables[varname] = Pair(address, value.type)
} catch (x: ZeropageDepletedError) {
printWarning(x.toString() + " variable ${variable.key} type ${variable.value.type}")
printWarning(x.toString() + " variable $varname type ${value.type}")
notAllocated++
}
}
Expand Down Expand Up @@ -399,6 +404,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
if(decl.parent is StructDecl)
return

val valueparams = VariableParameters(decl.zeropage, decl.struct)
val value = when(decl.datatype) {
in NumericDatatypes -> {
RuntimeValue(decl.datatype, (decl.value as LiteralValue).asNumericValue!!)
Expand All @@ -421,9 +427,11 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
}
else -> throw CompilerException("weird datatype")
}
currentBlock.variables[scopedname] = value
if(decl.zeropage)
currentBlock.variables.add(Triple(scopedname, value, valueparams))
if(decl.zeropage==ZeropageWish.PREFER_ZEROPAGE)
currentBlock.variablesMarkedForZeropage.add(scopedname)
else if(decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE)
TODO("REQUIRE_ZEROPAGE not yet implemented")
}
VarDeclType.MEMORY -> {
// note that constants are all folded away, but assembly code may still refer to them
Expand Down Expand Up @@ -513,9 +521,11 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
out.println("\n%block ${blk.name} ${blk.address?.toString(16) ?: ""}")

out.println("%variables")
for (variable in blk.variables) {
val valuestr = variable.value.toString()
out.println("${variable.key} ${variable.value.type.name.toLowerCase()} $valuestr")
for ((vname, value, parameters) in blk.variables) {
if(parameters.zp==ZeropageWish.REQUIRE_ZEROPAGE)
throw CompilerException("zp conflict")
val valuestr = value.toString()
out.println("$vname ${value.type.name.toLowerCase()} $valuestr")
}
out.println("%end_variables")
out.println("%memorypointers")
Expand Down
Loading

0 comments on commit 1f54200

Please sign in to comment.