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 optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320,640)
internal val loopEndLabels = ArrayDeque<String>() internal val loopEndLabels = ArrayDeque<String>()
private val zeropage = options.compTarget.machine.zeropage 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 assemblyLines = mutableListOf<String>()
private val breakpointLabels = mutableListOf<String>() private val breakpointLabels = mutableListOf<String>()
private val forloopsAsmGen = ForLoopsAsmGen(program, this, zeropage) private val forloopsAsmGen = ForLoopsAsmGen(program, this, zeropage)
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this) private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
private val functioncallAsmGen = FunctionCallAsmGen(program, this) private val functioncallAsmGen = FunctionCallAsmGen(program, this)
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator) 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 assignmentAsmGen = AssignmentAsmGen(program, this, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen, allocator) private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen, allocator)

View File

@ -23,10 +23,9 @@ import kotlin.math.absoluteValue
*/ */
internal class ProgramAndVarsGen( internal class ProgramAndVarsGen(
val program: Program, val program: Program,
val variables: IVariablesAndConsts,
val symboltable: SymbolTable,
val options: CompilationOptions, val options: CompilationOptions,
val errors: IErrorReporter, val errors: IErrorReporter,
private val symboltable: SymbolTable, // TODO stick this in Program
private val functioncallAsmGen: FunctionCallAsmGen, private val functioncallAsmGen: FunctionCallAsmGen,
private val asmgen: AsmGen, private val asmgen: AsmGen,
private val allocator: VariableAllocator, private val allocator: VariableAllocator,
@ -383,15 +382,19 @@ internal class ProgramAndVarsGen(
clc""") clc""")
} }
// TODO avoid looking up the variables multiple times because the Ast node is used as an anchor
private fun zeropagevars2asm(block: Block) { private fun zeropagevars2asm(block: Block) {
//val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup fail") } val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup") }
//require(scope.type==StNodeType.BLOCK) require(scope.type==StNodeType.BLOCK)
val varnames = variables.blockVars.getOrDefault(block, emptySet()).map { it.scopedname }.toSet() val varnames = scope.children.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet()
zeropagevars2asm(varnames) zeropagevars2asm(varnames)
} }
// TODO avoid looking up the variables multiple times because the Ast node is used as an anchor
private fun zeropagevars2asm(sub: Subroutine) { 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) 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) { 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) 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) { 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) nonZpVariables2asm(variables)
} }
private fun nonZpVariables2asm(variables: List<IVariablesAndConsts.StaticVariable>) { private fun nonZpVariables2asm(variables: List<StStaticVariable>) {
asmgen.out("") asmgen.out("")
asmgen.out("; non-zeropage variables") 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 { stringvars.forEach {
val stringvalue = it.initialValue as StringLiteral val stringvalue = it.initialvalue as StringLiteral
outputStringvar(it.scopedname.last(), it.type, stringvalue.encoding, stringvalue.value) outputStringvar(it.name, it.dt, stringvalue.encoding, stringvalue.value)
} }
othervars.sortedBy { it.type }.forEach { othervars.sortedBy { it.type }.forEach {
staticVariable2asm(it) staticVariable2asm(it)
} }
} }
private fun staticVariable2asm(variable: IVariablesAndConsts.StaticVariable) { private fun staticVariable2asm(variable: StStaticVariable) {
val name = variable.scopedname.last() val name = variable.name
val value = variable.initialValue val value = variable.initialvalue
val staticValue: Number = val staticValue: Number =
if(value!=null) { if(value!=null) {
if(value is NumericLiteral) { if(value is NumericLiteral) {
@ -438,13 +453,13 @@ internal class ProgramAndVarsGen(
else else
value.number.toInt() value.number.toInt()
} else { } else {
if(variable.type in NumericDatatypes) if(variable.dt in NumericDatatypes)
throw AssemblyError("can only deal with constant numeric values for global vars") throw AssemblyError("can only deal with constant numeric values for global vars")
else 0 else 0
} }
} else 0 } else 0
when (variable.type) { when (variable.dt) {
DataType.UBYTE -> asmgen.out("$name\t.byte ${staticValue.toHex()}") DataType.UBYTE -> asmgen.out("$name\t.byte ${staticValue.toHex()}")
DataType.BYTE -> asmgen.out("$name\t.char $staticValue") DataType.BYTE -> asmgen.out("$name\t.char $staticValue")
DataType.UWORD -> asmgen.out("$name\t.word ${staticValue.toHex()}") DataType.UWORD -> asmgen.out("$name\t.word ${staticValue.toHex()}")
@ -460,7 +475,7 @@ internal class ProgramAndVarsGen(
DataType.STR -> { DataType.STR -> {
throw AssemblyError("all string vars should have been interned into prog") 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 -> { else -> {
throw AssemblyError("weird dt") 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) { private fun memdefsAndConsts2asm(block: Block) {
val mvs = variables.blockMemvars[block] ?: emptySet() val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup") }
val consts = variables.blockConsts[block] ?: emptySet() 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) 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) { private fun memdefsAndConsts2asm(sub: Subroutine) {
val mvs = variables.subroutineMemvars[sub] ?: emptySet() val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
val consts = variables.subroutineConsts[sub] ?: emptySet() 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) memdefsAndConsts2asm(mvs, consts)
} }
private fun memdefsAndConsts2asm( private fun memdefsAndConsts2asm(memvars: Collection<StMemVar>, consts: Collection<StConstant>) {
memvars: Set<IVariablesAndConsts.MemoryMappedVariable>,
consts: Set<IVariablesAndConsts.ConstantNumberSymbol>
) {
memvars.forEach { memvars.forEach {
asmgen.out(" ${it.scopedname.last()} = ${it.address.toHex()}") asmgen.out(" ${it.name} = ${it.address.toHex()}")
} }
consts.forEach { consts.forEach {
if(it.type==DataType.FLOAT) if(it.dt==DataType.FLOAT)
asmgen.out(" ${it.scopedname.last()} = ${it.value}") asmgen.out(" ${it.name} = ${it.value}")
else 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, internal class VariableAllocator(private val vars: IVariablesAndConsts,
private val symboltable: SymbolTable,
private val options: CompilationOptions, private val options: CompilationOptions,
private val errors: IErrorReporter) { private val errors: IErrorReporter) {
@ -35,28 +36,59 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
if(options.zeropage== ZeropageType.DONTUSE) if(options.zeropage== ZeropageType.DONTUSE)
return return
val allVariables = ( val allVariablesOld = (
vars.blockVars.asSequence().flatMap { it.value } + vars.blockVars.asSequence().flatMap { it.value } +
vars.subroutineVars.asSequence().flatMap { it.value } vars.subroutineVars.asSequence().flatMap { it.value }
).toList() ).toList()
val numberOfAllocatableVariables = allVariables.size val allVariables = collectAllVariables(symboltable)
val varsRequiringZp = allVariables.filter { it.zp == ZeropageWish.REQUIRE_ZEROPAGE } require(allVariables.size == allVariablesOld.size)
val varsPreferringZp = allVariables.filter { it.zp == ZeropageWish.PREFER_ZEROPAGE } require(allVariables.map{it.scopedName}.toSet()==allVariablesOld.map{it.scopedname}.toSet())
val varsDontCare = allVariables.filter { it.zp == ZeropageWish.DONTCARE }
val numberOfExplicitNonZpVariables = allVariables.count { it.zp == ZeropageWish.NOT_IN_ZEROPAGE } 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) 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 numVariablesAllocatedInZP = 0
var numberOfNonIntegerVariables = 0 var numberOfNonIntegerVariables = 0
varsRequiringZp.forEach { variable -> varsRequiringZp2.forEach { variable ->
val numElements = numArrayElements(variable) val numElements = numArrayElements(variable)
val result = zeropage.allocate( val result = zeropage.allocate(
variable.scopedname, variable.scopedName,
variable.type, variable.dt,
numElements, numElements,
variable.initialValue, variable.initialvalue,
variable.position, variable.position,
errors errors
) )
@ -71,13 +103,13 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
} }
if(errors.noErrors()) { if(errors.noErrors()) {
varsPreferringZp.forEach { variable -> varsPreferringZp2.forEach { variable ->
val numElements = numArrayElements(variable) val numElements = numArrayElements(variable)
val result = zeropage.allocate( val result = zeropage.allocate(
variable.scopedname, variable.scopedName,
variable.type, variable.dt,
numElements, numElements,
variable.initialValue, variable.initialvalue,
variable.position, variable.position,
errors 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. // 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? ...? // TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
if(errors.noErrors()) { if(errors.noErrors()) {
for (variable in varsDontCare) { for (variable in varsDontCare2.sortedWith(compareBy({it.scopedName.size}, {it.name}))) {
if(variable.type in IntegerDatatypes) { if(variable.dt in IntegerDatatypes) {
if(zeropage.free.isEmpty()) { if(zeropage.free.isEmpty()) {
break break
} else { } else {
val numElements = numArrayElements(variable) val numElements = numArrayElements(variable)
val result = zeropage.allocate( val result = zeropage.allocate(
variable.scopedname, variable.scopedName,
variable.type, variable.dt,
numElements, numElements,
variable.initialValue, variable.initialvalue,
variable.position, variable.position,
errors errors
) )
@ -115,6 +147,20 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
println(" zeropage free space: ${zeropage.free.size} bytes") 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 internal fun isZpVar(scopedName: List<String>) = scopedName in zeropage.allocatedVariables
private fun numArrayElements(variable: IVariablesAndConsts.StaticVariable) = private fun numArrayElements(variable: IVariablesAndConsts.StaticVariable) =
@ -124,6 +170,13 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts,
else -> null 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 { internal fun subroutineExtra(sub: Subroutine): SubroutineExtraAsmInfo {
var extra = subroutineExtras[sub] var extra = subroutineExtras[sub]
return if(extra==null) { return if(extra==null) {