diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 7c779ea76..5ad1ef3bb 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -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() 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() private val breakpointLabels = mutableListOf() 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) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index 307db2614..e8ea36a93 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -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) { + private fun nonZpVariables2asm(variables: List) { 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, - consts: Set - ) { + private fun memdefsAndConsts2asm(memvars: Collection, consts: Collection) { 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()}") } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt index 8b263a6ab..60a9a031b 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt @@ -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 { + val vars = mutableListOf() + 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) = 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) {