From 9acc2f92d15bd80edd6c44810a0e234ddce4f632 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 7 Feb 2022 22:44:58 +0100 Subject: [PATCH] start to rewrite variable allocation --- .../src/prog8/codegen/cpu6502/AsmGen.kt | 16 ++--- .../src/prog8/codegen/cpu6502/AsmOptimizer.kt | 2 +- .../prog8/codegen/cpu6502/AssemblyProgram.kt | 2 +- .../src/prog8/codegen/cpu6502/ProgramGen.kt | 61 ++--------------- .../codegen/cpu6502/VariableAllocation.kt | 66 +++++++++++++++++++ .../experimental6502/AssemblyProgram.kt | 2 +- 6 files changed, 80 insertions(+), 69 deletions(-) create mode 100644 codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocation.kt diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index ff95aec7c..084794d94 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -13,9 +13,9 @@ import kotlin.io.path.Path import kotlin.io.path.writeLines -const val generatedLabelPrefix = "prog8_label_" -const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1" -const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2" +internal const val generatedLabelPrefix = "prog8_label_" +internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1" +internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2" class AsmGen(internal val program: Program, @@ -124,7 +124,7 @@ class AsmGen(internal val program: Program, fun asmSymbolName(identifier: IdentifierReference) = asmSymbolName(identifier.nameInSource) fun asmVariableName(identifier: IdentifierReference) = asmVariableName(identifier.nameInSource) - fun getTempVarName(dt: DataType): List { + internal fun getTempVarName(dt: DataType): List { return when(dt) { DataType.UBYTE -> listOf("cx16", "r9L") DataType.BYTE -> listOf("cx16", "r9sL") @@ -330,7 +330,6 @@ class AsmGen(internal val program: Program, internal fun translate(stmt: Statement) { outputSourceLine(stmt) when(stmt) { - is VarDecl -> translate(stmt) is Directive -> translate(stmt) is Return -> translate(stmt) is Subroutine -> programGen.translateSubroutine(stmt) @@ -359,6 +358,7 @@ class AsmGen(internal val program: Program, is When -> translate(stmt) is AnonymousScope -> translate(stmt) is Pipe -> translatePipeExpression(stmt.expressions, stmt, true, false) + is VarDecl -> { /* do nothing; variables are handled elsewhere */ } is BuiltinFunctionPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore") is UntilLoop -> throw AssemblyError("do..until should have been converted to jumps") is WhileLoop -> throw AssemblyError("while should have been converted to jumps") @@ -882,12 +882,6 @@ $repeatLabel lda $counterVar } } - private fun translate(decl: VarDecl) { - if(decl.type==VarDeclType.VAR && decl.value != null && decl.datatype in NumericDatatypes) - throw AssemblyError("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl") - // at this time, nothing has to be done here anymore code-wise - } - private fun translate(stmt: Directive) { when(stmt.directive) { "%asminclude" -> { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmOptimizer.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmOptimizer.kt index 39de83f84..3581cd440 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmOptimizer.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmOptimizer.kt @@ -10,7 +10,7 @@ import prog8.compilerinterface.IMachineDefinition // note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations -fun optimizeAssembly(lines: MutableList, machine: IMachineDefinition, program: Program): Int { +internal fun optimizeAssembly(lines: MutableList, machine: IMachineDefinition, program: Program): Int { var numberOfOptimizations = 0 diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt index 0922ddbe0..3f8acd54a 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AssemblyProgram.kt @@ -11,7 +11,7 @@ import kotlin.io.path.Path import kotlin.io.path.isRegularFile -class AssemblyProgram( +internal class AssemblyProgram( override val name: String, outputDir: Path, private val compTarget: ICompilationTarget) : IAssemblyProgram { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramGen.kt index d24892171..545b86cac 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramGen.kt @@ -1,7 +1,6 @@ package prog8.codegen.cpu6502 import com.github.michaelbull.result.fold -import com.github.michaelbull.result.onSuccess import prog8.ast.IFunctionCall import prog8.ast.IStatementContainer import prog8.ast.Program @@ -28,7 +27,7 @@ internal class ProgramGen( ) { private val compTarget = options.compTarget private val removals = mutableListOf>() - private val varsInZeropage = mutableSetOf() + private val allocation = VariableAllocation(variables, errors) private val callGraph = CallGraph(program) private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance() } @@ -43,7 +42,7 @@ internal class ProgramGen( if(allBlocks.first().name != "main") throw AssemblyError("first block should be 'main'") - allocateAllZeropageVariables() + allocation.allocateAllZeropageVariables(options, callGraph) if(errors.noErrors()) { program.allBlocks.forEach { block2asm(it) } @@ -52,7 +51,7 @@ internal class ProgramGen( removals.remove(removal) } - slaballocations() + memorySlabs() footer() } } @@ -134,7 +133,7 @@ internal class ProgramGen( } } - private fun slaballocations() { + private fun memorySlabs() { asmgen.out("; memory slabs") asmgen.out("prog8_slabs\t.block") for((name, info) in asmgen.allMemorySlabs) { @@ -307,8 +306,8 @@ internal class ProgramGen( } // string and array variables in zeropage that have initializer value, should be initialized - val stringVarsInZp = varsInZeropage.filter { it.datatype==DataType.STR && it.value!=null } - val arrayVarsInZp = varsInZeropage.filter { it.datatype in ArrayDatatypes && it.value!=null } + val stringVarsInZp = allocation.varsInZeropage.filter { it.datatype==DataType.STR && it.value!=null } + val arrayVarsInZp = allocation.varsInZeropage.filter { it.datatype in ArrayDatatypes && it.value!=null } if(stringVarsInZp.isNotEmpty() || arrayVarsInZp.isNotEmpty()) { asmgen.out("; zp str and array initializations") stringVarsInZp.forEach { @@ -354,54 +353,6 @@ internal class ProgramGen( clc""") } - private fun allocateAllZeropageVariables() { - if(options.zeropage==ZeropageType.DONTUSE) - return - val allVariables = this.callGraph.allIdentifiers.asSequence() - .map { it.value } - .filterIsInstance() - .filter { it.type==VarDeclType.VAR } - .toSet() - .map { it to it.scopedName } - val varsRequiringZp = allVariables.filter { it.first.zeropage==ZeropageWish.REQUIRE_ZEROPAGE } - val varsPreferringZp = allVariables - .filter { it.first.zeropage==ZeropageWish.PREFER_ZEROPAGE } - .sortedBy { options.compTarget.memorySize(it.first.datatype) } // allocate the smallest DT first - - for ((vardecl, scopedname) in varsRequiringZp) { - val numElements: Int? = when(vardecl.datatype) { - DataType.STR -> { - (vardecl.value as StringLiteralValue).value.length - } - in ArrayDatatypes -> { - vardecl.arraysize!!.constIndex() - } - else -> null - } - val result = asmgen.zeropage.allocate(scopedname, vardecl.datatype, numElements, vardecl.position, errors) - result.fold( - success = { varsInZeropage.add(vardecl) }, - failure = { errors.err(it.message!!, vardecl.position) } - ) - } - if(errors.noErrors()) { - varsPreferringZp.forEach { (vardecl, scopedname) -> - val arraySize: Int? = when (vardecl.datatype) { - DataType.STR -> { - (vardecl.value as StringLiteralValue).value.length - } - in ArrayDatatypes -> { - vardecl.arraysize!!.constIndex() - } - else -> null - } - val result = asmgen.zeropage.allocate(scopedname, vardecl.datatype, arraySize, vardecl.position, errors) - result.onSuccess { varsInZeropage.add(vardecl) } - // no need to check for error, if there is one, just allocate in normal system ram later. - } - } - } - private fun zeropagevars2asm(statements: List, inBlock: Block?) { asmgen.out("; vars allocated on zeropage") val variables = statements.asSequence().filterIsInstance().filter { it.type==VarDeclType.VAR } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocation.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocation.kt new file mode 100644 index 000000000..e80f3b11c --- /dev/null +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocation.kt @@ -0,0 +1,66 @@ +package prog8.codegen.cpu6502 + +import com.github.michaelbull.result.fold +import com.github.michaelbull.result.onSuccess +import prog8.ast.base.ArrayDatatypes +import prog8.ast.base.DataType +import prog8.ast.base.VarDeclType +import prog8.ast.expressions.StringLiteralValue +import prog8.ast.statements.VarDecl +import prog8.ast.statements.ZeropageWish +import prog8.compilerinterface.* + + +internal class VariableAllocation(val vars: IVariablesAndConsts, val errors: IErrorReporter) { + val varsInZeropage = mutableSetOf() + + fun allocateAllZeropageVariables(options: CompilationOptions, callGraph: CallGraph) { + if(options.zeropage== ZeropageType.DONTUSE) + return + + val zeropage = options.compTarget.machine.zeropage + val allVariables = callGraph.allIdentifiers.asSequence() + .map { it.value } + .filterIsInstance() + .filter { it.type== VarDeclType.VAR } + .toSet() + .map { it to it.scopedName } + val varsRequiringZp = allVariables.filter { it.first.zeropage== ZeropageWish.REQUIRE_ZEROPAGE } + val varsPreferringZp = allVariables + .filter { it.first.zeropage== ZeropageWish.PREFER_ZEROPAGE } + .sortedBy { options.compTarget.memorySize(it.first.datatype) } // allocate the smallest DT first + + for ((vardecl, scopedname) in varsRequiringZp) { + val numElements: Int? = when(vardecl.datatype) { + DataType.STR -> { + (vardecl.value as StringLiteralValue).value.length + } + in ArrayDatatypes -> { + vardecl.arraysize!!.constIndex() + } + else -> null + } + val result = zeropage.allocate(scopedname, vardecl.datatype, numElements, vardecl.position, errors) + result.fold( + success = { varsInZeropage.add(vardecl) }, + failure = { errors.err(it.message!!, vardecl.position) } + ) + } + if(errors.noErrors()) { + varsPreferringZp.forEach { (vardecl, scopedname) -> + val arraySize: Int? = when (vardecl.datatype) { + DataType.STR -> { + (vardecl.value as StringLiteralValue).value.length + } + in ArrayDatatypes -> { + vardecl.arraysize!!.constIndex() + } + else -> null + } + val result = zeropage.allocate(scopedname, vardecl.datatype, arraySize, vardecl.position, errors) + result.onSuccess { varsInZeropage.add(vardecl) } + // no need to check for error, if there is one, just allocate in normal system ram later. + } + } + } +} \ No newline at end of file diff --git a/codeGenExperimental6502/src/prog8/codegen/experimental6502/AssemblyProgram.kt b/codeGenExperimental6502/src/prog8/codegen/experimental6502/AssemblyProgram.kt index b7681a632..e8e7ca1c3 100644 --- a/codeGenExperimental6502/src/prog8/codegen/experimental6502/AssemblyProgram.kt +++ b/codeGenExperimental6502/src/prog8/codegen/experimental6502/AssemblyProgram.kt @@ -4,7 +4,7 @@ import prog8.compilerinterface.CompilationOptions import prog8.compilerinterface.IAssemblyProgram -class AssemblyProgram(override val name: String) : IAssemblyProgram +internal class AssemblyProgram(override val name: String) : IAssemblyProgram { override fun assemble(options: CompilationOptions): Boolean { println("..todo: assemble code into binary..")