start to rewrite variable allocation

This commit is contained in:
Irmen de Jong 2022-02-07 22:44:58 +01:00
parent 72dfb0bda3
commit 9acc2f92d1
6 changed files with 80 additions and 69 deletions

View File

@ -13,9 +13,9 @@ import kotlin.io.path.Path
import kotlin.io.path.writeLines import kotlin.io.path.writeLines
const val generatedLabelPrefix = "prog8_label_" internal const val generatedLabelPrefix = "prog8_label_"
const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1" internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1"
const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2" internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
class AsmGen(internal val program: Program, class AsmGen(internal val program: Program,
@ -124,7 +124,7 @@ class AsmGen(internal val program: Program,
fun asmSymbolName(identifier: IdentifierReference) = asmSymbolName(identifier.nameInSource) fun asmSymbolName(identifier: IdentifierReference) = asmSymbolName(identifier.nameInSource)
fun asmVariableName(identifier: IdentifierReference) = asmVariableName(identifier.nameInSource) fun asmVariableName(identifier: IdentifierReference) = asmVariableName(identifier.nameInSource)
fun getTempVarName(dt: DataType): List<String> { internal fun getTempVarName(dt: DataType): List<String> {
return when(dt) { return when(dt) {
DataType.UBYTE -> listOf("cx16", "r9L") DataType.UBYTE -> listOf("cx16", "r9L")
DataType.BYTE -> listOf("cx16", "r9sL") DataType.BYTE -> listOf("cx16", "r9sL")
@ -330,7 +330,6 @@ class AsmGen(internal val program: Program,
internal fun translate(stmt: Statement) { internal fun translate(stmt: Statement) {
outputSourceLine(stmt) outputSourceLine(stmt)
when(stmt) { when(stmt) {
is VarDecl -> translate(stmt)
is Directive -> translate(stmt) is Directive -> translate(stmt)
is Return -> translate(stmt) is Return -> translate(stmt)
is Subroutine -> programGen.translateSubroutine(stmt) is Subroutine -> programGen.translateSubroutine(stmt)
@ -359,6 +358,7 @@ class AsmGen(internal val program: Program,
is When -> translate(stmt) is When -> translate(stmt)
is AnonymousScope -> translate(stmt) is AnonymousScope -> translate(stmt)
is Pipe -> translatePipeExpression(stmt.expressions, stmt, true, false) 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 BuiltinFunctionPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore")
is UntilLoop -> throw AssemblyError("do..until should have been converted to jumps") is UntilLoop -> throw AssemblyError("do..until should have been converted to jumps")
is WhileLoop -> throw AssemblyError("while 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) { private fun translate(stmt: Directive) {
when(stmt.directive) { when(stmt.directive) {
"%asminclude" -> { "%asminclude" -> {

View File

@ -10,7 +10,7 @@ import prog8.compilerinterface.IMachineDefinition
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations // note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, program: Program): Int { internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, program: Program): Int {
var numberOfOptimizations = 0 var numberOfOptimizations = 0

View File

@ -11,7 +11,7 @@ import kotlin.io.path.Path
import kotlin.io.path.isRegularFile import kotlin.io.path.isRegularFile
class AssemblyProgram( internal class AssemblyProgram(
override val name: String, override val name: String,
outputDir: Path, outputDir: Path,
private val compTarget: ICompilationTarget) : IAssemblyProgram { private val compTarget: ICompilationTarget) : IAssemblyProgram {

View File

@ -1,7 +1,6 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold import com.github.michaelbull.result.fold
import com.github.michaelbull.result.onSuccess
import prog8.ast.IFunctionCall import prog8.ast.IFunctionCall
import prog8.ast.IStatementContainer import prog8.ast.IStatementContainer
import prog8.ast.Program import prog8.ast.Program
@ -28,7 +27,7 @@ internal class ProgramGen(
) { ) {
private val compTarget = options.compTarget private val compTarget = options.compTarget
private val removals = mutableListOf<Pair<Statement, IStatementContainer>>() private val removals = mutableListOf<Pair<Statement, IStatementContainer>>()
private val varsInZeropage = mutableSetOf<VarDecl>() private val allocation = VariableAllocation(variables, errors)
private val callGraph = CallGraph(program) private val callGraph = CallGraph(program)
private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance<Assignment>() } private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance<Assignment>() }
@ -43,7 +42,7 @@ internal class ProgramGen(
if(allBlocks.first().name != "main") if(allBlocks.first().name != "main")
throw AssemblyError("first block should be 'main'") throw AssemblyError("first block should be 'main'")
allocateAllZeropageVariables() allocation.allocateAllZeropageVariables(options, callGraph)
if(errors.noErrors()) { if(errors.noErrors()) {
program.allBlocks.forEach { block2asm(it) } program.allBlocks.forEach { block2asm(it) }
@ -52,7 +51,7 @@ internal class ProgramGen(
removals.remove(removal) removals.remove(removal)
} }
slaballocations() memorySlabs()
footer() footer()
} }
} }
@ -134,7 +133,7 @@ internal class ProgramGen(
} }
} }
private fun slaballocations() { private fun memorySlabs() {
asmgen.out("; memory slabs") asmgen.out("; memory slabs")
asmgen.out("prog8_slabs\t.block") asmgen.out("prog8_slabs\t.block")
for((name, info) in asmgen.allMemorySlabs) { 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 // 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 stringVarsInZp = allocation.varsInZeropage.filter { it.datatype==DataType.STR && it.value!=null }
val arrayVarsInZp = varsInZeropage.filter { it.datatype in ArrayDatatypes && it.value!=null } val arrayVarsInZp = allocation.varsInZeropage.filter { it.datatype in ArrayDatatypes && it.value!=null }
if(stringVarsInZp.isNotEmpty() || arrayVarsInZp.isNotEmpty()) { if(stringVarsInZp.isNotEmpty() || arrayVarsInZp.isNotEmpty()) {
asmgen.out("; zp str and array initializations") asmgen.out("; zp str and array initializations")
stringVarsInZp.forEach { stringVarsInZp.forEach {
@ -354,54 +353,6 @@ internal class ProgramGen(
clc""") clc""")
} }
private fun allocateAllZeropageVariables() {
if(options.zeropage==ZeropageType.DONTUSE)
return
val allVariables = this.callGraph.allIdentifiers.asSequence()
.map { it.value }
.filterIsInstance<VarDecl>()
.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<Statement>, inBlock: Block?) { private fun zeropagevars2asm(statements: List<Statement>, inBlock: Block?) {
asmgen.out("; vars allocated on zeropage") asmgen.out("; vars allocated on zeropage")
val variables = statements.asSequence().filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR } val variables = statements.asSequence().filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }

View File

@ -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<VarDecl>()
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<VarDecl>()
.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.
}
}
}
}

View File

@ -4,7 +4,7 @@ import prog8.compilerinterface.CompilationOptions
import prog8.compilerinterface.IAssemblyProgram 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 { override fun assemble(options: CompilationOptions): Boolean {
println("..todo: assemble code into binary..") println("..todo: assemble code into binary..")