From f97b3f23e28e4f1cc5abe93eeeb344c422a1cf68 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 11 Dec 2023 22:51:33 +0100 Subject: [PATCH 1/2] optimize symbol table for IR --- .../src/prog8/codegen/cpu6502/AsmGen.kt | 13 ++ .../cpu6502/assignment/AssignmentAsmGen.kt | 26 +-- .../prog8/codegen/intermediate/IRCodeGen.kt | 3 +- codeGenIntermediate/test/TestIRPeepholeOpt.kt | 2 +- .../src/prog8/buildversion/BuildVersion.kt | 14 +- docs/source/todo.rst | 9 +- examples/test.p8 | 24 +- .../src/prog8/intermediate/IRFileReader.kt | 2 +- .../src/prog8/intermediate/IRFileWriter.kt | 27 +-- .../src/prog8/intermediate/IRSymbolTable.kt | 206 ++++++++++++++---- intermediate/src/prog8/intermediate/Utils.kt | 36 +-- intermediate/test/TestIRFileInOut.kt | 14 +- .../src/prog8/vm/VmProgramLoader.kt | 15 +- virtualmachine/test/TestVm.kt | 6 +- 14 files changed, 237 insertions(+), 160 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 4a35aa795..4968ee22e 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -1,6 +1,7 @@ package prog8.codegen.cpu6502 import com.github.michaelbull.result.fold +import prog8.code.StNode import prog8.code.StNodeType import prog8.code.SymbolTable import prog8.code.SymbolTableMaker @@ -296,6 +297,18 @@ class AsmGen6502Internal ( name } + fun asmVariableName(st: StNode, scope: PtSub?): String { + val name = asmVariableName(st.scopedName) + if(scope==null) + return name + // remove the whole prefix and just make the variable name locally scoped (64tass scopes it to the proper .proc block) + val subName = scope.scopedName + return if (name.length>subName.length && name.startsWith(subName) && name[subName.length] == '.') + name.drop(subName.length+1) + else + name + } + internal fun getTempVarName(dt: DataType): String { return when(dt) { DataType.UBYTE -> "cx16.r9L" diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 3d259c405..ffbeb37f5 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -1,5 +1,7 @@ package prog8.codegen.cpu6502.assignment +import prog8.code.StMemVar +import prog8.code.StStaticVariable import prog8.code.ast.* import prog8.code.core.* import prog8.codegen.cpu6502.AsmGen6502Internal @@ -1814,23 +1816,21 @@ internal class AssignmentAsmGen(private val program: PtProgram, private fun containmentCheckIntoA(containment: PtContainmentCheck) { val elementDt = containment.element.type - val symbol = asmgen.symbolTable.lookup(containment.iterable.name) - val variable = symbol!!.astNode as IPtVariable - val varname = asmgen.asmVariableName(containment.iterable) - val numElements = when(variable) { - is PtConstant -> null - is PtMemMapped -> variable.arraySize - is PtVariable -> variable.arraySize + val symbol = asmgen.symbolTable.lookup(containment.iterable.name)!! + val symbolName = asmgen.asmVariableName(symbol, containment.definingSub()) + val (dt, numElements) = when(symbol) { + is StStaticVariable -> symbol.dt to symbol.length!! + is StMemVar -> symbol.dt to symbol.length!! + else -> DataType.UNDEFINED to 0 } - when(variable.type) { + when(dt) { DataType.STR -> { // use subroutine assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) asmgen.saveRegisterStack(CpuRegister.A, true) - assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position,"P8ZP_SCRATCH_W1"), varname, null, null) + assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position,"P8ZP_SCRATCH_W1"), symbolName, null, null) asmgen.restoreRegisterStack(CpuRegister.A, false) - val stringVal = (variable as PtVariable).value as PtString - asmgen.out(" ldy #${stringVal.value.length}") + asmgen.out(" ldy #${numElements-1}") asmgen.out(" jsr prog8_lib.containment_bytearray") return } @@ -1840,7 +1840,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, DataType.ARRAY_B, DataType.ARRAY_UB -> { assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) asmgen.saveRegisterStack(CpuRegister.A, true) - assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W1"), varname, null, null) + assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, null, null) asmgen.restoreRegisterStack(CpuRegister.A, false) asmgen.out(" ldy #$numElements") asmgen.out(" jsr prog8_lib.containment_bytearray") @@ -1848,7 +1848,7 @@ internal class AssignmentAsmGen(private val program: PtProgram, } DataType.ARRAY_W, DataType.ARRAY_UW -> { assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt) - assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), symbol.astNode.position, "P8ZP_SCRATCH_W2"), varname, null, null) + assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName, null, null) asmgen.out(" ldy #$numElements") asmgen.out(" jsr prog8_lib.containment_wordarray") return diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 19235f17d..8e47e808f 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -18,7 +18,6 @@ class IRCodeGen( private val expressionEval = ExpressionGen(this) private val builtinFuncGen = BuiltinFuncGen(this, expressionEval) private val assignmentGen = AssignmentGen(this, expressionEval) - private var irSymbolTable: IRSymbolTable = IRSymbolTable(null) internal val registers = RegisterPool() fun generate(): IRProgram { @@ -26,7 +25,7 @@ class IRCodeGen( moveAllNestedSubroutinesToBlockScope() verifyNameScoping(program, symbolTable) - irSymbolTable = IRSymbolTable(symbolTable) + val irSymbolTable = IRSymbolTable.fromStDuringCodegen(symbolTable) val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding) // collect global variables initializers diff --git a/codeGenIntermediate/test/TestIRPeepholeOpt.kt b/codeGenIntermediate/test/TestIRPeepholeOpt.kt index 9ed204739..ce01871c2 100644 --- a/codeGenIntermediate/test/TestIRPeepholeOpt.kt +++ b/codeGenIntermediate/test/TestIRPeepholeOpt.kt @@ -24,7 +24,7 @@ class TestIRPeepholeOpt: FunSpec({ compTarget = target, loadAddress = target.machine.PROGRAM_LOAD_ADDRESS ) - val prog = IRProgram("test", IRSymbolTable(null), options, target) + val prog = IRProgram("test", IRSymbolTable(), options, target) prog.addBlock(block) prog.linkChunks() prog.validate() diff --git a/compiler/src/prog8/buildversion/BuildVersion.kt b/compiler/src/prog8/buildversion/BuildVersion.kt index 7d77c3590..f1915a774 100644 --- a/compiler/src/prog8/buildversion/BuildVersion.kt +++ b/compiler/src/prog8/buildversion/BuildVersion.kt @@ -5,11 +5,11 @@ package prog8.buildversion */ const val MAVEN_GROUP = "prog8" const val MAVEN_NAME = "compiler" -const val VERSION = "9.7" -const val GIT_REVISION = 4286 -const val GIT_SHA = "cfc9c15f1e9fe96940e170fb14ad3a957109977c" -const val GIT_DATE = "2023-12-10T15:22:00Z" -const val GIT_BRANCH = "master" -const val BUILD_DATE = "2023-12-10T15:44:19Z" -const val BUILD_UNIX_TIME = 1702223059842L +const val VERSION = "9.8-SNAPSHOT" +const val GIT_REVISION = 4287 +const val GIT_SHA = "08a079a96e67c26298b81de2d35919be51a3dfd0" +const val GIT_DATE = "2023-12-11T20:15:48Z" +const val GIT_BRANCH = "no-vardecls" +const val BUILD_DATE = "2023-12-11T21:48:14Z" +const val BUILD_UNIX_TIME = 1702331294381L const val DIRTY = 1 diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 1e3191a92..1803b2865 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,6 +2,10 @@ TODO ==== +- [on branch: no-vardecls] + remove IPtVariable and the 3 derived types (var, constant, memmapped) in the codegen ast + remove VarDecls in compiler ast? + - [on branch: call-pointers] allow calling a subroutine via a pointer variable (indirect JSR, optimized form of callfar()) modify programs (shell, paint) that now use callfar @@ -28,11 +32,6 @@ 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) -- [on branch: no-vardecls] - remove astNode from StNode in the symboltable - remove IPtVariable and the 3 derived types (var, constant, memmapped) in the codegen ast - remove VarDecls in compiler ast - - do we need (array)variable alignment tag instead of block alignment tag? You want to align the data, not the code in the block? - ir: getting it in shape for code generation - ir: related to the one above: 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 diff --git a/examples/test.p8 b/examples/test.p8 index b0baed729..1fad6c070 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,21 +4,13 @@ main { sub start() { - ubyte x,y,z = math.rnd() - - txt.print_ub(x) - txt.nl() - txt.print_ub(y) - txt.nl() - txt.print_ub(z) - txt.nl() - - x=y=z=math.rnd() - txt.print_ub(x) - txt.nl() - txt.print_ub(y) - txt.nl() - txt.print_ub(z) - txt.nl() + str name = "irmen" + ubyte[] bytes = [1,2,3] + uword[] words = [1,2,3,4,5] + txt.print_ub('z' in name) + txt.print_ub('r' in name) + txt.print_ub('r' in "derp") + txt.print_ub(4 in bytes) + txt.print_ub($0004 in words) } } diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index a2312ad33..f070b349a 100644 --- a/intermediate/src/prog8/intermediate/IRFileReader.kt +++ b/intermediate/src/prog8/intermediate/IRFileReader.kt @@ -56,7 +56,7 @@ class IRFileReader { val initGlobals = parseInitGlobals(reader) val blocks = parseBlocksUntilProgramEnd(reader) - val st = IRSymbolTable(null) + val st = IRSymbolTable() asmsymbols.forEach { (name, value) -> st.addAsmSymbol(name, value)} varsWithoutInit.forEach { st.add(it) } variables.forEach { st.add(it) } diff --git a/intermediate/src/prog8/intermediate/IRFileWriter.kt b/intermediate/src/prog8/intermediate/IRFileWriter.kt index c790fe33e..ac923c6cf 100644 --- a/intermediate/src/prog8/intermediate/IRFileWriter.kt +++ b/intermediate/src/prog8/intermediate/IRFileWriter.kt @@ -142,7 +142,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { if(irProgram.options.includeSourcelines) { if(code.sourceLinesPositions.any {it !== Position.DUMMY}) { xml.writeStartElement("P8SRC") - var sourceTxt = StringBuilder("\n") + val sourceTxt = StringBuilder("\n") code.sourceLinesPositions.forEach { pos -> val line = SourceLineCache.retrieveLine(pos) if(line!=null) { @@ -210,8 +210,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_lsb zp=${variable.zpwish}\n") xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_msb zp=${variable.zpwish}\n") } else { - val typeStr = getTypeString(variable) - xml.writeCharacters("$typeStr ${variable.name} zp=${variable.zpwish}\n") + xml.writeCharacters("${variable.typeString} ${variable.name} zp=${variable.zpwish}\n") } } @@ -228,15 +227,15 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { lsbValue = "" msbValue = "" } else { - lsbValue = variable.onetimeInitializationArrayValue!!.joinToString(",") { + lsbValue = variable.onetimeInitializationArrayValue.joinToString(",") { if(it.number!=null) - (it.number!!.toInt() and 255).toHex() + (it.number.toInt() and 255).toHex() else "@<${it.addressOfSymbol}" } - msbValue = variable.onetimeInitializationArrayValue!!.joinToString(",") { + msbValue = variable.onetimeInitializationArrayValue.joinToString(",") { if(it.number!=null) - (it.number!!.toInt() shr 8).toHex() + (it.number.toInt() shr 8).toHex() else "@>${it.addressOfSymbol}" } @@ -244,26 +243,25 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_lsb=$lsbValue zp=${variable.zpwish}\n") xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_msb=$msbValue zp=${variable.zpwish}\n") } else { - val typeStr = getTypeString(variable) val value: String = when(variable.dt) { DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: "").toString() in NumericDatatypes -> (variable.onetimeInitializationNumericValue?.toInt()?.toHex() ?: "").toString() DataType.STR -> { - val encoded = irProgram.encoding.encodeString(variable.onetimeInitializationStringValue!!.first, variable.onetimeInitializationStringValue!!.second) + listOf(0u) + val encoded = irProgram.encoding.encodeString(variable.onetimeInitializationStringValue!!.first, variable.onetimeInitializationStringValue.second) + listOf(0u) encoded.joinToString(",") { it.toInt().toString() } } DataType.ARRAY_F -> { if(variable.onetimeInitializationArrayValue!=null) { - variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toString() } + variable.onetimeInitializationArrayValue.joinToString(",") { it.number!!.toString() } } else { "" // array will be zero'd out at program start } } in ArrayDatatypes -> { if(variable.onetimeInitializationArrayValue!==null) { - variable.onetimeInitializationArrayValue!!.joinToString(",") { + variable.onetimeInitializationArrayValue.joinToString(",") { if(it.number!=null) - it.number!!.toInt().toHex() + it.number.toInt().toHex() else "@${it.addressOfSymbol}" } @@ -273,7 +271,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { } else -> throw InternalCompilerException("weird dt") } - xml.writeCharacters("$typeStr ${variable.name}=$value zp=${variable.zpwish}\n") + xml.writeCharacters("${variable.typeString} ${variable.name}=$value zp=${variable.zpwish}\n") } } xml.writeEndElement() @@ -282,8 +280,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { xml.writeStartElement("MEMORYMAPPEDVARIABLES") xml.writeCharacters("\n") for (variable in irProgram.st.allMemMappedVariables()) { - val typeStr = getTypeString(variable) - xml.writeCharacters("@$typeStr ${variable.name}=${variable.address.toHex()}\n") + xml.writeCharacters("@${variable.typeString} ${variable.name}=${variable.address.toHex()}\n") } xml.writeEndElement() xml.writeCharacters("\n") diff --git a/intermediate/src/prog8/intermediate/IRSymbolTable.kt b/intermediate/src/prog8/intermediate/IRSymbolTable.kt index 80222f76a..323a1a3c7 100644 --- a/intermediate/src/prog8/intermediate/IRSymbolTable.kt +++ b/intermediate/src/prog8/intermediate/IRSymbolTable.kt @@ -1,88 +1,88 @@ package prog8.intermediate import prog8.code.* -import prog8.code.ast.PtVariable -import prog8.code.core.DataType -import prog8.code.core.ZeropageWish -import prog8.code.core.internedStringsModuleName +import prog8.code.core.* // In the Intermediate Representation, all nesting has been removed. // So the symbol table is just a big flat mapping of (scoped)name to node. +// We define a stripped down symbol table for use in the IR phase only, rather than reuse the codegen symboltable -class IRSymbolTable(sourceSt: SymbolTable?) { - private val table = mutableMapOf() +class IRSymbolTable { + private val table = mutableMapOf() private val asmSymbols = mutableMapOf() - init { - if(sourceSt!=null) { - sourceSt.allVariables.forEach { - add(it) - } - sourceSt.allMemMappedVariables.forEach { - add(it) - } - sourceSt.allMemorySlabs.forEach { - add(it) - } + companion object { + fun fromStDuringCodegen(sourceSt: SymbolTable?): IRSymbolTable { + val st = IRSymbolTable() + if (sourceSt != null) { + sourceSt.allVariables.forEach { + st.add(it) + } + sourceSt.allMemMappedVariables.forEach { + st.add(it) + } + sourceSt.allMemorySlabs.forEach { + st.add(it) + } - require(table.all { it.key==it.value.name }) + require(st.table.all { it.key == it.value.name }) - allVariables().forEach {variable -> - variable.onetimeInitializationArrayValue?.let { - it.forEach { arrayElt -> - if(arrayElt.addressOfSymbol!=null) { - require(arrayElt.addressOfSymbol!!.contains('.')) { - "pointer var in array should be properly scoped: ${arrayElt.addressOfSymbol} in ${variable.name}" + st.allVariables().forEach { variable -> + variable.onetimeInitializationArrayValue?.let { + it.forEach { arrayElt -> + if (arrayElt.addressOfSymbol != null) { + require(arrayElt.addressOfSymbol.contains('.')) { + "pointer var in array should be properly scoped: ${arrayElt.addressOfSymbol} in ${variable.name}" + } } } } } } + return st } } - fun allVariables(): Sequence = - table.asSequence().map { it.value }.filterIsInstance() + fun allVariables(): Sequence = + table.asSequence().map { it.value }.filterIsInstance() - fun allMemMappedVariables(): Sequence = - table.asSequence().map { it.value }.filterIsInstance() + fun allMemMappedVariables(): Sequence = + table.asSequence().map { it.value }.filterIsInstance() - fun allMemorySlabs(): Sequence = - table.asSequence().map { it.value }.filterIsInstance() + fun allMemorySlabs(): Sequence = + table.asSequence().map { it.value }.filterIsInstance() fun lookup(name: String) = table[name] fun add(variable: StStaticVariable) { val scopedName: String - val varToadd: StStaticVariable + val varToadd: IRStStaticVariable if('.' in variable.name) { scopedName = variable.name - varToadd = variable + varToadd = IRStStaticVariable.from(variable) } else { - fun fixupAddressOfInArray(array: List?): List? { + fun fixupAddressOfInArray(array: List?): List? { if(array==null) return null - val newArray = mutableListOf() + val newArray = mutableListOf() array.forEach { if(it.addressOfSymbol!=null) { val target = variable.lookup(it.addressOfSymbol!!)!! - newArray.add(StArrayElement(null, target.scopedName)) + newArray.add(IRStArrayElement(null, target.scopedName)) } else { - newArray.add(it) + newArray.add(IRStArrayElement.from(it)) } } return newArray } scopedName = variable.scopedName - val dummyNode = PtVariable(scopedName, variable.dt, variable.zpwish, null, null, variable.astNode.position) - varToadd = StStaticVariable(scopedName, variable.dt, + varToadd = IRStStaticVariable(scopedName, variable.dt, variable.onetimeInitializationNumericValue, variable.onetimeInitializationStringValue, fixupAddressOfInArray(variable.onetimeInitializationArrayValue), variable.length, - variable.zpwish, - dummyNode + variable.zpwish ) } table[scopedName] = varToadd @@ -91,27 +91,26 @@ class IRSymbolTable(sourceSt: SymbolTable?) { fun add(variable: StMemVar) { val scopedName: String - val varToadd: StMemVar + val varToadd: IRStMemVar if('.' in variable.name) { scopedName = variable.name - varToadd = variable + varToadd = IRStMemVar.from(variable) } else { scopedName = try { variable.scopedName } catch (ux: UninitializedPropertyAccessException) { variable.name } - varToadd = StMemVar(scopedName, variable.dt, variable.address, variable.length, variable.astNode) + varToadd = IRStMemVar(scopedName, variable.dt, variable.address, variable.length) } table[scopedName] = varToadd } fun add(variable: StMemorySlab) { val varToadd = if('.' in variable.name) - variable + IRStMemorySlab.from(variable) else { - val dummyNode = PtVariable(variable.name, DataType.ARRAY_UB, ZeropageWish.NOT_IN_ZEROPAGE, null, null, variable.astNode.position) - StMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align, dummyNode) + IRStMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align) } table[varToadd.name] = varToadd } @@ -133,3 +132,118 @@ class IRSymbolTable(sourceSt: SymbolTable?) { } } } + + +enum class IRStNodeType { + STATICVAR, + MEMVAR, + MEMORYSLAB + // the other StNodeType types aren't used here anymore. + // this symbol table only contains variables. +} + +open class IRStNode(val name: String, + val type: IRStNodeType, + val children: MutableMap = mutableMapOf() +) + +class IRStMemVar(name: String, + val dt: DataType, + val address: UInt, + val length: Int? // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte + ) : IRStNode(name, IRStNodeType.MEMVAR) { + companion object { + fun from(variable: StMemVar): IRStMemVar { + return IRStMemVar( + variable.name, + variable.dt, + variable.address, + variable.length + ) + } + } + + val typeString: String + get() = when (dt) { + DataType.UBYTE -> "ubyte" + DataType.BYTE -> "byte" + DataType.UWORD -> "uword" + DataType.WORD -> "word" + DataType.FLOAT -> "float" + DataType.BOOL, DataType.ARRAY_BOOL -> throw InternalCompilerException("bool should have been converted to ubyte") + DataType.ARRAY_UB, DataType.STR -> "ubyte[${length}]" + DataType.ARRAY_B -> "byte[${length}]" + DataType.ARRAY_UW -> "uword[${length}]" + DataType.ARRAY_W -> "word[${length}]" + DataType.ARRAY_F -> "float[${length}]" + in SplitWordArrayTypes -> throw InternalCompilerException("@split can't be used on memory mapped arrays") + else -> throw InternalCompilerException("weird dt") + } +} + +class IRStMemorySlab( + name: String, + val size: UInt, + val align: UInt +): IRStNode(name, IRStNodeType.MEMORYSLAB) { + companion object { + fun from(variable: StMemorySlab): IRStMemorySlab { + return IRStMemorySlab( + variable.name, + variable.size, + variable.align + ) + } + } +} + +class IRStStaticVariable(name: String, + val dt: DataType, + val onetimeInitializationNumericValue: Double?, // regular (every-run-time) initialization is done via regular assignments + val onetimeInitializationStringValue: IRStString?, + val onetimeInitializationArrayValue: IRStArray?, + val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte + val zpwish: ZeropageWish // used in the variable allocator +) : IRStNode(name, IRStNodeType.STATICVAR) { + companion object { + fun from(variable: StStaticVariable): IRStStaticVariable { + return IRStStaticVariable(variable.name, + variable.dt, + variable.onetimeInitializationNumericValue, + variable.onetimeInitializationStringValue, + variable.onetimeInitializationArrayValue?.map { IRStArrayElement.from(it) }, + variable.length, + variable.zpwish) + } + } + + val uninitialized = onetimeInitializationArrayValue==null && onetimeInitializationStringValue==null && onetimeInitializationNumericValue==null + + val typeString: String + get() = when (dt) { + DataType.UBYTE -> "ubyte" + DataType.BYTE -> "byte" + DataType.UWORD -> "uword" + DataType.WORD -> "word" + DataType.FLOAT -> "float" + DataType.BOOL, DataType.ARRAY_BOOL -> throw InternalCompilerException("bool should have been converted to ubyte") + DataType.ARRAY_UB, DataType.STR -> "ubyte[${length}]" + DataType.ARRAY_B -> "byte[${length}]" + DataType.ARRAY_UW -> "uword[${length}]" + DataType.ARRAY_W -> "word[${length}]" + DataType.ARRAY_F -> "float[${length}]" + in SplitWordArrayTypes -> throw InternalCompilerException("split array should have been converted to 2 ubyte arrays") + else -> throw InternalCompilerException("weird dt") + } +} + +class IRStArrayElement(val number: Double?, val addressOfSymbol: String?) { + companion object { + fun from(elt: StArrayElement): IRStArrayElement { + return IRStArrayElement(elt.number, elt.addressOfSymbol) + } + } +} + +typealias IRStArray = List +typealias IRStString = Pair diff --git a/intermediate/src/prog8/intermediate/Utils.kt b/intermediate/src/prog8/intermediate/Utils.kt index bf8599934..6f361a2de 100644 --- a/intermediate/src/prog8/intermediate/Utils.kt +++ b/intermediate/src/prog8/intermediate/Utils.kt @@ -1,7 +1,9 @@ package prog8.intermediate -import prog8.code.* +import prog8.code.Either import prog8.code.core.* +import prog8.code.left +import prog8.code.right fun getTypeString(dt : DataType): String = when(dt) { @@ -20,38 +22,6 @@ fun getTypeString(dt : DataType): String = when(dt) { else -> throw InternalCompilerException("weird dt") } -fun getTypeString(memvar: StMemVar): String = when(memvar.dt) { - DataType.UBYTE -> "ubyte" - DataType.BYTE -> "byte" - DataType.UWORD -> "uword" - DataType.WORD -> "word" - DataType.FLOAT -> "float" - DataType.BOOL, DataType.ARRAY_BOOL -> throw InternalCompilerException("bool should have been converted to ubyte") - DataType.ARRAY_UB, DataType.STR -> "ubyte[${memvar.length}]" - DataType.ARRAY_B -> "byte[${memvar.length}]" - DataType.ARRAY_UW -> "uword[${memvar.length}]" - DataType.ARRAY_W -> "word[${memvar.length}]" - DataType.ARRAY_F -> "float[${memvar.length}]" - in SplitWordArrayTypes -> throw InternalCompilerException("@split can't be used on memory mapped arrays") - else -> throw InternalCompilerException("weird dt") -} - -fun getTypeString(variable : StStaticVariable): String = when(variable.dt) { - DataType.UBYTE -> "ubyte" - DataType.BYTE -> "byte" - DataType.UWORD -> "uword" - DataType.WORD -> "word" - DataType.FLOAT -> "float" - DataType.BOOL, DataType.ARRAY_BOOL -> throw InternalCompilerException("bool should have been converted to ubyte") - DataType.ARRAY_UB, DataType.STR -> "ubyte[${variable.length}]" - DataType.ARRAY_B -> "byte[${variable.length}]" - DataType.ARRAY_UW -> "uword[${variable.length}]" - DataType.ARRAY_W -> "word[${variable.length}]" - DataType.ARRAY_F -> "float[${variable.length}]" - in SplitWordArrayTypes -> throw InternalCompilerException("split array should have been converted to 2 ubyte arrays") - else -> throw InternalCompilerException("weird dt") -} - fun convertIRType(typestr: String): IRDataType? { return when(typestr.lowercase()) { "" -> null diff --git a/intermediate/test/TestIRFileInOut.kt b/intermediate/test/TestIRFileInOut.kt index 73824793a..fada5c272 100644 --- a/intermediate/test/TestIRFileInOut.kt +++ b/intermediate/test/TestIRFileInOut.kt @@ -1,16 +1,12 @@ import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.ints.shouldBeGreaterThan import io.kotest.matchers.shouldBe -import prog8.code.StStaticVariable import prog8.code.core.CbmPrgLauncherType import prog8.code.core.CompilationOptions import prog8.code.core.OutputType import prog8.code.core.ZeropageType import prog8.code.target.Cx16Target -import prog8.intermediate.IRFileReader -import prog8.intermediate.IRFileWriter -import prog8.intermediate.IRProgram -import prog8.intermediate.IRSymbolTable +import prog8.intermediate.* import kotlin.io.path.* class TestIRFileInOut: FunSpec({ @@ -29,7 +25,7 @@ class TestIRFileInOut: FunSpec({ loadAddress = target.machine.PROGRAM_LOAD_ADDRESS, outputDir = tempdir ) - val program = IRProgram("unittest-irwriter", IRSymbolTable(null), options, target) + val program = IRProgram("unittest-irwriter", IRSymbolTable(), options, target) val writer = IRFileWriter(program, null) val generatedFile = writer.write() val lines = generatedFile.readLines() @@ -107,9 +103,9 @@ return program.name shouldBe "test-ir-reader" program.blocks.size shouldBe 2 program.st.allVariables().count() shouldBe 3 - val var1 = program.st.lookup("sys.wait.jiffies") as StStaticVariable - val var2 = program.st.lookup("sys.bssvar") as StStaticVariable - val var3 = program.st.lookup("sys.emptystring") as StStaticVariable + val var1 = program.st.lookup("sys.wait.jiffies") as IRStStaticVariable + val var2 = program.st.lookup("sys.bssvar") as IRStStaticVariable + val var3 = program.st.lookup("sys.emptystring") as IRStStaticVariable var1.uninitialized shouldBe false var2.uninitialized shouldBe true var3.uninitialized shouldBe true diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index 8d7844ec8..b887592c6 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -1,8 +1,5 @@ package prog8.vm -import prog8.code.StArray -import prog8.code.StArrayElement -import prog8.code.StStaticVariable import prog8.code.core.ArrayDatatypes import prog8.code.core.AssemblyError import prog8.code.core.DataType @@ -247,7 +244,7 @@ class VmProgramLoader { if(iElts.isEmpty() || iElts.size==1) { val iElt = if(iElts.isEmpty()) { require(variable.uninitialized) - StArrayElement(0.0, null) + IRStArrayElement(0.0, null) } else { require(!variable.uninitialized) iElts[0] @@ -262,8 +259,8 @@ class VmProgramLoader { } private fun initializeWithValues( - variable: StStaticVariable, - iElts: StArray, + variable: IRStStaticVariable, + iElts: IRStArray, startAddress: Int, symbolAddresses: MutableMap, memory: Memory, @@ -325,8 +322,8 @@ class VmProgramLoader { } private fun initializeWithOneValue( - variable: StStaticVariable, - iElt: StArrayElement, + variable: IRStStaticVariable, + iElt: IRStArrayElement, startAddress: Int, symbolAddresses: MutableMap, memory: Memory, @@ -389,7 +386,7 @@ class VmProgramLoader { } } - private fun getInitializerValue(arrayDt: DataType, elt: StArrayElement, symbolAddresses: MutableMap): Double { + private fun getInitializerValue(arrayDt: DataType, elt: IRStArrayElement, symbolAddresses: MutableMap): Double { if(elt.addressOfSymbol!=null) { when(arrayDt) { DataType.ARRAY_UB, DataType.STR, DataType.ARRAY_B, DataType.ARRAY_BOOL -> { diff --git a/virtualmachine/test/TestVm.kt b/virtualmachine/test/TestVm.kt index a5d95e10f..fbeec40eb 100644 --- a/virtualmachine/test/TestVm.kt +++ b/virtualmachine/test/TestVm.kt @@ -29,7 +29,7 @@ class TestVm: FunSpec( { } test("vm execution: empty program") { - val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) + val program = IRProgram("test", IRSymbolTable(), getTestOptions(), VMTarget()) val vm = VirtualMachine(program) vm.callStack.shouldBeEmpty() vm.valueStack.shouldBeEmpty() @@ -43,7 +43,7 @@ class TestVm: FunSpec( { } test("vm execution: modify memory") { - val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) + val program = IRProgram("test", IRSymbolTable(), getTestOptions(), VMTarget()) val block = IRBlock("testmain", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY) val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY) val code = IRCodeChunk(startSub.label, null) @@ -72,7 +72,7 @@ class TestVm: FunSpec( { } test("asmsub not supported in vm even with IR") { - val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) + val program = IRProgram("test", IRSymbolTable(), getTestOptions(), VMTarget()) val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY) val startSub = IRAsmSubroutine( "main.asmstart", From 00b32f64e69e3d78a7dbcdba202851fb8ec163e9 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 12 Dec 2023 19:40:23 +0100 Subject: [PATCH 2/2] tweak for,sort,reverse st use --- .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 187 +++++++++--------- .../prog8/codegen/cpu6502/ForLoopsAsmGen.kt | 29 +-- docs/source/todo.rst | 4 - 3 files changed, 105 insertions(+), 115 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 113720bad..ad87d3f07 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -1,5 +1,6 @@ package prog8.codegen.cpu6502 +import prog8.code.StMemVar import prog8.code.StStaticVariable import prog8.code.ast.* import prog8.code.core.* @@ -311,106 +312,97 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, } private fun funcReverse(fcall: PtBuiltinFunctionCall) { - val variable = fcall.args.single() - if (variable is PtIdentifier) { - val symbol = asmgen.symbolTable.lookup(variable.name) - val decl = symbol!!.astNode as IPtVariable - val numElements = when(decl) { - is PtConstant -> throw AssemblyError("cannot reverse a constant") - is PtMemMapped -> decl.arraySize - is PtVariable -> decl.arraySize + val variable = fcall.args.single() as PtIdentifier + val symbol = asmgen.symbolTable.lookup(variable.name) + val (dt, numElements) = when(symbol) { + is StStaticVariable -> symbol.dt to symbol.length!! + is StMemVar -> symbol.dt to symbol.length!! + else -> DataType.UNDEFINED to 0 + } + val varName = asmgen.asmVariableName(variable) + when (dt) { + DataType.ARRAY_UB, DataType.ARRAY_B -> { + asmgen.out(""" + lda #<$varName + ldy #>$varName + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda #$numElements + jsr prog8_lib.func_reverse_b""") } - val varName = asmgen.asmVariableName(variable) - when (decl.type) { - DataType.ARRAY_UB, DataType.ARRAY_B -> { - asmgen.out(""" - lda #<$varName - ldy #>$varName - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda #$numElements - jsr prog8_lib.func_reverse_b""") - } - DataType.ARRAY_UW, DataType.ARRAY_W -> { - asmgen.out(""" - lda #<$varName - ldy #>$varName - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda #$numElements - jsr prog8_lib.func_reverse_w""") - } - DataType.STR -> { - val stringLength = (symbol as StStaticVariable).length!!-1 - asmgen.out(""" - lda #<$varName - ldy #>$varName - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda #$stringLength - jsr prog8_lib.func_reverse_b""") - } - DataType.ARRAY_F -> { - asmgen.out(""" - lda #<$varName - ldy #>$varName - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda #$numElements - jsr floats.func_reverse_f""") - } - in SplitWordArrayTypes -> TODO("split word reverse") - else -> throw AssemblyError("weird type") + DataType.ARRAY_UW, DataType.ARRAY_W -> { + asmgen.out(""" + lda #<$varName + ldy #>$varName + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda #$numElements + jsr prog8_lib.func_reverse_w""") } + DataType.STR -> { + asmgen.out(""" + lda #<$varName + ldy #>$varName + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda #${numElements-1} + jsr prog8_lib.func_reverse_b""") + } + DataType.ARRAY_F -> { + asmgen.out(""" + lda #<$varName + ldy #>$varName + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda #$numElements + jsr floats.func_reverse_f""") + } + in SplitWordArrayTypes -> TODO("split word reverse") + else -> throw AssemblyError("weird type") } } private fun funcSort(fcall: PtBuiltinFunctionCall) { - val variable = fcall.args.single() - if (variable is PtIdentifier) { - val symbol = asmgen.symbolTable.lookup(variable.name) - val decl = symbol!!.astNode as IPtVariable - val varName = asmgen.asmVariableName(variable) - val numElements = when(decl) { - is PtConstant -> throw AssemblyError("cannot sort a constant") - is PtMemMapped -> decl.arraySize - is PtVariable -> decl.arraySize + val variable = fcall.args.single() as PtIdentifier + val symbol = asmgen.symbolTable.lookup(variable.name) + val varName = asmgen.asmVariableName(variable) + val (dt, numElements) = when(symbol) { + is StStaticVariable -> symbol.dt to symbol.length!! + is StMemVar -> symbol.dt to symbol.length!! + else -> DataType.UNDEFINED to 0 + } + when (dt) { + DataType.ARRAY_UB, DataType.ARRAY_B -> { + asmgen.out(""" + lda #<$varName + ldy #>$varName + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda #$numElements""") + asmgen.out(if (dt == DataType.ARRAY_UB) " jsr prog8_lib.func_sort_ub" else " jsr prog8_lib.func_sort_b") } - when (decl.type) { - DataType.ARRAY_UB, DataType.ARRAY_B -> { - asmgen.out(""" - lda #<$varName - ldy #>$varName - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda #$numElements""") - asmgen.out(if (decl.type == DataType.ARRAY_UB) " jsr prog8_lib.func_sort_ub" else " jsr prog8_lib.func_sort_b") - } - DataType.ARRAY_UW, DataType.ARRAY_W -> { - asmgen.out(""" - lda #<$varName - ldy #>$varName - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda #$numElements""") - asmgen.out(if (decl.type == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w") - } - DataType.STR -> { - val stringLength = (symbol as StStaticVariable).length!!-1 - asmgen.out(""" - lda #<$varName - ldy #>$varName - sta P8ZP_SCRATCH_W1 - sty P8ZP_SCRATCH_W1+1 - lda #$stringLength - jsr prog8_lib.func_sort_ub""") - } - DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported") - in SplitWordArrayTypes -> TODO("split word sort") - else -> throw AssemblyError("weird type") + DataType.ARRAY_UW, DataType.ARRAY_W -> { + asmgen.out(""" + lda #<$varName + ldy #>$varName + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda #$numElements""") + asmgen.out(if (dt == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w") } - } else - throw AssemblyError("weird type") + DataType.STR -> { + asmgen.out(""" + lda #<$varName + ldy #>$varName + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda #${numElements-1} + jsr prog8_lib.func_sort_ub""") + } + DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported") + in SplitWordArrayTypes -> TODO("split word sort") + else -> throw AssemblyError("weird type") + } } private fun funcRor2(fcall: PtBuiltinFunctionCall) { @@ -1245,12 +1237,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, // address in P8ZP_SCRATCH_W1, number of elements in A arg as PtIdentifier val symbol = asmgen.symbolTable.lookup(arg.name) - val arrayVar = symbol!!.astNode as IPtVariable - val numElements = when(arrayVar) { - is PtConstant -> null - is PtMemMapped -> arrayVar.arraySize - is PtVariable -> arrayVar.arraySize - } ?: throw AssemblyError("length of non-array requested") + val numElements = when(symbol) { + is StStaticVariable -> symbol.length!! + is StMemVar -> symbol.length!! + else -> 0 + } val identifierName = asmgen.asmVariableName(arg) asmgen.out(""" lda #<$identifierName diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt index 715838b0e..d00a42d36 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt @@ -1,7 +1,11 @@ package prog8.codegen.cpu6502 import com.github.michaelbull.result.fold -import prog8.code.ast.* +import prog8.code.StMemVar +import prog8.code.StStaticVariable +import prog8.code.ast.PtForLoop +import prog8.code.ast.PtIdentifier +import prog8.code.ast.PtRange import prog8.code.core.* import kotlin.math.absoluteValue @@ -332,11 +336,10 @@ $endLabel""") asmgen.loopEndLabels.push(endLabel) val iterableName = asmgen.asmVariableName(ident) val symbol = asmgen.symbolTable.lookup(ident.name) - val decl = symbol!!.astNode as IPtVariable - val numElements = when(decl) { - is PtConstant -> throw AssemblyError("length of non-array requested") - is PtMemMapped -> decl.arraySize - is PtVariable -> decl.arraySize + val numElements = when(symbol) { + is StStaticVariable -> symbol.length!! + is StMemVar -> symbol.length!! + else -> 0 } when(iterableDt) { DataType.STR -> { @@ -364,7 +367,7 @@ $loopLabel sty $indexVar lda $iterableName,y sta ${asmgen.asmVariableName(stmt.variable)}""") asmgen.translate(stmt.statements) - if(numElements!!<=255u) { + if(numElements<=255) { asmgen.out(""" ldy $indexVar iny @@ -379,7 +382,7 @@ $loopLabel sty $indexVar bne $loopLabel beq $endLabel""") } - if(numElements>=16u) { + if(numElements>=16) { // allocate index var on ZP if possible val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors) result.fold( @@ -392,7 +395,7 @@ $loopLabel sty $indexVar asmgen.out(endLabel) } DataType.ARRAY_W, DataType.ARRAY_UW -> { - val length = numElements!! * 2u + val length = numElements * 2 val indexVar = asmgen.makeLabel("for_index") val loopvarName = asmgen.asmVariableName(stmt.variable) asmgen.out(""" @@ -403,7 +406,7 @@ $loopLabel sty $indexVar lda $iterableName+1,y sta $loopvarName+1""") asmgen.translate(stmt.statements) - if(length<=127u) { + if(length<=127) { asmgen.out(""" ldy $indexVar iny @@ -420,7 +423,7 @@ $loopLabel sty $indexVar bne $loopLabel beq $endLabel""") } - if(length>=16u) { + if(length>=16) { // allocate index var on ZP if possible val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors) result.fold( @@ -444,7 +447,7 @@ $loopLabel sty $indexVar lda ${iterableName}_msb,y sta $loopvarName+1""") asmgen.translate(stmt.statements) - if(numElements<=255u) { + if(numElements<=255) { asmgen.out(""" ldy $indexVar iny @@ -459,7 +462,7 @@ $loopLabel sty $indexVar bne $loopLabel beq $endLabel""") } - if(numElements>=16u) { + if(numElements>=16) { // allocate index var on ZP if possible val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors) result.fold( diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 1803b2865..8db9c14cc 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,10 +2,6 @@ TODO ==== -- [on branch: no-vardecls] - remove IPtVariable and the 3 derived types (var, constant, memmapped) in the codegen ast - remove VarDecls in compiler ast? - - [on branch: call-pointers] allow calling a subroutine via a pointer variable (indirect JSR, optimized form of callfar()) modify programs (shell, paint) that now use callfar