optimizing new Ast

This commit is contained in:
Irmen de Jong 2022-03-13 02:43:51 +01:00
parent 4a0031080a
commit 9b81955544
11 changed files with 212 additions and 110 deletions

View File

@ -1,10 +1,6 @@
package prog8.code
import prog8.code.core.DataType
import prog8.code.core.Encoding
import prog8.code.core.Position
import prog8.code.core.ZeropageWish
import prog8.code.core.*
/**
@ -51,6 +47,7 @@ enum class StNodeType {
// MODULE, // not used with current scoping rules
BLOCK,
SUBROUTINE,
ROMSUB,
LABEL,
STATICVAR,
MEMVAR,
@ -131,6 +128,7 @@ open class StNode(val name: String,
StNodeType.MEMVAR -> print("(M) ")
StNodeType.CONSTANT -> print("(C) ")
StNodeType.BUILTINFUNC -> print("(F) ")
StNodeType.ROMSUB -> print("(R) ")
}
printProperties()
println()
@ -153,7 +151,7 @@ class StStaticVariable(name: String,
val initialStringValue: StString?,
val initialArrayValue: StArray?,
val arraysize: Int?,
val zpw: ZeropageWish,
val zpwish: ZeropageWish,
position: Position) : StNode(name, StNodeType.STATICVAR, position) {
init {
@ -164,7 +162,7 @@ class StStaticVariable(name: String,
}
override fun printProperties() {
print("$name dt=$dt zpw=$zpw")
print("$name dt=$dt zpw=$zpwish")
}
}
@ -177,14 +175,20 @@ class StConstant(name: String, val dt: DataType, val value: Double, position: Po
}
class StMemVar(name: String, val dt: DataType, val address: UInt, position: Position) :
StNode(name, StNodeType.MEMVAR, position
) {
StNode(name, StNodeType.MEMVAR, position) {
override fun printProperties() {
print("$name dt=$dt address=${address.toString(16).padStart(4,'0')}")
print("$name dt=$dt address=${address.toHex()}")
}
}
class StRomSub(name: String, val address: UInt, position: Position) :
StNode(name, StNodeType.ROMSUB, position) {
override fun printProperties() {
print("$name address=${address.toHex()}")
}
}
class StArrayElement(val number: Double?, val addressOf: List<String>?)
typealias StString = Pair<String, Encoding>

View File

@ -51,16 +51,19 @@ abstract class PtNamedNode(val name: String, position: Position): PtNode(positio
}
// TODO remove duplicates that are also in CompilationOptions (that is already passed into the AsmGen already)
// TODO make sure loadaddress is always set to a sensible value (not 0) see determineProgramLoadAddress()
class ProgramOptions(
val output: OutputType,
val launcher: CbmPrgLauncherType,
val zeropage: ZeropageType,
val zpReserved: Collection<UIntRange>,
val loadAddress: UInt?,
val loadAddress: UInt,
val floatsEnabled: Boolean,
val noSysInit: Boolean,
val dontReinitGlobals: Boolean,
val optimize: Boolean
val optimize: Boolean,
val target: String
)

View File

@ -156,14 +156,14 @@ class PtReturn(position: Position) : PtNode(position) {
}
class PtVariable(name: String, val type: DataType, var value: PtExpression?, position: Position) : PtNamedNode(name, position) {
class PtVariable(name: String, val type: DataType, var value: PtExpression?, var arraySize: UInt?, position: Position) : PtNamedNode(name, position) {
override fun printProperties() {
print("$type $name")
}
}
class PtConstant(val name: String, val type: DataType, val value: Double, position: Position) : PtNode(position) {
class PtConstant(name: String, val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position) {
override fun printProperties() {
print("$type $name = $value")
}

View File

@ -57,6 +57,7 @@ internal class ProgramAndVarsGen(
CpuType.CPU65c02 -> "w65c02"
else -> "unsupported"
}
determineProgramLoadAddress(program, errors)
asmgen.out("; $cpu assembly code for '${program.name}'")
asmgen.out("; generated by $ourName on ${LocalDateTime.now().withNano(0)}")
@ -65,32 +66,6 @@ internal class ProgramAndVarsGen(
asmgen.out("")
asmgen.out(".cpu '$cpu'\n.enc 'none'\n")
program.actualLoadAddress = program.definedLoadAddress ?: 0u
if (program.actualLoadAddress == 0u) {
when(options.output) {
OutputType.RAW -> {
errors.err("load address must be specified with %address when using raw output type", program.toplevelModule.position)
return
}
OutputType.PRG -> {
when(options.launcher) {
CbmPrgLauncherType.BASIC -> {
program.actualLoadAddress = compTarget.machine.PROGRAM_LOAD_ADDRESS
}
CbmPrgLauncherType.NONE -> {
errors.err("load address must be specified with %address when not using basic launcher", program.toplevelModule.position)
return
}
}
}
OutputType.XEX -> {
if(options.launcher!=CbmPrgLauncherType.NONE)
throw AssemblyError("atari xex output can't contain BASIC launcher")
program.actualLoadAddress = compTarget.machine.PROGRAM_LOAD_ADDRESS
}
}
}
// the global prog8 variables needed
val zp = zeropage
asmgen.out("P8ZP_SCRATCH_B1 = ${zp.SCRATCH_B1}")
@ -161,6 +136,35 @@ internal class ProgramAndVarsGen(
}
}
// TODO do this in the compiler, not in the code generator. Maybe it needs to be part of ICompilationTarget?
private fun determineProgramLoadAddress(program: Program, errors: IErrorReporter) {
program.actualLoadAddress = program.definedLoadAddress ?: 0u
if (program.actualLoadAddress == 0u) {
when(options.output) {
OutputType.RAW -> {
errors.err("load address must be specified with %address when using raw output type", program.toplevelModule.position)
return
}
OutputType.PRG -> {
when(options.launcher) {
CbmPrgLauncherType.BASIC -> {
program.actualLoadAddress = compTarget.machine.PROGRAM_LOAD_ADDRESS
}
CbmPrgLauncherType.NONE -> {
errors.err("load address must be specified with %address when not using basic launcher", program.toplevelModule.position)
return
}
}
}
OutputType.XEX -> {
if(options.launcher!=CbmPrgLauncherType.NONE)
throw AssemblyError("atari xex output can't contain BASIC launcher")
program.actualLoadAddress = compTarget.machine.PROGRAM_LOAD_ADDRESS
}
}
}
}
private fun memorySlabs() {
asmgen.out("; memory slabs")
asmgen.out("prog8_slabs\t.block")

View File

@ -53,10 +53,10 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
val allVariables = collectAllVariables(symboltable)
val numberOfAllocatableVariables = allVariables.size
val varsRequiringZp = allVariables.filter { it.zpw == ZeropageWish.REQUIRE_ZEROPAGE }
val varsPreferringZp = allVariables.filter { it.zpw == ZeropageWish.PREFER_ZEROPAGE }
val varsDontCare = allVariables.filter { it.zpw == ZeropageWish.DONTCARE }
val numberOfExplicitNonZpVariables = allVariables.count { it.zpw == ZeropageWish.NOT_IN_ZEROPAGE }
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
val varsDontCare = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }
val numberOfExplicitNonZpVariables = allVariables.count { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
require(varsDontCare.size + varsRequiringZp.size + varsPreferringZp.size + numberOfExplicitNonZpVariables == numberOfAllocatableVariables)
var numVariablesAllocatedInZP = 0

View File

@ -1,6 +1,6 @@
package prog8.codegen.experimental
import prog8.code.SymbolTable
import prog8.code.*
import prog8.code.ast.*
import prog8.code.core.*
import javax.xml.stream.XMLOutputFactory
@ -39,6 +39,7 @@ class AsmGen(internal val program: PtProgram,
xml.startChildren()
write(program.options)
program.children.forEach { writeNode(it) }
writeSymboltable(symbolTable)
xml.endElt()
xml.endDoc()
xml.close()
@ -46,13 +47,96 @@ class AsmGen(internal val program: PtProgram,
return AssemblyProgram("dummy")
}
private fun writeSymboltable(st: SymbolTable) {
xml.elt("symboltable")
xml.startChildren()
st.flat.forEach{ (name, entry) ->
xml.elt("entry")
xml.attr("name", name.joinToString("."))
xml.attr("type", entry.type.name)
xml.startChildren()
writeStNode(entry)
xml.endElt()
}
xml.endElt()
}
private fun writeStNode(node: StNode) {
when(node.type) {
StNodeType.GLOBAL,
StNodeType.LABEL,
StNodeType.BLOCK,
StNodeType.BUILTINFUNC,
StNodeType.SUBROUTINE -> {/* no additional info*/}
StNodeType.ROMSUB -> {
node as StRomSub
xml.elt("romsub")
xml.attr("address", node.address.toString())
xml.endElt()
}
StNodeType.STATICVAR -> {
node as StStaticVariable
xml.elt("var")
xml.attr("type", node.dt.name)
xml.attr("zpwish", node.zpwish.name)
if(node.arraysize!=null)
xml.attr("arraysize", node.arraysize.toString())
if(node.initialNumericValue!=null || node.initialArrayValue!=null || node.initialStringValue!=null) {
xml.startChildren()
if(node.initialNumericValue!=null) {
writeNumber(node.dt, node.initialNumericValue!!)
}
if(node.initialStringValue!=null) {
xml.writeTextNode(
"string",
listOf(Pair("encoding", node.initialStringValue!!.second.name)),
node.initialStringValue!!.first,
false
)
}
if(node.initialArrayValue!=null) {
xml.elt("array")
xml.startChildren()
val eltDt = ArrayToElementTypes.getValue(node.dt)
node.initialArrayValue!!.forEach {
if(it.number!=null) {
writeNumber(eltDt, it.number!!)
}
if(it.addressOf!=null) {
xml.elt("addressof")
xml.attr("symbol", it.addressOf!!.joinToString("."))
xml.endElt()
}
}
xml.endElt()
}
}
xml.endElt()
}
StNodeType.MEMVAR -> {
node as StMemVar
xml.writeTextNode("memvar",
listOf(Pair("type", node.dt.name)),
node.address.toString(),
false)
}
StNodeType.CONSTANT -> {
node as StConstant
xml.writeTextNode("const",
listOf(Pair("type", node.dt.name)),
intOrDouble(node.dt, node.value).toString(),
false)
}
}
}
private fun write(options: ProgramOptions) {
xml.elt("options")
xml.attr("target", options.target)
xml.attr("output", options.output.name)
xml.attr("launcher", options.launcher.name)
xml.attr("zeropage", options.zeropage.name)
if(options.loadAddress!=null)
xml.attr("loadaddress", options.loadAddress.toString())
xml.attr("loadaddress", options.loadAddress.toString())
xml.attr("floatsenabled", options.floatsEnabled.toString())
xml.attr("nosysinit", options.noSysInit.toString())
xml.attr("dontreinitglobals", options.dontReinitGlobals.toString())
@ -115,7 +199,6 @@ class AsmGen(internal val program: PtProgram,
private fun write(breakPt: PtBreakpoint) {
xml.elt("breakpoint")
xml.startChildren()
xml.pos(breakPt.position)
xml.endElt()
}
@ -149,12 +232,12 @@ class AsmGen(internal val program: PtProgram,
}
private fun write(string: PtString) =
xml.writeTextNode("string", listOf(Pair("encoding", string.encoding.name)), string.value)
xml.writeTextNode("string", listOf(Pair("encoding", string.encoding.name)), string.value, false)
private fun write(rept: PtRepeatLoop) {
xml.elt("repeat")
xml.startChildren()
xml.pos(rept.position)
xml.startChildren()
xml.elt("count")
xml.startChildren()
writeNode(rept.count)
@ -168,8 +251,8 @@ class AsmGen(internal val program: PtProgram,
private fun write(branch: PtConditionalBranch) {
xml.elt("conditionalbranch")
xml.attr("condition", branch.condition.name)
xml.startChildren()
xml.pos(branch.position)
xml.startChildren()
xml.elt("true")
xml.startChildren()
writeNode(branch.trueScope)
@ -219,9 +302,9 @@ class AsmGen(internal val program: PtProgram,
private fun write(forLoop: PtForLoop) {
xml.elt("for")
xml.attr("var", strTargetName(forLoop.variable))
xml.startChildren()
xml.attr("loopvar", strTargetName(forLoop.variable))
xml.pos(forLoop.position)
xml.startChildren()
xml.elt("iterable")
xml.startChildren()
writeNode(forLoop.iterable)
@ -246,8 +329,8 @@ class AsmGen(internal val program: PtProgram,
private fun write(whenStmt: PtWhen) {
xml.elt("when")
xml.startChildren()
xml.pos(whenStmt.position)
xml.startChildren()
xml.elt("value")
xml.startChildren()
writeNode(whenStmt.value)
@ -280,8 +363,8 @@ class AsmGen(internal val program: PtProgram,
private fun write(inlineAsm: PtInlineAssembly) {
xml.elt("assembly")
xml.startChildren()
xml.pos(inlineAsm.position)
xml.startChildren()
xml.writeTextNode("code", emptyList(), inlineAsm.assembly)
xml.endElt()
}
@ -293,7 +376,6 @@ class AsmGen(internal val program: PtProgram,
xml.attr("offset", inlineBinary.offset!!.toString())
if(inlineBinary.length!=null)
xml.attr("length", inlineBinary.length!!.toString())
xml.startChildren()
xml.pos(inlineBinary.position)
xml.endElt()
}
@ -338,9 +420,8 @@ class AsmGen(internal val program: PtProgram,
}
private fun write(addrof: PtAddressOf) {
xml.elt("addrof")
xml.startChildren()
write(addrof.identifier)
xml.elt("addressof")
xml.attr("symbol", strTargetName(addrof.identifier))
xml.endElt()
}
@ -351,18 +432,16 @@ class AsmGen(internal val program: PtProgram,
xml.attr("type", "VOID")
else
xml.attr("type", fcall.type.name)
xml.startChildren()
xml.pos(fcall.position)
xml.startChildren()
fcall.children.forEach { writeNode(it) }
xml.endElt()
}
private fun write(number: PtNumber) {
xml.elt("number")
xml.attr("type", number.type.name)
xml.attr("value", intOrDouble(number.type, number.number).toString())
xml.endElt()
}
private fun write(number: PtNumber) = writeNumber(number.type, number.number)
private fun writeNumber(type: DataType, number: Double) =
xml.writeTextNode("number", listOf(Pair("type", type.name)), intOrDouble(type, number).toString(), false)
private fun write(symbol: PtIdentifier) {
xml.elt("symbol")
@ -374,8 +453,8 @@ class AsmGen(internal val program: PtProgram,
private fun write(assign: PtAssignment) {
xml.elt("assign")
xml.attr("aug", assign.augmentable.toString())
xml.startChildren()
xml.pos(assign.position)
xml.startChildren()
write(assign.target)
writeNode(assign.value)
xml.endElt()
@ -383,21 +462,21 @@ class AsmGen(internal val program: PtProgram,
private fun write(ifElse: PtIfElse) {
xml.elt("ifelse")
xml.startChildren()
xml.pos(ifElse.position)
xml.startChildren()
xml.elt("condition")
xml.startChildren()
writeNode(ifElse.condition)
xml.endElt()
xml.elt("true")
xml.startChildren()
xml.pos(ifElse.ifScope.position)
xml.startChildren()
writeNode(ifElse.ifScope)
xml.endElt()
if(ifElse.elseScope.children.isNotEmpty()) {
xml.elt("false")
xml.startChildren()
xml.pos(ifElse.elseScope.position)
xml.startChildren()
writeNode(ifElse.elseScope)
xml.endElt()
}
@ -422,30 +501,31 @@ class AsmGen(internal val program: PtProgram,
private fun write(label: PtLabel) {
xml.elt("label")
xml.attr("name", label.name)
xml.startChildren()
xml.attr("name", label.scopedName.joinToString("."))
xml.pos(label.position)
xml.endElt()
}
private fun write(block: PtBlock) {
xml.elt("block")
xml.attr("name", block.name)
xml.attr("name", block.scopedName.joinToString("."))
if(block.address!=null)
xml.attr("address", block.address!!.toString())
xml.attr("library", block.library.toString())
xml.startChildren()
xml.pos(block.position)
xml.startChildren()
block.children.forEach { writeNode(it) }
xml.endElt()
}
private fun write(memMapped: PtMemMapped) {
xml.elt("memvar")
xml.attr("type", memMapped.type.name)
xml.attr("name", memMapped.name)
xml.attr("address", memMapped.address.toString())
xml.endElt()
xml.writeTextNode("memvar",
listOf(
Pair("name", memMapped.scopedName.joinToString(".")),
Pair("type", memMapped.type.name)
),
memMapped.address.toString(),
false)
}
private fun write(target: PtAssignTarget) {
@ -484,12 +564,12 @@ class AsmGen(internal val program: PtProgram,
private fun write(sub: PtSub) {
xml.elt("sub")
xml.attr("name", sub.name)
xml.attr("name", sub.scopedName.joinToString("."))
if(sub.inline)
xml.attr("inline", "true")
xml.attr("returntype", sub.returntype?.toString() ?: "VOID")
xml.startChildren()
xml.pos(sub.position)
xml.startChildren()
if(sub.parameters.isNotEmpty()) {
xml.elt("parameters")
xml.startChildren()
@ -519,22 +599,22 @@ class AsmGen(internal val program: PtProgram,
private fun write(asmSub: PtAsmSub) {
if(asmSub.address!=null) {
xml.elt("romsub")
xml.attr("name", asmSub.name)
xml.attr("name", asmSub.scopedName.joinToString("."))
xml.attr("address", asmSub.address!!.toString())
if(asmSub.inline)
xml.attr("inline", "true")
xml.startChildren()
xml.pos(asmSub.position)
xml.startChildren()
paramsEtcetera(asmSub)
xml.endElt()
}
else {
xml.elt("asmsub")
xml.attr("name", asmSub.name)
xml.attr("name", asmSub.scopedName.joinToString("."))
if(asmSub.inline)
xml.attr("inline", "true")
xml.startChildren()
xml.pos(asmSub.position)
xml.startChildren()
paramsEtcetera(asmSub)
xml.elt("code")
xml.startChildren()
@ -572,18 +652,21 @@ class AsmGen(internal val program: PtProgram,
}
private fun write(constant: PtConstant) {
xml.elt("const")
xml.attr("name", constant.name)
xml.attr("type", constant.type.name)
xml.attr("value", intOrDouble(constant.type, constant.value).toString())
xml.endElt()
xml.writeTextNode("const",
listOf(
Pair("name", constant.scopedName.joinToString(".")),
Pair("type", constant.type.name)
),
intOrDouble(constant.type, constant.value).toString(), false)
}
private fun write(variable: PtVariable) {
// TODO get this from the AST only?
xml.elt("var")
xml.attr("name", variable.name)
xml.elt("vardecl")
xml.attr("name", variable.scopedName.joinToString("."))
xml.attr("type", variable.type.name)
if(variable.arraySize!=null)
xml.attr("arraysize", variable.arraySize.toString())
if(variable.value!=null) {
xml.startChildren()
writeNode(variable.value!!)

View File

@ -19,11 +19,7 @@ class IndentingXmlWriter(val xml: XMLStreamWriter): XMLStreamWriter by xml {
content.push(true)
}
fun endElt(writeIndent: Boolean=true) = writeEndElement(writeIndent)
fun pos(pos: Position) {
elt("src")
attr("pos", pos.toString())
endElt()
}
fun pos(pos: Position) = writeAttribute("src", pos.toString())
fun comment(text: String) {
writeComment(text)
writeCharacters("\n")
@ -79,11 +75,14 @@ class IndentingXmlWriter(val xml: XMLStreamWriter): XMLStreamWriter by xml {
content.push(false)
}
fun writeTextNode(name: String, attrs: List<Pair<String, String>>, text: String) {
fun writeTextNode(name: String, attrs: List<Pair<String, String>>, text: String, cdata: Boolean = true) {
xml.writeCharacters(" ".repeat(indent))
xml.writeStartElement(name)
attrs.forEach { (name, value) -> xml.writeAttribute(name, value) }
xml.writeCData(text)
if(cdata)
xml.writeCData(text)
else
xml.writeCharacters(text)
xml.writeEndElement()
xml.writeCharacters("\n")
}

View File

@ -12,16 +12,18 @@ import kotlin.io.path.Path
class IntermediateAstMaker(val program: Program, val comp: CompilationOptions) {
fun transform(): PtProgram {
val loadAddress = program.actualLoadAddress
val options = ProgramOptions(
comp.output,
comp.launcher,
comp.zeropage,
comp.zpReserved,
program.definedLoadAddress,
loadAddress,
comp.floats,
comp.noSysInit,
comp.dontReinitGlobals,
comp.optimize
comp.optimize,
comp.compTarget.name
)
val ptProgram = PtProgram(
@ -312,7 +314,7 @@ class IntermediateAstMaker(val program: Program, val comp: CompilationOptions) {
return when(srcVar.type) {
VarDeclType.VAR -> {
val value = if(srcVar.value!=null) transformExpression(srcVar.value!!) else null
PtVariable(srcVar.name, srcVar.datatype, value, srcVar.position)
PtVariable(srcVar.name, srcVar.datatype, value, srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position)
}
VarDeclType.CONST -> PtConstant(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number, srcVar.position)
VarDeclType.MEMORY -> PtMemMapped(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number.toUInt(), srcVar.position)
@ -359,7 +361,7 @@ class IntermediateAstMaker(val program: Program, val comp: CompilationOptions) {
}
private fun transform(srcArr: ArrayLiteral): PtArrayLiteral {
val arr = PtArrayLiteral(srcArr.type.getOrElse { throw FatalAstException("array must know its type") }, srcArr.position)
val arr = PtArrayLiteral(srcArr.inferType(program).getOrElse { throw FatalAstException("array must know its type") }, srcArr.position)
for (elt in srcArr.value)
arr.add(transformExpression(elt))
return arr

View File

@ -35,12 +35,18 @@ internal class SymbolTableMaker: IAstVisitor {
}
override fun visit(subroutine: Subroutine) {
val node = StNode(subroutine.name, StNodeType.SUBROUTINE, subroutine.position)
scopestack.peek().add(node)
scopestack.push(node)
super.visit(subroutine)
scopestack.pop()
// st.origAstLinks[subroutine] = node
if(subroutine.asmAddress!=null) {
val node = StRomSub(subroutine.name, subroutine.asmAddress!!, subroutine.position)
scopestack.peek().add(node)
// st.origAstLinks[subroutine] = node
} else {
val node = StNode(subroutine.name, StNodeType.SUBROUTINE, subroutine.position)
scopestack.peek().add(node)
scopestack.push(node)
super.visit(subroutine)
scopestack.pop()
// st.origAstLinks[subroutine] = node
}
}
override fun visit(decl: VarDecl) {

View File

@ -3,6 +3,8 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- ProgramOptions: remove duplicates that are also in CompilationOptions (that is already passed into the AsmGen already)
- ProgramOptions: make sure loadaddress is always set to a sensible value (not 0) see determineProgramLoadAddress()
- unit test for PtProgram AST: should also test new things such as the Datatype in nodes.
...

View File

@ -3,7 +3,6 @@
%zeropage basicsafe
%zpreserved 50,80
%zpreserved 150,155
%address $4000
; Note: this program is compatible with C64 and CX16.
%option align_word