optimize symbol table for IR

This commit is contained in:
Irmen de Jong
2023-12-11 22:51:33 +01:00
parent 08a079a96e
commit f97b3f23e2
14 changed files with 237 additions and 160 deletions

View File

@@ -1,6 +1,7 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold import com.github.michaelbull.result.fold
import prog8.code.StNode
import prog8.code.StNodeType import prog8.code.StNodeType
import prog8.code.SymbolTable import prog8.code.SymbolTable
import prog8.code.SymbolTableMaker import prog8.code.SymbolTableMaker
@@ -296,6 +297,18 @@ class AsmGen6502Internal (
name 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 { internal fun getTempVarName(dt: DataType): String {
return when(dt) { return when(dt) {
DataType.UBYTE -> "cx16.r9L" DataType.UBYTE -> "cx16.r9L"

View File

@@ -1,5 +1,7 @@
package prog8.codegen.cpu6502.assignment package prog8.codegen.cpu6502.assignment
import prog8.code.StMemVar
import prog8.code.StStaticVariable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal import prog8.codegen.cpu6502.AsmGen6502Internal
@@ -1814,23 +1816,21 @@ internal class AssignmentAsmGen(private val program: PtProgram,
private fun containmentCheckIntoA(containment: PtContainmentCheck) { private fun containmentCheckIntoA(containment: PtContainmentCheck) {
val elementDt = containment.element.type val elementDt = containment.element.type
val symbol = asmgen.symbolTable.lookup(containment.iterable.name) val symbol = asmgen.symbolTable.lookup(containment.iterable.name)!!
val variable = symbol!!.astNode as IPtVariable val symbolName = asmgen.asmVariableName(symbol, containment.definingSub())
val varname = asmgen.asmVariableName(containment.iterable) val (dt, numElements) = when(symbol) {
val numElements = when(variable) { is StStaticVariable -> symbol.dt to symbol.length!!
is PtConstant -> null is StMemVar -> symbol.dt to symbol.length!!
is PtMemMapped -> variable.arraySize else -> DataType.UNDEFINED to 0
is PtVariable -> variable.arraySize
} }
when(variable.type) { when(dt) {
DataType.STR -> { DataType.STR -> {
// use subroutine // use subroutine
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
asmgen.saveRegisterStack(CpuRegister.A, true) 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.restoreRegisterStack(CpuRegister.A, false)
val stringVal = (variable as PtVariable).value as PtString asmgen.out(" ldy #${numElements-1}")
asmgen.out(" ldy #${stringVal.value.length}")
asmgen.out(" jsr prog8_lib.containment_bytearray") asmgen.out(" jsr prog8_lib.containment_bytearray")
return return
} }
@@ -1840,7 +1840,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
DataType.ARRAY_B, DataType.ARRAY_UB -> { DataType.ARRAY_B, DataType.ARRAY_UB -> {
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
asmgen.saveRegisterStack(CpuRegister.A, true) 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.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(" ldy #$numElements") asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_bytearray") asmgen.out(" jsr prog8_lib.containment_bytearray")
@@ -1848,7 +1848,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
DataType.ARRAY_W, DataType.ARRAY_UW -> { DataType.ARRAY_W, DataType.ARRAY_UW -> {
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt) 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(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_wordarray") asmgen.out(" jsr prog8_lib.containment_wordarray")
return return

View File

@@ -18,7 +18,6 @@ class IRCodeGen(
private val expressionEval = ExpressionGen(this) private val expressionEval = ExpressionGen(this)
private val builtinFuncGen = BuiltinFuncGen(this, expressionEval) private val builtinFuncGen = BuiltinFuncGen(this, expressionEval)
private val assignmentGen = AssignmentGen(this, expressionEval) private val assignmentGen = AssignmentGen(this, expressionEval)
private var irSymbolTable: IRSymbolTable = IRSymbolTable(null)
internal val registers = RegisterPool() internal val registers = RegisterPool()
fun generate(): IRProgram { fun generate(): IRProgram {
@@ -26,7 +25,7 @@ class IRCodeGen(
moveAllNestedSubroutinesToBlockScope() moveAllNestedSubroutinesToBlockScope()
verifyNameScoping(program, symbolTable) verifyNameScoping(program, symbolTable)
irSymbolTable = IRSymbolTable(symbolTable) val irSymbolTable = IRSymbolTable.fromStDuringCodegen(symbolTable)
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding) val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
// collect global variables initializers // collect global variables initializers

View File

@@ -24,7 +24,7 @@ class TestIRPeepholeOpt: FunSpec({
compTarget = target, compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS 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.addBlock(block)
prog.linkChunks() prog.linkChunks()
prog.validate() prog.validate()

View File

@@ -5,11 +5,11 @@ package prog8.buildversion
*/ */
const val MAVEN_GROUP = "prog8" const val MAVEN_GROUP = "prog8"
const val MAVEN_NAME = "compiler" const val MAVEN_NAME = "compiler"
const val VERSION = "9.7" const val VERSION = "9.8-SNAPSHOT"
const val GIT_REVISION = 4286 const val GIT_REVISION = 4287
const val GIT_SHA = "cfc9c15f1e9fe96940e170fb14ad3a957109977c" const val GIT_SHA = "08a079a96e67c26298b81de2d35919be51a3dfd0"
const val GIT_DATE = "2023-12-10T15:22:00Z" const val GIT_DATE = "2023-12-11T20:15:48Z"
const val GIT_BRANCH = "master" const val GIT_BRANCH = "no-vardecls"
const val BUILD_DATE = "2023-12-10T15:44:19Z" const val BUILD_DATE = "2023-12-11T21:48:14Z"
const val BUILD_UNIX_TIME = 1702223059842L const val BUILD_UNIX_TIME = 1702331294381L
const val DIRTY = 1 const val DIRTY = 1

View File

@@ -2,6 +2,10 @@
TODO 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()) - [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 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? - 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 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? - 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: 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 - 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

View File

@@ -4,21 +4,13 @@
main { main {
sub start() { sub start() {
ubyte x,y,z = math.rnd() str name = "irmen"
ubyte[] bytes = [1,2,3]
txt.print_ub(x) uword[] words = [1,2,3,4,5]
txt.nl() txt.print_ub('z' in name)
txt.print_ub(y) txt.print_ub('r' in name)
txt.nl() txt.print_ub('r' in "derp")
txt.print_ub(z) txt.print_ub(4 in bytes)
txt.nl() txt.print_ub($0004 in words)
x=y=z=math.rnd()
txt.print_ub(x)
txt.nl()
txt.print_ub(y)
txt.nl()
txt.print_ub(z)
txt.nl()
} }
} }

View File

@@ -56,7 +56,7 @@ class IRFileReader {
val initGlobals = parseInitGlobals(reader) val initGlobals = parseInitGlobals(reader)
val blocks = parseBlocksUntilProgramEnd(reader) val blocks = parseBlocksUntilProgramEnd(reader)
val st = IRSymbolTable(null) val st = IRSymbolTable()
asmsymbols.forEach { (name, value) -> st.addAsmSymbol(name, value)} asmsymbols.forEach { (name, value) -> st.addAsmSymbol(name, value)}
varsWithoutInit.forEach { st.add(it) } varsWithoutInit.forEach { st.add(it) }
variables.forEach { st.add(it) } variables.forEach { st.add(it) }

View File

@@ -142,7 +142,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
if(irProgram.options.includeSourcelines) { if(irProgram.options.includeSourcelines) {
if(code.sourceLinesPositions.any {it !== Position.DUMMY}) { if(code.sourceLinesPositions.any {it !== Position.DUMMY}) {
xml.writeStartElement("P8SRC") xml.writeStartElement("P8SRC")
var sourceTxt = StringBuilder("\n") val sourceTxt = StringBuilder("\n")
code.sourceLinesPositions.forEach { pos -> code.sourceLinesPositions.forEach { pos ->
val line = SourceLineCache.retrieveLine(pos) val line = SourceLineCache.retrieveLine(pos)
if(line!=null) { 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}_lsb zp=${variable.zpwish}\n")
xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_msb zp=${variable.zpwish}\n") xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_msb zp=${variable.zpwish}\n")
} else { } else {
val typeStr = getTypeString(variable) xml.writeCharacters("${variable.typeString} ${variable.name} zp=${variable.zpwish}\n")
xml.writeCharacters("$typeStr ${variable.name} zp=${variable.zpwish}\n")
} }
} }
@@ -228,15 +227,15 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
lsbValue = "" lsbValue = ""
msbValue = "" msbValue = ""
} else { } else {
lsbValue = variable.onetimeInitializationArrayValue!!.joinToString(",") { lsbValue = variable.onetimeInitializationArrayValue.joinToString(",") {
if(it.number!=null) if(it.number!=null)
(it.number!!.toInt() and 255).toHex() (it.number.toInt() and 255).toHex()
else else
"@<${it.addressOfSymbol}" "@<${it.addressOfSymbol}"
} }
msbValue = variable.onetimeInitializationArrayValue!!.joinToString(",") { msbValue = variable.onetimeInitializationArrayValue.joinToString(",") {
if(it.number!=null) if(it.number!=null)
(it.number!!.toInt() shr 8).toHex() (it.number.toInt() shr 8).toHex()
else else
"@>${it.addressOfSymbol}" "@>${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}_lsb=$lsbValue zp=${variable.zpwish}\n")
xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_msb=$msbValue zp=${variable.zpwish}\n") xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_msb=$msbValue zp=${variable.zpwish}\n")
} else { } else {
val typeStr = getTypeString(variable)
val value: String = when(variable.dt) { val value: String = when(variable.dt) {
DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: "").toString() DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: "").toString()
in NumericDatatypes -> (variable.onetimeInitializationNumericValue?.toInt()?.toHex() ?: "").toString() in NumericDatatypes -> (variable.onetimeInitializationNumericValue?.toInt()?.toHex() ?: "").toString()
DataType.STR -> { 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() } encoded.joinToString(",") { it.toInt().toString() }
} }
DataType.ARRAY_F -> { DataType.ARRAY_F -> {
if(variable.onetimeInitializationArrayValue!=null) { if(variable.onetimeInitializationArrayValue!=null) {
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toString() } variable.onetimeInitializationArrayValue.joinToString(",") { it.number!!.toString() }
} else { } else {
"" // array will be zero'd out at program start "" // array will be zero'd out at program start
} }
} }
in ArrayDatatypes -> { in ArrayDatatypes -> {
if(variable.onetimeInitializationArrayValue!==null) { if(variable.onetimeInitializationArrayValue!==null) {
variable.onetimeInitializationArrayValue!!.joinToString(",") { variable.onetimeInitializationArrayValue.joinToString(",") {
if(it.number!=null) if(it.number!=null)
it.number!!.toInt().toHex() it.number.toInt().toHex()
else else
"@${it.addressOfSymbol}" "@${it.addressOfSymbol}"
} }
@@ -273,7 +271,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
} }
else -> throw InternalCompilerException("weird dt") 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() xml.writeEndElement()
@@ -282,8 +280,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
xml.writeStartElement("MEMORYMAPPEDVARIABLES") xml.writeStartElement("MEMORYMAPPEDVARIABLES")
xml.writeCharacters("\n") xml.writeCharacters("\n")
for (variable in irProgram.st.allMemMappedVariables()) { for (variable in irProgram.st.allMemMappedVariables()) {
val typeStr = getTypeString(variable) xml.writeCharacters("@${variable.typeString} ${variable.name}=${variable.address.toHex()}\n")
xml.writeCharacters("@$typeStr ${variable.name}=${variable.address.toHex()}\n")
} }
xml.writeEndElement() xml.writeEndElement()
xml.writeCharacters("\n") xml.writeCharacters("\n")

View File

@@ -1,88 +1,88 @@
package prog8.intermediate package prog8.intermediate
import prog8.code.* import prog8.code.*
import prog8.code.ast.PtVariable import prog8.code.core.*
import prog8.code.core.DataType
import prog8.code.core.ZeropageWish
import prog8.code.core.internedStringsModuleName
// In the Intermediate Representation, all nesting has been removed. // In the Intermediate Representation, all nesting has been removed.
// So the symbol table is just a big flat mapping of (scoped)name to node. // 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?) { class IRSymbolTable {
private val table = mutableMapOf<String, StNode>() private val table = mutableMapOf<String, IRStNode>()
private val asmSymbols = mutableMapOf<String, String>() private val asmSymbols = mutableMapOf<String, String>()
init { companion object {
if(sourceSt!=null) { fun fromStDuringCodegen(sourceSt: SymbolTable?): IRSymbolTable {
sourceSt.allVariables.forEach { val st = IRSymbolTable()
add(it) if (sourceSt != null) {
} sourceSt.allVariables.forEach {
sourceSt.allMemMappedVariables.forEach { st.add(it)
add(it) }
} sourceSt.allMemMappedVariables.forEach {
sourceSt.allMemorySlabs.forEach { st.add(it)
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 -> st.allVariables().forEach { variable ->
variable.onetimeInitializationArrayValue?.let { variable.onetimeInitializationArrayValue?.let {
it.forEach { arrayElt -> it.forEach { arrayElt ->
if(arrayElt.addressOfSymbol!=null) { if (arrayElt.addressOfSymbol != null) {
require(arrayElt.addressOfSymbol!!.contains('.')) { require(arrayElt.addressOfSymbol.contains('.')) {
"pointer var in array should be properly scoped: ${arrayElt.addressOfSymbol} in ${variable.name}" "pointer var in array should be properly scoped: ${arrayElt.addressOfSymbol} in ${variable.name}"
}
} }
} }
} }
} }
} }
return st
} }
} }
fun allVariables(): Sequence<StStaticVariable> = fun allVariables(): Sequence<IRStStaticVariable> =
table.asSequence().map { it.value }.filterIsInstance<StStaticVariable>() table.asSequence().map { it.value }.filterIsInstance<IRStStaticVariable>()
fun allMemMappedVariables(): Sequence<StMemVar> = fun allMemMappedVariables(): Sequence<IRStMemVar> =
table.asSequence().map { it.value }.filterIsInstance<StMemVar>() table.asSequence().map { it.value }.filterIsInstance<IRStMemVar>()
fun allMemorySlabs(): Sequence<StMemorySlab> = fun allMemorySlabs(): Sequence<IRStMemorySlab> =
table.asSequence().map { it.value }.filterIsInstance<StMemorySlab>() table.asSequence().map { it.value }.filterIsInstance<IRStMemorySlab>()
fun lookup(name: String) = table[name] fun lookup(name: String) = table[name]
fun add(variable: StStaticVariable) { fun add(variable: StStaticVariable) {
val scopedName: String val scopedName: String
val varToadd: StStaticVariable val varToadd: IRStStaticVariable
if('.' in variable.name) { if('.' in variable.name) {
scopedName = variable.name scopedName = variable.name
varToadd = variable varToadd = IRStStaticVariable.from(variable)
} else { } else {
fun fixupAddressOfInArray(array: List<StArrayElement>?): List<StArrayElement>? { fun fixupAddressOfInArray(array: List<StArrayElement>?): List<IRStArrayElement>? {
if(array==null) if(array==null)
return null return null
val newArray = mutableListOf<StArrayElement>() val newArray = mutableListOf<IRStArrayElement>()
array.forEach { array.forEach {
if(it.addressOfSymbol!=null) { if(it.addressOfSymbol!=null) {
val target = variable.lookup(it.addressOfSymbol!!)!! val target = variable.lookup(it.addressOfSymbol!!)!!
newArray.add(StArrayElement(null, target.scopedName)) newArray.add(IRStArrayElement(null, target.scopedName))
} else { } else {
newArray.add(it) newArray.add(IRStArrayElement.from(it))
} }
} }
return newArray return newArray
} }
scopedName = variable.scopedName scopedName = variable.scopedName
val dummyNode = PtVariable(scopedName, variable.dt, variable.zpwish, null, null, variable.astNode.position) varToadd = IRStStaticVariable(scopedName, variable.dt,
varToadd = StStaticVariable(scopedName, variable.dt,
variable.onetimeInitializationNumericValue, variable.onetimeInitializationNumericValue,
variable.onetimeInitializationStringValue, variable.onetimeInitializationStringValue,
fixupAddressOfInArray(variable.onetimeInitializationArrayValue), fixupAddressOfInArray(variable.onetimeInitializationArrayValue),
variable.length, variable.length,
variable.zpwish, variable.zpwish
dummyNode
) )
} }
table[scopedName] = varToadd table[scopedName] = varToadd
@@ -91,27 +91,26 @@ class IRSymbolTable(sourceSt: SymbolTable?) {
fun add(variable: StMemVar) { fun add(variable: StMemVar) {
val scopedName: String val scopedName: String
val varToadd: StMemVar val varToadd: IRStMemVar
if('.' in variable.name) { if('.' in variable.name) {
scopedName = variable.name scopedName = variable.name
varToadd = variable varToadd = IRStMemVar.from(variable)
} else { } else {
scopedName = try { scopedName = try {
variable.scopedName variable.scopedName
} catch (ux: UninitializedPropertyAccessException) { } catch (ux: UninitializedPropertyAccessException) {
variable.name 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 table[scopedName] = varToadd
} }
fun add(variable: StMemorySlab) { fun add(variable: StMemorySlab) {
val varToadd = if('.' in variable.name) val varToadd = if('.' in variable.name)
variable IRStMemorySlab.from(variable)
else { else {
val dummyNode = PtVariable(variable.name, DataType.ARRAY_UB, ZeropageWish.NOT_IN_ZEROPAGE, null, null, variable.astNode.position) IRStMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align)
StMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align, dummyNode)
} }
table[varToadd.name] = varToadd 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<String, StNode> = 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<IRStArrayElement>
typealias IRStString = Pair<String, Encoding>

View File

@@ -1,7 +1,9 @@
package prog8.intermediate package prog8.intermediate
import prog8.code.* import prog8.code.Either
import prog8.code.core.* import prog8.code.core.*
import prog8.code.left
import prog8.code.right
fun getTypeString(dt : DataType): String = when(dt) { fun getTypeString(dt : DataType): String = when(dt) {
@@ -20,38 +22,6 @@ fun getTypeString(dt : DataType): String = when(dt) {
else -> throw InternalCompilerException("weird 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? { fun convertIRType(typestr: String): IRDataType? {
return when(typestr.lowercase()) { return when(typestr.lowercase()) {
"" -> null "" -> null

View File

@@ -1,16 +1,12 @@
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeGreaterThan import io.kotest.matchers.ints.shouldBeGreaterThan
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import prog8.code.StStaticVariable
import prog8.code.core.CbmPrgLauncherType import prog8.code.core.CbmPrgLauncherType
import prog8.code.core.CompilationOptions import prog8.code.core.CompilationOptions
import prog8.code.core.OutputType import prog8.code.core.OutputType
import prog8.code.core.ZeropageType import prog8.code.core.ZeropageType
import prog8.code.target.Cx16Target import prog8.code.target.Cx16Target
import prog8.intermediate.IRFileReader import prog8.intermediate.*
import prog8.intermediate.IRFileWriter
import prog8.intermediate.IRProgram
import prog8.intermediate.IRSymbolTable
import kotlin.io.path.* import kotlin.io.path.*
class TestIRFileInOut: FunSpec({ class TestIRFileInOut: FunSpec({
@@ -29,7 +25,7 @@ class TestIRFileInOut: FunSpec({
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS, loadAddress = target.machine.PROGRAM_LOAD_ADDRESS,
outputDir = tempdir 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 writer = IRFileWriter(program, null)
val generatedFile = writer.write() val generatedFile = writer.write()
val lines = generatedFile.readLines() val lines = generatedFile.readLines()
@@ -107,9 +103,9 @@ return
program.name shouldBe "test-ir-reader" program.name shouldBe "test-ir-reader"
program.blocks.size shouldBe 2 program.blocks.size shouldBe 2
program.st.allVariables().count() shouldBe 3 program.st.allVariables().count() shouldBe 3
val var1 = program.st.lookup("sys.wait.jiffies") as StStaticVariable val var1 = program.st.lookup("sys.wait.jiffies") as IRStStaticVariable
val var2 = program.st.lookup("sys.bssvar") as StStaticVariable val var2 = program.st.lookup("sys.bssvar") as IRStStaticVariable
val var3 = program.st.lookup("sys.emptystring") as StStaticVariable val var3 = program.st.lookup("sys.emptystring") as IRStStaticVariable
var1.uninitialized shouldBe false var1.uninitialized shouldBe false
var2.uninitialized shouldBe true var2.uninitialized shouldBe true
var3.uninitialized shouldBe true var3.uninitialized shouldBe true

View File

@@ -1,8 +1,5 @@
package prog8.vm package prog8.vm
import prog8.code.StArray
import prog8.code.StArrayElement
import prog8.code.StStaticVariable
import prog8.code.core.ArrayDatatypes import prog8.code.core.ArrayDatatypes
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
import prog8.code.core.DataType import prog8.code.core.DataType
@@ -247,7 +244,7 @@ class VmProgramLoader {
if(iElts.isEmpty() || iElts.size==1) { if(iElts.isEmpty() || iElts.size==1) {
val iElt = if(iElts.isEmpty()) { val iElt = if(iElts.isEmpty()) {
require(variable.uninitialized) require(variable.uninitialized)
StArrayElement(0.0, null) IRStArrayElement(0.0, null)
} else { } else {
require(!variable.uninitialized) require(!variable.uninitialized)
iElts[0] iElts[0]
@@ -262,8 +259,8 @@ class VmProgramLoader {
} }
private fun initializeWithValues( private fun initializeWithValues(
variable: StStaticVariable, variable: IRStStaticVariable,
iElts: StArray, iElts: IRStArray,
startAddress: Int, startAddress: Int,
symbolAddresses: MutableMap<String, Int>, symbolAddresses: MutableMap<String, Int>,
memory: Memory, memory: Memory,
@@ -325,8 +322,8 @@ class VmProgramLoader {
} }
private fun initializeWithOneValue( private fun initializeWithOneValue(
variable: StStaticVariable, variable: IRStStaticVariable,
iElt: StArrayElement, iElt: IRStArrayElement,
startAddress: Int, startAddress: Int,
symbolAddresses: MutableMap<String, Int>, symbolAddresses: MutableMap<String, Int>,
memory: Memory, memory: Memory,
@@ -389,7 +386,7 @@ class VmProgramLoader {
} }
} }
private fun getInitializerValue(arrayDt: DataType, elt: StArrayElement, symbolAddresses: MutableMap<String, Int>): Double { private fun getInitializerValue(arrayDt: DataType, elt: IRStArrayElement, symbolAddresses: MutableMap<String, Int>): Double {
if(elt.addressOfSymbol!=null) { if(elt.addressOfSymbol!=null) {
when(arrayDt) { when(arrayDt) {
DataType.ARRAY_UB, DataType.STR, DataType.ARRAY_B, DataType.ARRAY_BOOL -> { DataType.ARRAY_UB, DataType.STR, DataType.ARRAY_B, DataType.ARRAY_BOOL -> {

View File

@@ -29,7 +29,7 @@ class TestVm: FunSpec( {
} }
test("vm execution: empty program") { test("vm execution: empty program") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) val program = IRProgram("test", IRSymbolTable(), getTestOptions(), VMTarget())
val vm = VirtualMachine(program) val vm = VirtualMachine(program)
vm.callStack.shouldBeEmpty() vm.callStack.shouldBeEmpty()
vm.valueStack.shouldBeEmpty() vm.valueStack.shouldBeEmpty()
@@ -43,7 +43,7 @@ class TestVm: FunSpec( {
} }
test("vm execution: modify memory") { 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 block = IRBlock("testmain", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY) val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
val code = IRCodeChunk(startSub.label, null) val code = IRCodeChunk(startSub.label, null)
@@ -72,7 +72,7 @@ class TestVm: FunSpec( {
} }
test("asmsub not supported in vm even with IR") { 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 block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRAsmSubroutine( val startSub = IRAsmSubroutine(
"main.asmstart", "main.asmstart",