mirror of
https://github.com/irmen/prog8.git
synced 2025-02-16 07:31:48 +00:00
making IR file reader
This commit is contained in:
parent
12ed07a607
commit
3c315703c0
@ -221,7 +221,7 @@ class StMemorySlab(
|
||||
name: String,
|
||||
val size: UInt,
|
||||
val align: UInt,
|
||||
val allocatedAddress: UInt? = null, // this is used (for now) in the VM code generator
|
||||
val allocatedAddress: UInt? = null, // this is used (for now) in the VM code generator. TODO remove this once no longer used
|
||||
position: Position
|
||||
):
|
||||
StNode(name, StNodeType.MEMORYSLAB, position) {
|
||||
|
@ -0,0 +1,103 @@
|
||||
package prog8.codegen.experimental
|
||||
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.IAssemblyProgram
|
||||
import prog8.intermediate.*
|
||||
import java.io.BufferedWriter
|
||||
import kotlin.io.path.bufferedWriter
|
||||
import kotlin.io.path.div
|
||||
|
||||
class AssemblyProgram(override val name: String, val irProgram: IRProgram): IAssemblyProgram {
|
||||
|
||||
// TODO once this is working, replace the codeGenVirtual by codeGenExperimental
|
||||
// after that,
|
||||
|
||||
override fun assemble(options: CompilationOptions): Boolean {
|
||||
val outfile = options.outputDir / ("$name.p8virt")
|
||||
println("write code to $outfile")
|
||||
|
||||
// at last, allocate the variables in memory.
|
||||
val allocations = VariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
|
||||
|
||||
outfile.bufferedWriter().use { out ->
|
||||
allocations.asVmMemory().forEach { (name, alloc) ->
|
||||
out.write("; ${name.joinToString(".")}\n")
|
||||
out.write(alloc + "\n")
|
||||
}
|
||||
// with(irProgram.st) {
|
||||
// allVariables.forEach {
|
||||
// //TODO("var $it")
|
||||
// }
|
||||
// allMemMappedVariables.forEach {
|
||||
// println("MAPPED ${it.name} ${it.address}")
|
||||
// // TODO implement proper memory mapped variable in VM - for now put them as regular variable to get it to compile
|
||||
//
|
||||
// }
|
||||
// allMemorySlabs.forEach {
|
||||
// TODO("implement proper memory slab allocation in VM")
|
||||
// }
|
||||
// }
|
||||
|
||||
out.write("------PROGRAM------\n")
|
||||
|
||||
if(!options.dontReinitGlobals) {
|
||||
out.write("; global var inits\n")
|
||||
irProgram.globalInits.forEach { out.writeLine(it) }
|
||||
}
|
||||
irProgram.blocks.firstOrNull()?.let {
|
||||
if(it.subroutines.any { it.name=="main.start" }) {
|
||||
// there is a "main.start" entrypoint, jump to it
|
||||
out.writeLine(IRCodeInstruction(Opcode.JUMP, labelSymbol = listOf("main.start")))
|
||||
}
|
||||
}
|
||||
|
||||
out.write("; actual program code\n")
|
||||
|
||||
irProgram.blocks.forEach { block ->
|
||||
if(block.address!=null)
|
||||
TODO("blocks can't have a load address for vm")
|
||||
out.write("; BLOCK ${block.name} ${block.position}\n")
|
||||
block.subroutines.forEach { sub ->
|
||||
out.write("; SUB ${sub.name} ${sub.position}\n")
|
||||
out.write("_${sub.name}:\n")
|
||||
sub.chunks.forEach { chunk ->
|
||||
chunk.lines.forEach { out.writeLine(it) }
|
||||
}
|
||||
out.write("; END SUB ${sub.name}\n")
|
||||
}
|
||||
block.asmSubroutines.forEach { sub ->
|
||||
out.write("; ASMSUB ${sub.name} ${sub.position}\n")
|
||||
out.write("_${sub.name}:\n")
|
||||
out.write(sub.assembly)
|
||||
out.write("\n; END ASMSUB ${sub.name}\n")
|
||||
}
|
||||
out.write("; END BLOCK ${block.name}\n")
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private fun BufferedWriter.writeLine(line: IRCodeLine) {
|
||||
when(line) {
|
||||
is IRCodeComment -> {
|
||||
write("; ${line.comment}\n")
|
||||
}
|
||||
is IRCodeInstruction -> {
|
||||
write(line.ins.toString() + "\n")
|
||||
}
|
||||
is IRCodeInlineBinary -> {
|
||||
write("incbin \"${line.file}\"")
|
||||
if(line.offset!=null)
|
||||
write(",${line.offset}")
|
||||
if(line.length!=null)
|
||||
write(",${line.length}")
|
||||
write("\n")
|
||||
}
|
||||
is IRCodeLabel -> {
|
||||
write("_" + line.name.joinToString(".") + ":\n")
|
||||
}
|
||||
else -> throw AssemblyError("invalid IR code line")
|
||||
}
|
||||
}
|
@ -40,14 +40,13 @@ class CodeGen(internal val program: PtProgram,
|
||||
internal val errors: IErrorReporter
|
||||
): IAssemblyGenerator {
|
||||
|
||||
internal val allocations = VariableAllocator(symbolTable, program.memsizer)
|
||||
internal val allocations = VariableAllocator(symbolTable, program.encoding, program.memsizer)
|
||||
private val expressionEval = ExpressionGen(this)
|
||||
private val builtinFuncGen = BuiltinFuncGen(this, expressionEval)
|
||||
private val assignmentGen = AssignmentGen(this, expressionEval)
|
||||
internal val vmRegisters = VmRegisterPool()
|
||||
|
||||
override fun compileToAssembly(): IAssemblyProgram? {
|
||||
|
||||
flattenNestedSubroutines()
|
||||
|
||||
val irProg = IRProgram(program.name, symbolTable, options, program.encoding)
|
||||
@ -66,8 +65,6 @@ class CodeGen(internal val program: PtProgram,
|
||||
if(options.evalStackBaseAddress!=null)
|
||||
throw AssemblyError("virtual target doesn't use eval-stack")
|
||||
|
||||
// TODO flatten nested subroutines
|
||||
|
||||
for (block in program.allBlocks()) {
|
||||
irProg.addBlock(translate(block))
|
||||
}
|
||||
@ -77,18 +74,24 @@ class CodeGen(internal val program: PtProgram,
|
||||
optimizer.optimize()
|
||||
}
|
||||
|
||||
println("IR codegen: virtual registers=${vmRegisters.peekNext()}")
|
||||
// create IR file on disk and read it back.
|
||||
// TODO: this makes sure those I/O routines are correct, but this step should be skipped eventually.
|
||||
IRFileWriter(irProg).writeFile()
|
||||
|
||||
return DummyAssemblyProgram(irProg.name)
|
||||
val irProgFromDisk = IRFileReader(options.outputDir, irProg.name).readFile()
|
||||
return AssemblyProgram(irProgFromDisk.name, irProgFromDisk)
|
||||
}
|
||||
|
||||
private fun flattenNestedSubroutines() {
|
||||
// this moves all nested subroutines up to the block scope.
|
||||
// also changes the name to be the fully scoped one so it becomes unique at the top level.
|
||||
// also moves the start() entrypoint as first subroutine.
|
||||
val flattenedSubs = mutableListOf<Pair<PtBlock, PtSub>>()
|
||||
val flattenedAsmSubs = mutableListOf<Pair<PtBlock, PtAsmSub>>()
|
||||
val removalsSubs = mutableListOf<Pair<PtSub, PtSub>>()
|
||||
val removalsAsmSubs = mutableListOf<Pair<PtSub, PtAsmSub>>()
|
||||
val renameSubs = mutableListOf<Pair<PtBlock, PtSub>>()
|
||||
val renameAsmSubs = mutableListOf<Pair<PtBlock, PtAsmSub>>()
|
||||
val entrypoint = program.entrypoint()
|
||||
|
||||
fun flattenNestedAsm(block: PtBlock, parentSub: PtSub, asmsub: PtAsmSub) {
|
||||
val flattened = PtAsmSub(asmsub.scopedName.joinToString("."),
|
||||
@ -123,7 +126,10 @@ class CodeGen(internal val program: PtProgram,
|
||||
// Only regular subroutines can have nested subroutines.
|
||||
it.children.filterIsInstance<PtSub>().forEach { subsub->flattenNested(block, it, subsub)}
|
||||
it.children.filterIsInstance<PtAsmSub>().forEach { asmsubsub->flattenNestedAsm(block, it, asmsubsub)}
|
||||
renameSubs += Pair(block, it)
|
||||
}
|
||||
if(it is PtAsmSub)
|
||||
renameAsmSubs += Pair(block, it)
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,9 +137,32 @@ class CodeGen(internal val program: PtProgram,
|
||||
removalsAsmSubs.forEach { (parent, asmsub) -> parent.children.remove(asmsub) }
|
||||
flattenedSubs.forEach { (block, sub) -> block.add(sub) }
|
||||
flattenedAsmSubs.forEach { (block, asmsub) -> block.add(asmsub) }
|
||||
renameSubs.forEach { (parent, sub) ->
|
||||
val renamedSub = PtSub(sub.scopedName.joinToString("."), sub.parameters, sub.returntype, sub.inline, sub.position)
|
||||
sub.children.forEach { renamedSub.add(it) }
|
||||
parent.children.remove(sub)
|
||||
if (sub === entrypoint) {
|
||||
// entrypoint sub must be first sub
|
||||
val firstsub = parent.children.withIndex().first { it.value is PtSub || it.value is PtAsmSub }
|
||||
parent.add(firstsub.index, renamedSub)
|
||||
} else {
|
||||
parent.add(renamedSub)
|
||||
}
|
||||
}
|
||||
renameAsmSubs.forEach { (parent, sub) ->
|
||||
val renamedSub = PtAsmSub(sub.scopedName.joinToString("."),
|
||||
sub.address,
|
||||
sub.clobbers,
|
||||
sub.parameters,
|
||||
sub.returnTypes,
|
||||
sub.retvalRegisters,
|
||||
sub.inline,
|
||||
sub.position)
|
||||
parent.children.remove(sub)
|
||||
parent.add(renamedSub)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun translateNode(node: PtNode): IRCodeChunk {
|
||||
val code = when(node) {
|
||||
is PtScopeVarsDecls -> IRCodeChunk(node.position) // vars should be looked up via symbol table
|
||||
@ -824,14 +853,16 @@ class CodeGen(internal val program: PtProgram,
|
||||
is PtScopeVarsDecls -> { /* vars should be looked up via symbol table */ }
|
||||
is PtSub -> {
|
||||
val vmsub = IRSubroutine(child.name, child.returntype, child.position)
|
||||
for (line in child.children) {
|
||||
vmsub += translateNode(line)
|
||||
for (subchild in child.children) {
|
||||
val translated = translateNode(subchild)
|
||||
if(translated.isNotEmpty())
|
||||
vmsub += translated
|
||||
}
|
||||
vmblock += vmsub
|
||||
}
|
||||
is PtAsmSub -> {
|
||||
val assembly = if(child.children.isEmpty()) "" else (child.children.single() as PtInlineAssembly).assembly
|
||||
vmblock += IRAsmSubroutine(child.scopedName, child.position, child.address, assembly)
|
||||
vmblock += IRAsmSubroutine(child.name, child.position, child.address, assembly)
|
||||
}
|
||||
is PtInlineAssembly -> {
|
||||
vmblock += IRInlineAsmChunk(child.assembly, child.position)
|
||||
|
@ -1,12 +0,0 @@
|
||||
package prog8.codegen.experimental
|
||||
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.IAssemblyProgram
|
||||
|
||||
class DummyAssemblyProgram(override val name: String): IAssemblyProgram {
|
||||
|
||||
override fun assemble(options: CompilationOptions): Boolean {
|
||||
println("TODO WRITE ASSEMBLY")
|
||||
return false
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ package prog8.codegen.experimental
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.core.*
|
||||
|
||||
class VariableAllocator(st: SymbolTable, memsizer: IMemSizer) {
|
||||
class VariableAllocator(val st: SymbolTable, val encoding: IStringEncoding, memsizer: IMemSizer) {
|
||||
|
||||
internal val allocations = mutableMapOf<List<String>, Int>()
|
||||
private var freeMemoryStart: Int
|
||||
@ -43,4 +43,64 @@ class VariableAllocator(st: SymbolTable, memsizer: IMemSizer) {
|
||||
}
|
||||
|
||||
fun get(name: List<String>) = allocations.getValue(name)
|
||||
|
||||
fun asVmMemory(): List<Pair<List<String>, String>> {
|
||||
val mm = mutableListOf<Pair<List<String>, String>>()
|
||||
for (variable in st.allVariables) {
|
||||
val location = allocations.getValue(variable.scopedName)
|
||||
val typeStr = when(variable.dt) {
|
||||
DataType.UBYTE, DataType.ARRAY_UB, DataType.STR -> "ubyte"
|
||||
DataType.BYTE, DataType.ARRAY_B -> "byte"
|
||||
DataType.UWORD, DataType.ARRAY_UW -> "uword"
|
||||
DataType.WORD, DataType.ARRAY_W -> "word"
|
||||
DataType.FLOAT, DataType.ARRAY_F -> "float"
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
val value = when(variable.dt) {
|
||||
DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: 0.0).toString()
|
||||
in NumericDatatypes -> (variable.onetimeInitializationNumericValue ?: 0).toHex()
|
||||
DataType.STR -> {
|
||||
val encoded = encoding.encodeString(variable.onetimeInitializationStringValue!!.first, variable.onetimeInitializationStringValue!!.second) + listOf(0u)
|
||||
encoded.joinToString(",") { it.toInt().toHex() }
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
if(variable.onetimeInitializationArrayValue!=null) {
|
||||
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toString() }
|
||||
} else {
|
||||
(1..variable.length!!).joinToString(",") { "0" }
|
||||
}
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
if(variable.onetimeInitializationArrayValue!==null) {
|
||||
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toHex() }
|
||||
} else {
|
||||
(1..variable.length!!).joinToString(",") { "0" }
|
||||
}
|
||||
}
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
mm.add(Pair(variable.scopedName, "$location $typeStr $value"))
|
||||
}
|
||||
for (variable in st.allMemMappedVariables) {
|
||||
val location = allocations.getValue(variable.scopedName)
|
||||
val typeStr = when(variable.dt) {
|
||||
DataType.UBYTE, DataType.ARRAY_UB, DataType.STR -> "ubyte"
|
||||
DataType.BYTE, DataType.ARRAY_B -> "byte"
|
||||
DataType.UWORD, DataType.ARRAY_UW -> "uword"
|
||||
DataType.WORD, DataType.ARRAY_W -> "word"
|
||||
DataType.FLOAT, DataType.ARRAY_F -> "float"
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
val value = when(variable.dt) {
|
||||
DataType.FLOAT -> "0.0"
|
||||
in NumericDatatypes -> "0"
|
||||
DataType.ARRAY_F -> (1..variable.length!!).joinToString(",") { "0.0" }
|
||||
in ArrayDatatypes -> (1..variable.length!!).joinToString(",") { "0" }
|
||||
else -> throw InternalCompilerException("weird dt for mem mapped var")
|
||||
}
|
||||
mm.add(Pair(variable.scopedName, "$location $typeStr $value"))
|
||||
}
|
||||
return mm
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class CodeGen(internal val program: PtProgram,
|
||||
optimizer.optimize()
|
||||
}
|
||||
|
||||
println("Vm codegen: virtual registers=${vmRegisters.peekNext()} memory usage=${allocations.freeMem}")
|
||||
println("Vm codegen: memory usage=${allocations.freeMem}")
|
||||
|
||||
return vmprog
|
||||
}
|
||||
|
@ -1,19 +1,46 @@
|
||||
%zpreserved 10,20
|
||||
%zpreserved 30,40
|
||||
|
||||
main {
|
||||
|
||||
uword global1 = 1234
|
||||
|
||||
%asm {{
|
||||
nop
|
||||
nop
|
||||
return
|
||||
}}
|
||||
|
||||
asmsub testasmsub(ubyte arg1 @A) clobbers(Y) -> uword @AX {
|
||||
%asm {{
|
||||
nop
|
||||
return
|
||||
}}
|
||||
}
|
||||
|
||||
sub start() {
|
||||
sys.wait(1)
|
||||
|
||||
%asm {{
|
||||
nop
|
||||
}}
|
||||
|
||||
; TODO should generate address
|
||||
uword @shared slab1 = memory("slab 1", 2000, 0)
|
||||
uword @shared slab2 = memory("slab 1", 2000, 0)
|
||||
uword @shared slab3 = memory("other # slab", 2000, 64)
|
||||
&uword mapped = $c000
|
||||
&ubyte[20] mappedarray = $c100
|
||||
|
||||
uword @shared zz = slab1+slab2+slab3
|
||||
|
||||
uword @shared qq = zz
|
||||
uword @shared qq2 = &zz
|
||||
uword @shared @zp qq = zz
|
||||
uword @shared @zp qq2 = &zz
|
||||
|
||||
str @shared namestring = "irmen"
|
||||
uword[] @shared wordarray1 = [1111,2222,3333,4444]
|
||||
uword[4] @shared wordarray2 = 12345
|
||||
uword[4] @shared wordzeroarray
|
||||
|
||||
qq=4242 ; TODO should generate symbol not allocated address
|
||||
mapped = 42 ; TODO wrong VMASM code generated... should generate mapped memory address
|
||||
@ -23,7 +50,6 @@ main {
|
||||
nested()
|
||||
main.start.nested.nested2()
|
||||
|
||||
; TODO flatten nested subroutines in codegen
|
||||
sub nested() {
|
||||
qq++
|
||||
txt.print("zzz")
|
||||
|
521
intermediate/src/prog8/intermediate/IRFileReader.kt
Normal file
521
intermediate/src/prog8/intermediate/IRFileReader.kt
Normal file
@ -0,0 +1,521 @@
|
||||
package prog8.intermediate
|
||||
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.StMemorySlab
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtBlock
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.bufferedReader
|
||||
import kotlin.io.path.div
|
||||
|
||||
|
||||
class IRFileReader(outputDir: Path, programName: String) {
|
||||
private val infile = outputDir / ("${programName}.p8ir")
|
||||
|
||||
fun readFile(): IRProgram {
|
||||
println("Reading intermediate representation from $infile")
|
||||
infile.bufferedReader().use { reader ->
|
||||
val lines = reader.readText().lines()
|
||||
return parseProgram(lines.iterator())
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseProgram(lines: Iterator<String>): IRProgram {
|
||||
val programPattern = Regex("<PROGRAM NAME=(.+)>")
|
||||
val line = lines.next()
|
||||
val match = programPattern.matchEntire(line) ?: throw IRParseException("invalid PROGRAM")
|
||||
val programName = match.groups[1]!!.value
|
||||
val options = parseOptions(lines)
|
||||
val variables = parseVariables(lines)
|
||||
val memorymapped = parseMemMapped(lines)
|
||||
val slabs = parseSlabs(lines)
|
||||
val initGlobals = parseInitGlobals(lines)
|
||||
val blocks = parseBlocksUntilProgramEnd(lines)
|
||||
|
||||
val st = SymbolTable()
|
||||
variables.forEach { st.add(it) }
|
||||
memorymapped.forEach { st.add(it) }
|
||||
slabs.forEach { st.add(it) }
|
||||
|
||||
val program = IRProgram(programName, st, options, options.compTarget)
|
||||
program.addGlobalInits(initGlobals)
|
||||
blocks.forEach{ program.addBlock(it) }
|
||||
return program
|
||||
}
|
||||
|
||||
private fun parseOptions(lines: Iterator<String>): CompilationOptions {
|
||||
var line = lines.next()
|
||||
while(line.isBlank())
|
||||
line = lines.next()
|
||||
var target: ICompilationTarget = VMTarget()
|
||||
var outputType = OutputType.PRG
|
||||
var launcher = CbmPrgLauncherType.NONE
|
||||
var zeropage = ZeropageType.FULL
|
||||
val zpReserved = mutableListOf<UIntRange>()
|
||||
var loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
|
||||
var dontReinitGlobals = false
|
||||
var evalStackBaseAddress: UInt? = null
|
||||
if(line!="<OPTIONS>")
|
||||
throw IRParseException("invalid OPTIONS")
|
||||
while(true) {
|
||||
line = lines.next()
|
||||
if(line=="</OPTIONS>")
|
||||
break
|
||||
val (name, value) = line.split('=', limit=2)
|
||||
when(name) {
|
||||
"compTarget" -> {
|
||||
target = when(value) {
|
||||
VMTarget.NAME -> VMTarget()
|
||||
C64Target.NAME -> C64Target()
|
||||
C128Target.NAME -> C128Target()
|
||||
AtariTarget.NAME -> AtariTarget()
|
||||
Cx16Target.NAME -> Cx16Target()
|
||||
else -> throw IRParseException("invalid target $value")
|
||||
}
|
||||
}
|
||||
"output" -> outputType = OutputType.valueOf(value)
|
||||
"launcher" -> launcher = CbmPrgLauncherType.valueOf(value)
|
||||
"zeropage" -> zeropage = ZeropageType.valueOf(value)
|
||||
"loadAddress" -> loadAddress = value.toUInt()
|
||||
"dontReinitGlobals" -> dontReinitGlobals = value.toBoolean()
|
||||
"evalStackBaseAddress" -> evalStackBaseAddress = if(value=="null") null else value.toUInt()
|
||||
"zpReserved" -> {
|
||||
val (start, end) = value.split(',')
|
||||
zpReserved.add(UIntRange(start.toUInt(), end.toUInt()))
|
||||
}
|
||||
else -> throw IRParseException("illegal OPTION $name")
|
||||
}
|
||||
}
|
||||
|
||||
return CompilationOptions(
|
||||
outputType,
|
||||
launcher,
|
||||
zeropage,
|
||||
zpReserved,
|
||||
false,
|
||||
false,
|
||||
target,
|
||||
loadAddress,
|
||||
dontReinitGlobals = dontReinitGlobals,
|
||||
evalStackBaseAddress = evalStackBaseAddress
|
||||
)
|
||||
}
|
||||
|
||||
private fun parseVariables(lines: Iterator<String>): List<StStaticVariable> {
|
||||
var line = lines.next()
|
||||
while(line.isBlank())
|
||||
line = lines.next()
|
||||
if(line!="<VARIABLES>")
|
||||
throw IRParseException("invalid VARIABLES")
|
||||
val variables = mutableListOf<StStaticVariable>()
|
||||
val varPattern = Regex("(.+?)(\\[.+?\\])? (.+)=(.+?) (zp=(.+))?")
|
||||
while(true) {
|
||||
line = lines.next()
|
||||
if(line=="</VARIABLES>")
|
||||
break
|
||||
// examples:
|
||||
// uword main.start.qq2=0 zp=REQUIRE_ZP
|
||||
// ubyte[6] main.start.namestring=105,114,109,101,110,0
|
||||
val match = varPattern.matchEntire(line) ?: throw IRParseException("invalid VARIABLE $line")
|
||||
val (type, arrayspec, name, value, _, zpwish) = match.destructured
|
||||
val arraysize = if(arrayspec.isNotBlank()) arrayspec.substring(1, arrayspec.length-1).toInt() else null
|
||||
val dt: DataType = parseDatatype(type, arraysize!=null)
|
||||
val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish)
|
||||
variables.add(StStaticVariable(name, dt, null, null, null, arraysize, zp, Position.DUMMY))
|
||||
}
|
||||
return variables
|
||||
}
|
||||
|
||||
private fun parseMemMapped(lines: Iterator<String>): List<StMemVar> {
|
||||
var line = lines.next()
|
||||
while(line.isBlank())
|
||||
line = lines.next()
|
||||
if(line!="<MEMORYMAPPEDVARIABLES>")
|
||||
throw IRParseException("invalid MEMORYMAPPEDVARIABLES")
|
||||
val memvars = mutableListOf<StMemVar>()
|
||||
val mappedPattern = Regex("&(.+?)(\\[.+?\\])? (.+)=(.+)")
|
||||
while(true) {
|
||||
line = lines.next()
|
||||
if(line=="</MEMORYMAPPEDVARIABLES>")
|
||||
break
|
||||
// examples:
|
||||
// &uword main.start.mapped=49152
|
||||
// &ubyte[20] main.start.mappedarray=49408
|
||||
val match = mappedPattern.matchEntire(line) ?: throw IRParseException("invalid MEMORYMAPPEDVARIABLES $line")
|
||||
val (type, arrayspec, name, address) = match.destructured
|
||||
val arraysize = if(arrayspec.isNotBlank()) arrayspec.substring(1, arrayspec.length-1).toInt() else null
|
||||
val dt: DataType = parseDatatype(type, arraysize!=null)
|
||||
memvars.add(StMemVar(name, dt, address.toUInt(), arraysize, Position.DUMMY))
|
||||
}
|
||||
return memvars
|
||||
}
|
||||
|
||||
private fun parseDatatype(type: String, isArray: Boolean): DataType {
|
||||
if(isArray) {
|
||||
return when(type) {
|
||||
"byte" -> DataType.ARRAY_B
|
||||
"ubyte", "str" -> DataType.ARRAY_UB
|
||||
"word" -> DataType.ARRAY_W
|
||||
"uword" -> DataType.ARRAY_UW
|
||||
"float" -> DataType.ARRAY_F
|
||||
"bool" -> DataType.ARRAY_B
|
||||
else -> throw IRParseException("invalid dt")
|
||||
}
|
||||
} else {
|
||||
return when(type) {
|
||||
"byte" -> DataType.BYTE
|
||||
"ubyte" -> DataType.UBYTE
|
||||
"word" -> DataType.WORD
|
||||
"uword" -> DataType.UWORD
|
||||
"float" -> DataType.FLOAT
|
||||
"bool" -> DataType.BOOL
|
||||
else -> throw IRParseException("invalid dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseSlabs(lines: Iterator<String>): List<StMemorySlab> {
|
||||
var line = lines.next()
|
||||
while(line.isBlank())
|
||||
line = lines.next()
|
||||
if(line!="<MEMORYSLABS>")
|
||||
throw IRParseException("invalid MEMORYSLABS")
|
||||
val slabs = mutableListOf<StMemorySlab>()
|
||||
val slabPattern = Regex("SLAB (.+) (.+) (.+)")
|
||||
while(true) {
|
||||
line = lines.next()
|
||||
if(line=="</MEMORYSLABS>")
|
||||
break
|
||||
// example: "SLAB slabname 4096 0"
|
||||
val match = slabPattern.matchEntire(line) ?: throw IRParseException("invalid SLAB $line")
|
||||
val (name, size, align) = match.destructured
|
||||
slabs.add(StMemorySlab(name, size.toUInt(), align.toUInt(), null, Position.DUMMY))
|
||||
}
|
||||
return slabs
|
||||
}
|
||||
|
||||
private fun parseInitGlobals(lines: Iterator<String>): IRCodeChunk {
|
||||
var line = lines.next()
|
||||
while(line.isBlank())
|
||||
line = lines.next()
|
||||
if(line!="<INITGLOBALS>")
|
||||
throw IRParseException("invalid INITGLOBALS")
|
||||
val chunk = parseCodeChunk(lines.next(), lines)!!
|
||||
line = lines.next()
|
||||
if(line!="</INITGLOBALS>")
|
||||
throw IRParseException("missing INITGLOBALS close tag")
|
||||
return chunk
|
||||
}
|
||||
|
||||
private fun parseBlocksUntilProgramEnd(lines: Iterator<String>): List<IRBlock> {
|
||||
val blocks = mutableListOf<IRBlock>()
|
||||
while(true) {
|
||||
var line = lines.next()
|
||||
while (line.isBlank())
|
||||
line = lines.next()
|
||||
if (line == "</PROGRAM>")
|
||||
break
|
||||
blocks.add(parseBlock(line, lines))
|
||||
}
|
||||
return blocks
|
||||
}
|
||||
|
||||
private val blockPattern = Regex("<BLOCK NAME=(.+) ADDRESS=(.+) ALIGN=(.+) POS=(.+)>")
|
||||
private val inlineAsmPattern = Regex("<INLINEASM POS=(.+)>")
|
||||
private val asmsubPattern = Regex("<ASMSUB NAME=(.+) ADDRESS=(.+) POS=(.+)>")
|
||||
private val subPattern = Regex("<SUB NAME=(.+) RETURNTYPE=(.+) POS=(.+)>")
|
||||
private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]")
|
||||
private val instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.f)?(.*)""", RegexOption.IGNORE_CASE)
|
||||
private val labelPattern = Regex("""_([a-zA-Z\d\._]+):""")
|
||||
|
||||
private fun parseBlock(startline: String, lines: Iterator<String>): IRBlock {
|
||||
var line = startline
|
||||
if(!line.startsWith("<BLOCK "))
|
||||
throw IRParseException("invalid BLOCK")
|
||||
val match = blockPattern.matchEntire(line) ?: throw IRParseException("invalid BLOCK")
|
||||
val (name, address, align, position) = match.destructured
|
||||
val addressNum = if(address=="null") null else address.toUInt()
|
||||
val block = IRBlock(name, addressNum, PtBlock.BlockAlignment.valueOf(align), parsePosition(position))
|
||||
while(true) {
|
||||
line = lines.next()
|
||||
if(line.isBlank())
|
||||
continue
|
||||
if(line=="</BLOCK>")
|
||||
return block
|
||||
if(line.startsWith("<SUB ")) {
|
||||
val sub = parseSubroutine(line, lines)
|
||||
block += sub
|
||||
} else if(line.startsWith("<ASMSUB ")) {
|
||||
val sub = parseAsmSubroutine(line, lines)
|
||||
block += sub
|
||||
} else if(line.startsWith("<INLINEASM ")) {
|
||||
val asm = parseInlineAssembly(line, lines)
|
||||
block += asm
|
||||
} else
|
||||
throw IRParseException("invalid line in BLOCK")
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseInlineAssembly(startline: String, lines: Iterator<String>): IRInlineAsmChunk {
|
||||
// <INLINEASM POS=[examples/test.p8: line 8 col 6-9]>
|
||||
val match = inlineAsmPattern.matchEntire(startline) ?: throw IRParseException("invalid INLINEASM")
|
||||
val pos = parsePosition(match.groupValues[1])
|
||||
val asmlines = mutableListOf<String>()
|
||||
var line = lines.next()
|
||||
while(line!="</INLINEASM>") {
|
||||
asmlines.add(line)
|
||||
line = lines.next()
|
||||
}
|
||||
return IRInlineAsmChunk(asmlines.joinToString("\n"), pos)
|
||||
}
|
||||
|
||||
private fun parseAsmSubroutine(startline: String, lines: Iterator<String>): IRAsmSubroutine {
|
||||
// <ASMSUB NAME=main.testasmsub ADDRESS=null POS=[examples/test.p8: line 14 col 6-11]>
|
||||
// TODO parse more signature stuff once it's there.
|
||||
val match = asmsubPattern.matchEntire(startline) ?: throw IRParseException("invalid ASMSUB")
|
||||
val (scopedname, address, pos) = match.destructured
|
||||
var line = lines.next()
|
||||
val asm = parseInlineAssembly(line, lines)
|
||||
while(line!="</ASMSUB>")
|
||||
line = lines.next()
|
||||
return IRAsmSubroutine(scopedname, parsePosition(pos), if(address=="null") null else address.toUInt(), asm.asm)
|
||||
}
|
||||
|
||||
private fun parseSubroutine(startline: String, lines: Iterator<String>): IRSubroutine {
|
||||
// <SUB NAME=main.start.nested.nested2 RETURNTYPE=null POS=[examples/test.p8: line 54 col 14-16]>
|
||||
// TODO parse more signature stuff once it's there.
|
||||
val match = subPattern.matchEntire(startline) ?: throw IRParseException("invalid SUB")
|
||||
val (name, returntype, pos) = match.destructured
|
||||
val sub = IRSubroutine(name, if(returntype=="null") null else parseDatatype(returntype, false), parsePosition(pos))
|
||||
while(true) {
|
||||
val line = lines.next()
|
||||
if(line=="</SUB>")
|
||||
return sub
|
||||
val chunk = if(line=="<CODE>")
|
||||
parseCodeChunk(line, lines)
|
||||
else if(line.startsWith("<INLINEASM "))
|
||||
parseInlineAssembly(line, lines)
|
||||
else
|
||||
throw IRParseException("invalid sub child node")
|
||||
|
||||
if (chunk == null)
|
||||
break
|
||||
else
|
||||
sub += chunk
|
||||
}
|
||||
val line = lines.next()
|
||||
if(line=="</SUB>")
|
||||
throw IRParseException("missing SUB close tag")
|
||||
return sub
|
||||
}
|
||||
|
||||
private fun parseCodeChunk(firstline: String, lines: Iterator<String>): IRCodeChunk? {
|
||||
if(firstline!="<CODE>") {
|
||||
if(firstline=="</SUB>")
|
||||
return null
|
||||
else
|
||||
throw IRParseException("invalid or empty CODE chunk")
|
||||
}
|
||||
val chunk = IRCodeChunk(Position.DUMMY)
|
||||
while(true) {
|
||||
val line = lines.next()
|
||||
if (line == "</CODE>")
|
||||
return chunk
|
||||
if (line.isBlank() || line.startsWith(';'))
|
||||
continue
|
||||
chunk += parseCodeLine(line)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseCodeLine(line: String): IRCodeLine {
|
||||
val match = instructionPattern.matchEntire(line.trim())
|
||||
if(match==null) {
|
||||
// it's a label.
|
||||
val labelmatch = labelPattern.matchEntire(line.trim()) ?: throw IRParseException("invalid label")
|
||||
return IRCodeLabel(labelmatch.groupValues[1].split('.'))
|
||||
}
|
||||
|
||||
// it's an instruction.
|
||||
val (_, instr, typestr, rest) = match.groupValues
|
||||
if(instr=="incbin") {
|
||||
TODO("incbin")
|
||||
}
|
||||
val opcode = try {
|
||||
Opcode.valueOf(instr.uppercase())
|
||||
} catch (ax: IllegalArgumentException) {
|
||||
throw IRParseException("invalid vmasm instruction: $instr")
|
||||
}
|
||||
var type: VmDataType? = convertVmType(typestr)
|
||||
val formats = instructionFormats.getValue(opcode)
|
||||
val format: InstructionFormat
|
||||
if(type !in formats) {
|
||||
type = VmDataType.BYTE
|
||||
format = if(type !in formats)
|
||||
formats.getValue(null)
|
||||
else
|
||||
formats.getValue(type)
|
||||
} else {
|
||||
format = formats.getValue(type)
|
||||
}
|
||||
// parse the operands
|
||||
val operands = rest.lowercase().split(",").toMutableList()
|
||||
var reg1: Int? = null
|
||||
var reg2: Int? = null
|
||||
var reg3: Int? = null
|
||||
var fpReg1: Int? = null
|
||||
var fpReg2: Int? = null
|
||||
var fpReg3: Int? = null
|
||||
var value: Float? = null
|
||||
var operand: String?
|
||||
var labelSymbol: List<String>? = null
|
||||
if(operands.isNotEmpty() && operands[0].isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg1 = operand.substring(1).toInt()
|
||||
else if(operand[0]=='f' && operand[1]=='r')
|
||||
fpReg1 = operand.substring(2).toInt()
|
||||
else {
|
||||
if(operand.startsWith('_')) {
|
||||
// it's a label.
|
||||
labelSymbol = rest.split(",")[0].trim().substring(1).split('.') // keep the original case
|
||||
value = null
|
||||
} else {
|
||||
value = parseValue(operand)
|
||||
}
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg2 = operand.substring(1).toInt()
|
||||
else if(operand[0]=='f' && operand[1]=='r')
|
||||
fpReg2 = operand.substring(2).toInt()
|
||||
else {
|
||||
if(operand.startsWith('_')) {
|
||||
// it's a label.
|
||||
labelSymbol = rest.split(",")[1].trim().substring(1).split('.') // keep the original case
|
||||
value = null
|
||||
} else {
|
||||
value = parseValue(operand)
|
||||
}
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand[0]=='r')
|
||||
reg3 = operand.substring(1).toInt()
|
||||
else if(operand[0]=='f' && operand[1]=='r')
|
||||
fpReg3 = operand.substring(2).toInt()
|
||||
else {
|
||||
if(operand.startsWith('_')) {
|
||||
// it's a label.
|
||||
labelSymbol = rest.split(",")[2].trim().split('.') // keep the original case
|
||||
value = null
|
||||
} else {
|
||||
value = parseValue(operand)
|
||||
}
|
||||
operands.clear()
|
||||
}
|
||||
if(operands.isNotEmpty()) {
|
||||
operand = operands.removeFirst().trim()
|
||||
if(operand.startsWith('_')) {
|
||||
// it's a label.
|
||||
labelSymbol = rest.split(",")[3].trim().split('.') // keep the original case
|
||||
value = null
|
||||
} else {
|
||||
value = parseValue(operand)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// shift the operands back into place
|
||||
while(reg1==null && reg2!=null) {
|
||||
reg1 = reg2
|
||||
reg2 = reg3
|
||||
reg3 = null
|
||||
}
|
||||
while(fpReg1==null && fpReg2!=null) {
|
||||
fpReg1 = fpReg2
|
||||
fpReg2 = fpReg3
|
||||
fpReg3 = null
|
||||
}
|
||||
if(reg3!=null)
|
||||
throw IRParseException("too many reg arguments $line")
|
||||
if(fpReg3!=null)
|
||||
throw IRParseException("too many fpreg arguments $line")
|
||||
|
||||
if(type!=null && type !in formats)
|
||||
throw IRParseException("invalid type code for $line")
|
||||
if(format.reg1 && reg1==null)
|
||||
throw IRParseException("needs reg1 for $line")
|
||||
if(format.reg2 && reg2==null)
|
||||
throw IRParseException("needs reg2 for $line")
|
||||
if(format.value && value==null && labelSymbol==null)
|
||||
throw IRParseException("needs value or label for $line")
|
||||
if(!format.reg1 && reg1!=null)
|
||||
throw IRParseException("invalid reg1 for $line")
|
||||
if(!format.reg2 && reg2!=null)
|
||||
throw IRParseException("invalid reg2 for $line")
|
||||
if(value!=null && opcode !in OpcodesWithAddress) {
|
||||
when (type) {
|
||||
VmDataType.BYTE -> {
|
||||
if (value < -128 || value > 255)
|
||||
throw IRParseException("value out of range for byte: $value")
|
||||
}
|
||||
VmDataType.WORD -> {
|
||||
if (value < -32768 || value > 65535)
|
||||
throw IRParseException("value out of range for word: $value")
|
||||
}
|
||||
VmDataType.FLOAT -> {}
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
var floatValue: Float? = null
|
||||
var intValue: Int? = null
|
||||
|
||||
if(format.value && value!=null)
|
||||
intValue = value.toInt()
|
||||
if(format.fpValue && value!=null)
|
||||
floatValue = value
|
||||
|
||||
return IRCodeInstruction(opcode, type, reg1, reg2, fpReg1, fpReg2, intValue, floatValue, labelSymbol)
|
||||
}
|
||||
|
||||
private fun convertVmType(typestr: String): VmDataType? {
|
||||
return when(typestr.lowercase()) {
|
||||
"" -> null
|
||||
".b" -> VmDataType.BYTE
|
||||
".w" -> VmDataType.WORD
|
||||
".f" -> VmDataType.FLOAT
|
||||
else -> throw IRParseException("invalid type $typestr")
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseValue(value: String): Float {
|
||||
return if(value.startsWith("-"))
|
||||
-parseValue(value.substring(1))
|
||||
else if(value.startsWith('$'))
|
||||
value.substring(1).toInt(16).toFloat()
|
||||
else if(value.startsWith('%'))
|
||||
value.substring(1).toInt(2).toFloat()
|
||||
else if(value.startsWith("0x"))
|
||||
value.substring(2).toInt(16).toFloat()
|
||||
else if(value.startsWith('_'))
|
||||
throw IRParseException("attempt to parse a label as value")
|
||||
else
|
||||
return value.toFloat()
|
||||
}
|
||||
|
||||
private fun parsePosition(strpos: String): Position {
|
||||
// example: "[library:/prog8lib/virtual/textio.p8: line 5 col 2-4]"
|
||||
val match = posPattern.matchEntire(strpos) ?: throw IRParseException("invalid Position")
|
||||
val (file, line, startCol, endCol) = match.destructured
|
||||
return Position(file, line.toInt(), startCol.toInt(), endCol.toInt())
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +1,16 @@
|
||||
package prog8.intermediate
|
||||
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.core.*
|
||||
import java.io.BufferedWriter
|
||||
import kotlin.io.path.bufferedWriter
|
||||
import kotlin.io.path.div
|
||||
|
||||
|
||||
// TODO incbins
|
||||
|
||||
|
||||
class IRFileWriter(private val irProgram: IRProgram) {
|
||||
private val outfile = irProgram.options.outputDir / ("${irProgram.name}.p8ir")
|
||||
private val out = outfile.bufferedWriter()
|
||||
@ -17,9 +23,9 @@ class IRFileWriter(private val irProgram: IRProgram) {
|
||||
|
||||
if(!irProgram.options.dontReinitGlobals) {
|
||||
// note: this a block of code that loads values and stores them into the global variables to reset their values.
|
||||
out.write("\n<INITGLOBALS>\n")
|
||||
out.write("\n<INITGLOBALS>\n<CODE>\n")
|
||||
irProgram.globalInits.forEach { out.writeLine(it) }
|
||||
out.write("</INITGLOBALS>\n")
|
||||
out.write("</CODE>\n</INITGLOBALS>\n")
|
||||
}
|
||||
writeBlocks()
|
||||
out.write("</PROGRAM>\n")
|
||||
@ -30,36 +36,53 @@ class IRFileWriter(private val irProgram: IRProgram) {
|
||||
irProgram.blocks.forEach { block ->
|
||||
out.write("\n<BLOCK NAME=${block.name} ADDRESS=${block.address} ALIGN=${block.alignment} POS=${block.position}>\n")
|
||||
block.inlineAssembly.forEach {
|
||||
out.write("<INLINEASM POS=${it.position}>\n")
|
||||
out.write(it.asm)
|
||||
if(!it.asm.endsWith('\n'))
|
||||
out.write("\n")
|
||||
out.write("</INLINEASM>\n")
|
||||
writeInlineAsm(it)
|
||||
}
|
||||
block.subroutines.forEach {
|
||||
out.write("<SUB NAME=${it.name} RETURNTYPE=${it.returnType} POS=${it.position}>\n")
|
||||
it.lines.forEach { line -> out.writeLine(line) }
|
||||
out.write("<SUB NAME=${it.name} RETURNTYPE=${it.returnType.toString().lowercase()} POS=${it.position}>\n")
|
||||
// TODO rest of the signature
|
||||
it.chunks.forEach { chunk ->
|
||||
if(chunk is IRInlineAsmChunk) {
|
||||
writeInlineAsm(chunk)
|
||||
} else {
|
||||
out.write("<CODE>\n")
|
||||
if (chunk.lines.isEmpty())
|
||||
throw InternalCompilerException("empty code chunk in ${it.name} ${it.position}")
|
||||
chunk.lines.forEach { line -> out.writeLine(line) }
|
||||
out.write("</CODE>\n")
|
||||
}
|
||||
}
|
||||
out.write("</SUB>\n")
|
||||
}
|
||||
block.asmSubroutines.forEach {
|
||||
out.write("<ASMSUB SCOPEDNAME=${it.scopedName.joinToString(".")} ADDRESS=${it.address} POS=${it.position}>\n")
|
||||
it.lines.forEach { line -> out.writeLine(line) }
|
||||
out.write("</ASMSUB>\n")
|
||||
out.write("<ASMSUB NAME=${it.name} ADDRESS=${it.address} POS=${it.position}>\n")
|
||||
// TODO rest of the signature
|
||||
out.write("<INLINEASM POS=${it.position}>\n")
|
||||
out.write(it.assembly.trimStart('\n').trimEnd(' ', '\n'))
|
||||
out.write("\n</INLINEASM>\n</ASMSUB>\n")
|
||||
}
|
||||
out.write("</BLOCK>\n")
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeInlineAsm(chunk: IRInlineAsmChunk) {
|
||||
out.write("<INLINEASM POS=${chunk.position}>\n")
|
||||
out.write(chunk.asm.trimStart('\n').trimEnd(' ', '\n'))
|
||||
out.write("\n</INLINEASM>\n")
|
||||
}
|
||||
|
||||
private fun writeOptions() {
|
||||
out.write("<OPTIONS>\n")
|
||||
out.write("compTarget = ${irProgram.options.compTarget.name}\n")
|
||||
out.write("output = ${irProgram.options.output}\n")
|
||||
out.write("launcher = ${irProgram.options.launcher}\n")
|
||||
out.write("zeropage = ${irProgram.options.zeropage}\n")
|
||||
out.write("zpReserved = ${irProgram.options.zpReserved}\n")
|
||||
out.write("loadAddress = ${irProgram.options.loadAddress}\n")
|
||||
out.write("dontReinitGlobals = ${irProgram.options.dontReinitGlobals}\n")
|
||||
out.write("evalStackBaseAddress = ${irProgram.options.evalStackBaseAddress}\n")
|
||||
out.write("compTarget=${irProgram.options.compTarget.name}\n")
|
||||
out.write("output=${irProgram.options.output}\n")
|
||||
out.write("launcher=${irProgram.options.launcher}\n")
|
||||
out.write("zeropage=${irProgram.options.zeropage}\n")
|
||||
for(range in irProgram.options.zpReserved) {
|
||||
out.write("zpReserved=${range.first},${range.last}\n")
|
||||
}
|
||||
out.write("loadAddress=${irProgram.options.loadAddress}\n")
|
||||
out.write("dontReinitGlobals=${irProgram.options.dontReinitGlobals}\n")
|
||||
out.write("evalStackBaseAddress=${irProgram.options.evalStackBaseAddress}\n")
|
||||
// other options not yet useful here?
|
||||
out.write("</OPTIONS>\n")
|
||||
}
|
||||
@ -67,20 +90,13 @@ class IRFileWriter(private val irProgram: IRProgram) {
|
||||
private fun writeVariableAllocations() {
|
||||
out.write("\n<VARIABLES>\n")
|
||||
for (variable in irProgram.st.allVariables) {
|
||||
val typeStr = when(variable.dt) {
|
||||
DataType.UBYTE, DataType.ARRAY_UB, DataType.STR -> "ubyte"
|
||||
DataType.BYTE, DataType.ARRAY_B -> "byte"
|
||||
DataType.UWORD, DataType.ARRAY_UW -> "uword"
|
||||
DataType.WORD, DataType.ARRAY_W -> "word"
|
||||
DataType.FLOAT, DataType.ARRAY_F -> "float"
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
val typeStr = getTypeString(variable)
|
||||
val value = when(variable.dt) {
|
||||
DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: 0.0).toString()
|
||||
in NumericDatatypes -> (variable.onetimeInitializationNumericValue ?: 0).toHex()
|
||||
in NumericDatatypes -> (variable.onetimeInitializationNumericValue ?: 0)
|
||||
DataType.STR -> {
|
||||
val encoded = irProgram.encoding.encodeString(variable.onetimeInitializationStringValue!!.first, variable.onetimeInitializationStringValue!!.second) + listOf(0u)
|
||||
encoded.joinToString(",") { it.toInt().toHex() }
|
||||
encoded.joinToString(",") { it.toInt().toString() }
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
if(variable.onetimeInitializationArrayValue!=null) {
|
||||
@ -91,29 +107,22 @@ class IRFileWriter(private val irProgram: IRProgram) {
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
if(variable.onetimeInitializationArrayValue!==null) {
|
||||
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toHex() }
|
||||
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toInt().toString() }
|
||||
} else {
|
||||
(1..variable.length!!).joinToString(",") { "0" }
|
||||
}
|
||||
}
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
// TODO have uninitialized variables? (BSS SECTION)
|
||||
out.write("VAR ${variable.scopedName.joinToString(".")} $typeStr = $value\n")
|
||||
// TODO have uninitialized variables and arrays? (BSS SECTION)
|
||||
out.write("$typeStr ${variable.scopedName.joinToString(".")}=$value zp=${variable.zpwish}\n")
|
||||
}
|
||||
out.write("</VARIABLES>\n")
|
||||
|
||||
out.write("\n<MEMORYMAPPEDVARIABLES>\n")
|
||||
for (variable in irProgram.st.allMemMappedVariables) {
|
||||
val typeStr = when(variable.dt) {
|
||||
DataType.UBYTE, DataType.ARRAY_UB, DataType.STR -> "ubyte"
|
||||
DataType.BYTE, DataType.ARRAY_B -> "byte"
|
||||
DataType.UWORD, DataType.ARRAY_UW -> "uword"
|
||||
DataType.WORD, DataType.ARRAY_W -> "word"
|
||||
DataType.FLOAT, DataType.ARRAY_F -> "float"
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
out.write("MAP ${variable.scopedName.joinToString(".")} $typeStr ${variable.address}\n")
|
||||
val typeStr = getTypeString(variable)
|
||||
out.write("&$typeStr ${variable.scopedName.joinToString(".")}=${variable.address}\n")
|
||||
}
|
||||
out.write("</MEMORYMAPPEDVARIABLES>\n")
|
||||
|
||||
@ -122,6 +131,38 @@ class IRFileWriter(private val irProgram: IRProgram) {
|
||||
out.write("</MEMORYSLABS>\n")
|
||||
}
|
||||
|
||||
private fun getTypeString(memvar: StMemVar): String {
|
||||
return when(memvar.dt) {
|
||||
DataType.UBYTE -> "ubyte"
|
||||
DataType.BYTE -> "byte"
|
||||
DataType.UWORD -> "uword"
|
||||
DataType.WORD -> "word"
|
||||
DataType.FLOAT -> "float"
|
||||
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}]"
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTypeString(variable : StStaticVariable): String {
|
||||
return when(variable.dt) {
|
||||
DataType.UBYTE -> "ubyte"
|
||||
DataType.BYTE -> "byte"
|
||||
DataType.UWORD -> "uword"
|
||||
DataType.WORD -> "word"
|
||||
DataType.FLOAT -> "float"
|
||||
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}]"
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun BufferedWriter.writeLine(line: IRCodeLine) {
|
||||
when(line) {
|
||||
is IRCodeComment -> write("; ${line.comment}\n")
|
||||
|
3
intermediate/src/prog8/intermediate/IRParseException.kt
Normal file
3
intermediate/src/prog8/intermediate/IRParseException.kt
Normal file
@ -0,0 +1,3 @@
|
||||
package prog8.intermediate
|
||||
|
||||
class IRParseException(message: String) : Exception(message)
|
@ -2,36 +2,24 @@ package prog8.intermediate
|
||||
|
||||
class IRPeepholeOptimizer(private val vmprog: IRProgram) {
|
||||
fun optimize() {
|
||||
vmprog.blocks.forEach { block ->
|
||||
block.subroutines.forEach { sub ->
|
||||
/*
|
||||
sub.forEach { child ->
|
||||
when (child) {
|
||||
is IRCodeChunk -> {
|
||||
do {
|
||||
val indexedInstructions = child.lines.withIndex()
|
||||
.filter { it.value is IRCodeInstruction }
|
||||
.map { IndexedValue(it.index, (it.value as IRCodeInstruction).ins) }
|
||||
val changed = removeNops(child, indexedInstructions)
|
||||
|| removeDoubleLoadsAndStores(
|
||||
child,
|
||||
indexedInstructions
|
||||
) // TODO not yet implemented
|
||||
|| removeUselessArithmetic(child, indexedInstructions)
|
||||
|| removeWeirdBranches(child, indexedInstructions)
|
||||
|| removeDoubleSecClc(child, indexedInstructions)
|
||||
|| cleanupPushPop(child, indexedInstructions)
|
||||
// TODO other optimizations:
|
||||
// more complex optimizations such as unused registers
|
||||
} while (changed)
|
||||
}
|
||||
|
||||
else -> {
|
||||
TODO("block child $child")
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
vmprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
|
||||
sub.chunks.forEach { chunk ->
|
||||
do {
|
||||
val indexedInstructions = chunk.lines.withIndex()
|
||||
.filter { it.value is IRCodeInstruction }
|
||||
.map { IndexedValue(it.index, (it.value as IRCodeInstruction).ins) }
|
||||
val changed = removeNops(chunk, indexedInstructions)
|
||||
|| removeDoubleLoadsAndStores(
|
||||
chunk,
|
||||
indexedInstructions
|
||||
) // TODO not yet implemented
|
||||
|| removeUselessArithmetic(chunk, indexedInstructions)
|
||||
|| removeWeirdBranches(chunk, indexedInstructions)
|
||||
|| removeDoubleSecClc(chunk, indexedInstructions)
|
||||
|| cleanupPushPop(chunk, indexedInstructions)
|
||||
// TODO other optimizations:
|
||||
// more complex optimizations such as unused registers
|
||||
} while (changed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,22 +14,29 @@ PROGRAM:
|
||||
VARIABLES (from Symboltable)
|
||||
MEMORYMAPPEDVARIABLES (from Symboltable)
|
||||
MEMORYSLABS (from Symboltable)
|
||||
GLOBALINITS
|
||||
CODE-LINE (assignment to initialize a variable)
|
||||
CODE-LINE (assignment to initialize a variable)
|
||||
...
|
||||
INITGLOBALS
|
||||
CODE
|
||||
CODE-LINE (assignment to initialize a variable)
|
||||
...
|
||||
BLOCK
|
||||
INLINEASM
|
||||
INLINEASM
|
||||
SUB
|
||||
CODE-LINE (label, instruction, comment, inlinebinary)
|
||||
CODE-LINE
|
||||
CODE-LINE
|
||||
INLINEASM
|
||||
INLINEASM
|
||||
CODE
|
||||
CODE-LINE (label, instruction, comment, inlinebinary)
|
||||
CODE-LINE
|
||||
...
|
||||
CODE
|
||||
CODE
|
||||
...
|
||||
SUB
|
||||
SUB
|
||||
ASMSUB
|
||||
INLINEASM
|
||||
ASMSUB
|
||||
INLINEASM
|
||||
...
|
||||
BLOCK
|
||||
BLOCK
|
||||
@ -45,7 +52,11 @@ class IRProgram(val name: String,
|
||||
val blocks = mutableListOf<IRBlock>()
|
||||
|
||||
fun addGlobalInits(chunk: IRCodeChunk) = globalInits.addAll(chunk.lines)
|
||||
fun addBlock(block: IRBlock) = blocks.add(block)
|
||||
fun addBlock(block: IRBlock) {
|
||||
if(blocks.any { it.name==block.name })
|
||||
throw IllegalArgumentException("duplicate block ${block.name} ${block.position}")
|
||||
blocks.add(block)
|
||||
}
|
||||
}
|
||||
|
||||
class IRBlock(
|
||||
@ -68,17 +79,30 @@ class IRBlock(
|
||||
class IRSubroutine(val name: String,
|
||||
val returnType: DataType?,
|
||||
val position: Position) {
|
||||
val lines = mutableListOf<IRCodeLine>()
|
||||
val chunks = mutableListOf<IRCodeChunk>()
|
||||
|
||||
operator fun plusAssign(chunk: IRCodeChunk) { lines += chunk.lines }
|
||||
init {
|
||||
if(!name.contains('.'))
|
||||
throw IllegalArgumentException("subroutine name is not scoped: $name")
|
||||
if(name.startsWith("main.main."))
|
||||
throw IllegalArgumentException("subroutine name invalid main prefix: $name")
|
||||
}
|
||||
|
||||
operator fun plusAssign(chunk: IRCodeChunk) { chunks+= chunk }
|
||||
}
|
||||
|
||||
class IRAsmSubroutine(val scopedName: List<String>,
|
||||
class IRAsmSubroutine(val name: String,
|
||||
val position: Position,
|
||||
val address: UInt?,
|
||||
val assembly: String) {
|
||||
val lines = mutableListOf<IRCodeLine>()
|
||||
|
||||
init {
|
||||
if(!name.contains('.'))
|
||||
throw IllegalArgumentException("subroutine name is not scoped: $name")
|
||||
if(name.startsWith("main.main."))
|
||||
throw IllegalArgumentException("subroutine name invalid main prefix: $name")
|
||||
}
|
||||
}
|
||||
|
||||
sealed class IRCodeLine
|
||||
@ -131,6 +155,9 @@ class IRCodeInlineBinary(val file: Path, val offset: UInt?, val length: UInt?):
|
||||
open class IRCodeChunk(val position: Position) {
|
||||
val lines = mutableListOf<IRCodeLine>()
|
||||
|
||||
open fun isEmpty() = lines.isEmpty()
|
||||
open fun isNotEmpty() = lines.isNotEmpty()
|
||||
|
||||
operator fun plusAssign(line: IRCodeLine) {
|
||||
lines.add(line)
|
||||
}
|
||||
@ -140,5 +167,9 @@ open class IRCodeChunk(val position: Position) {
|
||||
}
|
||||
}
|
||||
|
||||
class IRInlineAsmChunk(val asm: String, position: Position): IRCodeChunk(position) // note: no lines, asm is in the property
|
||||
class IRInlineAsmChunk(val asm: String, position: Position): IRCodeChunk(position) {
|
||||
// note: no lines, asm is in the property
|
||||
override fun isEmpty() = asm.isBlank()
|
||||
override fun isNotEmpty() = asm.isNotBlank()
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user