diff --git a/codeCore/src/prog8/code/SymbolTable.kt b/codeCore/src/prog8/code/SymbolTable.kt index 781c21260..2df718c0d 100644 --- a/codeCore/src/prog8/code/SymbolTable.kt +++ b/codeCore/src/prog8/code/SymbolTable.kt @@ -196,13 +196,10 @@ class StStaticVariable(name: String, } if(onetimeInitializationNumericValue!=null) { require(dt in NumericDatatypes) - require(onetimeInitializationNumericValue!=0.0) { "zero as init value should just remain uninitialized"} } if(onetimeInitializationArrayValue!=null) { require(dt in ArrayDatatypes) - if(onetimeInitializationArrayValue.all { it.number!=null} ) { - require(onetimeInitializationArrayValue.any { it.number != 0.0 }) { "array of all zerors as init value should just remain uninitialized" } - } + require(length==onetimeInitializationArrayValue.size) } if(onetimeInitializationStringValue!=null) { require(dt == DataType.STR) diff --git a/codeCore/src/prog8/code/SymbolTableMaker.kt b/codeCore/src/prog8/code/SymbolTableMaker.kt index 43af75e08..f46d5b254 100644 --- a/codeCore/src/prog8/code/SymbolTableMaker.kt +++ b/codeCore/src/prog8/code/SymbolTableMaker.kt @@ -74,10 +74,9 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp numElements = value.value.length + 1 // include the terminating 0-byte } is PtArray -> { - val array = makeInitialArray(value) - initialArray = if(array.all { it.number==0.0 }) null else array // all 0 as init value -> just uninitialized + initialArray = makeInitialArray(value) initialString = null - numElements = array.size + numElements = initialArray.size require(node.arraySize?.toInt()==numElements) } else -> { diff --git a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt index 3c932cc05..6f1bc8f33 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt @@ -276,107 +276,10 @@ internal class ConstantIdentifierReplacer(private val program: Program, private return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl)) } } - DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT -> { - val rangeExpr = decl.value as? RangeExpression - if(rangeExpr!=null) { - // convert the initializer range expression to an actual array - val constRange = rangeExpr.toConstantIntegerRange() - if(constRange?.isEmpty()==true) { - if(constRange.first>constRange.last && constRange.step>=0) - errors.err("descending range with positive step", decl.value?.position!!) - else if(constRange.first { - if(fillvalue !in 0..255) - errors.err("ubyte value overflow", numericLv.position) - } - DataType.ARRAY_B -> { - if(fillvalue !in -128..127) - errors.err("byte value overflow", numericLv.position) - } - DataType.ARRAY_UW -> { - if(fillvalue !in 0..65535) - errors.err("uword value overflow", numericLv.position) - } - DataType.ARRAY_W -> { - if(fillvalue !in -32768..32767) - errors.err("word value overflow", numericLv.position) - } - else -> {} - } - // create the array itself, filled with the fillvalue. - val array = Array(size) {fillvalue}.map { NumericLiteral(ArrayToElementTypes.getValue(decl.datatype), it.toDouble(), numericLv.position) }.toTypedArray() - val refValue = ArrayLiteral(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position) - return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl)) - } - } - DataType.ARRAY_F -> { - val rangeExpr = decl.value as? RangeExpression - if(rangeExpr!=null) { - // convert the initializer range expression to an actual array of floats - val declArraySize = decl.arraysize?.constIndex() - if(declArraySize!=null && declArraySize!=rangeExpr.size()) - errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!) - val constRange = rangeExpr.toConstantIntegerRange() - if(constRange!=null) { - val newValue = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F), - constRange.map { NumericLiteral(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(), - position = decl.value!!.position) - return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl)) - } - } - - val numericLv = decl.value as? NumericLiteral - val size = decl.arraysize?.constIndex() ?: return noModifications - if(rangeExpr==null && numericLv!=null) { - // arraysize initializer is a single int, and we know the size. - val fillvalue = numericLv.number - if (fillvalue < compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > compTarget.machine.FLOAT_MAX_POSITIVE) - errors.err("float value overflow", numericLv.position) - else { - // create the array itself, filled with the fillvalue. - val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.FLOAT, it, numericLv.position) }.toTypedArray() - val refValue = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position) - return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl)) - } - } - } - DataType.ARRAY_BOOL -> { - val numericLv = decl.value as? NumericLiteral - val size = decl.arraysize?.constIndex() ?: return noModifications - if(numericLv!=null) { - // arraysize initializer is a single int, and we know the size. - val fillvalue = if(numericLv.number==0.0) 0.0 else 1.0 - val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.UBYTE, fillvalue, numericLv.position) }.toTypedArray() - val refValue = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), array, position = numericLv.position) - return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl)) - } + in ArrayDatatypes -> { + val replacedArrayInitializer = createConstArrayInitializerValue(decl) + if(replacedArrayInitializer!=null) + return listOf(IAstModification.ReplaceNode(decl.value!!, replacedArrayInitializer, decl)) } else -> { // nothing to do for this type @@ -386,4 +289,109 @@ internal class ConstantIdentifierReplacer(private val program: Program, private return noModifications } + + private fun createConstArrayInitializerValue(decl: VarDecl): ArrayLiteral? { + // convert the initializer range expression from a range or int, to an actual array. + + when(decl.datatype) { + DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT -> { + val rangeExpr = decl.value as? RangeExpression + if(rangeExpr!=null) { + val constRange = rangeExpr.toConstantIntegerRange() + if(constRange?.isEmpty()==true) { + if(constRange.first>constRange.last && constRange.step>=0) + errors.err("descending range with positive step", decl.value?.position!!) + else if(constRange.first { + if(fillvalue !in 0..255) + errors.err("ubyte value overflow", numericLv.position) + } + DataType.ARRAY_B -> { + if(fillvalue !in -128..127) + errors.err("byte value overflow", numericLv.position) + } + DataType.ARRAY_UW -> { + if(fillvalue !in 0..65535) + errors.err("uword value overflow", numericLv.position) + } + DataType.ARRAY_W -> { + if(fillvalue !in -32768..32767) + errors.err("word value overflow", numericLv.position) + } + else -> {} + } + // create the array itself, filled with the fillvalue. + val array = Array(size) {fillvalue}.map { NumericLiteral(ArrayToElementTypes.getValue(decl.datatype), it.toDouble(), numericLv.position) }.toTypedArray() + return ArrayLiteral(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position) + } + } + DataType.ARRAY_F -> { + val rangeExpr = decl.value as? RangeExpression + if(rangeExpr!=null) { + // convert the initializer range expression to an actual array of floats + val declArraySize = decl.arraysize?.constIndex() + if(declArraySize!=null && declArraySize!=rangeExpr.size()) + errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!) + val constRange = rangeExpr.toConstantIntegerRange() + if(constRange!=null) { + return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F), + constRange.map { NumericLiteral(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(), + position = decl.value!!.position) + } + } + + val numericLv = decl.value as? NumericLiteral + val size = decl.arraysize?.constIndex() ?: return null + if(rangeExpr==null && numericLv!=null) { + // arraysize initializer is a single int, and we know the size. + val fillvalue = numericLv.number + if (fillvalue < compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > compTarget.machine.FLOAT_MAX_POSITIVE) + errors.err("float value overflow", numericLv.position) + else { + // create the array itself, filled with the fillvalue. + val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.FLOAT, it, numericLv.position) }.toTypedArray() + return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position) + } + } + } + DataType.ARRAY_BOOL -> { + val numericLv = decl.value as? NumericLiteral + val size = decl.arraysize?.constIndex() ?: return null + if(numericLv!=null) { + // arraysize initializer is a single int, and we know the size. + val fillvalue = if(numericLv.number==0.0) 0.0 else 1.0 + val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.UBYTE, fillvalue, numericLv.position) }.toTypedArray() + return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), array, position = numericLv.position) + } + } + else -> return null + } + return null + } } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 974a83875..ad078ff6f 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -109,7 +109,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? { program.processAstBeforeAsmGeneration(compilationOptions, args.errors) args.errors.report() - val intermediateAst = IntermediateAstMaker(program).transform() + val intermediateAst = IntermediateAstMaker(program, args.errors).transform() // println("*********** COMPILER AST RIGHT BEFORE ASM GENERATION *************") // printProgram(program) // println("*********** AST RIGHT BEFORE ASM GENERATION *************") diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 00a5068a9..6a5cd929f 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -824,6 +824,8 @@ internal class AstChecker(private val program: Program, err("missing option directive argument(s)") else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page", "merge", "splitarrays", "no_symbol_prefixing")}.any { !it }) err("invalid option directive argument(s)") + if(directive.args.any {it.name=="align_word"} && directive.args.any { it.name=="align_page"}) + err("conflicting alignment options") } else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position) } diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt index 66af216fa..5572c106e 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt @@ -19,7 +19,7 @@ import kotlin.io.path.isRegularFile /** * Convert 'old' compiler-AST into the 'new' simplified AST with baked types. */ -class IntermediateAstMaker(private val program: Program) { +class IntermediateAstMaker(private val program: Program, private val errors: IErrorReporter) { fun transform(): PtProgram { val ptProgram = PtProgram( program.name, @@ -398,13 +398,26 @@ class IntermediateAstMaker(private val program: Program) { } private fun transform(srcVar: VarDecl): PtNode { - return when(srcVar.type) { + when(srcVar.type) { VarDeclType.VAR -> { val value = if(srcVar.value!=null) transformExpression(srcVar.value!!) else null - PtVariable(srcVar.name, srcVar.datatype, srcVar.zeropage, value, srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position) + if(srcVar.datatype in ArrayDatatypes) { + if(value==null) { + val blockOptions = srcVar.definingBlock.options() + if("align_page" in blockOptions || "align_word" in blockOptions) { + errors.warn("converting uninitialized array to explicit zeros because of block alignment option", srcVar.position) + val zeros = PtArray(srcVar.datatype, srcVar.position) + repeat(srcVar.arraysize!!.constIndex()!!) { + zeros.children.add(PtNumber(ArrayToElementTypes.getValue(srcVar.datatype), 0.0, srcVar.position)) + } + return PtVariable(srcVar.name, srcVar.datatype, srcVar.zeropage, zeros, srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position) + } + } + } + return PtVariable(srcVar.name, srcVar.datatype, srcVar.zeropage, value, srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position) } - VarDeclType.CONST -> PtConstant(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number, srcVar.position) - VarDeclType.MEMORY -> PtMemMapped(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number.toUInt(), srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position) + VarDeclType.CONST -> return PtConstant(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number, srcVar.position) + VarDeclType.MEMORY -> return PtMemMapped(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number.toUInt(), srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position) } } diff --git a/compiler/test/TestSymbolTable.kt b/compiler/test/TestSymbolTable.kt index bbcf59089..592ff9a38 100644 --- a/compiler/test/TestSymbolTable.kt +++ b/compiler/test/TestSymbolTable.kt @@ -82,7 +82,28 @@ class TestSymbolTable: FunSpec({ st.allMemMappedVariables.single().scopedName shouldBe "block1.sub1.v3" st.allMemorySlabs.single().scopedName shouldBe "block1.sub1.slab1" } - + + 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 arrayInitNonzero = listOf(StArrayElement(1.1, null), StArrayElement(2.2, null), StArrayElement(3.3, null)) + val arrayInitAllzero = listOf(StArrayElement(0.0, null), StArrayElement(0.0, null), StArrayElement(0.0, 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) + + stVar1.uninitialized shouldBe false + stVar1.length shouldBe null + stVar2.uninitialized shouldBe true + stVar2.length shouldBe null + stVar3.uninitialized shouldBe false + stVar3.length shouldBe 3 + stVar4.uninitialized shouldBe false + stVar4.length shouldBe 3 + stVar5.uninitialized shouldBe true + stVar5.length shouldBe 3 + } }) diff --git a/compiler/test/ast/TestIntermediateAst.kt b/compiler/test/ast/TestIntermediateAst.kt index 37d4da577..15a791022 100644 --- a/compiler/test/ast/TestIntermediateAst.kt +++ b/compiler/test/ast/TestIntermediateAst.kt @@ -8,6 +8,7 @@ import prog8.code.ast.* import prog8.code.core.DataType import prog8.code.target.C64Target import prog8.compiler.astprocessing.IntermediateAstMaker +import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.compileText class TestIntermediateAst: FunSpec({ @@ -26,8 +27,9 @@ class TestIntermediateAst: FunSpec({ } """ val target = C64Target() + val errors = ErrorReporterForTests() val result = compileText(target, false, text, writeAssembly = false)!! - val ast = IntermediateAstMaker(result.compilerAst).transform() + val ast = IntermediateAstMaker(result.compilerAst, errors).transform() ast.name shouldBe result.compilerAst.name ast.allBlocks().any() shouldBe true val entry = ast.entrypoint() ?: fail("no main.start() found") diff --git a/compiler/test/codegeneration/TestAsmGenSymbols.kt b/compiler/test/codegeneration/TestAsmGenSymbols.kt index 83f689631..3503ae2ee 100644 --- a/compiler/test/codegeneration/TestAsmGenSymbols.kt +++ b/compiler/test/codegeneration/TestAsmGenSymbols.kt @@ -75,7 +75,7 @@ class TestAsmGenSymbols: StringSpec({ fun createTestAsmGen6502(program: Program): AsmGen6502Internal { val errors = ErrorReporterForTests() val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target(), 999u) - val ptProgram = IntermediateAstMaker(program).transform() + val ptProgram = IntermediateAstMaker(program, errors).transform() val st = SymbolTableMaker(ptProgram, options).make() return AsmGen6502Internal(ptProgram, st, options, errors) } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index c31f9a79b..02ad459f3 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -35,6 +35,8 @@ Compiler: - OR.... make all this more generic and use some %segment option to create real segments for 64tass? - (need separate step in codegen and IR to write the "golden" variables) +- need variable alignment tag instead of block alignment tag, you want to align the data not the code in the block perse +- ir: block alignment doesn't translate well to variables in the block (the actual stuff that needs to be aligned in memory) but: need variable alignment tag instead of block alignment tag, really - ir: idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype) global initialization values are simply a list of LOAD instructions. Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings. diff --git a/examples/test.p8 b/examples/test.p8 index b984d0f21..916d1e8ce 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,20 +1,20 @@ %import textio -%import gfx2 -%import verafx %zeropage basicsafe main { sub start() { - gfx2.screen_mode(4) - gfx2.disc(160, 120, 100, 2) - ; verafx.transparency(true) - gfx2.position(0, 70) - repeat 3000 { - gfx2.next_pixel(7) - repeat 10 - gfx2.next_pixel(0) ; transparent! - } - verafx.transparency(false) + alignblock.flags[0] = 222 + cx16.r0++ + cx16.r1++ + txt.print_uwhex(alignblock.flags, true) + txt.spc() + txt.print_ub(alignblock.flags[0]) + txt.nl() } } +alignblock { + %option align_page + ubyte[10] flags +} +