introducing IVariableAllocation (WIP)

This commit is contained in:
Irmen de Jong 2022-02-06 18:57:23 +01:00
parent 6bdd81623f
commit d2309b8114
6 changed files with 239 additions and 50 deletions

View File

@ -10,7 +10,6 @@ import prog8.ast.statements.*
import prog8.codegen.cpu6502.assignment.*
import prog8.compilerinterface.*
import prog8.parser.SourceCode
import java.nio.file.Path
import java.time.LocalDate
import java.time.LocalDateTime
import java.util.*
@ -330,7 +329,7 @@ class AsmGen6502(internal val program: Program,
if(blockname=="prog8_lib" && variable.name.startsWith("P8ZP_SCRATCH_"))
continue // the "hooks" to the temp vars are not generated as new variables
val scopedName = variable.scopedName
val zpAlloc = zeropage.allocatedZeropageVariable(scopedName)
val zpAlloc = zeropage.variables[scopedName]
if (zpAlloc == null) {
// This var is not on the ZP yet. Attempt to move it there if it's an integer type
if(variable.zeropage != ZeropageWish.NOT_IN_ZEROPAGE &&
@ -482,7 +481,7 @@ class AsmGen6502(internal val program: Program,
.filter {
it.type==VarDeclType.VAR
&& it.zeropage!=ZeropageWish.REQUIRE_ZEROPAGE
&& zeropage.allocatedZeropageVariable(it.scopedName)==null
&& it.scopedName !in zeropage.variables
}
vars.filter { it.datatype == DataType.STR && shouldActuallyOutputStringVar(it) }
@ -1742,12 +1741,11 @@ $repeatLabel lda $counterVar
}
}
internal fun isZpVar(scopedName: List<String>): Boolean =
zeropage.allocatedZeropageVariable(scopedName)!=null
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropage.variables
internal fun isZpVar(variable: IdentifierReference): Boolean {
val vardecl = variable.targetVarDecl(program)!!
return zeropage.allocatedZeropageVariable(vardecl.scopedName)!=null
return vardecl.scopedName in zeropage.variables
}
internal fun jmp(asmLabel: String, indirect: Boolean=false) {

View File

@ -343,13 +343,14 @@ private fun writeAssembly(program: Program,
compilerOptions: CompilationOptions
): WriteAssemblyResult {
// asm generation directly from the Ast
compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
program.processAstBeforeAsmGeneration(compilerOptions, errors)
errors.report()
// TODO do something with the VariableAllocation, pass it to the asmgenerator
// println("*********** AST RIGHT BEFORE ASM GENERATION *************")
// printProgram(program)
compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
val assembly = asmGeneratorFor(program, errors, compilerOptions).compileToAssembly()
errors.report()

View File

@ -1,9 +1,6 @@
package prog8.compiler.astprocessing
import prog8.ast.IFunctionCall
import prog8.ast.IStatementContainer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.*
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
@ -18,14 +15,22 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
) : AstWalker() {
private val allBlockVars = mutableMapOf<Block, MutableSet<VarDecl>>()
private val allBlockConsts = mutableMapOf<Block, MutableSet<VarDecl>>()
private val allBlockMemoryvars = mutableMapOf<Block, MutableSet<VarDecl>>()
private val allSubroutineVars = mutableMapOf<Subroutine, MutableSet<VarDecl>>()
// internal lateinit var allocation: IVariableAllocation
//
// override fun after(program: Program): Iterable<IAstModification> {
// allocation = VariableAllocation(allBlockVars, allSubroutineVars)
// allocation.dump(program.memsizer)
// return super.after(program)
// }
private val allSubroutineConsts = mutableMapOf<Subroutine, MutableSet<VarDecl>>()
private val allSubroutineMemoryvars = mutableMapOf<Subroutine, MutableSet<VarDecl>>()
internal lateinit var allocation: IVariableAllocation
/* TODO complete the use of VariableAllocation and get rid of it from the AsmGen (and that should no longer use vardecl either):
override fun after(program: Program): Iterable<IAstModification> {
allocation = VariableAllocation(options,
allBlockVars, allBlockConsts, allBlockMemoryvars,
allSubroutineVars, allSubroutineConsts, allSubroutineMemoryvars)
allocation.dump(program.memsizer)
return super.after(program)
}
*/
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
throw FatalAstException("break should have been replaced by goto $breakStmt")
@ -66,24 +71,66 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
throw FatalAstException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
}
if(decl.type==VarDeclType.VAR) {
when(val scope=decl.definingScope) {
is Block -> {
val blockVars = allBlockVars[scope] ?: mutableSetOf()
blockVars.add(decl)
allBlockVars[scope] = blockVars
val scope=decl.definingScope
when (decl.type) {
VarDeclType.VAR -> {
when(scope) {
is Block -> {
val decls = allBlockVars[scope] ?: mutableSetOf()
decls.add(decl)
allBlockVars[scope] = decls
}
is Subroutine -> {
val decls = allSubroutineVars[scope] ?: mutableSetOf()
decls.add(decl)
allSubroutineVars[scope] = decls
}
else -> {
throw FatalAstException("var can only occur in subroutine or block scope")
}
}
is Subroutine -> {
val subroutineVars = allSubroutineVars[scope] ?: mutableSetOf()
subroutineVars.add(decl)
allSubroutineVars[scope] = subroutineVars
}
VarDeclType.CONST -> {
when(scope) {
is Block -> {
val decls = allBlockConsts[scope] ?: mutableSetOf()
decls.add(decl)
allBlockConsts[scope] = decls
}
is Subroutine -> {
val decls = allSubroutineConsts[scope] ?: mutableSetOf()
decls.add(decl)
allSubroutineConsts[scope] = decls
}
else -> {
throw FatalAstException("var can only occur in subroutine or block scope")
}
}
else -> {
throw FatalAstException("var can only occur in subroutine or block scope")
}
VarDeclType.MEMORY -> {
when(scope) {
is Block -> {
val decls = allBlockMemoryvars[scope] ?: mutableSetOf()
decls.add(decl)
allBlockMemoryvars[scope] = decls
}
is Subroutine -> {
val decls = allSubroutineMemoryvars[scope] ?: mutableSetOf()
decls.add(decl)
allSubroutineMemoryvars[scope] = decls
}
else -> {
throw FatalAstException("var can only occur in subroutine or block scope")
}
}
}
else -> {
throw FatalAstException("invalid var type")
}
}
// TODO get rid of the vardecl inside the ast - codegen should depend on the IVariableAllocation object
// return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
return noModifications
}
@ -399,25 +446,154 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
}
internal class VariableAllocation(
override val blockVars: Map<Block, Set<VarDecl>>,
override val subroutineVars: Map<Subroutine, Set<VarDecl>>) : IVariableAllocation
internal class VariableAllocation (
options: CompilationOptions,
astBlockVars: Map<Block, Set<VarDecl>>,
astBlockConsts: Map<Block, Set<VarDecl>>,
astBlockMemvars: Map<Block, Set<VarDecl>>,
astSubroutineVars: Map<Subroutine, Set<VarDecl>>,
astSubroutineConsts: Map<Subroutine, Set<VarDecl>>,
astSubroutineMemvars: Map<Subroutine, Set<VarDecl>>
) : IVariableAllocation
{
override val zeropageVars: Set<IVariableAllocation.ZeropageVariable>
override val blockVars: Map<Block, Set<IVariableAllocation.StaticBlockVariable>>
override val blockConsts: Map<Block, Set<IVariableAllocation.ConstantNumberSymbol>>
override val blockMemvars: Map<Block, Set<IVariableAllocation.MemoryMappedSymbol>>
override val subroutineVars: Map<Subroutine, Set<IVariableAllocation.StaticSubroutineVariable>>
override val subroutineConsts: Map<Subroutine, Set<IVariableAllocation.ConstantNumberSymbol>>
override val subroutineMemvars: Map<Subroutine, Set<IVariableAllocation.MemoryMappedSymbol>>
init {
if(options.zeropage!=ZeropageType.DONTUSE)
allocateVarsInZeropage(options.compTarget.machine.zeropage)
val zpv = mutableSetOf<IVariableAllocation.ZeropageVariable>()
val bv = astBlockVars.keys.associateWith { mutableSetOf<IVariableAllocation.StaticBlockVariable>() }
val bc = astBlockConsts.keys.associateWith { mutableSetOf<IVariableAllocation.ConstantNumberSymbol>() }
val bmv = astBlockMemvars.keys.associateWith { mutableSetOf<IVariableAllocation.MemoryMappedSymbol>() }
val sv = astSubroutineVars.keys.associateWith { mutableSetOf<IVariableAllocation.StaticSubroutineVariable>() }
val sc = astSubroutineConsts.keys.associateWith { mutableSetOf<IVariableAllocation.ConstantNumberSymbol>() }
val smv = astSubroutineMemvars.keys.associateWith { mutableSetOf<IVariableAllocation.MemoryMappedSymbol>() }
astBlockVars.forEach { (block, decls) ->
val vars = bv.getValue(block)
vars.addAll(decls.map {
// TODO make sure the zp-allocated variables are not added here
IVariableAllocation.StaticBlockVariable(it.datatype, it.name, it.value, it.position, it)
})
}
astBlockConsts.forEach { (block, decls) ->
bc.getValue(block).addAll(
decls.map {
IVariableAllocation.ConstantNumberSymbol(
it.datatype,
it.name,
(it.value as NumericLiteralValue).number,
it.position
)
})
}
astBlockMemvars.forEach { (block, decls) ->
bmv.getValue(block).addAll(
decls.map {
IVariableAllocation.MemoryMappedSymbol(
it.datatype,
it.name,
(it.value as NumericLiteralValue).number.toUInt(),
it.position
)
})
}
astSubroutineVars.forEach { (sub, decls) ->
val vars = sv.getValue(sub)
vars.addAll(decls.map {
// TODO make sure the zp-allocated variables are not added here
IVariableAllocation.StaticSubroutineVariable(it.datatype, it.name, it.position, it)
})
}
astSubroutineConsts.forEach { (sub, decls) ->
sc.getValue(sub).addAll(
decls.map {
IVariableAllocation.ConstantNumberSymbol(
it.datatype,
it.name,
(it.value as NumericLiteralValue).number,
it.position
)
})
}
astSubroutineMemvars.forEach { (sub, decls) ->
smv.getValue(sub).addAll(
decls.map {
IVariableAllocation.MemoryMappedSymbol(
it.datatype,
it.name,
(it.value as NumericLiteralValue).number.toUInt(),
it.position
)
})
}
zeropageVars = zpv
blockVars = bv
blockConsts = bc
blockMemvars = bmv
subroutineVars = sv
subroutineConsts = sc
subroutineMemvars = smv
}
private fun allocateVarsInZeropage(zeropage: Zeropage) {
println("TODO: allocate vars on zeropage") // TODO
}
override fun dump(memsizer: IMemSizer) {
println("ALL BLOCK VARS:")
println("\nALL ZEROPAGE VARS:")
zeropageVars.forEach {
println(" ${it.type} ${it.scopedname} ${it.position}")
}
println("\nALL BLOCK VARS:")
blockVars.forEach { (block, vars) ->
val totalsize = vars.sumOf { memsizer.memorySize(it) }
val totalsize = vars.sumOf { memsizer.memorySize(it.origVar) }
println("BLOCK: ${block.name} total size: $totalsize")
vars.forEach {
println(" ${it.datatype} ${it.name} ${it.position}")
println(" ${it.type} ${it.name} = ${it.initialValue} ${it.position}")
}
}
println("ALL SUBROUTINE VARS:")
println("\nALL BLOCK CONSTS:")
blockConsts.forEach { (block, vars) ->
println("BLOCK: ${block.name}")
vars.forEach {
println(" ${it.type} ${it.name} = ${it.value} ${it.position}")
}
}
println("\nALL BLOCK MEMORYVARS:")
blockMemvars.forEach { (block, vars) ->
println("BLOCK: ${block.name}")
vars.forEach {
println(" ${it.type} ${it.name} = ${it.address.toHex()} ${it.position}")
}
}
println("\nALL SUBROUTINE VARS:")
subroutineVars.forEach { (sub, vars) ->
val totalsize = vars.sumOf { memsizer.memorySize(it) }
val totalsize = vars.sumOf { memsizer.memorySize(it.origVar) }
println("SUBROUTINE: ${sub.name} total size: $totalsize")
vars.forEach {
println(" ${it.datatype} ${it.name} ${it.position}")
println(" ${it.type} ${it.name} ${it.position}")
}
}
println("\nALL SUBROUTINE CONSTS:")
subroutineConsts.forEach { (sub, vars) ->
println("SUBROUTINE: ${sub.name}")
vars.forEach {
println(" ${it.type} ${it.name} = ${it.value} ${it.position}")
}
}
println("\nALL SUBROUTINE MEMORYVARS:")
subroutineMemvars.forEach { (sub, vars) ->
println("SUBROUTINE: ${sub.name}")
vars.forEach {
println(" ${it.type} ${it.name} = ${it.address.toHex()} ${it.position}")
}
}
}

View File

@ -259,12 +259,12 @@ class TestCx16Zeropage: FunSpec({
test("preallocated zp vars") {
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
zp1.allocatedZeropageVariable(listOf("test")) shouldBe null
zp1.allocatedZeropageVariable(listOf("cx16", "r0")) shouldNotBe null
zp1.allocatedZeropageVariable(listOf("cx16", "r15")) shouldNotBe null
zp1.allocatedZeropageVariable(listOf("cx16", "r0L")) shouldNotBe null
zp1.allocatedZeropageVariable(listOf("cx16", "r15L")) shouldNotBe null
zp1.allocatedZeropageVariable(listOf("cx16", "r0sH")) shouldNotBe null
zp1.allocatedZeropageVariable(listOf("cx16", "r15sH")) shouldNotBe null
zp1.variables[listOf("test")] shouldBe null
zp1.variables[listOf("cx16", "r0")] shouldNotBe null
zp1.variables[listOf("cx16", "r15")] shouldNotBe null
zp1.variables[listOf("cx16", "r0L")] shouldNotBe null
zp1.variables[listOf("cx16", "r15L")] shouldNotBe null
zp1.variables[listOf("cx16", "r0sH")] shouldNotBe null
zp1.variables[listOf("cx16", "r15sH")] shouldNotBe null
}
})

View File

@ -1,12 +1,27 @@
package prog8.compilerinterface
import prog8.ast.base.DataType
import prog8.ast.base.Position
import prog8.ast.expressions.Expression
import prog8.ast.statements.Block
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
interface IVariableAllocation {
data class ConstantNumberSymbol(val type: DataType, val name: String, val value: Double, val position: Position)
data class MemoryMappedSymbol(val type: DataType, val name: String, val address: UInt, val position: Position)
data class StaticBlockVariable(val type: DataType, val name: String, val initialValue: Expression?, val position: Position, val origVar: VarDecl) // TODO should get rid of origVar altogether
data class StaticSubroutineVariable(val type: DataType, val name: String, val position: Position, val origVar: VarDecl) // TODO should get rid of origVar altogether
data class ZeropageVariable(val type: DataType, val scopedname: List<String>, val position: Position)
fun dump(memsizer: IMemSizer)
val blockVars: Map<Block, Set<VarDecl>>
val subroutineVars: Map<Subroutine, Set<VarDecl>>
val zeropageVars: Set<ZeropageVariable> // also present in the Zeropage object after this allocation
val blockVars: Map<Block, Set<StaticBlockVariable>>
val blockConsts: Map<Block, Set<ConstantNumberSymbol>>
val blockMemvars: Map<Block, Set<MemoryMappedSymbol>>
val subroutineVars: Map<Subroutine, Set<StaticSubroutineVariable>>
val subroutineConsts: Map<Subroutine, Set<ConstantNumberSymbol>>
val subroutineMemvars: Map<Subroutine, Set<MemoryMappedSymbol>>
}

View File

@ -21,6 +21,7 @@ abstract class Zeropage(protected val options: CompilationOptions) {
// name (scoped) ==> pair of address to (Datatype + bytesize)
protected val allocatedVariables = mutableMapOf<List<String>, Pair<UInt, Pair<DataType, Int>>>()
private val allocations = mutableMapOf<UInt, Pair<List<String>, DataType>>()
public val variables: Map<List<String>, Pair<UInt, Pair<DataType, Int>>> = allocatedVariables
val free = mutableListOf<UInt>() // subclasses must set this to the appropriate free locations.
@ -107,6 +108,4 @@ abstract class Zeropage(protected val options: CompilationOptions) {
require(size>0)
return free.containsAll((address until address+size.toUInt()).toList())
}
fun allocatedZeropageVariable(name: List<String>): Pair<UInt, Pair<DataType, Int>>? = allocatedVariables[name]
}