streamlining non-zpvars asmgen using new mechanism

This commit is contained in:
Irmen de Jong 2022-02-09 22:32:18 +01:00
parent b043c3a6da
commit e5d7316e5d
8 changed files with 116 additions and 197 deletions

View File

@ -27,7 +27,7 @@ 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, errors) private val allocator = VariableAllocator(variables, 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)
@ -130,7 +130,7 @@ class AsmGen(internal val program: Program,
is VarDecl -> { is VarDecl -> {
val sourceName = asmVariableName(pointervar) val sourceName = asmVariableName(pointervar)
if (isTargetCpu(CpuType.CPU65c02)) { if (isTargetCpu(CpuType.CPU65c02)) {
return if (isZpVar(target.scopedName)) { return if (allocator.isZpVar(target.scopedName)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
out(" lda ($sourceName)") out(" lda ($sourceName)")
sourceName sourceName
@ -144,7 +144,7 @@ class AsmGen(internal val program: Program,
"P8ZP_SCRATCH_W1" "P8ZP_SCRATCH_W1"
} }
} else { } else {
return if (isZpVar(target.scopedName)) { return if (allocator.isZpVar(target.scopedName)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
out(" ldy #0 | lda ($sourceName),y") out(" ldy #0 | lda ($sourceName),y")
sourceName sourceName
@ -168,7 +168,7 @@ class AsmGen(internal val program: Program,
val sourceName = asmVariableName(pointervar) val sourceName = asmVariableName(pointervar)
val vardecl = pointervar.targetVarDecl(program)!! val vardecl = pointervar.targetVarDecl(program)!!
if (isTargetCpu(CpuType.CPU65c02)) { if (isTargetCpu(CpuType.CPU65c02)) {
if (isZpVar(vardecl.scopedName)) { if (allocator.isZpVar(vardecl.scopedName)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
out(" sta ($sourceName)") out(" sta ($sourceName)")
} else { } else {
@ -180,7 +180,7 @@ class AsmGen(internal val program: Program,
sta (P8ZP_SCRATCH_W2)""") sta (P8ZP_SCRATCH_W2)""")
} }
} else { } else {
if (isZpVar(vardecl.scopedName)) { if (allocator.isZpVar(vardecl.scopedName)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
out(" ldy #0 | sta ($sourceName),y") out(" ldy #0 | sta ($sourceName),y")
} else { } else {
@ -1037,12 +1037,8 @@ $repeatLabel lda $counterVar
} }
} }
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropage.variables internal fun isZpVar(variable: IdentifierReference): Boolean =
allocator.isZpVar(variable.targetVarDecl(program)!!.scopedName)
internal fun isZpVar(variable: IdentifierReference): Boolean {
val vardecl = variable.targetVarDecl(program)!!
return vardecl.scopedName in zeropage.variables
}
internal fun jmp(asmLabel: String, indirect: Boolean=false) { internal fun jmp(asmLabel: String, indirect: Boolean=false) {
if(indirect) { if(indirect) {

View File

@ -31,7 +31,7 @@ internal class ProgramGen(
val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value } val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value }
require(allInitializers.all { it.origin==AssignmentOrigin.VARINIT }) {"all block-level assignments must be a variable initializer"} require(allInitializers.all { it.origin==AssignmentOrigin.VARINIT }) {"all block-level assignments must be a variable initializer"}
allocator.allocateZeropageVariables(options) allocator.allocateZeropageVariables()
header() header()
val allBlocks = program.allBlocks val allBlocks = program.allBlocks
if(allBlocks.first().name != "main") if(allBlocks.first().name != "main")
@ -347,28 +347,31 @@ internal class ProgramGen(
} }
} }
private fun nonZpVariables2asm(scope: INameScope) { private fun nonZpVariables2asm(block: Block) {
val variables = variables.blockVars[block]?.filter { !allocator.isZpVar(it.scopedname) } ?: emptyList()
nonZpVariables2asm(variables)
}
private fun nonZpVariables2asm(sub: Subroutine) {
val variables = variables.subroutineVars[sub]?.filter { !allocator.isZpVar(it.scopedname) } ?: emptyList()
nonZpVariables2asm(variables)
}
private fun nonZpVariables2asm(variables: List<IVariablesAndConsts.StaticVariable>) {
asmgen.out("\n; non-zeropage variables") asmgen.out("\n; non-zeropage variables")
val (stringvars, othervars) = variables.partition { it.type==DataType.STR }
val vars = scope.statements stringvars.forEach {
.filterIsInstance<VarDecl>() val stringvalue = it.initialValue as StringLiteralValue
.filter { outputStringvar(it.scopedname.last(), it.type, stringvalue.encoding, stringvalue.value)
it.type==VarDeclType.VAR && it.scopedName !in zeropage.variables }
} othervars.sortedBy { it.type }.forEach {
vardecl2asm(it)
vars.filter { it.datatype == DataType.STR && shouldActuallyOutputStringVar(it) }
.forEach { outputStringvar(it) }
vars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach {
require(it.zeropage!= ZeropageWish.REQUIRE_ZEROPAGE)
if(!asmgen.isZpVar(it.scopedName))
vardecl2asm(it)
} }
} }
private fun vardecl2asm(decl: VarDecl, nameOverride: String?=null) { private fun vardecl2asm(variable: IVariablesAndConsts.StaticVariable) {
val name = nameOverride ?: decl.name val name = variable.scopedname.last()
val value = decl.value val value = variable.initialValue
val staticValue: Number = val staticValue: Number =
if(value!=null) { if(value!=null) {
if(value is NumericLiteralValue) { if(value is NumericLiteralValue) {
@ -377,13 +380,13 @@ internal class ProgramGen(
else else
value.number.toInt() value.number.toInt()
} else { } else {
if(decl.datatype in NumericDatatypes) if(variable.type in NumericDatatypes)
throw AssemblyError("can only deal with constant numeric values for global vars $value at ${decl.position}") throw AssemblyError("can only deal with constant numeric values for global vars")
else 0 else 0
} }
} else 0 } else 0
when (decl.datatype) { when (variable.type) {
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()}")
@ -399,7 +402,7 @@ internal class ProgramGen(
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 -> arrayVardecl2asm(name, decl.datatype, decl.value as? ArrayLiteralValue, decl.arraysize?.constIndex()) in ArrayDatatypes -> arrayVardecl2asm(name, variable.type, value as? ArrayLiteralValue, variable.arraysize)
else -> { else -> {
throw AssemblyError("weird dt") throw AssemblyError("weird dt")
} }
@ -480,13 +483,13 @@ internal class ProgramGen(
consts: Set<IVariablesAndConsts.ConstantNumberSymbol> consts: Set<IVariablesAndConsts.ConstantNumberSymbol>
) { ) {
memvars.forEach { memvars.forEach {
asmgen.out(" ${it.name} = ${it.address.toHex()}") asmgen.out(" ${it.scopedname.last()} = ${it.address.toHex()}")
} }
consts.forEach { consts.forEach {
if(it.type==DataType.FLOAT) if(it.type==DataType.FLOAT)
asmgen.out(" ${it.name} = ${it.value}") asmgen.out(" ${it.scopedname.last()} = ${it.value}")
else else
asmgen.out(" ${it.name} = ${it.value.toHex()}") asmgen.out(" ${it.scopedname.last()} = ${it.value.toHex()}")
} }
} }
@ -499,23 +502,6 @@ internal class ProgramGen(
} }
} }
private fun shouldActuallyOutputStringVar(strvar: VarDecl): Boolean {
if(strvar.sharedWithAsm)
return true
val uses = callGraph.usages(strvar)
val onlyInMemoryFuncs = uses.all {
val builtinfunc = (it.parent as? IFunctionCall)?.target?.targetStatement(program) as? BuiltinFunctionPlaceholder
builtinfunc?.name=="memory"
}
return !onlyInMemoryFuncs
}
private fun outputStringvar(strdecl: VarDecl, nameOverride: String?=null) {
val varname = nameOverride ?: strdecl.name
val sv = strdecl.value as StringLiteralValue
outputStringvar(varname, strdecl.datatype, sv.encoding, sv.value)
}
private fun outputStringvar(varname: String, dt: DataType, encoding: Encoding, value: String) { private fun outputStringvar(varname: String, dt: DataType, encoding: Encoding, value: String) {
asmgen.out("$varname\t; $dt $encoding:\"${escape(value).replace("\u0000", "<NULL>")}\"") asmgen.out("$varname\t; $dt $encoding:\"${escape(value).replace("\u0000", "<NULL>")}\"")
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte()) val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
@ -589,10 +575,3 @@ internal class ProgramGen(
} }
} }
private fun sameScope(varname: List<String>, scopename: List<String>): Boolean {
if(varname.size!=scopename.size+1)
return false
val pairs = scopename.zip(varname)
return pairs.all { it.first==it.second }
}

View File

@ -6,7 +6,6 @@ import prog8.ast.base.DataType
import prog8.ast.base.IntegerDatatypes import prog8.ast.base.IntegerDatatypes
import prog8.ast.expressions.StringLiteralValue import prog8.ast.expressions.StringLiteralValue
import prog8.ast.statements.Subroutine import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
import prog8.ast.statements.ZeropageWish import prog8.ast.statements.ZeropageWish
import prog8.compilerinterface.CompilationOptions import prog8.compilerinterface.CompilationOptions
import prog8.compilerinterface.IErrorReporter import prog8.compilerinterface.IErrorReporter
@ -14,8 +13,11 @@ import prog8.compilerinterface.IVariablesAndConsts
import prog8.compilerinterface.ZeropageType import prog8.compilerinterface.ZeropageType
internal class VariableAllocator(private val vars: IVariablesAndConsts, private val errors: IErrorReporter) { internal class VariableAllocator(private val vars: IVariablesAndConsts,
private val options: CompilationOptions,
private val errors: IErrorReporter) {
private val zeropage = options.compTarget.machine.zeropage
private val subroutineExtras = mutableMapOf<Subroutine, SubroutineExtraAsmInfo>() private val subroutineExtras = mutableMapOf<Subroutine, SubroutineExtraAsmInfo>()
private val memorySlabsInternal = mutableMapOf<String, Pair<UInt, UInt>>() private val memorySlabsInternal = mutableMapOf<String, Pair<UInt, UInt>>()
internal val memorySlabs: Map<String, Pair<UInt, UInt>> = memorySlabsInternal internal val memorySlabs: Map<String, Pair<UInt, UInt>> = memorySlabsInternal
@ -30,40 +32,39 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts, private
* Allocate variables into the Zeropage. * Allocate variables into the Zeropage.
* The result should be retrieved from the current machine's zeropage object! * The result should be retrieved from the current machine's zeropage object!
*/ */
fun allocateZeropageVariables(options: CompilationOptions) { internal fun allocateZeropageVariables() {
if(options.zeropage== ZeropageType.DONTUSE) if(options.zeropage== ZeropageType.DONTUSE)
return return
val zeropage = options.compTarget.machine.zeropage
val allVariables = ( val allVariables = (
vars.blockVars.asSequence().flatMap { it.value }.map {it.origVar to it.origVar.scopedName} + vars.blockVars.asSequence().flatMap { it.value } +
vars.subroutineVars.asSequence().flatMap { it.value }.map {it.origVar to it.origVar.scopedName}) vars.subroutineVars.asSequence().flatMap { it.value }
.toList() ).toList()
val varsRequiringZp = allVariables.filter { it.first.zeropage == ZeropageWish.REQUIRE_ZEROPAGE } val varsRequiringZp = allVariables.filter { it.zp == ZeropageWish.REQUIRE_ZEROPAGE }
val varsPreferringZp = allVariables.filter { it.first.zeropage == ZeropageWish.PREFER_ZEROPAGE } val varsPreferringZp = allVariables.filter { it.zp == ZeropageWish.PREFER_ZEROPAGE }
val varsDontCare = allVariables.filter { it.first.zeropage == ZeropageWish.DONTCARE } val varsDontCare = allVariables.filter { it.zp == ZeropageWish.DONTCARE }
varsRequiringZp.forEach { (vardecl, scopedname) -> varsRequiringZp.forEach { variable ->
val numElements = numArrayElements(vardecl) val numElements = numArrayElements(variable)
val result = zeropage.allocate(scopedname, vardecl.datatype, vardecl.definingScope, numElements, vardecl.value, vardecl.position, errors) val result = zeropage.allocate(variable.scopedname, variable.type, variable.scope, numElements, variable.initialValue, variable.position, errors)
result.onFailure { errors.err(it.message!!, vardecl.position) } result.onFailure { errors.err(it.message!!, variable.position) }
} }
if(errors.noErrors()) { if(errors.noErrors()) {
varsPreferringZp.forEach { (vardecl, scopedname) -> varsPreferringZp.forEach { variable ->
val numElements = numArrayElements(vardecl) val numElements = numArrayElements(variable)
zeropage.allocate(scopedname, vardecl.datatype, vardecl.definingScope, numElements, vardecl.value, vardecl.position, errors) zeropage.allocate(variable.scopedname, variable.type, variable.scope, numElements, variable.initialValue, variable.position, errors)
// no need to check for allocation error, if there is one, just allocate in normal system ram. // no need to check for allocation error, if there is one, just allocate in normal system ram.
} }
// 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 ((vardecl, scopedname) in varsDontCare) { for (variable in varsDontCare) {
if(vardecl.datatype in IntegerDatatypes) { if(variable.type in IntegerDatatypes) {
val numElements = numArrayElements(vardecl) val numElements = numArrayElements(variable)
zeropage.allocate(scopedname, vardecl.datatype, vardecl.definingScope, numElements, vardecl.value, vardecl.position, errors) zeropage.allocate(variable.scopedname, variable.type, variable.scope, numElements, variable.initialValue, variable.position, errors)
if(zeropage.free.isEmpty()) if(zeropage.free.isEmpty())
break break
} }
@ -72,15 +73,14 @@ internal class VariableAllocator(private val vars: IVariablesAndConsts, private
} }
} }
private fun numArrayElements(vardecl: VarDecl): Int? = when(vardecl.datatype) { internal fun isZpVar(scopedName: List<String>) = scopedName in zeropage.variables
DataType.STR -> {
(vardecl.value as StringLiteralValue).value.length private fun numArrayElements(variable: IVariablesAndConsts.StaticVariable) =
when(variable.type) {
DataType.STR -> (variable.initialValue as StringLiteralValue).value.length
in ArrayDatatypes -> variable.arraysize!!
else -> null
} }
in ArrayDatatypes -> {
vardecl.arraysize!!.constIndex()
}
else -> null
}
fun subroutineExtra(sub: Subroutine): SubroutineExtraAsmInfo { fun subroutineExtra(sub: Subroutine): SubroutineExtraAsmInfo {
var extra = subroutineExtras[sub] var extra = subroutineExtras[sub]

View File

@ -7,10 +7,7 @@ import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.statements.Block import prog8.ast.statements.Block
import prog8.ast.statements.Subroutine import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.ast.statements.VarDeclOrigin
import prog8.ast.toHex
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.compilerinterface.IMemSizer
import prog8.compilerinterface.IVariablesAndConsts import prog8.compilerinterface.IVariablesAndConsts
@ -33,21 +30,19 @@ internal class VariableExtractor: IAstVisitor {
val scope=decl.definingScope val scope=decl.definingScope
when (decl.type) { when (decl.type) {
VarDeclType.VAR -> { VarDeclType.VAR -> {
if(decl.origin!= VarDeclOrigin.SUBROUTINEPARAM) { when (scope) {
when (scope) { is Block -> {
is Block -> { val decls = allBlockVars[scope] ?: mutableSetOf()
val decls = allBlockVars[scope] ?: mutableSetOf() decls.add(decl)
decls.add(decl) allBlockVars[scope] = decls
allBlockVars[scope] = decls }
} is Subroutine -> {
is Subroutine -> { val decls = allSubroutineVars[scope] ?: mutableSetOf()
val decls = allSubroutineVars[scope] ?: mutableSetOf() decls.add(decl)
decls.add(decl) allSubroutineVars[scope] = decls
allSubroutineVars[scope] = decls }
} else -> {
else -> { throw FatalAstException("var can only occur in subroutine or block scope")
throw FatalAstException("var can only occur in subroutine or block scope")
}
} }
} }
} }
@ -102,24 +97,24 @@ internal class VariablesAndConsts (
astSubroutineMemvars: Map<Subroutine, Set<VarDecl>> astSubroutineMemvars: Map<Subroutine, Set<VarDecl>>
) : IVariablesAndConsts ) : IVariablesAndConsts
{ {
override val blockVars: Map<Block, Set<IVariablesAndConsts.StaticBlockVariable>> override val blockVars: Map<Block, Set<IVariablesAndConsts.StaticVariable>>
override val blockConsts: Map<Block, Set<IVariablesAndConsts.ConstantNumberSymbol>> override val blockConsts: Map<Block, Set<IVariablesAndConsts.ConstantNumberSymbol>>
override val blockMemvars: Map<Block, Set<IVariablesAndConsts.MemoryMappedVariable>> override val blockMemvars: Map<Block, Set<IVariablesAndConsts.MemoryMappedVariable>>
override val subroutineVars: Map<Subroutine, Set<IVariablesAndConsts.StaticSubroutineVariable>> override val subroutineVars: Map<Subroutine, Set<IVariablesAndConsts.StaticVariable>>
override val subroutineConsts: Map<Subroutine, Set<IVariablesAndConsts.ConstantNumberSymbol>> override val subroutineConsts: Map<Subroutine, Set<IVariablesAndConsts.ConstantNumberSymbol>>
override val subroutineMemvars: Map<Subroutine, Set<IVariablesAndConsts.MemoryMappedVariable>> override val subroutineMemvars: Map<Subroutine, Set<IVariablesAndConsts.MemoryMappedVariable>>
init { init {
val bv = astBlockVars.keys.associateWith { mutableSetOf<IVariablesAndConsts.StaticBlockVariable>() } val bv = astBlockVars.keys.associateWith { mutableSetOf<IVariablesAndConsts.StaticVariable>() }
val bc = astBlockConsts.keys.associateWith { mutableSetOf<IVariablesAndConsts.ConstantNumberSymbol>() } val bc = astBlockConsts.keys.associateWith { mutableSetOf<IVariablesAndConsts.ConstantNumberSymbol>() }
val bmv = astBlockMemvars.keys.associateWith { mutableSetOf<IVariablesAndConsts.MemoryMappedVariable>() } val bmv = astBlockMemvars.keys.associateWith { mutableSetOf<IVariablesAndConsts.MemoryMappedVariable>() }
val sv = astSubroutineVars.keys.associateWith { mutableSetOf<IVariablesAndConsts.StaticSubroutineVariable>() } val sv = astSubroutineVars.keys.associateWith { mutableSetOf<IVariablesAndConsts.StaticVariable>() }
val sc = astSubroutineConsts.keys.associateWith { mutableSetOf<IVariablesAndConsts.ConstantNumberSymbol>() } val sc = astSubroutineConsts.keys.associateWith { mutableSetOf<IVariablesAndConsts.ConstantNumberSymbol>() }
val smv = astSubroutineMemvars.keys.associateWith { mutableSetOf<IVariablesAndConsts.MemoryMappedVariable>() } val smv = astSubroutineMemvars.keys.associateWith { mutableSetOf<IVariablesAndConsts.MemoryMappedVariable>() }
astBlockVars.forEach { (block, decls) -> astBlockVars.forEach { (block, decls) ->
val vars = bv.getValue(block) val vars = bv.getValue(block)
vars.addAll(decls.map { vars.addAll(decls.map {
IVariablesAndConsts.StaticBlockVariable(it.datatype, it.name, it.value, it.zeropage, it.position, it) IVariablesAndConsts.StaticVariable(it.datatype, it.scopedName, it.definingScope, it.value, it.arraysize?.constIndex(), it.zeropage, it.position)
}) })
} }
astBlockConsts.forEach { (block, decls) -> astBlockConsts.forEach { (block, decls) ->
@ -127,7 +122,7 @@ internal class VariablesAndConsts (
decls.map { decls.map {
IVariablesAndConsts.ConstantNumberSymbol( IVariablesAndConsts.ConstantNumberSymbol(
it.datatype, it.datatype,
it.name, it.scopedName,
(it.value as NumericLiteralValue).number, (it.value as NumericLiteralValue).number,
it.position it.position
) )
@ -141,7 +136,7 @@ internal class VariablesAndConsts (
vars.add( vars.add(
IVariablesAndConsts.MemoryMappedVariable( IVariablesAndConsts.MemoryMappedVariable(
decl.datatype, decl.datatype,
decl.name, decl.scopedName,
(decl.value as NumericLiteralValue).number.toUInt(), (decl.value as NumericLiteralValue).number.toUInt(),
decl.position decl.position
) )
@ -152,7 +147,7 @@ internal class VariablesAndConsts (
astSubroutineVars.forEach { (sub, decls) -> astSubroutineVars.forEach { (sub, decls) ->
val vars = sv.getValue(sub) val vars = sv.getValue(sub)
vars.addAll(decls.map { vars.addAll(decls.map {
IVariablesAndConsts.StaticSubroutineVariable(it.datatype, it.name, it.zeropage, it.position, it) IVariablesAndConsts.StaticVariable(it.datatype, it.scopedName, it.definingScope, it.value, it.arraysize?.constIndex(), it.zeropage, it.position)
}) })
} }
astSubroutineConsts.forEach { (sub, decls) -> astSubroutineConsts.forEach { (sub, decls) ->
@ -160,7 +155,7 @@ internal class VariablesAndConsts (
decls.map { decls.map {
IVariablesAndConsts.ConstantNumberSymbol( IVariablesAndConsts.ConstantNumberSymbol(
it.datatype, it.datatype,
it.name, it.scopedName,
(it.value as NumericLiteralValue).number, (it.value as NumericLiteralValue).number,
it.position it.position
) )
@ -171,7 +166,7 @@ internal class VariablesAndConsts (
decls.map { decls.map {
IVariablesAndConsts.MemoryMappedVariable( IVariablesAndConsts.MemoryMappedVariable(
it.datatype, it.datatype,
it.name, it.scopedName,
(it.value as NumericLiteralValue).number.toUInt(), (it.value as NumericLiteralValue).number.toUInt(),
it.position it.position
) )
@ -184,52 +179,4 @@ internal class VariablesAndConsts (
subroutineConsts = sc subroutineConsts = sc
subroutineMemvars = smv subroutineMemvars = smv
} }
override fun dump(memsizer: IMemSizer) {
println("\nALL BLOCK VARS:")
blockVars.forEach { (block, vars) ->
val totalsize = vars.sumOf { memsizer.memorySize(it.origVar) }
println("BLOCK: ${block.name} total size: $totalsize")
vars.forEach {
println(" ${it.type} ${it.name} = ${it.initialValue} ${it.position}")
}
}
println("\nALL BLOCK CONSTS:")
blockConsts.forEach { (block, vars) ->
println("BLOCK: ${block.name}")
vars.forEach {
println(" ${it.type} ${it.name} = ${it.value} ${it.position}")
}
}
println("\nALL BLOCK MEMORYVARS:")
blockMemvars.forEach { (block, vars) ->
println("BLOCK: ${block.name}")
vars.forEach {
println(" ${it.type} ${it.name} = ${it.address.toHex()} ${it.position}")
}
}
println("\nALL SUBROUTINE VARS:")
subroutineVars.forEach { (sub, vars) ->
val totalsize = vars.sumOf { memsizer.memorySize(it.origVar) }
println("SUBROUTINE: ${sub.name} total size: $totalsize")
vars.forEach {
println(" ${it.type} ${it.name} ${it.position}")
}
}
println("\nALL SUBROUTINE CONSTS:")
subroutineConsts.forEach { (sub, vars) ->
println("SUBROUTINE: ${sub.name}")
vars.forEach {
println(" ${it.type} ${it.name} = ${it.value} ${it.position}")
}
}
println("\nALL SUBROUTINE MEMORYVARS:")
subroutineMemvars.forEach { (sub, vars) ->
println("SUBROUTINE: ${sub.name}")
vars.forEach {
println(" ${it.type} ${it.name} = ${it.address.toHex()} ${it.position}")
}
}
}
} }

View File

@ -70,22 +70,21 @@ class TestAsmGenSymbols: StringSpec({
val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test")) val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder).addModule(module) val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder).addModule(module)
val variables = object : IVariablesAndConsts { val variables = object : IVariablesAndConsts {
override fun dump(memsizer: IMemSizer) { } override val blockVars: Map<Block, Set<IVariablesAndConsts.StaticVariable>>
override val blockVars: Map<Block, Set<IVariablesAndConsts.StaticBlockVariable>>
override val blockConsts: Map<Block, Set<IVariablesAndConsts.ConstantNumberSymbol>> override val blockConsts: Map<Block, Set<IVariablesAndConsts.ConstantNumberSymbol>>
override val blockMemvars: Map<Block, Set<IVariablesAndConsts.MemoryMappedVariable>> override val blockMemvars: Map<Block, Set<IVariablesAndConsts.MemoryMappedVariable>>
override val subroutineVars: Map<Subroutine, Set<IVariablesAndConsts.StaticSubroutineVariable>> override val subroutineVars: Map<Subroutine, Set<IVariablesAndConsts.StaticVariable>>
override val subroutineConsts: Map<Subroutine, Set<IVariablesAndConsts.ConstantNumberSymbol>> override val subroutineConsts: Map<Subroutine, Set<IVariablesAndConsts.ConstantNumberSymbol>>
override val subroutineMemvars: Map<Subroutine, Set<IVariablesAndConsts.MemoryMappedVariable>> override val subroutineMemvars: Map<Subroutine, Set<IVariablesAndConsts.MemoryMappedVariable>>
init { init {
blockVars = mutableMapOf() blockVars = mutableMapOf()
blockVars[block] = mutableSetOf(IVariablesAndConsts.StaticBlockVariable(varInBlock.datatype, varInBlock.name, varInBlock.value, varInBlock.zeropage, varInBlock.position, varInBlock)) blockVars[block] = mutableSetOf(IVariablesAndConsts.StaticVariable(varInBlock.datatype, varInBlock.scopedName, varInBlock.definingScope, varInBlock.value, varInBlock.arraysize?.constIndex(), varInBlock.zeropage, varInBlock.position))
blockConsts = mutableMapOf() blockConsts = mutableMapOf()
blockMemvars = mutableMapOf() blockMemvars = mutableMapOf()
subroutineVars = mutableMapOf() subroutineVars = mutableMapOf()
subroutineVars[subroutine] = mutableSetOf( subroutineVars[subroutine] = mutableSetOf(
IVariablesAndConsts.StaticSubroutineVariable(varInSub.datatype, varInSub.name, var2InSub.zeropage, varInSub.position, varInSub), IVariablesAndConsts.StaticVariable(varInSub.datatype, varInSub.scopedName, varInSub.definingScope, varInSub.value, varInSub.arraysize?.constIndex(), varInSub.zeropage, varInSub.position),
IVariablesAndConsts.StaticSubroutineVariable(var2InSub.datatype, var2InSub.name, var2InSub.zeropage, var2InSub.position, var2InSub) IVariablesAndConsts.StaticVariable(var2InSub.datatype, var2InSub.scopedName, var2InSub.definingScope, var2InSub.value, var2InSub.arraysize?.constIndex(), var2InSub.zeropage, var2InSub.position)
) )
subroutineConsts = mutableMapOf() subroutineConsts = mutableMapOf()
subroutineMemvars = mutableMapOf() subroutineMemvars = mutableMapOf()

View File

@ -1,31 +1,30 @@
package prog8.compilerinterface package prog8.compilerinterface
import prog8.ast.INameScope
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.expressions.Expression import prog8.ast.expressions.Expression
import prog8.ast.statements.Block import prog8.ast.statements.*
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
import prog8.ast.statements.ZeropageWish
/** /**
* Experimental attempt for:
* A more convenient way to pass variable (and constant values) definitions to the code generator, * A more convenient way to pass variable (and constant values) definitions to the code generator,
* so that it doesn't have to scavenge all VerDecl nodes in the AST for this information. * so that it doesn't have to scavenge and manipulate the VerDecl nodes in the AST for this information.
*/ */
interface IVariablesAndConsts { interface IVariablesAndConsts {
data class ConstantNumberSymbol(val type: DataType, val name: String, val value: Double, val position: Position) data class ConstantNumberSymbol(val type: DataType, val scopedname: List<String>, val value: Double, val position: Position)
data class MemoryMappedVariable(val type: DataType, val name: String, val address: UInt, val position: Position) data class MemoryMappedVariable(val type: DataType, val scopedname: List<String>, val address: UInt, val position: Position)
// TODO should get rid of origVar altogether in the following two: data class StaticVariable(val type: DataType,
data class StaticBlockVariable(val type: DataType, val name: String, val initialValue: Expression?, val zp: ZeropageWish, val position: Position, val origVar: VarDecl) val scopedname: List<String>,
data class StaticSubroutineVariable(val type: DataType, val name: String, val zp: ZeropageWish, val position: Position, val origVar: VarDecl) val scope: INameScope,
val initialValue: Expression?,
val arraysize: Int?,
val zp: ZeropageWish,
val position: Position)
fun dump(memsizer: IMemSizer) val blockVars: Map<Block, Set<StaticVariable>>
val blockVars: Map<Block, Set<StaticBlockVariable>>
val blockConsts: Map<Block, Set<ConstantNumberSymbol>> val blockConsts: Map<Block, Set<ConstantNumberSymbol>>
val blockMemvars: Map<Block, Set<MemoryMappedVariable>> val blockMemvars: Map<Block, Set<MemoryMappedVariable>>
val subroutineVars: Map<Subroutine, Set<StaticSubroutineVariable>> val subroutineVars: Map<Subroutine, Set<StaticVariable>>
val subroutineConsts: Map<Subroutine, Set<ConstantNumberSymbol>> val subroutineConsts: Map<Subroutine, Set<ConstantNumberSymbol>>
val subroutineMemvars: Map<Subroutine, Set<MemoryMappedVariable>> val subroutineMemvars: Map<Subroutine, Set<MemoryMappedVariable>>
} }

View File

@ -3,14 +3,10 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- programGen: don't generate variables from the VarDecl nodes, use allocator/zeropage tables - get rid of the interned string literals from memory() calls.
after that is done:
- (newvaralloc) UnusedCodeRemover after(decl: VarDecl): fix that vars defined in a library can also safely be removed if unused. Currently this breaks programs such as textelite (due to diskio.save().end_address ?) - (newvaralloc) UnusedCodeRemover after(decl: VarDecl): fix that vars defined in a library can also safely be removed if unused. Currently this breaks programs such as textelite (due to diskio.save().end_address ?)
- check that retval_interm_* are not in the varallocation if they're not used - check that retval_interm_* are not in the varallocation if they're not used
- make it so that subroutine parameters as variables can again be allocated in ZP, if there's still space - make it so that subroutine parameters as variables can again be allocated in ZP, if there's still space
- wormfood became a lot larger??? why??? (and chess a little bit larger, but usually program size is down)
Need help with Need help with
@ -28,11 +24,6 @@ Blocked by an official Commander-x16 r39 release
Future Things and Ideas Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
Ast modifications done in AsmGen that perhaps should not be necessary:
- block2asm: after vardecls2asm it clears the vardecl.value of all variables
--> Don't rely on vardecls at all any longer but use the new IVariablesAndConsts object passed to the AsmGen, this will solve this item.
- block2asm: removes init-assignments to no longer output the initialization assignments as regular statements (is done separately in block initialization routine)
- remove support for old @"screencodes" string encoding syntax (parser+code+docs) - remove support for old @"screencodes" string encoding syntax (parser+code+docs)
- allow "xxx" * constexpr (where constexpr is not a number literal), now gives expression error not same type - allow "xxx" * constexpr (where constexpr is not a number literal), now gives expression error not same type
- unify FunctioncallExpression + FunctioncallStatement and PipeExpression + Pipe statement classes, may require moving Expression/Statement into interfaces instead of abstract base classes - unify FunctioncallExpression + FunctioncallStatement and PipeExpression + Pipe statement classes, may require moving Expression/Statement into interfaces instead of abstract base classes
@ -66,6 +57,7 @@ Ast modifications done in AsmGen that perhaps should not be necessary:
More optimization ideas More optimization ideas
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served
- translateFunctioncall() in BuiltinFunctionsAsmGen: should be able to assign parameters to a builtin function directly from register(s), this will make the use of a builtin function in a pipe expression more efficient without using a temporary variable - translateFunctioncall() in BuiltinFunctionsAsmGen: should be able to assign parameters to a builtin function directly from register(s), this will make the use of a builtin function in a pipe expression more efficient without using a temporary variable
- translateNormalAssignment() -> better code gen for assigning boolean comparison expressions - translateNormalAssignment() -> better code gen for assigning boolean comparison expressions
- when a for loop's loopvariable isn't referenced in the body, and the iterations are known, replace the loop by a repeatloop - when a for loop's loopvariable isn't referenced in the body, and the iterations are known, replace the loop by a repeatloop

View File

@ -6,6 +6,8 @@ main {
ubyte @zp mainglobal1=10 ubyte @zp mainglobal1=10
float @shared fl1 = floats.TWOPI float @shared fl1 = floats.TWOPI
uword @shared m1 = memory("eee", 200, 0)
uword [2] nullwords uword [2] nullwords
ubyte [2] nullbytes ubyte [2] nullbytes
uword [2] valuewords = [1111,22222] uword [2] valuewords = [1111,22222]
@ -21,7 +23,12 @@ main {
sub start() { sub start() {
prog8_lib.P8ZP_SCRATCH_B1 = 1 prog8_lib.P8ZP_SCRATCH_B1 = 1
prog8_lib.P8ZP_SCRATCH_W1 = 1111 prog8_lib.P8ZP_SCRATCH_W1 = 1111
str alsoname = "also name"
txt.print(alsoname)
txt.print(zpname)
txt.print("internedstring")
txt.spc()
txt.print_uwhex(&prog8_lib.P8ZP_SCRATCH_B1, true) txt.print_uwhex(&prog8_lib.P8ZP_SCRATCH_B1, true)
txt.spc() txt.spc()
txt.print_uwhex(&prog8_lib.P8ZP_SCRATCH_W1, true) txt.print_uwhex(&prog8_lib.P8ZP_SCRATCH_W1, true)