remove redundant variable=0 initializations (BSS clear takes care of them)

This commit is contained in:
Irmen de Jong
2025-04-23 14:28:43 +02:00
parent 2cb183c6d8
commit d2cc7ccdfa
9 changed files with 69 additions and 39 deletions

View File

@@ -353,7 +353,9 @@ internal class ProgramAndVarsGen(
initializers.forEach { assign ->
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
asmgen.translate(assign)
// the other variables that should be set to zero are done so as part of the BSS section.
else
throw AssemblyError("non-zp variable should not be initialized to zero; it will be zeroed as part of BSS clear")
// the other variables that should be set to zero are done so as part of the BSS section clear.
}
asmgen.out(" rts\n .bend")
}

View File

@@ -64,15 +64,17 @@ class IRCodeGen(
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
it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedName
} as PtAssignment?)
val initValue = initialization?.value
when(initValue){
is PtBool -> {
require(initValue.asInt()!=0) { "boolean var should not be initialized with false, it wil be set to false as part of BSS clear, initializer=$initialization" }
variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
initsToRemove += block to initialization
}
is PtNumber -> {
require(initValue.number!=0.0) { "variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
variable.setOnetimeInitNumeric(initValue.number)
initsToRemove += block to initialization
}

View File

@@ -164,6 +164,9 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
}
}
if(srcAssign.origin == AssignmentOrigin.VARINIT && srcAssign.parent is Block && srcAssign.value.constValue(program)?.number==0.0)
throw FatalAstException("should not have a redundant block-level variable=0 assignment; it will be zeroed as part of BSS clear")
val assign = PtAssignment(srcAssign.position, srcAssign.origin==AssignmentOrigin.VARINIT)
val multi = srcAssign.target.multi
if(multi==null) {

View File

@@ -52,7 +52,8 @@ internal class StatementReorderer(
if (decl.datatype.isNumericOrBool) {
if(decl !in declsProcessedWithInitAssignment) {
declsProcessedWithInitAssignment.add(decl)
if (decl.value == null) {
if (decl.value == null || decl.value?.constValue(program)?.number==0.0) {
decl.value = null
if (decl.origin==VarDeclOrigin.USERCODE && decl.allowInitializeWithZero) {
if(decl.dirty) {
// no initialization at all!
@@ -62,11 +63,9 @@ internal class StatementReorderer(
// unless there's already an assignment below it, that initializes the value (or a for loop that uses it as loopvar).
// This allows you to restart the program and have the same starting values of the variables
// So basically consider 'ubyte xx' as a short form for 'ubyte xx; xx=0'
decl.value = null
val canskip = canSkipInitializationWith0(decl)
if (!canskip) {
// (this explicit initialization assignment to 0 is not required for global variables in the block scope, these are zeroed as part of the BSS section clear)
if (!canSkipInitializationWith0(decl)) {
// Add assignment to initialize with zero
// Note: for block-level vars, this will introduce assignments in the block scope. These have to be dealt with correctly later.
val identifier = IdentifierReference(listOf(decl.name), decl.position)
val assignzero = Assignment(AssignTarget(identifier, null, null, null, false, decl.position),
decl.zeroElementValue(), AssignmentOrigin.VARINIT, decl.position)
@@ -115,6 +114,11 @@ internal class StatementReorderer(
}
private fun canSkipInitializationWith0(decl: VarDecl): Boolean {
// if the variable is declared in a block, we can omit the init with 0 because
// the variable will be initialized to zero when the BSS section is cleared as a whole.
if(decl.parent is Block)
return true
// if there is an assignment to the variable below it (regular assign, or For loop),
// and there is nothing important in between, we can skip the initialization.
val statements = (decl.parent as? IStatementContainer)?.statements ?: return false

View File

@@ -125,29 +125,24 @@ main {
val result = compileText(C64Target(), true, src, outputDir, writeAssembly = true)!!.compilerAst
val main = result.allBlocks.first { it.name=="main" }
main.statements.size shouldBe 17
main.statements.size shouldBe 15
val assigns = main.statements.filterIsInstance<Assignment>()
assigns[0].target.identifier?.nameInSource shouldBe listOf("value1")
assigns[1].target.identifier?.nameInSource shouldBe listOf("value2")
assigns[2].target.identifier?.nameInSource shouldBe listOf("value3")
assigns[3].target.identifier?.nameInSource shouldBe listOf("value4")
assigns[4].target.identifier?.nameInSource shouldBe listOf("value5")
assigns[5].target.identifier?.nameInSource shouldBe listOf("value6")
assigns[6].target.identifier?.nameInSource shouldBe listOf("value7")
assigns.size shouldBe 5
assigns[0].target.identifier?.nameInSource shouldBe listOf("value2")
assigns[1].target.identifier?.nameInSource shouldBe listOf("value3")
assigns[2].target.identifier?.nameInSource shouldBe listOf("value5")
assigns[3].target.identifier?.nameInSource shouldBe listOf("value6")
assigns[4].target.identifier?.nameInSource shouldBe listOf("value7")
assigns[0].origin shouldBe AssignmentOrigin.VARINIT
assigns[1].origin shouldBe AssignmentOrigin.VARINIT
assigns[2].origin shouldBe AssignmentOrigin.VARINIT
assigns[3].origin shouldBe AssignmentOrigin.VARINIT
assigns[4].origin shouldBe AssignmentOrigin.VARINIT
assigns[5].origin shouldBe AssignmentOrigin.VARINIT
assigns[6].origin shouldBe AssignmentOrigin.VARINIT
assigns[0].value.constValue(result)?.number shouldBe 0
assigns[1].value.constValue(result)?.number shouldBe 1
assigns[2].value.constValue(result)?.number shouldBe 1
assigns[3].value.constValue(result)?.number shouldBe 0
assigns[0].value.constValue(result)?.asBooleanValue shouldBe true
assigns[1].value.constValue(result)?.asBooleanValue shouldBe true
assigns[2].value.constValue(result) shouldBe null
assigns[3].value.constValue(result)?.number shouldBe 4242
assigns[4].value.constValue(result) shouldBe null
assigns[5].value.constValue(result)?.number shouldBe 4242
assigns[6].value.constValue(result) shouldBe null
}

View File

@@ -7,6 +7,7 @@ TODO
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
- randrange() function can be optimized by just multiplying by the range and bit shifting down?
- romable: should we have a way to explicitly set the memory address for the BSS area (instead of only the highram bank number on X16, allow a memory address too for the -varshigh option?)
- Kotlin: can we use inline value classes in certain spots?
- add float support to the configurable compiler targets

View File

@@ -1,25 +1,37 @@
%import textio
%zeropage basicsafe
%zeropage dontuse
%option no_sysinit
main {
ubyte @shared ub_global
uword @shared uw_global = 0
bool[4] @shared bool_array_global
sub start() {
uword[] @nosplit ptrs = [one, two, three]
ubyte @shared ub_scoped
uword @shared uw_scoped = 0
bool[4] @shared bool_array_scoped
ubyte @shared x =1
dump()
goto ptrs[x]
}
ub_scoped++
uw_scoped++
bool_array_scoped[2]=true
ub_global++
uw_global++
bool_array_global[2]=true
sub one() {
txt.print("one\n")
}
sub two() {
txt.print("two\n")
}
sub three() {
txt.print("three\n")
}
dump()
sub dump() {
txt.print_ub(ub_global)
txt.print_uw(uw_global)
txt.print_bool(bool_array_global[2])
txt.nl()
txt.print_ub(ub_scoped)
txt.print_uw(uw_scoped)
txt.print_bool(bool_array_scoped[2])
txt.nl()
}
}
}

View File

@@ -198,6 +198,8 @@ class StStaticVariable(name: String,
// 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.
require(number!=0.0) { "variable should not be initialized with 0, it will already be zeroed as part of BSS clear" }
initializationNumericValue = number
}

View File

@@ -1,10 +1,10 @@
package prog8.vm
import prog8.Either
import prog8.code.core.DataType
import prog8.intermediate.*
import prog8.left
import prog8.right
import prog8.intermediate.*
import prog8.code.core.DataType
class VmProgramLoader {
private val placeholders = mutableMapOf<Pair<IRCodeChunk, Int>, String>() // program chunk+index to symbolname
@@ -223,6 +223,15 @@ class VmProgramLoader {
else -> throw IRParseException("invalid dt")
}
}
} else {
when {
variable.dt.isUnsignedByte || variable.dt.isBool -> memory.setUB(addr, 0u)
variable.dt.isSignedByte -> memory.setSB(addr, 0)
variable.dt.isUnsignedWord -> memory.setUW(addr, 0u)
variable.dt.isSignedWord -> memory.setSW(addr, 0)
variable.dt.isFloat -> memory.setFloat(addr, 0.0)
else -> throw IRParseException("invalid dt")
}
}
}