IR codegen: global vars with numeric initialization value are now also put into the VARIABLESWITHINIT section rather than requiring explicit code instructions to initialize them in INITGLOBALS.

Note that something similar, such as putting those variables inline in the program initialized with their value and all, cannot be done for the 6502 codegen: the program needs a mechanism to reset ALL variables when it runs a second time.
This commit is contained in:
Irmen de Jong 2024-10-16 20:07:48 +02:00
parent abbf7c7cb0
commit 38ef394e15
9 changed files with 103 additions and 42 deletions

View File

@ -178,31 +178,43 @@ open class StNode(val name: String,
class StStaticVariable(name: String, class StStaticVariable(name: String,
val dt: DataType, val dt: DataType,
val onetimeInitializationNumericValue: Double?, // regular (every-run-time) initialization is done via regular assignments val initializationStringValue: StString?,
val onetimeInitializationStringValue: StString?, val initializationArrayValue: StArray?,
val onetimeInitializationArrayValue: StArray?,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
val zpwish: ZeropageWish, // used in the variable allocator val zpwish: ZeropageWish, // used in the variable allocator
astNode: PtNode) : StNode(name, StNodeType.STATICVAR, astNode) { astNode: PtNode) : StNode(name, StNodeType.STATICVAR, astNode) {
val uninitialized = onetimeInitializationArrayValue==null && onetimeInitializationStringValue==null && onetimeInitializationNumericValue==null var initializationNumericValue: Double? = null
private set
fun setOnetimeInitNumeric(number: Double) {
// In certain cases the init value of an existing var should be updated,
// so we can't ask this as a constructor parameter.
// This has to do with the way Prog8 does the (re)initialization of such variables: via code assignment statements.
// Certain codegens might want to put them back into the variable directly.
// For strings and arrays this doesn't occur - these are always already specced at creation time.
initializationNumericValue = number
}
val uninitialized: Boolean
get() = initializationArrayValue==null && initializationStringValue==null && initializationNumericValue==null
init { init {
if(length!=null) { if(length!=null) {
require(onetimeInitializationNumericValue == null) require(initializationNumericValue == null)
if(onetimeInitializationArrayValue!=null) if(initializationArrayValue!=null)
require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length) require(initializationArrayValue.isEmpty() ||initializationArrayValue.size==length)
} }
if(onetimeInitializationNumericValue!=null) { if(initializationNumericValue!=null) {
require(dt in NumericDatatypes || dt==DataType.BOOL) require(dt in NumericDatatypes || dt==DataType.BOOL)
} }
if(onetimeInitializationArrayValue!=null) { if(initializationArrayValue!=null) {
require(dt in ArrayDatatypes) require(dt in ArrayDatatypes)
require(length==onetimeInitializationArrayValue.size) require(length==initializationArrayValue.size)
} }
if(onetimeInitializationStringValue!=null) { if(initializationStringValue!=null) {
require(dt == DataType.STR) require(dt == DataType.STR)
require(length == onetimeInitializationStringValue.first.length+1) require(length == initializationStringValue.first.length+1)
} }
} }
} }

View File

@ -95,7 +95,10 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
// if(node.type in SplitWordArrayTypes) { // if(node.type in SplitWordArrayTypes) {
// ... split array also add _lsb and _msb to symboltable? // ... split array also add _lsb and _msb to symboltable?
// } // }
StStaticVariable(node.name, node.type, initialNumeric, initialString, initialArray, numElements, node.zeropage, node) val stVar = StStaticVariable(node.name, node.type, initialString, initialArray, numElements, node.zeropage, node)
if(initialNumeric!=null)
stVar.setOnetimeInitNumeric(initialNumeric)
stVar
} }
is PtBuiltinFunctionCall -> { is PtBuiltinFunctionCall -> {
if(node.name=="memory") { if(node.name=="memory") {

View File

@ -545,8 +545,8 @@ internal class ProgramAndVarsGen(
for (variable in vars) { for (variable in vars) {
val scopedName = variable.key val scopedName = variable.key
val svar = symboltable.lookup(scopedName) as? StStaticVariable val svar = symboltable.lookup(scopedName) as? StStaticVariable
if(svar?.onetimeInitializationStringValue!=null) if(svar?.initializationStringValue!=null)
result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!)) result.add(ZpStringWithInitial(scopedName, variable.value, svar.initializationStringValue!!))
} }
return result return result
} }
@ -557,8 +557,8 @@ internal class ProgramAndVarsGen(
for (variable in vars) { for (variable in vars) {
val scopedName = variable.key val scopedName = variable.key
val svar = symboltable.lookup(scopedName) as? StStaticVariable val svar = symboltable.lookup(scopedName) as? StStaticVariable
if(svar?.onetimeInitializationArrayValue!=null) if(svar?.initializationArrayValue!=null)
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!)) result.add(ZpArrayWithInitial(scopedName, variable.value, svar.initializationArrayValue!!))
} }
return result return result
} }
@ -598,8 +598,8 @@ internal class ProgramAndVarsGen(
stringvars.forEach { stringvars.forEach {
outputStringvar( outputStringvar(
it.name, it.name,
it.onetimeInitializationStringValue!!.second, it.initializationStringValue!!.second,
it.onetimeInitializationStringValue!!.first it.initializationStringValue!!.first
) )
} }
othervars.sortedBy { it.type }.forEach { othervars.sortedBy { it.type }.forEach {
@ -632,11 +632,11 @@ internal class ProgramAndVarsGen(
private fun staticVariable2asm(variable: StStaticVariable) { private fun staticVariable2asm(variable: StStaticVariable) {
val initialValue: Number = val initialValue: Number =
if(variable.onetimeInitializationNumericValue!=null) { if(variable.initializationNumericValue!=null) {
if(variable.dt== DataType.FLOAT) if(variable.dt== DataType.FLOAT)
variable.onetimeInitializationNumericValue!! variable.initializationNumericValue!!
else else
variable.onetimeInitializationNumericValue!!.toInt() variable.initializationNumericValue!!.toInt()
} else 0 } else 0
when (variable.dt) { when (variable.dt) {
@ -655,7 +655,7 @@ internal class ProgramAndVarsGen(
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 -> arrayVariable2asm(variable.name, variable.dt, variable.onetimeInitializationArrayValue, variable.length) in ArrayDatatypes -> arrayVariable2asm(variable.name, variable.dt, variable.initializationArrayValue, variable.length)
else -> { else -> {
throw AssemblyError("weird dt") throw AssemblyError("weird dt")
} }

View File

@ -23,6 +23,7 @@ class IRCodeGen(
makeAllNodenamesScoped(program) makeAllNodenamesScoped(program)
moveAllNestedSubroutinesToBlockScope(program) moveAllNestedSubroutinesToBlockScope(program)
verifyNameScoping(program, symbolTable) verifyNameScoping(program, symbolTable)
changeGlobalVarInits(symbolTable)
val irSymbolTable = IRSymbolTable.fromStDuringCodegen(symbolTable) val irSymbolTable = IRSymbolTable.fromStDuringCodegen(symbolTable)
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding) val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
@ -54,6 +55,40 @@ class IRCodeGen(
return irProg return irProg
} }
private fun changeGlobalVarInits(symbolTable: SymbolTable) {
// Normally, block level (global) variables that have a numeric initialization value
// are initialized via an assignment statement.
val initsToRemove = mutableListOf<Pair<PtBlock, PtAssignment>>()
symbolTable.allVariables.forEach { variable ->
if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
val block = variable.parent.astNode as PtBlock
val initialization = (block.children.firstOrNull {
it is PtAssignment && it.target.identifier?.name==variable.scopedName
} as PtAssignment?)
val initValue = initialization?.value
when(initValue){
is PtBool -> {
variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
initsToRemove += block to initialization
println("${variable.name} = bool ${initValue.value}")
}
is PtNumber -> {
variable.setOnetimeInitNumeric(initValue.number)
initsToRemove += block to initialization
println("${variable.name} = number ${initValue.number}")
}
is PtArray, is PtString -> throw AssemblyError("array or string initialization values should already be part of the vardecl, not a separate assignment")
else -> {}
}
}
}
for((block, assign) in initsToRemove) {
block.children.remove(assign)
}
}
private fun verifyNameScoping(program: PtProgram, symbolTable: SymbolTable) { private fun verifyNameScoping(program: PtProgram, symbolTable: SymbolTable) {
fun verifyPtNode(node: PtNode) { fun verifyPtNode(node: PtNode) {
when (node) { when (node) {

View File

@ -85,13 +85,14 @@ class TestSymbolTable: FunSpec({
test("static vars") { test("static vars") {
val node = PtIdentifier("dummy", DataType.UBYTE, Position.DUMMY) val node = PtIdentifier("dummy", DataType.UBYTE, Position.DUMMY)
val stVar1 = StStaticVariable("initialized", DataType.UBYTE, 99.0, null, null, null, ZeropageWish.DONTCARE, node) val stVar1 = StStaticVariable("initialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, node)
val stVar2 = StStaticVariable("uninitialized", DataType.UBYTE, null, null, null, null, ZeropageWish.DONTCARE, node) stVar1.setOnetimeInitNumeric(99.0)
val stVar2 = StStaticVariable("uninitialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, node)
val arrayInitNonzero = listOf(StArrayElement(1.1, null, null), StArrayElement(2.2, null, null), StArrayElement(3.3, null, null)) val arrayInitNonzero = listOf(StArrayElement(1.1, null, null), StArrayElement(2.2, null, null), StArrayElement(3.3, null, null))
val arrayInitAllzero = listOf(StArrayElement(0.0, null, null), StArrayElement(0.0, null, null), StArrayElement(0.0, null, null)) val arrayInitAllzero = listOf(StArrayElement(0.0, null, null), StArrayElement(0.0, null, null), StArrayElement(0.0, null, null))
val stVar3 = StStaticVariable("initialized", DataType.ARRAY_UW, null, null, arrayInitNonzero, 3, ZeropageWish.DONTCARE, node) val stVar3 = StStaticVariable("initialized", DataType.ARRAY_UW, null, arrayInitNonzero, 3, ZeropageWish.DONTCARE, node)
val stVar4 = StStaticVariable("initialized", DataType.ARRAY_UW, null, null, arrayInitAllzero, 3, ZeropageWish.DONTCARE, node) val stVar4 = StStaticVariable("initialized", DataType.ARRAY_UW, null, arrayInitAllzero, 3, ZeropageWish.DONTCARE, node)
val stVar5 = StStaticVariable("uninitialized", DataType.ARRAY_UW, null, null, null, 3, ZeropageWish.DONTCARE, node) val stVar5 = StStaticVariable("uninitialized", DataType.ARRAY_UW, null, null, 3, ZeropageWish.DONTCARE, node)
stVar1.uninitialized shouldBe false stVar1.uninitialized shouldBe false
stVar1.length shouldBe null stVar1.length shouldBe null
@ -155,12 +156,12 @@ private fun makeSt(): SymbolTable {
block1.add(sub12) block1.add(sub12)
block1.add(StConstant("c1", DataType.UWORD, 12345.0, astConstant1)) block1.add(StConstant("c1", DataType.UWORD, 12345.0, astConstant1))
block1.add(StConstant("blockc", DataType.UWORD, 999.0, astConstant2)) block1.add(StConstant("blockc", DataType.UWORD, 999.0, astConstant2))
sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, astSub1v1)) sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, astSub1v1))
sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, astSub1v2)) sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, astSub1v2))
sub11.add(StMemVar("v3", DataType.FLOAT, 12345u, null, astSub1v3)) sub11.add(StMemVar("v3", DataType.FLOAT, 12345u, null, astSub1v3))
sub11.add(StMemorySlab("slab1", 200u, 64u, astSub1v4)) sub11.add(StMemorySlab("slab1", 200u, 64u, astSub1v4))
sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, astSub2v1)) sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, astSub2v1))
sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, astSub2v2)) sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, astSub2v2))
val block2 = StNode("block2", StNodeType.BLOCK, astBlock2) val block2 = StNode("block2", StNodeType.BLOCK, astBlock2)
val sub21 = StNode("sub1", StNodeType.SUBROUTINE, astSub21) val sub21 = StNode("sub1", StNodeType.SUBROUTINE, astSub21)
val sub22 = StNode("sub2", StNodeType.SUBROUTINE, astSub22) val sub22 = StNode("sub2", StNodeType.SUBROUTINE, astSub22)

View File

@ -9,8 +9,8 @@ Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEva
Future Things and Ideas Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
- ir: there are vars in INITGLOBALS that are initialized with code where they simpley have a static numerical initializer value, and could just as well be in VARIABLESWITHINIT . Why are their StVar's not initialized!? - improve detection that a variable is not read before being written so that initializing it to zero can be omitted
- improve detection that a variable is not read before being written so that initializing it to zero can be omitted (only happens now if a vardecl is immediately followed by a for loop for instance) (only happens now if a vardecl is immediately followed by a for loop for instance) BUT this may break stuff if the variable is read from a function call for instance in between?
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions - Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
- Can we support signed % (remainder) somehow? - Can we support signed % (remainder) somehow?
- Don't add "random" rts to %asm blocks but instead give a warning about it? (but this breaks existing behavior that others already depend on... command line switch? block directive?) - Don't add "random" rts to %asm blocks but instead give a warning about it? (but this breaks existing behavior that others already depend on... command line switch? block directive?)

View File

@ -7,6 +7,7 @@ main {
bool[] barray = [true, false, true, false] bool[] barray = [true, false, true, false]
uword[] warray = [&value1, &barray, &value5, 4242] uword[] warray = [&value1, &barray, &value5, 4242]
ubyte @shared integer = 99
bool @shared value1 bool @shared value1
bool @shared value2 = barray[2] ; should be const! bool @shared value2 = barray[2] ; should be const!
bool @shared value3 = true bool @shared value3 = true
@ -16,6 +17,12 @@ main {
uword @shared value7 = warray[2] ; cannot be const uword @shared value7 = warray[2] ; cannot be const
sub start() { sub start() {
txt.print_ub(integer)
integer++
txt.spc()
txt.print_ub(integer)
txt.nl()
txt.print_bool(value1) txt.print_bool(value1)
txt.spc() txt.spc()
txt.print_bool(value2) txt.print_bool(value2)

View File

@ -169,7 +169,7 @@ class IRFileReader {
val dt = parseDatatype(type, arraysize!=null) val dt = parseDatatype(type, arraysize!=null)
val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish) val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish)
val dummyNode = PtVariable(name, dt, zp, null, null, Position.DUMMY) val dummyNode = PtVariable(name, dt, zp, null, null, Position.DUMMY)
val newVar = StStaticVariable(name, dt, null, null, null, arraysize, zp, dummyNode) val newVar = StStaticVariable(name, dt, null, null, arraysize, zp, dummyNode)
variables.add(newVar) variables.add(newVar)
} }
return variables return variables
@ -224,7 +224,10 @@ class IRFileReader {
if(arraysize!=null && initArray!=null && initArray.all { it.number==0.0 }) { if(arraysize!=null && initArray!=null && initArray.all { it.number==0.0 }) {
initArray=null // arrays with just zeros can be left uninitialized initArray=null // arrays with just zeros can be left uninitialized
} }
variables.add(StStaticVariable(name, dt, initNumeric, null, initArray, arraysize, zp, dummyNode)) val stVar = StStaticVariable(name, dt, null, initArray, arraysize, zp, dummyNode)
if(initNumeric!=null)
stVar.setOnetimeInitNumeric(initNumeric)
variables.add(stVar)
} }
return variables return variables
} }

View File

@ -82,9 +82,9 @@ class IRSymbolTable {
scopedName = variable.scopedName scopedName = variable.scopedName
varToadd = IRStStaticVariable(scopedName, varToadd = IRStStaticVariable(scopedName,
variable.dt, variable.dt,
variable.onetimeInitializationNumericValue, variable.initializationNumericValue,
variable.onetimeInitializationStringValue, variable.initializationStringValue,
fixupAddressOfInArray(variable.onetimeInitializationArrayValue), fixupAddressOfInArray(variable.initializationArrayValue),
variable.length, variable.length,
variable.zpwish variable.zpwish
) )
@ -202,9 +202,9 @@ class IRStStaticVariable(name: String,
fun from(variable: StStaticVariable): IRStStaticVariable { fun from(variable: StStaticVariable): IRStStaticVariable {
return IRStStaticVariable(variable.name, return IRStStaticVariable(variable.name,
variable.dt, variable.dt,
variable.onetimeInitializationNumericValue, variable.initializationNumericValue,
variable.onetimeInitializationStringValue, variable.initializationStringValue,
variable.onetimeInitializationArrayValue?.map { IRStArrayElement.from(it) }, variable.initializationArrayValue?.map { IRStArrayElement.from(it) },
variable.length, variable.length,
variable.zpwish) variable.zpwish)
} }