optimizing scoped names more and fix scoping of identifier names in arrays (pointers) in SymbolTable

This commit is contained in:
Irmen de Jong 2022-12-30 19:01:00 +01:00
parent e0913a39ab
commit 8e730ef93d
13 changed files with 73 additions and 57 deletions

View File

@ -18,7 +18,7 @@ class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
val flat: Map<List<String>, StNode> by lazy {
val result = mutableMapOf<List<String>, StNode>()
fun flatten(node: StNode) {
result[node.scopedName] = node
result[node.scopedName.split('.')] = node
node.children.values.forEach { flatten(it) }
}
children.values.forEach { flatten(it) }
@ -57,7 +57,7 @@ class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
children.mapNotNull { if (it.value.type == StNodeType.MEMORYSLAB) it.value as StMemorySlab else null }
}
override fun lookup(scopedName: List<String>) = flat[scopedName]
override fun lookup(scopedName: String) = flat[scopedName.split('.')] // TODO dotted string as keys
}
@ -84,23 +84,27 @@ open class StNode(val name: String,
lateinit var parent: StNode
val scopedName: List<String> by lazy {
if(type== StNodeType.GLOBAL)
emptyList()
else
parent.scopedName + name
val scopedName: String by lazy {
scopedNameList.joinToString(".")
}
open fun lookup(scopedName: List<String>) =
if(scopedName.size>1) lookupQualified(scopedName) else lookupUnqualified(scopedName[0])
open fun lookup(scopedName: String) =
lookup(scopedName.split('.'))
fun lookupUnqualifiedOrElse(name: String, default: () -> StNode) =
lookupUnqualified(name) ?: default()
fun lookupUnqualifiedOrElse(scopedName: List<String>, default: () -> StNode) =
fun lookupQualifiedOrElse(scopedName: List<String>, default: () -> StNode) =
lookup(scopedName) ?: default()
private fun lookupQualified(scopedName: List<String>): StNode? {
private val scopedNameList: List<String> by lazy {
if(type== StNodeType.GLOBAL)
emptyList()
else
parent.scopedNameList + name
}
private fun lookup(scopedName: List<String>): StNode? {
// a scoped name refers to a name in another namespace, and always stars from the root.
var node = this
while(node.type!= StNodeType.GLOBAL)
@ -218,7 +222,11 @@ class StRomSub(name: String,
class StSubroutineParameter(val name: String, val type: DataType)
class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType)
class StArrayElement(val number: Double?, val addressOfSymbol: String?)
class StArrayElement(val number: Double?, val addressOfSymbol: String?) {
init {
require(addressOfSymbol==null || addressOfSymbol.contains('.'))
}
}
typealias StString = Pair<String, Encoding>
typealias StArray = List<StArrayElement>

View File

@ -286,7 +286,7 @@ internal class ProgramAndVarsGen(
// regular subroutine
asmgen.out("${sub.name}\t$asmStartScope")
val scope = symboltable.lookupUnqualifiedOrElse(sub.scopedName) { throw AssemblyError("lookup") }
val scope = symboltable.lookupQualifiedOrElse(sub.scopedName) { throw AssemblyError("lookup") }
require(scope.type==StNodeType.SUBROUTINE)
val varsInSubroutine = getVars(scope)
@ -461,8 +461,9 @@ internal class ProgramAndVarsGen(
return result
}
private fun zeropagevars2asm(varNames: Set<List<String>>) {
val zpVariables = allocator.zeropageVars.filter { it.key in varNames }
private fun zeropagevars2asm(varNames2: Set<String>) {
val varNamesAsList = varNames2.map { it.split('.') } // TODO use dotted string
val zpVariables = allocator.zeropageVars.filter { it.key in varNamesAsList }
for ((scopedName, zpvar) in zpVariables) {
if (scopedName.size == 2 && scopedName[0] == "cx16" && scopedName[1][0] == 'r' && scopedName[1][1].isDigit())
continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped

View File

@ -22,7 +22,8 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
allocateZeropageVariables()
}
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropage.allocatedVariables
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropage.allocatedVariables // TODO remove, use dotted string
internal fun isZpVar(scopedName: String) = scopedName.split('.') in zeropage.allocatedVariables
internal fun getFloatAsmConst(number: Double): String {
val asmName = globalFloatConsts[number]
@ -56,7 +57,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
varsRequiringZp.forEach { variable ->
val result = zeropage.allocate(
variable.scopedName,
variable.scopedName.split('.'), // TODO use dotted name
variable.dt,
variable.length,
variable.position,
@ -75,7 +76,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
if(errors.noErrors()) {
varsPreferringZp.forEach { variable ->
val result = zeropage.allocate(
variable.scopedName,
variable.scopedName.split('.'), // TODO use dotted name
variable.dt,
variable.length,
variable.position,
@ -88,13 +89,13 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
// try to allocate any other interger variables into the zeropage until it is full.
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
if(errors.noErrors()) {
for (variable in varsDontCare.sortedBy { it.scopedName.size }) {
for (variable in varsDontCare.sortedBy { it.scopedName.length }) {
if(variable.dt in IntegerDatatypes) {
if(zeropage.free.isEmpty()) {
break
} else {
val result = zeropage.allocate(
variable.scopedName,
variable.scopedName.split('.'), // TODO use dotted name,
variable.dt,
variable.length,
variable.position,

View File

@ -454,7 +454,7 @@ class IRCodeGen(
}
private fun translate(forLoop: PtForLoop): IRCodeChunks {
val loopvar = symbolTable.lookup(forLoop.variable.name.split('.'))!!
val loopvar = symbolTable.lookup(forLoop.variable.name)!!
val iterable = forLoop.iterable
val result = mutableListOf<IRCodeChunkBase>()
when(iterable) {
@ -465,8 +465,8 @@ class IRCodeGen(
result += translateForInNonConstantRange(forLoop, loopvar)
}
is PtIdentifier -> {
val iterableVar = symbolTable.lookup(iterable.name.split('.')) as StStaticVariable
val loopvarSymbol = loopvar.scopedName.joinToString(".")
val iterableVar = symbolTable.lookup(iterable.name) as StStaticVariable
val loopvarSymbol = loopvar.scopedName
val indexReg = registers.nextFree()
val tmpReg = registers.nextFree()
val loopLabel = createLabelName()
@ -529,7 +529,7 @@ class IRCodeGen(
throw AssemblyError("step 0")
val indexReg = registers.nextFree()
val endvalueReg = registers.nextFree()
val loopvarSymbol = loopvar.scopedName.joinToString(".")
val loopvarSymbol = loopvar.scopedName
val loopvarDt = when(loopvar) {
is StMemVar -> loopvar.dt
is StStaticVariable -> loopvar.dt
@ -560,7 +560,7 @@ class IRCodeGen(
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StNode): IRCodeChunks {
val loopLabel = createLabelName()
val loopvarSymbol = loopvar.scopedName.joinToString(".")
val loopvarSymbol = loopvar.scopedName
val indexReg = registers.nextFree()
val loopvarDt = when(loopvar) {
is StMemVar -> loopvar.dt

View File

@ -392,7 +392,7 @@ private fun createAssemblyAndAssemble(program: Program,
compilerOptions.compTarget.machine.initializeMemoryAreas(compilerOptions)
program.processAstBeforeAsmGeneration(compilerOptions, errors)
errors.report()
val symbolTable = SymbolTableMaker().makeFrom(program, compilerOptions)
val symbolTable = SymbolTableMaker(program, compilerOptions).make()
// TODO make removing all VarDecls work, but this needs inferType to be able to get its information from somewhere else as the VarDecl nodes in the Ast,
// or don't use inferType at all anymore and "bake the type information" into the Ast somehow.

View File

@ -111,18 +111,8 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable
return target
}
private fun targetOf(identifier: IdentifierReference): Pair<String, DataType> {
val target=identifier.targetStatement(program)!! as INamedStatement
val targetname: String = if(target.name in program.builtinFunctions.names)
"<builtin>.${target.name}"
else
target.scopedName.joinToString(".")
val type = identifier.inferType(program).getOr(DataType.UNDEFINED)
return Pair(targetname, type)
}
private fun transform(identifier: IdentifierReference): PtIdentifier {
val (target, type) = targetOf(identifier)
val (target, type) = identifier.targetNameAndType(program)
return PtIdentifier(target, type, identifier.position)
}
@ -220,7 +210,7 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable
}
private fun transform(srcCall: FunctionCallStatement): PtFunctionCall {
val (target, type) = targetOf(srcCall.target)
val (target, type) = srcCall.target.targetNameAndType(program)
val call = PtFunctionCall(target,true, type, srcCall.position)
for (arg in srcCall.args)
call.add(transformExpression(arg))
@ -228,7 +218,7 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable
}
private fun transform(srcCall: FunctionCallExpression): PtFunctionCall {
val (target, _) = targetOf(srcCall.target)
val (target, _) = srcCall.target.targetNameAndType(program)
val type = srcCall.inferType(program).getOrElse {
throw FatalAstException("unknown dt $srcCall")
}

View File

@ -12,13 +12,13 @@ import prog8.code.core.DataType
import prog8.code.core.Position
import java.util.*
internal class SymbolTableMaker: IAstVisitor {
internal class SymbolTableMaker(private val program: Program, private val options: CompilationOptions): IAstVisitor {
private val st = SymbolTable()
private val scopestack = Stack<StNode>()
private var dontReinitGlobals = false
fun makeFrom(program: Program, options: CompilationOptions): SymbolTable {
fun make(): SymbolTable {
scopestack.clear()
st.children.clear()
dontReinitGlobals = options.dontReinitGlobals
@ -104,8 +104,14 @@ internal class SymbolTableMaker: IAstVisitor {
return null
return arrayLit.value.map {
when(it){
is AddressOf -> StArrayElement(null, it.identifier.nameInSource.joinToString("."))
is IdentifierReference -> StArrayElement(null, it.nameInSource.joinToString("."))
is AddressOf -> {
val scopedName = it.identifier.targetNameAndType(program).first
StArrayElement(null, scopedName)
}
is IdentifierReference -> {
val scopedName = it.targetNameAndType(program).first
StArrayElement(null, scopedName)
}
is NumericLiteral -> StArrayElement(it.number, null)
else -> throw FatalAstException("weird element dt in array literal")
}

View File

@ -12,7 +12,7 @@ import prog8.code.core.ZeropageWish
class TestSymbolTable: FunSpec({
test("empty symboltable") {
val st = SymbolTable()
st.scopedName shouldBe emptyList()
st.scopedName shouldBe ""
st.name shouldBe ""
st.type shouldBe StNodeType.GLOBAL
st.children shouldBe mutableMapOf()
@ -31,25 +31,25 @@ class TestSymbolTable: FunSpec({
test("symboltable global lookups") {
val st = makeSt()
st.lookupUnqualified("undefined") shouldBe null
st.lookup(listOf("undefined")) shouldBe null
st.lookup("undefined") shouldBe null
var default = st.lookupUnqualifiedOrElse("undefined") { StNode("default", StNodeType.LABEL, Position.DUMMY) }
default.name shouldBe "default"
default = st.lookupUnqualifiedOrElse(listOf("undefined")) { StNode("default", StNodeType.LABEL, Position.DUMMY) }
default = st.lookupUnqualifiedOrElse("undefined") { StNode("default", StNodeType.LABEL, Position.DUMMY) }
default.name shouldBe "default"
val msbFunc = st.lookupUnqualifiedOrElse("msb") { fail("msb must be found") }
msbFunc.type shouldBe StNodeType.BUILTINFUNC
val variable = st.lookupUnqualifiedOrElse(listOf("block1", "sub2", "v2")) { fail("v2 must be found") }
val variable = st.lookupQualifiedOrElse(listOf("block1", "sub2", "v2")) { fail("v2 must be found") }
variable.type shouldBe StNodeType.STATICVAR
}
test("symboltable nested lookups") {
val st = makeSt()
val sub1 = st.lookupUnqualifiedOrElse(listOf("block1", "sub1")) { fail("should find sub1") }
val sub1 = st.lookupQualifiedOrElse(listOf("block1", "sub1")) { fail("should find sub1") }
sub1.name shouldBe "sub1"
sub1.scopedName shouldBe listOf("block1", "sub1")
sub1.scopedName shouldBe "block1.sub1"
sub1.type shouldBe StNodeType.SUBROUTINE
sub1.children.size shouldBe 2
@ -62,7 +62,7 @@ class TestSymbolTable: FunSpec({
blockc.type shouldBe StNodeType.CONSTANT
blockc.value shouldBe 999.0
val subsub = st.lookupUnqualifiedOrElse(listOf("block2", "sub2", "subsub")) { fail("should find subsub") }
val subsub = st.lookupQualifiedOrElse(listOf("block2", "sub2", "subsub")) { fail("should find subsub") }
subsub.lookupUnqualified("blockc") shouldBe null
subsub.lookupUnqualified("label") shouldNotBe null
}

View File

@ -38,7 +38,7 @@ class TestIntermediateAst: FunSpec({
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
)
val result = compileText(target, false, text, writeAssembly = false)!!
val st = SymbolTableMaker().makeFrom(result.program, options)
val st = SymbolTableMaker(result.program, options).make()
val ast = IntermediateAstMaker(result.program, st, options).transform()
ast.name shouldBe result.program.name
ast.allBlocks().any() shouldBe true

View File

@ -72,7 +72,7 @@ class TestAsmGenSymbols: StringSpec({
val errors = ErrorReporterForTests()
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target(), 999u)
options.compTarget.machine.zeropage = C64Zeropage(options)
val st = SymbolTableMaker().makeFrom(program, options)
val st = SymbolTableMaker(program, options).make()
return AsmGen(program, st, options, errors)
}

View File

@ -914,6 +914,16 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
fun targetVarDecl(program: Program): VarDecl? = targetStatement(program) as? VarDecl
fun targetSubroutine(program: Program): Subroutine? = targetStatement(program) as? Subroutine
fun targetNameAndType(program: Program): Pair<String, DataType> {
val target=targetStatement(program)!! as INamedStatement
val targetname: String = if(target.name in program.builtinFunctions.names)
"<builtin>.${target.name}"
else
target.scopedName.joinToString(".")
val type = inferType(program).getOr(DataType.UNDEFINED)
return Pair(targetname, type)
}
override fun linkParents(parent: Node) {
this.parent = parent
}

View File

@ -3,7 +3,7 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- optimize scoped symbols: .split('.') / .joinToString(".")
- optimize scoped symbols: .split('.') / .joinToString(".") / 'dotted string' comments
...

View File

@ -62,15 +62,15 @@ class IRSymbolTable(sourceSt: SymbolTable?) {
val newArray = mutableListOf<StArrayElement>()
array.forEach {
if(it.addressOfSymbol!=null) {
val target = variable.lookup(it.addressOfSymbol!!.split('.'))!!
newArray.add(StArrayElement(null, target.scopedName.joinToString(".")))
val target = variable.lookup(it.addressOfSymbol!!)!!
newArray.add(StArrayElement(null, target.scopedName))
} else {
newArray.add(it)
}
}
return newArray
}
scopedName = variable.scopedName.joinToString(".")
scopedName = variable.scopedName
varToadd = StStaticVariable(scopedName, variable.dt, variable.bss,
variable.onetimeInitializationNumericValue,
variable.onetimeInitializationStringValue,
@ -91,7 +91,7 @@ class IRSymbolTable(sourceSt: SymbolTable?) {
scopedName = variable.name
varToadd = variable
} else {
scopedName = variable.scopedName.joinToString(".")
scopedName = variable.scopedName
varToadd = StMemVar(scopedName, variable.dt, variable.address, variable.length, variable.position)
}
table[scopedName] = varToadd