diff --git a/codeCore/src/prog8/code/SymbolTable.kt b/codeCore/src/prog8/code/SymbolTable.kt index d73177bf6..7b43a0f53 100644 --- a/codeCore/src/prog8/code/SymbolTable.kt +++ b/codeCore/src/prog8/code/SymbolTable.kt @@ -178,31 +178,43 @@ open class StNode(val name: String, class StStaticVariable(name: String, val dt: DataType, - val onetimeInitializationNumericValue: Double?, // regular (every-run-time) initialization is done via regular assignments - val onetimeInitializationStringValue: StString?, - val onetimeInitializationArrayValue: StArray?, + val initializationStringValue: StString?, + val initializationArrayValue: StArray?, 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 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 { if(length!=null) { - require(onetimeInitializationNumericValue == null) - if(onetimeInitializationArrayValue!=null) - require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length) + require(initializationNumericValue == null) + if(initializationArrayValue!=null) + require(initializationArrayValue.isEmpty() ||initializationArrayValue.size==length) } - if(onetimeInitializationNumericValue!=null) { + if(initializationNumericValue!=null) { require(dt in NumericDatatypes || dt==DataType.BOOL) } - if(onetimeInitializationArrayValue!=null) { + if(initializationArrayValue!=null) { require(dt in ArrayDatatypes) - require(length==onetimeInitializationArrayValue.size) + require(length==initializationArrayValue.size) } - if(onetimeInitializationStringValue!=null) { + if(initializationStringValue!=null) { require(dt == DataType.STR) - require(length == onetimeInitializationStringValue.first.length+1) + require(length == initializationStringValue.first.length+1) } } } diff --git a/codeCore/src/prog8/code/SymbolTableMaker.kt b/codeCore/src/prog8/code/SymbolTableMaker.kt index 1b3b44a7a..77e414913 100644 --- a/codeCore/src/prog8/code/SymbolTableMaker.kt +++ b/codeCore/src/prog8/code/SymbolTableMaker.kt @@ -95,7 +95,10 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp // if(node.type in SplitWordArrayTypes) { // ... 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 -> { if(node.name=="memory") { diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index ddb1bf072..c7dfa33d0 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -545,8 +545,8 @@ internal class ProgramAndVarsGen( for (variable in vars) { val scopedName = variable.key val svar = symboltable.lookup(scopedName) as? StStaticVariable - if(svar?.onetimeInitializationStringValue!=null) - result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!)) + if(svar?.initializationStringValue!=null) + result.add(ZpStringWithInitial(scopedName, variable.value, svar.initializationStringValue!!)) } return result } @@ -557,8 +557,8 @@ internal class ProgramAndVarsGen( for (variable in vars) { val scopedName = variable.key val svar = symboltable.lookup(scopedName) as? StStaticVariable - if(svar?.onetimeInitializationArrayValue!=null) - result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!)) + if(svar?.initializationArrayValue!=null) + result.add(ZpArrayWithInitial(scopedName, variable.value, svar.initializationArrayValue!!)) } return result } @@ -598,8 +598,8 @@ internal class ProgramAndVarsGen( stringvars.forEach { outputStringvar( it.name, - it.onetimeInitializationStringValue!!.second, - it.onetimeInitializationStringValue!!.first + it.initializationStringValue!!.second, + it.initializationStringValue!!.first ) } othervars.sortedBy { it.type }.forEach { @@ -632,11 +632,11 @@ internal class ProgramAndVarsGen( private fun staticVariable2asm(variable: StStaticVariable) { val initialValue: Number = - if(variable.onetimeInitializationNumericValue!=null) { + if(variable.initializationNumericValue!=null) { if(variable.dt== DataType.FLOAT) - variable.onetimeInitializationNumericValue!! + variable.initializationNumericValue!! else - variable.onetimeInitializationNumericValue!!.toInt() + variable.initializationNumericValue!!.toInt() } else 0 when (variable.dt) { @@ -655,7 +655,7 @@ internal class ProgramAndVarsGen( DataType.STR -> { 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 -> { throw AssemblyError("weird dt") } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 748035a56..e69b7441a 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -23,6 +23,7 @@ class IRCodeGen( makeAllNodenamesScoped(program) moveAllNestedSubroutinesToBlockScope(program) verifyNameScoping(program, symbolTable) + changeGlobalVarInits(symbolTable) val irSymbolTable = IRSymbolTable.fromStDuringCodegen(symbolTable) val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding) @@ -54,6 +55,40 @@ class IRCodeGen( 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>() + + 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) { fun verifyPtNode(node: PtNode) { when (node) { diff --git a/compiler/test/TestSymbolTable.kt b/compiler/test/TestSymbolTable.kt index bf6b1c051..1891c38e8 100644 --- a/compiler/test/TestSymbolTable.kt +++ b/compiler/test/TestSymbolTable.kt @@ -85,13 +85,14 @@ class TestSymbolTable: FunSpec({ test("static vars") { val node = PtIdentifier("dummy", DataType.UBYTE, Position.DUMMY) - val stVar1 = StStaticVariable("initialized", DataType.UBYTE, 99.0, null, null, null, ZeropageWish.DONTCARE, node) - val stVar2 = StStaticVariable("uninitialized", DataType.UBYTE, null, null, null, null, ZeropageWish.DONTCARE, node) + val stVar1 = StStaticVariable("initialized", DataType.UBYTE, 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 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 stVar4 = StStaticVariable("initialized", DataType.ARRAY_UW, null, null, arrayInitAllzero, 3, ZeropageWish.DONTCARE, node) - val stVar5 = StStaticVariable("uninitialized", DataType.ARRAY_UW, null, null, null, 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, arrayInitAllzero, 3, ZeropageWish.DONTCARE, node) + val stVar5 = StStaticVariable("uninitialized", DataType.ARRAY_UW, null, null, 3, ZeropageWish.DONTCARE, node) stVar1.uninitialized shouldBe false stVar1.length shouldBe null @@ -155,12 +156,12 @@ private fun makeSt(): SymbolTable { block1.add(sub12) block1.add(StConstant("c1", DataType.UWORD, 12345.0, astConstant1)) 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("v2", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, astSub1v2)) + sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, astSub1v1)) + sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, astSub1v2)) sub11.add(StMemVar("v3", DataType.FLOAT, 12345u, null, astSub1v3)) sub11.add(StMemorySlab("slab1", 200u, 64u, astSub1v4)) - sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, astSub2v1)) - sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, astSub2v2)) + sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, astSub2v1)) + sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, astSub2v2)) val block2 = StNode("block2", StNodeType.BLOCK, astBlock2) val sub21 = StNode("sub1", StNodeType.SUBROUTINE, astSub21) val sub22 = StNode("sub2", StNodeType.SUBROUTINE, astSub22) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 993be1f82..0033a592a 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -9,8 +9,8 @@ Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEva 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 (only happens now if a vardecl is immediately followed by a for loop for instance) +- 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) 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 - 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?) diff --git a/examples/test.p8 b/examples/test.p8 index e77b0ced1..b8437f7b8 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,6 +7,7 @@ main { bool[] barray = [true, false, true, false] uword[] warray = [&value1, &barray, &value5, 4242] + ubyte @shared integer = 99 bool @shared value1 bool @shared value2 = barray[2] ; should be const! bool @shared value3 = true @@ -16,6 +17,12 @@ main { uword @shared value7 = warray[2] ; cannot be const sub start() { + txt.print_ub(integer) + integer++ + txt.spc() + txt.print_ub(integer) + txt.nl() + txt.print_bool(value1) txt.spc() txt.print_bool(value2) diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index df7bbf8c3..6d5bcc293 100644 --- a/intermediate/src/prog8/intermediate/IRFileReader.kt +++ b/intermediate/src/prog8/intermediate/IRFileReader.kt @@ -169,7 +169,7 @@ class IRFileReader { val dt = parseDatatype(type, arraysize!=null) val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish) 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) } return variables @@ -224,7 +224,10 @@ class IRFileReader { if(arraysize!=null && initArray!=null && initArray.all { it.number==0.0 }) { 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 } diff --git a/intermediate/src/prog8/intermediate/IRSymbolTable.kt b/intermediate/src/prog8/intermediate/IRSymbolTable.kt index 19b41ef80..340756994 100644 --- a/intermediate/src/prog8/intermediate/IRSymbolTable.kt +++ b/intermediate/src/prog8/intermediate/IRSymbolTable.kt @@ -82,9 +82,9 @@ class IRSymbolTable { scopedName = variable.scopedName varToadd = IRStStaticVariable(scopedName, variable.dt, - variable.onetimeInitializationNumericValue, - variable.onetimeInitializationStringValue, - fixupAddressOfInArray(variable.onetimeInitializationArrayValue), + variable.initializationNumericValue, + variable.initializationStringValue, + fixupAddressOfInArray(variable.initializationArrayValue), variable.length, variable.zpwish ) @@ -202,9 +202,9 @@ class IRStStaticVariable(name: String, fun from(variable: StStaticVariable): IRStStaticVariable { return IRStStaticVariable(variable.name, variable.dt, - variable.onetimeInitializationNumericValue, - variable.onetimeInitializationStringValue, - variable.onetimeInitializationArrayValue?.map { IRStArrayElement.from(it) }, + variable.initializationNumericValue, + variable.initializationStringValue, + variable.initializationArrayValue?.map { IRStArrayElement.from(it) }, variable.length, variable.zpwish) }