diff --git a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt index a2aa74c6c..2de9819f8 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt @@ -321,6 +321,31 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors: return noModifications } + override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable { + 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 { val constvalue = functionCallExpr.constValue(program) return if(constvalue!=null) diff --git a/compiler/test/codegeneration/TestVariables.kt b/compiler/test/codegeneration/TestVariables.kt index cb65c6575..a97c01ee7 100644 --- a/compiler/test/codegeneration/TestVariables.kt +++ b/compiler/test/codegeneration/TestVariables.kt @@ -4,6 +4,9 @@ import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe 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 prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.compileText @@ -95,4 +98,95 @@ class TestVariables: FunSpec({ errors.errors[0] 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() + 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() + 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 + } }) diff --git a/examples/test.p8 b/examples/test.p8 index 134611c2b..2fc31ff52 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,20 +4,18 @@ main { - bool[] barray = [true, false, true, false] - bool value1 - bool value2 = barray[cx16.r0L] - bool value3 = true - bool value4 = false - sub start() { - txt.print_bool(value1) - txt.spc() - txt.print_bool(value2) - txt.spc() - txt.print_bool(value3) - txt.nl() - txt.print_bool(value4) + 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 + txt.nl() } }