diff --git a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt index c3136b941..0b9a68164 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantFoldingOptimizer.kt @@ -2,6 +2,7 @@ package prog8.optimizer import prog8.ast.Node import prog8.ast.Program +import prog8.ast.base.FatalAstException import prog8.ast.expressions.* import prog8.ast.maySwapOperandOrder import prog8.ast.statements.* @@ -112,6 +113,28 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors: } } + if(expr.left.inferType(program).isArray) { + if (expr.operator=="*" && rightconst!=null) { + if (expr.left is ArrayLiteral) { + // concatenate array literal. + val part = expr.left as ArrayLiteral + if(part.value.isEmpty()) + errors.warn("resulting array has length zero", part.position) + val tmp = mutableListOf() + repeat(rightconst.number.toInt()) { + tmp += part.value + } + val newArray = ArrayLiteral(part.type, tmp.toTypedArray(), part.position) + return listOf(IAstModification.ReplaceNode(expr, newArray, parent)) + } + else { + val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl(program) + if(leftTarget!=null && leftTarget.origin==VarDeclOrigin.ARRAYLITERAL) + throw FatalAstException("shouldn't see an array literal converted to an autovar here") + } + } + } + if(expr.operator=="==" && rightconst!=null) { val leftExpr = expr.left as? BinaryExpression // only do this shuffling when the LHS is not a constant itself (otherwise problematic nested replacements) diff --git a/compiler/src/prog8/compiler/astprocessing/LiteralsToAutoVars.kt b/compiler/src/prog8/compiler/astprocessing/LiteralsToAutoVars.kt index 326e80d9f..26de5ee99 100644 --- a/compiler/src/prog8/compiler/astprocessing/LiteralsToAutoVars.kt +++ b/compiler/src/prog8/compiler/astprocessing/LiteralsToAutoVars.kt @@ -60,17 +60,19 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro return noModifications } if(arrayDt.isKnown) { - val parentAssign = parent as? Assignment - val targetDt = parentAssign?.target?.inferType(program) ?: arrayDt - // turn the array literal it into an identifier reference - val litval2 = array.cast(targetDt.getOr(DataType.UNDEFINED)) - if(litval2!=null) { - val vardecl2 = VarDecl.createAuto(litval2, targetDt.getOr(DataType.UNDEFINED) in SplitWordArrayTypes) - val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position) - return listOf( - IAstModification.ReplaceNode(array, identifier, parent), - IAstModification.InsertFirst(vardecl2, array.definingScope) - ) + if((array.parent as? BinaryExpression)?.operator!="*") { + val parentAssign = parent as? Assignment + val targetDt = parentAssign?.target?.inferType(program) ?: arrayDt + // turn the array literal it into an identifier reference + val litval2 = array.cast(targetDt.getOr(DataType.UNDEFINED)) + if (litval2 != null) { + val vardecl2 = VarDecl.createAuto(litval2, targetDt.getOr(DataType.UNDEFINED) in SplitWordArrayTypes) + val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position) + return listOf( + IAstModification.ReplaceNode(array, identifier, parent), + IAstModification.InsertFirst(vardecl2, array.definingScope) + ) + } } } } diff --git a/compiler/test/codegeneration/TestArrayThings.kt b/compiler/test/codegeneration/TestArrayThings.kt index 72099cc11..ae1500f06 100644 --- a/compiler/test/codegeneration/TestArrayThings.kt +++ b/compiler/test/codegeneration/TestArrayThings.kt @@ -4,7 +4,10 @@ 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.code.ast.PtBuiltinFunctionCall +import prog8.code.StStaticVariable +import prog8.code.SymbolTableMaker +import prog8.code.ast.* +import prog8.code.core.* import prog8.code.target.C64Target import prog8.code.target.VMTarget import prog8tests.helpers.ErrorReporterForTests @@ -403,5 +406,139 @@ main { (x.children[18] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy" (x.children[19] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy" } + + test("array and string initializer with multiplication") { + val src=""" +%option enable_floats + +main { + sub start() { + str name = "xyz" * 3 + bool[3] boolarray = [true] * 3 + ubyte[3] bytearray = [42] * 3 + uword[3] wordarray = [5555] * 3 + float[3] floatarray = [123.45] * 3 + } +}""" + val result = compileText(C64Target(), false, src, writeAssembly = true)!! + val x = result.codegenAst!!.entrypoint()!! + x.children.size shouldBe 6 + ((x.children[0] as PtVariable).value as PtString).value shouldBe "xyzxyzxyz" + val array1 = (x.children[1] as PtVariable).value as PtArray + val array2 = (x.children[2] as PtVariable).value as PtArray + val array3 = (x.children[3] as PtVariable).value as PtArray + val array4 = (x.children[4] as PtVariable).value as PtArray + array1.children.map { (it as PtBool).value } shouldBe listOf(true, true, true) + array2.children.map { (it as PtNumber).number } shouldBe listOf(42, 42, 42) + array3.children.map { (it as PtNumber).number } shouldBe listOf(5555, 5555, 5555) + array4.children.map { (it as PtNumber).number } shouldBe listOf(123.45, 123.45, 123.45) + } + + test("array initializer with range") { + val src=""" +%option enable_floats + +main { + sub start() { + ubyte[3] bytearray2 = 10 to 12 + uword[3] wordarray2 = 5000 to 5002 + float[3] floatarray2 = 100 to 102 + } +}""" + val result = compileText(C64Target(), false, src, writeAssembly = true)!! + val x = result.codegenAst!!.entrypoint()!! + x.children.size shouldBe 4 + val array1 = (x.children[0] as PtVariable).value as PtArray + val array2 = (x.children[1] as PtVariable).value as PtArray + val array3 = (x.children[2] as PtVariable).value as PtArray + array1.children.map { (it as PtNumber).number } shouldBe listOf(10, 11, 12) + array2.children.map { (it as PtNumber).number } shouldBe listOf(5000, 5001, 5002) + array3.children.map { (it as PtNumber).number } shouldBe listOf(100, 101, 102) + } + + + fun getTestOptions(): CompilationOptions { + val target = VMTarget() + return CompilationOptions( + OutputType.RAW, + CbmPrgLauncherType.NONE, + ZeropageType.DONTUSE, + zpReserved = emptyList(), + zpAllowed = CompilationOptions.AllZeropageAllowed, + floats = true, + noSysInit = false, + compTarget = target, + loadAddress = target.machine.PROGRAM_LOAD_ADDRESS + ) + } + + + test("array assignments with ranges and multiplications") { + val src=""" +%option enable_floats + +main { + sub start() { + bool[4] boolarray3 + ubyte[4] bytearray3 + uword[4] wordarray3 + float[4] floatarray3 + + boolarray3 = [true] *4 + bytearray3 = [42]*4 + wordarray3 = [999]*4 + wordarray3 = [&bytearray3]*4 + wordarray3 = [bytearray3]*4 + floatarray3 = [99.77]*4 + + bytearray3 = 10 to 13 + wordarray3 = 5000 to 5003 + floatarray3 = 100 to 103 + } +}""" + val ast = compileText(C64Target(), false, src, writeAssembly = true)!!.codegenAst!! + val x = ast.entrypoint()!! + x.children.size shouldBe 23 + val assign1value = (x.children[13] as PtBuiltinFunctionCall).args[1] + val assign2value = (x.children[14] as PtBuiltinFunctionCall).args[1] + val assign3value = (x.children[15] as PtBuiltinFunctionCall).args[1] + val assign4value = (x.children[16] as PtBuiltinFunctionCall).args[1] + val assign5value = (x.children[17] as PtBuiltinFunctionCall).args[1] + val assign6value = (x.children[18] as PtBuiltinFunctionCall).args[1] + val assign7value = (x.children[19] as PtBuiltinFunctionCall).args[1] + val assign8value = (x.children[20] as PtBuiltinFunctionCall).args[1] + val assign9value = (x.children[21] as PtBuiltinFunctionCall).args[1] + val options = getTestOptions() + val st = SymbolTableMaker(ast, options).make() + + val heapvar1 = st.lookup((assign1value as PtIdentifier).name) as StStaticVariable + val heapvar2 = st.lookup((assign2value as PtIdentifier).name) as StStaticVariable + val heapvar3 = st.lookup((assign3value as PtIdentifier).name) as StStaticVariable + val heapvar4 = st.lookup((assign4value as PtIdentifier).name) as StStaticVariable + val heapvar5 = st.lookup((assign5value as PtIdentifier).name) as StStaticVariable + val heapvar6 = st.lookup((assign6value as PtIdentifier).name) as StStaticVariable + val heapvar7 = st.lookup((assign7value as PtIdentifier).name) as StStaticVariable + val heapvar8 = st.lookup((assign8value as PtIdentifier).name) as StStaticVariable + val heapvar9 = st.lookup((assign9value as PtIdentifier).name) as StStaticVariable + heapvar1.length shouldBe 4 + heapvar2.length shouldBe 4 + heapvar3.length shouldBe 4 + heapvar4.length shouldBe 4 + heapvar5.length shouldBe 4 + heapvar6.length shouldBe 4 + heapvar7.length shouldBe 4 + heapvar8.length shouldBe 4 + heapvar9.length shouldBe 4 + heapvar1.dt shouldBe DataType.ARRAY_BOOL + heapvar2.dt shouldBe DataType.ARRAY_UB + heapvar3.dt shouldBe DataType.ARRAY_UW + heapvar4.dt shouldBe DataType.ARRAY_UW + heapvar5.dt shouldBe DataType.ARRAY_UW + heapvar6.dt shouldBe DataType.ARRAY_F + heapvar7.dt shouldBe DataType.ARRAY_UB + heapvar8.dt shouldBe DataType.ARRAY_UW + heapvar9.dt shouldBe DataType.ARRAY_F + } + }) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 9a03d3f85..7b274be0f 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,12 +1,11 @@ TODO ==== -- remove support for array variable initialization with a single value, just require explicitly creating the value array [42] * 10 (which is what the compiler now does for you implicitly) +- word arrays (after ast processing) should no longer contain identifiers, these should have been replaced by &identifier. - should the array-to-array assignment support be removed and instead require an explicit copy function call? What prog8_lib_arraycopy() now does. Or just use memcopy. - should we add a cleararray builtin function that can efficiently set every element in the array to the given value - Improve register load order in subroutine call args assignments: in certain situations, the "wrong" order of evaluation of function call arguments is done which results in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!) diff --git a/examples/test.p8 b/examples/test.p8 index 0622fd138..d64713dbc 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -6,22 +6,13 @@ main { - str name = "xyz" * 3 - bool[3] boolarray = true - ubyte[3] bytearray = 52 - uword[3] wordarray = 5544 - float[3] floatarray = 123.45 + sub arrayinit_with_multiplier() { + str name = "xyz" * 3 + bool[3] boolarray = [true] * 3 + ubyte[3] bytearray = [42] * 3 + uword[3] wordarray = [5555] * 3 + float[3] floatarray = [123.45] * 3 - ubyte[3] bytearray2 = 10 to 12 - uword[3] wordarray2 = 5540 to 5542 - float[3] floatarray2 = 123 to 125 - - bool[3] boolarray3 - ubyte[3] bytearray3 - uword[3] wordarray3 - float[3] floatarray3 - - sub start() { txt.print(name) txt.nl() for cx16.r1L in 0 to 2 { @@ -36,6 +27,12 @@ main { } txt.nl() txt.nl() + } + + sub arrayinit_with_range() { + ubyte[3] bytearray2 = 10 to 12 + uword[3] wordarray2 = 5000 to 5002 + float[3] floatarray2 = 100 to 102 for cx16.r1L in 0 to 2 { txt.print_ub(bytearray2[cx16.r1L]) @@ -47,6 +44,20 @@ main { } txt.nl() txt.nl() + } + + sub arrayassign() { + bool[4] boolarray3 + ubyte[4] bytearray3 + uword[4] wordarray3 + float[4] floatarray3 + + boolarray3 = [true] *4 + bytearray3 = [42]*4 + wordarray3 = [999]*4 + wordarray3 = [&bytearray3]*4 + wordarray3 = [bytearray3]*4 + floatarray3 = [99.77]*4 for cx16.r1L in 0 to 2 { txt.print_bool(boolarray3[cx16.r1L]) @@ -60,4 +71,10 @@ main { } txt.nl() } + + sub start() { + arrayinit_with_multiplier() + arrayinit_with_range() + arrayassign() + } }