mirror of
https://github.com/irmen/prog8.git
synced 2024-11-29 17:50:35 +00:00
fix alignment of uninitialized arrays in aligned blocks (make them initialized with zeros so they don't end up in the BSS section)
fix alignment of uninitialized arrays in aligned blocks (make them initialized with zeros so they don't end up in the BSS section)
This commit is contained in:
parent
a8507b437d
commit
b7a622c68e
@ -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)
|
||||
|
@ -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 -> {
|
||||
|
@ -276,10 +276,27 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
// 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)
|
||||
@ -292,7 +309,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
||||
if(constRange!=null) {
|
||||
val eltType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
|
||||
val newValue = if(eltType in ByteDatatypes) {
|
||||
return if(eltType in ByteDatatypes) {
|
||||
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
|
||||
constRange.map { NumericLiteral(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||
position = decl.value!!.position)
|
||||
@ -301,13 +318,12 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
constRange.map { NumericLiteral(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||
position = decl.value!!.position)
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||
}
|
||||
}
|
||||
val numericLv = decl.value as? NumericLiteral
|
||||
if(numericLv!=null && numericLv.type== DataType.FLOAT)
|
||||
errors.err("arraysize requires only integers here", numericLv.position)
|
||||
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||
val size = decl.arraysize?.constIndex() ?: return null
|
||||
if (rangeExpr==null && numericLv!=null) {
|
||||
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
||||
val fillvalue = numericLv.number.toInt()
|
||||
@ -332,8 +348,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
}
|
||||
// create the array itself, filled with the fillvalue.
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteral(ArrayToElementTypes.getValue(decl.datatype), it.toDouble(), numericLv.position) }.toTypedArray<Expression>()
|
||||
val refValue = ArrayLiteral(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||
return ArrayLiteral(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
@ -345,15 +360,14 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
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),
|
||||
return 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
|
||||
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
|
||||
@ -362,28 +376,22 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
else {
|
||||
// create the array itself, filled with the fillvalue.
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.FLOAT, it, numericLv.position) }.toTypedArray<Expression>()
|
||||
val refValue = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||
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 noModifications
|
||||
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<Expression>()
|
||||
val refValue = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), array, position = numericLv.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||
return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), array, position = numericLv.position)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// nothing to do for this type
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
@ -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 *************")
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
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)
|
||||
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 -> 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,27 @@ class TestSymbolTable: FunSpec({
|
||||
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
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user