global (block-level) variables that get initialized with an array index expression now get a constant value as well if possible. This reduces the number of instructions in the init globals code block

This commit is contained in:
Irmen de Jong
2024-10-16 02:14:19 +02:00
parent cbc4b75e50
commit ca5f7ae32f
3 changed files with 130 additions and 13 deletions

View File

@@ -321,6 +321,31 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
return noModifications return noModifications
} }
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
if(parent is VarDecl && parent.parent is Block) {
// only block level (global) initializers are considered here, because they're run just once at program startup
val const = arrayIndexedExpression.constValue(program)
if (const != null)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, const, parent))
val constIndex = arrayIndexedExpression.indexer.constIndex()
if (constIndex != null) {
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
if(arrayVar!=null) {
val array =arrayVar.value as? ArrayLiteral
if(array!=null) {
val value = array.value[constIndex].constValue(program)
if(value!=null) {
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent))
}
}
}
}
}
return noModifications
}
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> { override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
val constvalue = functionCallExpr.constValue(program) val constvalue = functionCallExpr.constValue(program)
return if(constvalue!=null) return if(constvalue!=null)

View File

@@ -4,6 +4,9 @@ import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldContain
import prog8.ast.statements.Assignment
import prog8.ast.statements.AssignmentOrigin
import prog8.code.ast.PtAssignment
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText import prog8tests.helpers.compileText
@@ -95,4 +98,95 @@ class TestVariables: FunSpec({
errors.errors[0] shouldContain "value has incompatible type" errors.errors[0] shouldContain "value has incompatible type"
errors.errors[1] shouldContain "value has incompatible type" errors.errors[1] shouldContain "value has incompatible type"
} }
test("global var init with array lookup should sometimes be const") {
val src="""
main {
bool[] barray = [true, false, true, false]
uword[] warray = [&value1, &barray, &value5, 4242]
bool @shared value1
bool @shared value2 = barray[2] ; should be const!
bool @shared value3 = true
bool @shared value4 = false
bool @shared value5 = barray[cx16.r0L] ; cannot be const
uword @shared value6 = warray[3] ; should be const!
uword @shared value7 = warray[2] ; cannot be const
sub start() {
}
}"""
val result = compileText(C64Target(), true, src, writeAssembly = true)!!.compilerAst
val main = result.allBlocks.first { it.name=="main" }
main.statements.size shouldBe 17
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[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[4].value.constValue(result) shouldBe null
assigns[5].value.constValue(result)?.number shouldBe 4242
assigns[6].value.constValue(result) shouldBe null
}
test("scoped var init with array lookup should never be const") {
val src="""
main {
sub start() {
bool[] barray = [true, false, true, false]
uword[] warray = [&value1, &barray, &value5, 4242]
bool @shared value1
bool @shared value2 = barray[2]
bool @shared value3 = true
bool @shared value4 = false
bool @shared value5 = barray[cx16.r0L]
uword @shared value6 = warray[3]
uword @shared value7 = warray[2]
}
}"""
val result = compileText(C64Target(), true, src, writeAssembly = true)!!.compilerAst
val st = result.entrypoint.statements
st.size shouldBe 17
val assigns = st.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[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) shouldBe null
assigns[2].value.constValue(result)?.number shouldBe 1
assigns[3].value.constValue(result)?.number shouldBe 0
assigns[4].value.constValue(result) shouldBe null
assigns[5].value.constValue(result) shouldBe null
assigns[6].value.constValue(result) shouldBe null
}
}) })

View File

@@ -4,20 +4,18 @@
main { main {
bool[] barray = [true, false, true, false]
bool value1
bool value2 = barray[cx16.r0L]
bool value3 = true
bool value4 = false
sub start() { sub start() {
txt.print_bool(value1) bool[] barray = [true, false, true, false]
txt.spc() uword[] warray = [&value1, &barray, &value5, 4242]
txt.print_bool(value2)
txt.spc() bool @shared value1
txt.print_bool(value3) bool @shared value2 = barray[2] ; should be const!
txt.nl() bool @shared value3 = true
txt.print_bool(value4) bool @shared value4 = false
bool @shared value5 = barray[cx16.r0L] ; cannot be const
uword @shared value6 = warray[3] ; should be const!
uword @shared value7 = warray[2] ; cannot be const
txt.nl() txt.nl()
} }
} }