getting rid of old IVariablesAndConsts object

This commit is contained in:
Irmen de Jong 2022-03-05 12:38:10 +01:00
parent 496245c801
commit cf362c4a61
3 changed files with 130 additions and 51 deletions

View File

@ -31,14 +31,14 @@ class AsmGen(internal val program: Program,
internal val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320,640)
internal val loopEndLabels = ArrayDeque<String>()
private val zeropage = options.compTarget.machine.zeropage
private val allocator = VariableAllocator(variables, options, errors)
private val allocator = VariableAllocator(variables, symbolTable, options, errors)
private val assemblyLines = mutableListOf<String>()
private val breakpointLabels = mutableListOf<String>()
private val forloopsAsmGen = ForLoopsAsmGen(program, this, zeropage)
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
private val programGen = ProgramAndVarsGen(program, variables, symbolTable, options, errors, functioncallAsmGen, this, allocator, zeropage)
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
private val assignmentAsmGen = AssignmentAsmGen(program, this, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen, allocator)

View File

@ -23,10 +23,9 @@ import kotlin.math.absoluteValue
*/
internal class ProgramAndVarsGen(
val program: Program,
val variables: IVariablesAndConsts,
val symboltable: SymbolTable,
val options: CompilationOptions,
val errors: IErrorReporter,
private val symboltable: SymbolTable, // TODO stick this in Program
private val functioncallAsmGen: FunctionCallAsmGen,
private val asmgen: AsmGen,
private val allocator: VariableAllocator,
@ -383,15 +382,19 @@ internal class ProgramAndVarsGen(
clc""")
}
// TODO avoid looking up the variables multiple times because the Ast node is used as an anchor
private fun zeropagevars2asm(block: Block) {
//val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup fail") }
//require(scope.type==StNodeType.BLOCK)
val varnames = variables.blockVars.getOrDefault(block, emptySet()).map { it.scopedname }.toSet()
val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup") }
require(scope.type==StNodeType.BLOCK)
val varnames = scope.children.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
zeropagevars2asm(varnames)
}
// TODO avoid looking up the variables multiple times because the Ast node is used as an anchor
private fun zeropagevars2asm(sub: Subroutine) {
val varnames = variables.subroutineVars.getOrDefault(sub, emptySet()).map { it.scopedname }.toSet()
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
require(scope.type==StNodeType.SUBROUTINE)
val varnames = scope.children.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
zeropagevars2asm(varnames)
}
@ -404,32 +407,44 @@ internal class ProgramAndVarsGen(
}
}
// TODO avoid looking up the variables multiple times because the Ast node is used as an anchor
// TODO don't have this as a separate loop, use a partition over the variables in the block; ZP<->NonZP
private fun nonZpVariables2asm(block: Block) {
val variables = variables.blockVars[block]?.filter { !allocator.isZpVar(it.scopedname) } ?: emptyList()
val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup") }
require(scope.type==StNodeType.BLOCK)
val variables = scope.children
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
.map { it.value as StStaticVariable }
nonZpVariables2asm(variables)
}
// TODO avoid looking up the variables multiple times because the Ast node is used as an anchor
// TODO don't have this as a separate loop, use a partition over the variables in the subroutine; ZP<->NonZP
private fun nonZpVariables2asm(sub: Subroutine) {
val variables = variables.subroutineVars[sub]?.filter { !allocator.isZpVar(it.scopedname) } ?: emptyList()
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
require(scope.type==StNodeType.SUBROUTINE)
val variables = scope.children
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) }
.map { it.value as StStaticVariable }
nonZpVariables2asm(variables)
}
private fun nonZpVariables2asm(variables: List<IVariablesAndConsts.StaticVariable>) {
private fun nonZpVariables2asm(variables: List<StStaticVariable>) {
asmgen.out("")
asmgen.out("; non-zeropage variables")
val (stringvars, othervars) = variables.partition { it.type==DataType.STR }
val (stringvars, othervars) = variables.partition { it.dt==DataType.STR }
stringvars.forEach {
val stringvalue = it.initialValue as StringLiteral
outputStringvar(it.scopedname.last(), it.type, stringvalue.encoding, stringvalue.value)
val stringvalue = it.initialvalue as StringLiteral
outputStringvar(it.name, it.dt, stringvalue.encoding, stringvalue.value)
}
othervars.sortedBy { it.type }.forEach {
staticVariable2asm(it)
}
}
private fun staticVariable2asm(variable: IVariablesAndConsts.StaticVariable) {
val name = variable.scopedname.last()
val value = variable.initialValue
private fun staticVariable2asm(variable: StStaticVariable) {
val name = variable.name
val value = variable.initialvalue
val staticValue: Number =
if(value!=null) {
if(value is NumericLiteral) {
@ -438,13 +453,13 @@ internal class ProgramAndVarsGen(
else
value.number.toInt()
} else {
if(variable.type in NumericDatatypes)
if(variable.dt in NumericDatatypes)
throw AssemblyError("can only deal with constant numeric values for global vars")
else 0
}
} else 0
when (variable.type) {
when (variable.dt) {
DataType.UBYTE -> asmgen.out("$name\t.byte ${staticValue.toHex()}")
DataType.BYTE -> asmgen.out("$name\t.char $staticValue")
DataType.UWORD -> asmgen.out("$name\t.word ${staticValue.toHex()}")
@ -460,7 +475,7 @@ internal class ProgramAndVarsGen(
DataType.STR -> {
throw AssemblyError("all string vars should have been interned into prog")
}
in ArrayDatatypes -> arrayVariable2asm(name, variable.type, value as? ArrayLiteral, variable.arraysize)
in ArrayDatatypes -> arrayVariable2asm(name, variable.dt, value as? ArrayLiteral, variable.arraysize)
else -> {
throw AssemblyError("weird dt")
}
@ -524,30 +539,41 @@ internal class ProgramAndVarsGen(
}
}
// TODO avoid looking up the variables multiple times because the Ast node is used as an anchor
private fun memdefsAndConsts2asm(block: Block) {
val mvs = variables.blockMemvars[block] ?: emptySet()
val consts = variables.blockConsts[block] ?: emptySet()
val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup") }
require(scope.type==StNodeType.BLOCK)
val mvs = scope.children
.filter { it.value.type==StNodeType.MEMVAR }
.map { it.value as StMemVar }
val consts = scope.children
.filter { it.value.type==StNodeType.CONSTANT }
.map { it.value as StConstant }
memdefsAndConsts2asm(mvs, consts)
}
// TODO avoid looking up the variables multiple times because the Ast node is used as an anchor
private fun memdefsAndConsts2asm(sub: Subroutine) {
val mvs = variables.subroutineMemvars[sub] ?: emptySet()
val consts = variables.subroutineConsts[sub] ?: emptySet()
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
require(scope.type==StNodeType.SUBROUTINE)
val mvs = scope.children
.filter { it.value.type==StNodeType.MEMVAR }
.map { it.value as StMemVar }
val consts = scope.children
.filter { it.value.type==StNodeType.CONSTANT }
.map { it.value as StConstant }
memdefsAndConsts2asm(mvs, consts)
}
private fun memdefsAndConsts2asm(
memvars: Set<IVariablesAndConsts.MemoryMappedVariable>,
consts: Set<IVariablesAndConsts.ConstantNumberSymbol>
) {
private fun memdefsAndConsts2asm(memvars: Collection<StMemVar>, consts: Collection<StConstant>) {
memvars.forEach {
asmgen.out(" ${it.scopedname.last()} = ${it.address.toHex()}")
asmgen.out(" ${it.name} = ${it.address.toHex()}")
}
consts.forEach {
if(it.type==DataType.FLOAT)
asmgen.out(" ${it.scopedname.last()} = ${it.value}")
if(it.dt==DataType.FLOAT)
asmgen.out(" ${it.name} = ${it.value}")
else
asmgen.out(" ${it.scopedname.last()} = ${it.value.toHex()}")
asmgen.out(" ${it.name} = ${it.value.toHex()}")
}
}

View File

@ -12,6 +12,7 @@ import prog8.compilerinterface.*
internal class VariableAllocator(private val vars: IVariablesAndConsts,
private val symboltable: SymbolTable,
private val options: CompilationOptions,
private val errors: IErrorReporter) {
@ -35,28 +36,59 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
if(options.zeropage== ZeropageType.DONTUSE)
return
val allVariables = (
val allVariablesOld = (
vars.blockVars.asSequence().flatMap { it.value } +
vars.subroutineVars.asSequence().flatMap { it.value }
).toList()
val numberOfAllocatableVariables = allVariables.size
val varsRequiringZp = allVariables.filter { it.zp == ZeropageWish.REQUIRE_ZEROPAGE }
val varsPreferringZp = allVariables.filter { it.zp == ZeropageWish.PREFER_ZEROPAGE }
val varsDontCare = allVariables.filter { it.zp == ZeropageWish.DONTCARE }
val numberOfExplicitNonZpVariables = allVariables.count { it.zp == ZeropageWish.NOT_IN_ZEROPAGE }
val allVariables = collectAllVariables(symboltable)
require(allVariables.size == allVariablesOld.size)
require(allVariables.map{it.scopedName}.toSet()==allVariablesOld.map{it.scopedname}.toSet())
val numberOfAllocatableVariables = allVariablesOld.size
val varsRequiringZp = allVariablesOld.filter { it.zp == ZeropageWish.REQUIRE_ZEROPAGE }
val varsPreferringZp = allVariablesOld.filter { it.zp == ZeropageWish.PREFER_ZEROPAGE }
val varsDontCare = allVariablesOld.filter { it.zp == ZeropageWish.DONTCARE }
val numberOfExplicitNonZpVariables = allVariablesOld.count { it.zp == ZeropageWish.NOT_IN_ZEROPAGE }
require(varsDontCare.size + varsRequiringZp.size + varsPreferringZp.size + numberOfExplicitNonZpVariables == numberOfAllocatableVariables)
val numberOfAllocatableVariables2 = allVariables.size
val varsRequiringZp2 = allVariables.filter { it.zpw == ZeropageWish.REQUIRE_ZEROPAGE }
val varsPreferringZp2 = allVariables.filter { it.zpw == ZeropageWish.PREFER_ZEROPAGE }
val varsDontCare2 = allVariables.filter { it.zpw == ZeropageWish.DONTCARE }
val numberOfExplicitNonZpVariables2 = allVariables.count { it.zpw == ZeropageWish.NOT_IN_ZEROPAGE }
require(varsDontCare2.size + varsRequiringZp2.size + varsPreferringZp2.size + numberOfExplicitNonZpVariables2 == numberOfAllocatableVariables2)
require(varsDontCare2.size==varsDontCare.size)
require(varsRequiringZp2.size==varsRequiringZp.size)
require(varsPreferringZp2.size==varsPreferringZp.size)
require(numberOfExplicitNonZpVariables2==numberOfExplicitNonZpVariables)
require(numberOfAllocatableVariables2==numberOfAllocatableVariables)
val oldvarsByName = varsDontCare.associateBy { it.scopedname }
val newvarsByName = varsDontCare2.associateBy { it.scopedName }
require(oldvarsByName.keys==newvarsByName.keys)
oldvarsByName.forEach { (name, oldvar) ->
val newvar = newvarsByName.getValue(name)
require(oldvar.scopedname==newvar.scopedName)
require(oldvar.type==newvar.dt)
require(oldvar.zp==newvar.zpw)
require(oldvar.initialValue==newvar.initialvalue)
require(oldvar.arraysize==newvar.arraysize)
require(oldvar.position==newvar.position)
require(numArrayElements(oldvar) == numArrayElements(newvar))
}
var numVariablesAllocatedInZP = 0
var numberOfNonIntegerVariables = 0
varsRequiringZp.forEach { variable ->
varsRequiringZp2.forEach { variable ->
val numElements = numArrayElements(variable)
val result = zeropage.allocate(
variable.scopedname,
variable.type,
variable.scopedName,
variable.dt,
numElements,
variable.initialValue,
variable.initialvalue,
variable.position,
errors
)
@ -71,13 +103,13 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
}
if(errors.noErrors()) {
varsPreferringZp.forEach { variable ->
varsPreferringZp2.forEach { variable ->
val numElements = numArrayElements(variable)
val result = zeropage.allocate(
variable.scopedname,
variable.type,
variable.scopedName,
variable.dt,
numElements,
variable.initialValue,
variable.initialvalue,
variable.position,
errors
)
@ -88,17 +120,17 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
// try to allocate any other interger variables into the zeropage until it is full.
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
if(errors.noErrors()) {
for (variable in varsDontCare) {
if(variable.type in IntegerDatatypes) {
for (variable in varsDontCare2.sortedWith(compareBy({it.scopedName.size}, {it.name}))) {
if(variable.dt in IntegerDatatypes) {
if(zeropage.free.isEmpty()) {
break
} else {
val numElements = numArrayElements(variable)
val result = zeropage.allocate(
variable.scopedname,
variable.type,
variable.scopedName,
variable.dt,
numElements,
variable.initialValue,
variable.initialvalue,
variable.position,
errors
)
@ -115,6 +147,20 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
println(" zeropage free space: ${zeropage.free.size} bytes")
}
private fun collectAllVariables(st: SymbolTable): Collection<StStaticVariable> {
val vars = mutableListOf<StStaticVariable>()
fun collect(node: StNode) {
for(child in node.children) {
if(child.value.type==StNodeType.STATICVAR)
vars.add(child.value as StStaticVariable)
else
collect(child.value)
}
}
collect(st)
return vars
}
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropage.allocatedVariables
private fun numArrayElements(variable: IVariablesAndConsts.StaticVariable) =
@ -124,6 +170,13 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
else -> null
}
private fun numArrayElements(variable: StStaticVariable) =
when(variable.dt) {
DataType.STR -> (variable.initialvalue as StringLiteral).value.length
in ArrayDatatypes -> variable.arraysize!!
else -> null
}
internal fun subroutineExtra(sub: Subroutine): SubroutineExtraAsmInfo {
var extra = subroutineExtras[sub]
return if(extra==null) {