mirror of
https://github.com/irmen/prog8.git
synced 2024-11-22 15:33:02 +00:00
streamlining non-zpvars asmgen using new mechanism
This commit is contained in:
parent
b043c3a6da
commit
e5d7316e5d
@ -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) {
|
||||||
|
@ -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 }
|
|
||||||
}
|
|
@ -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]
|
||||||
|
@ -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}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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>>
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user