mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
ir: keep order of children in block
This commit is contained in:
parent
77e956a29f
commit
c21913a66b
@ -75,36 +75,36 @@ class IRCodeGen(
|
||||
// make sure that first chunks in Blocks and Subroutines share the name of the block/sub as label.
|
||||
|
||||
irProg.blocks.forEach { block ->
|
||||
if(block.inlineAssemblies.isNotEmpty()) {
|
||||
val first = block.inlineAssemblies.first()
|
||||
block.children.firstOrNull { it is IRInlineAsmChunk }?.let { first->
|
||||
first as IRInlineAsmChunk
|
||||
if(first.label==null) {
|
||||
val replacement = IRInlineAsmChunk(block.name, first.assembly, first.isIR, first.next)
|
||||
block.inlineAssemblies.removeAt(0)
|
||||
block.inlineAssemblies.add(0, replacement)
|
||||
block.children.removeAt(0)
|
||||
block.children.add(0, replacement)
|
||||
} else if(first.label != block.name) {
|
||||
throw AssemblyError("first chunk in block has label that differs from block name")
|
||||
}
|
||||
}
|
||||
|
||||
block.subroutines.forEach { sub ->
|
||||
block.children.filterIsInstance<IRSubroutine>().forEach { sub ->
|
||||
if(sub.chunks.isNotEmpty()) {
|
||||
val first = sub.chunks.first()
|
||||
if(first.label==null) {
|
||||
val replacement = when(first) {
|
||||
is IRCodeChunk -> {
|
||||
val replacement = IRCodeChunk(sub.name, first.next)
|
||||
val replacement = IRCodeChunk(sub.label, first.next)
|
||||
replacement.instructions += first.instructions
|
||||
replacement
|
||||
}
|
||||
is IRInlineAsmChunk -> IRInlineAsmChunk(sub.name, first.assembly, first.isIR, first.next)
|
||||
is IRInlineBinaryChunk -> IRInlineBinaryChunk(sub.name, first.data, first.next)
|
||||
is IRInlineAsmChunk -> IRInlineAsmChunk(sub.label, first.assembly, first.isIR, first.next)
|
||||
is IRInlineBinaryChunk -> IRInlineBinaryChunk(sub.label, first.data, first.next)
|
||||
else -> throw AssemblyError("invalid chunk")
|
||||
}
|
||||
sub.chunks.removeAt(0)
|
||||
sub.chunks.add(0, replacement)
|
||||
} else if(first.label != sub.name) {
|
||||
} else if(first.label != sub.label) {
|
||||
val next = if(first is IRCodeChunk) first else null
|
||||
sub.chunks.add(0, IRCodeChunk(sub.name, next))
|
||||
sub.chunks.add(0, IRCodeChunk(sub.label, next))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,7 +116,7 @@ class IRCodeGen(
|
||||
// note: we do still export the memory mapped symbols so a code generator can use those
|
||||
// for instance when a piece of inlined assembly references them.
|
||||
val replacements = mutableListOf<Triple<IRCodeChunkBase, Int, UInt>>()
|
||||
irProg.blocks.asSequence().flatMap { it.subroutines }.flatMap { it.chunks }.forEach { chunk ->
|
||||
irProg.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.flatMap { it.chunks }.forEach { chunk ->
|
||||
chunk.instructions.withIndex().forEach {
|
||||
(idx, instr) ->
|
||||
val symbolExpr = instr.labelSymbol
|
||||
|
@ -4,7 +4,7 @@ import prog8.intermediate.*
|
||||
|
||||
internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
fun optimize() {
|
||||
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
|
||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||
removeEmptyChunks(sub)
|
||||
joinChunks(sub)
|
||||
sub.chunks.withIndex().forEach { (index, chunk1) ->
|
||||
|
@ -9,7 +9,7 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
|
||||
fun optimize(): Int {
|
||||
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
|
||||
|
||||
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
|
||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||
sub.chunks.forEach { chunk ->
|
||||
chunk.label?.let { allLabeledChunks[it] = chunk }
|
||||
}
|
||||
@ -19,11 +19,11 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
|
||||
|
||||
// remove empty subs
|
||||
irprog.blocks.forEach { block ->
|
||||
block.subroutines.reversed().forEach { sub ->
|
||||
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
|
||||
if(sub.isEmpty()) {
|
||||
if(!sub.position.file.startsWith(libraryFilePrefix))
|
||||
errors.warn("unused subroutine ${sub.name}", sub.position)
|
||||
block.subroutines.remove(sub)
|
||||
errors.warn("unused subroutine ${sub.label}", sub.position)
|
||||
block.children.remove(sub)
|
||||
numRemoved++
|
||||
}
|
||||
}
|
||||
@ -41,7 +41,8 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
|
||||
}
|
||||
|
||||
private fun removeUnreachable(allLabeledChunks: MutableMap<String, IRCodeChunkBase>): Int {
|
||||
val reachable = mutableSetOf(irprog.blocks.single { it.name=="main" }.subroutines.single { it.name=="main.start" }.chunks.first())
|
||||
val entrypointSub = irprog.blocks.single { it.name=="main" }.children.single { it is IRSubroutine && it.label=="main.start" }
|
||||
val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first())
|
||||
|
||||
fun grow() {
|
||||
val new = mutableSetOf<IRCodeChunkBase>()
|
||||
@ -71,7 +72,7 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
|
||||
private fun removeSimpleUnlinked(allLabeledChunks: Map<String, IRCodeChunkBase>): Int {
|
||||
val linkedChunks = mutableSetOf<IRCodeChunkBase>()
|
||||
|
||||
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
|
||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||
sub.chunks.forEach { chunk ->
|
||||
chunk.next?.let { next -> linkedChunks += next }
|
||||
chunk.instructions.forEach {
|
||||
@ -93,7 +94,7 @@ internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val er
|
||||
linkedChunks: MutableSet<IRCodeChunkBase>
|
||||
): Int {
|
||||
var numRemoved = 0
|
||||
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
|
||||
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub ->
|
||||
sub.chunks.withIndex().reversed().forEach { (index, chunk) ->
|
||||
if (chunk !in linkedChunks) {
|
||||
if (chunk === sub.chunks[0]) {
|
||||
|
@ -36,7 +36,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
return makeIRProgram(listOf(chunk))
|
||||
}
|
||||
|
||||
fun IRProgram.chunks(): List<IRCodeChunkBase> = this.blocks.flatMap { it.subroutines }.flatMap { it.chunks }
|
||||
fun IRProgram.chunks(): List<IRCodeChunkBase> = this.blocks.flatMap { it.children.filterIsInstance<IRSubroutine>() }.flatMap { it.chunks }
|
||||
|
||||
test("remove nops") {
|
||||
val irProg = makeIRProgram(listOf(
|
||||
|
@ -3,12 +3,13 @@ TODO
|
||||
|
||||
For next release
|
||||
^^^^^^^^^^^^^^^^
|
||||
- ir/vm: allow label in block scope (correct order of block nodes!)
|
||||
- regression test the various projects
|
||||
- ir/vm: check weird asm chunks appearing in block?
|
||||
- attempt to fix the expression codegen bug with reused temp vars (github #89)
|
||||
- AstIdentifiersChecker: can a subroutine really not have the same name as its enclosing block? 64tass problem?
|
||||
- 6502 codegen: make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``p8v_``? Or not worth it (most 3 letter opcodes as variables are nonsensical anyway)
|
||||
then we can get rid of the instruction lists in the machinedefinitions as well. This is already no problem at all in the IR codegen.
|
||||
- create BSS section in output program and put StStaticVariables in there with bss=true. Don't forget to add init code to zero out everything that was put in bss. If array in bss->only zero ONCE! So requires self-modifying code
|
||||
- regression test the various projects
|
||||
|
||||
...
|
||||
|
||||
@ -24,7 +25,6 @@ Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Compiler:
|
||||
|
||||
- AstIdentifiersChecker: can a subroutine really not have the same name as its enclosing block?
|
||||
- ir: mechanism to determine for chunks which registers are getting input values from "outside"
|
||||
- ir: mechanism to determine for chunks which registers are passing values out? (i.e. are used again in another chunk)
|
||||
- ir: peephole opt: renumber registers in chunks to start with 1 again every time (but keep entry values in mind!)
|
||||
|
@ -4,6 +4,14 @@
|
||||
main {
|
||||
|
||||
alsostart:
|
||||
|
||||
%asm {{
|
||||
; inline asm in block #1
|
||||
nop
|
||||
}}
|
||||
|
||||
%asmbinary "../gradle.properties"
|
||||
|
||||
sub start() {
|
||||
|
||||
internalstart:
|
||||
@ -30,6 +38,18 @@ alsostart:
|
||||
internalend:
|
||||
}
|
||||
|
||||
%asm {{
|
||||
; inline asm in block #2
|
||||
nop
|
||||
}}
|
||||
|
||||
startend:
|
||||
|
||||
%asmbinary "../settings.gradle"
|
||||
|
||||
%asm {{
|
||||
; inline asm in block #3
|
||||
nop
|
||||
}}
|
||||
|
||||
}
|
||||
|
@ -46,47 +46,45 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
private fun writeBlocks() {
|
||||
irProgram.blocks.forEach { block ->
|
||||
out.write("\n<BLOCK NAME=\"${block.name}\" ADDRESS=\"${block.address?.toHex()}\" ALIGN=\"${block.alignment}\" POS=\"${block.position}\">\n")
|
||||
block.inlineAssemblies.forEach {
|
||||
writeInlineAsm(it)
|
||||
}
|
||||
block.labels.forEach {
|
||||
writeCodeChunk(it) // TODO doing it like this isn't useful, block needs to have a list of nodes rather than a few separate collections
|
||||
}
|
||||
block.subroutines.forEach {
|
||||
out.write("<SUB NAME=\"${it.name}\" RETURNTYPE=\"${it.returnType.toString().lowercase()}\" POS=\"${it.position}\">\n")
|
||||
out.write("<PARAMS>\n")
|
||||
it.parameters.forEach { param -> out.write("${getTypeString(param.dt)} ${param.name}\n") }
|
||||
out.write("</PARAMS>\n")
|
||||
it.chunks.forEach { chunk ->
|
||||
numChunks++
|
||||
when (chunk) {
|
||||
is IRInlineAsmChunk -> writeInlineAsm(chunk)
|
||||
is IRInlineBinaryChunk -> writeInlineBytes(chunk)
|
||||
is IRCodeChunk -> writeCodeChunk(chunk)
|
||||
else -> throw InternalCompilerException("invalid chunk")
|
||||
block.children.forEach { child ->
|
||||
when(child) {
|
||||
is IRAsmSubroutine -> {
|
||||
val clobbers = child.clobbers.joinToString(",")
|
||||
val returns = child.returns.map { ret ->
|
||||
if(ret.reg.registerOrPair!=null) "${ret.reg.registerOrPair}:${ret.dt.toString().lowercase()}"
|
||||
else "${ret.reg.statusflag}:${ret.dt.toString().lowercase()}"
|
||||
}.joinToString(",")
|
||||
out.write("<ASMSUB NAME=\"${child.label}\" ADDRESS=\"${child.address?.toHex()}\" CLOBBERS=\"$clobbers\" RETURNS=\"$returns\" POS=\"${child.position}\">\n")
|
||||
out.write("<ASMPARAMS>\n")
|
||||
child.parameters.forEach { ret ->
|
||||
val reg = if(ret.reg.registerOrPair!=null) ret.reg.registerOrPair.toString()
|
||||
else ret.reg.statusflag.toString()
|
||||
out.write("${ret.dt.toString().lowercase()} $reg\n")
|
||||
}
|
||||
out.write("</ASMPARAMS>\n")
|
||||
writeInlineAsm(child.asmChunk)
|
||||
out.write("</ASMSUB>\n")
|
||||
}
|
||||
is IRCodeChunk -> writeCodeChunk(child)
|
||||
is IRInlineAsmChunk -> writeInlineAsm(child)
|
||||
is IRInlineBinaryChunk -> writeInlineBytes(child)
|
||||
is IRSubroutine -> {
|
||||
out.write("<SUB NAME=\"${child.label}\" RETURNTYPE=\"${child.returnType.toString().lowercase()}\" POS=\"${child.position}\">\n")
|
||||
out.write("<PARAMS>\n")
|
||||
child.parameters.forEach { param -> out.write("${getTypeString(param.dt)} ${param.name}\n") }
|
||||
out.write("</PARAMS>\n")
|
||||
child.chunks.forEach { chunk ->
|
||||
numChunks++
|
||||
when (chunk) {
|
||||
is IRInlineAsmChunk -> writeInlineAsm(chunk)
|
||||
is IRInlineBinaryChunk -> writeInlineBytes(chunk)
|
||||
is IRCodeChunk -> writeCodeChunk(chunk)
|
||||
else -> throw InternalCompilerException("invalid chunk")
|
||||
}
|
||||
}
|
||||
out.write("</SUB>\n")
|
||||
}
|
||||
}
|
||||
out.write("</SUB>\n")
|
||||
}
|
||||
block.asmSubroutines.forEach {
|
||||
val clobbers = it.clobbers.joinToString(",")
|
||||
val returns = it.returns.map { ret ->
|
||||
if(ret.reg.registerOrPair!=null) "${ret.reg.registerOrPair}:${ret.dt.toString().lowercase()}"
|
||||
else "${ret.reg.statusflag}:${ret.dt.toString().lowercase()}"
|
||||
}.joinToString(",")
|
||||
out.write("<ASMSUB NAME=\"${it.name}\" ADDRESS=\"${it.address?.toHex()}\" CLOBBERS=\"$clobbers\" RETURNS=\"$returns\" POS=\"${it.position}\">\n")
|
||||
out.write("<ASMPARAMS>\n")
|
||||
it.parameters.forEach { ret ->
|
||||
val reg = if(ret.reg.registerOrPair!=null) ret.reg.registerOrPair.toString()
|
||||
else ret.reg.statusflag.toString()
|
||||
out.write("${ret.dt.toString().lowercase()} $reg\n")
|
||||
}
|
||||
out.write("</ASMPARAMS>\n")
|
||||
writeInlineAsm(it.asmChunk)
|
||||
out.write("</ASMSUB>\n")
|
||||
}
|
||||
block.inlineBinaries.forEach {
|
||||
writeInlineBytes(it)
|
||||
}
|
||||
out.write("</BLOCK>\n")
|
||||
}
|
||||
|
@ -70,30 +70,45 @@ class IRProgram(val name: String,
|
||||
|
||||
fun linkChunks() {
|
||||
fun getLabeledChunks(): Map<String?, IRCodeChunkBase> {
|
||||
return blocks.flatMap { it.subroutines }.flatMap { it.chunks }.associateBy { it.label } +
|
||||
blocks.flatMap { it.asmSubroutines }.map { it.asmChunk }.associateBy { it.label }
|
||||
val result = mutableMapOf<String?, IRCodeChunkBase>()
|
||||
blocks.forEach { block ->
|
||||
block.children.forEach { child ->
|
||||
when(child) {
|
||||
is IRAsmSubroutine -> result[child.asmChunk.label] = child.asmChunk
|
||||
is IRCodeChunk -> result[child.label] = child
|
||||
is IRInlineAsmChunk -> result[child.label] = child
|
||||
is IRInlineBinaryChunk -> result[child.label] = child
|
||||
is IRSubroutine -> result.putAll(child.chunks.associateBy { it.label })
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
val labeledChunks = getLabeledChunks()
|
||||
|
||||
if(globalInits.isNotEmpty()) {
|
||||
if(globalInits.next==null) {
|
||||
// link globalinits to subsequent chunk
|
||||
val firstBlock = blocks.firstOrNull()
|
||||
if(firstBlock!=null) {
|
||||
// TODO what is the first chunk in a block?
|
||||
if(firstBlock.inlineAssemblies.isNotEmpty()) {
|
||||
globalInits.next = firstBlock.inlineAssemblies.first()
|
||||
} else if(firstBlock.subroutines.isNotEmpty()) {
|
||||
val firstSub = firstBlock.subroutines.first()
|
||||
if(firstSub.chunks.isNotEmpty())
|
||||
globalInits.next = firstSub.chunks.first()
|
||||
if(firstBlock!=null && firstBlock.isNotEmpty()) {
|
||||
firstBlock.children.forEach { child ->
|
||||
when(child) {
|
||||
is IRAsmSubroutine -> throw AssemblyError("cannot link next to asmsub $child")
|
||||
is IRCodeChunk -> globalInits.next = child
|
||||
is IRInlineAsmChunk -> globalInits.next = child
|
||||
is IRInlineBinaryChunk -> globalInits.next = child
|
||||
is IRSubroutine -> {
|
||||
if(child.chunks.isNotEmpty())
|
||||
globalInits.next = child.chunks.first()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
|
||||
|
||||
fun linkSubroutineChunks(sub: IRSubroutine) {
|
||||
sub.chunks.withIndex().forEach { (index, chunk) ->
|
||||
|
||||
fun nextChunk(): IRCodeChunkBase? = if(index<sub.chunks.size-1) sub.chunks[index + 1] else null
|
||||
@ -137,37 +152,46 @@ class IRProgram(val name: String,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blocks.forEach { block ->
|
||||
block.children.forEachIndexed { index, child ->
|
||||
val next = if(index<block.children.size-1) block.children[index+1] as? IRCodeChunkBase else null
|
||||
when (child) {
|
||||
is IRAsmSubroutine -> child.asmChunk.next = next
|
||||
is IRCodeChunk -> child.next = next
|
||||
is IRInlineAsmChunk -> child.next = next
|
||||
is IRInlineBinaryChunk -> child.next = next
|
||||
is IRSubroutine -> linkSubroutineChunks(child)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun validate() {
|
||||
blocks.forEach { block ->
|
||||
// TODO what is the *first* chunk in the block?
|
||||
if(block.inlineAssemblies.isNotEmpty()) {
|
||||
require(block.inlineAssemblies.first().label == block.name) { "first block chunk should have block name as its label" }
|
||||
}
|
||||
block.inlineAssemblies.forEach { chunk ->
|
||||
require(chunk.instructions.isEmpty())
|
||||
}
|
||||
block.subroutines.forEach { sub ->
|
||||
if(sub.chunks.isNotEmpty()) {
|
||||
require(sub.chunks.first().label == sub.name) { "first chunk in subroutine should have sub name as its label" }
|
||||
}
|
||||
sub.chunks.forEach { chunk ->
|
||||
if (chunk is IRCodeChunk) {
|
||||
require(chunk.instructions.isNotEmpty() || chunk.label != null)
|
||||
if(chunk.instructions.lastOrNull()?.opcode in OpcodesThatJump)
|
||||
require(chunk.next == null) { "chunk ending with a jump shouldn't be linked to next" }
|
||||
else {
|
||||
// if chunk is NOT the last in the block, it needs to link to next.
|
||||
val isLast = sub.chunks.last() === chunk
|
||||
require(isLast || chunk.next != null) { "chunk needs to be linked to next" }
|
||||
}
|
||||
if(block.isNotEmpty()) {
|
||||
block.children.filterIsInstance<IRInlineAsmChunk>().forEach { chunk -> require(chunk.instructions.isEmpty()) }
|
||||
block.children.filterIsInstance<IRSubroutine>().forEach { sub ->
|
||||
if(sub.chunks.isNotEmpty()) {
|
||||
require(sub.chunks.first().label == sub.label) { "first chunk in subroutine should have sub name (label) as its label" }
|
||||
}
|
||||
else
|
||||
require(chunk.instructions.isEmpty())
|
||||
chunk.instructions.forEach {
|
||||
if(it.labelSymbol!=null && it.opcode in OpcodesThatBranch)
|
||||
require(it.branchTarget != null) { "branching instruction to label should have branchTarget set" }
|
||||
sub.chunks.forEach { chunk ->
|
||||
if (chunk is IRCodeChunk) {
|
||||
require(chunk.instructions.isNotEmpty() || chunk.label != null)
|
||||
if(chunk.instructions.lastOrNull()?.opcode in OpcodesThatJump)
|
||||
require(chunk.next == null) { "chunk ending with a jump shouldn't be linked to next" }
|
||||
else {
|
||||
// if chunk is NOT the last in the block, it needs to link to next.
|
||||
val isLast = sub.chunks.last() === chunk
|
||||
require(isLast || chunk.next != null) { "chunk needs to be linked to next" }
|
||||
}
|
||||
}
|
||||
else
|
||||
require(chunk.instructions.isEmpty())
|
||||
chunk.instructions.forEach {
|
||||
if(it.labelSymbol!=null && it.opcode in OpcodesThatBranch)
|
||||
require(it.branchTarget != null) { "branching instruction to label should have branchTarget set" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -188,10 +212,16 @@ class IRProgram(val name: String,
|
||||
}
|
||||
|
||||
globalInits.instructions.forEach { it.addUsedRegistersCounts(inputRegs, outputRegs, inputFpRegs, outputFpRegs) }
|
||||
blocks.forEach {
|
||||
it.inlineAssemblies.forEach { chunk -> addUsed(chunk.usedRegisters()) }
|
||||
it.subroutines.flatMap { sub->sub.chunks }.forEach { chunk -> addUsed(chunk.usedRegisters()) }
|
||||
it.asmSubroutines.forEach { asmsub -> addUsed(asmsub.usedRegisters()) }
|
||||
blocks.forEach {block ->
|
||||
block.children.forEach { child ->
|
||||
when(child) {
|
||||
is IRAsmSubroutine -> addUsed(child.usedRegisters())
|
||||
is IRCodeChunk -> addUsed(child.usedRegisters())
|
||||
is IRInlineAsmChunk -> addUsed(child.usedRegisters())
|
||||
is IRInlineBinaryChunk -> addUsed(child.usedRegisters())
|
||||
is IRSubroutine -> child.chunks.forEach { chunk -> addUsed(chunk.usedRegisters()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RegistersUsed(inputRegs, outputRegs, inputFpRegs, outputFpRegs)
|
||||
@ -205,11 +235,7 @@ class IRBlock(
|
||||
val position: Position
|
||||
) {
|
||||
// TODO not separate lists but just a single list of chunks, like IRSubroutine? (but these are not all chunks...)
|
||||
val inlineAssemblies = mutableListOf<IRInlineAsmChunk>()
|
||||
val subroutines = mutableListOf<IRSubroutine>()
|
||||
val asmSubroutines = mutableListOf<IRAsmSubroutine>()
|
||||
val inlineBinaries = mutableListOf<IRInlineBinaryChunk>()
|
||||
val labels = mutableListOf<IRCodeChunk>() // empty code chunks having just a label.
|
||||
val children = mutableListOf<IIRBlockElement>()
|
||||
|
||||
enum class BlockAlignment {
|
||||
NONE,
|
||||
@ -217,40 +243,41 @@ class IRBlock(
|
||||
PAGE
|
||||
}
|
||||
|
||||
operator fun plusAssign(sub: IRSubroutine) {
|
||||
subroutines += sub
|
||||
}
|
||||
operator fun plusAssign(sub: IRAsmSubroutine) { asmSubroutines += sub }
|
||||
operator fun plusAssign(asm: IRInlineAsmChunk) { inlineAssemblies += asm }
|
||||
operator fun plusAssign(binary: IRInlineBinaryChunk) { inlineBinaries += binary }
|
||||
operator fun plusAssign(sub: IRSubroutine) { children += sub }
|
||||
operator fun plusAssign(sub: IRAsmSubroutine) { children += sub }
|
||||
operator fun plusAssign(asm: IRInlineAsmChunk) { children += asm }
|
||||
operator fun plusAssign(binary: IRInlineBinaryChunk) { children += binary }
|
||||
operator fun plusAssign(irCodeChunk: IRCodeChunk) {
|
||||
// this is for a separate label in the block scope. (random code statements are not allowed)
|
||||
require(irCodeChunk.isEmpty() && irCodeChunk.label!=null)
|
||||
labels += irCodeChunk
|
||||
}
|
||||
|
||||
fun isEmpty(): Boolean {
|
||||
val noAsm = inlineAssemblies.isEmpty() || inlineAssemblies.all { it.isEmpty() }
|
||||
val noSubs = subroutines.isEmpty() || subroutines.all { it.isEmpty() }
|
||||
val noAsmSubs = asmSubroutines.isEmpty() || asmSubroutines.all { it.isEmpty() }
|
||||
val noBins = inlineBinaries.isEmpty() || inlineBinaries.all { it.isEmpty() }
|
||||
return noAsm && noSubs && noAsmSubs && noBins
|
||||
children += irCodeChunk
|
||||
}
|
||||
|
||||
fun isEmpty(): Boolean = children.isEmpty() || children.all { it.isEmpty() }
|
||||
fun isNotEmpty(): Boolean = !isEmpty()
|
||||
}
|
||||
|
||||
class IRSubroutine(val name: String,
|
||||
val parameters: List<IRParam>,
|
||||
val returnType: DataType?,
|
||||
val position: Position) {
|
||||
|
||||
sealed interface IIRBlockElement {
|
||||
val label: String?
|
||||
fun isEmpty(): Boolean
|
||||
fun isNotEmpty(): Boolean
|
||||
}
|
||||
|
||||
|
||||
class IRSubroutine(
|
||||
override val label: String,
|
||||
val parameters: List<IRParam>,
|
||||
val returnType: DataType?,
|
||||
val position: Position): IIRBlockElement {
|
||||
|
||||
class IRParam(val name: String, val dt: DataType)
|
||||
|
||||
val chunks = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
init {
|
||||
require('.' in name) {"subroutine name is not scoped: $name"}
|
||||
require(!name.startsWith("main.main.")) {"subroutine name invalid main prefix: $name"}
|
||||
require('.' in label) {"subroutine name is not scoped: $label"}
|
||||
require(!label.startsWith("main.main.")) {"subroutine name invalid main prefix: $label"}
|
||||
|
||||
// params and return value should not be str
|
||||
require(parameters.all{ it.dt in NumericDatatypes }) {"non-numeric parameter"}
|
||||
@ -264,37 +291,41 @@ class IRSubroutine(val name: String,
|
||||
chunks+= chunk
|
||||
}
|
||||
|
||||
fun isEmpty(): Boolean = chunks.isEmpty() || chunks.all { it.isEmpty() }
|
||||
override fun isEmpty(): Boolean = chunks.isEmpty() || chunks.all { it.isEmpty() }
|
||||
override fun isNotEmpty(): Boolean = !isEmpty()
|
||||
}
|
||||
|
||||
|
||||
class IRAsmSubroutine(
|
||||
val name: String,
|
||||
override val label: String,
|
||||
val address: UInt?,
|
||||
val clobbers: Set<CpuRegister>,
|
||||
val parameters: List<IRAsmParam>,
|
||||
val returns: List<IRAsmParam>,
|
||||
val asmChunk: IRInlineAsmChunk,
|
||||
val position: Position
|
||||
) {
|
||||
): IIRBlockElement {
|
||||
|
||||
class IRAsmParam(val reg: RegisterOrStatusflag, val dt: DataType)
|
||||
|
||||
init {
|
||||
require('.' in name) { "subroutine name is not scoped: $name" }
|
||||
require(!name.startsWith("main.main.")) { "subroutine name invalid main prefix: $name" }
|
||||
require('.' in label) { "subroutine name is not scoped: $label" }
|
||||
require(!label.startsWith("main.main.")) { "subroutine name invalid main prefix: $label" }
|
||||
}
|
||||
|
||||
private val registersUsed by lazy { registersUsedInAssembly(asmChunk.isIR, asmChunk.assembly) }
|
||||
|
||||
fun usedRegisters() = registersUsed
|
||||
fun isEmpty(): Boolean = if(address==null) asmChunk.isEmpty() else false
|
||||
override fun isEmpty(): Boolean = if(address==null) asmChunk.isEmpty() else false
|
||||
override fun isNotEmpty(): Boolean = !isEmpty()
|
||||
}
|
||||
|
||||
sealed class IRCodeChunkBase(val label: String?, var next: IRCodeChunkBase?) {
|
||||
|
||||
sealed class IRCodeChunkBase(override val label: String?, var next: IRCodeChunkBase?): IIRBlockElement {
|
||||
val instructions = mutableListOf<IRInstruction>()
|
||||
|
||||
abstract fun isEmpty(): Boolean
|
||||
abstract fun isNotEmpty(): Boolean
|
||||
abstract override fun isEmpty(): Boolean
|
||||
abstract override fun isNotEmpty(): Boolean
|
||||
abstract fun usedRegisters(): RegistersUsed
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ class VmProgramLoader {
|
||||
|
||||
// make sure that if there is a "main.start" entrypoint, we jump to it
|
||||
irProgram.blocks.firstOrNull()?.let {
|
||||
if(it.subroutines.any { sub -> sub.name=="main.start" }) {
|
||||
if(it.children.any { sub -> sub is IRSubroutine && sub.label=="main.start" }) {
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
placeholders[Pair(chunk, 0)] = "main.start"
|
||||
chunk += IRInstruction(Opcode.JUMP, labelSymbol = "main.start")
|
||||
@ -37,37 +37,36 @@ class VmProgramLoader {
|
||||
if(block.address!=null)
|
||||
throw IRParseException("blocks cannot have a load address for vm: ${block.name}")
|
||||
|
||||
block.inlineAssemblies.forEach {
|
||||
val replacement = addAssemblyToProgram(it, programChunks, variableAddresses)
|
||||
chunkReplacements += replacement
|
||||
}
|
||||
block.labels.forEach {
|
||||
programChunks += it // TODO doing it like this isn't useful, block needs to have a list of nodes rather than a few separate collections
|
||||
}
|
||||
block.subroutines.forEach {
|
||||
it.chunks.forEach { chunk ->
|
||||
when (chunk) {
|
||||
is IRInlineAsmChunk -> {
|
||||
val replacement = addAssemblyToProgram(chunk, programChunks, variableAddresses)
|
||||
block.children.forEach { child ->
|
||||
when(child) {
|
||||
is IRAsmSubroutine -> {
|
||||
if(!child.asmChunk.isIR)
|
||||
throw IRParseException("vm currently does not support non-IR asmsubs: ${child.label}")
|
||||
else {
|
||||
val replacement = addAssemblyToProgram(child.asmChunk, programChunks, variableAddresses)
|
||||
chunkReplacements += replacement
|
||||
}
|
||||
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO
|
||||
is IRCodeChunk -> programChunks += chunk
|
||||
else -> throw AssemblyError("weird chunk type")
|
||||
}
|
||||
is IRCodeChunk -> programChunks += child
|
||||
is IRInlineAsmChunk -> {
|
||||
val replacement = addAssemblyToProgram(child, programChunks, variableAddresses)
|
||||
chunkReplacements += replacement
|
||||
}
|
||||
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO
|
||||
is IRSubroutine -> {
|
||||
child.chunks.forEach { chunk ->
|
||||
when (chunk) {
|
||||
is IRInlineAsmChunk -> {
|
||||
val replacement = addAssemblyToProgram(chunk, programChunks, variableAddresses)
|
||||
chunkReplacements += replacement
|
||||
}
|
||||
is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO
|
||||
is IRCodeChunk -> programChunks += chunk
|
||||
else -> throw AssemblyError("weird chunk type")
|
||||
}
|
||||
} }
|
||||
}
|
||||
}
|
||||
block.asmSubroutines.forEach {
|
||||
if(!it.asmChunk.isIR)
|
||||
throw IRParseException("vm currently does not support non-IR asmsubs: ${block.asmSubroutines.first().name}")
|
||||
else {
|
||||
val replacement = addAssemblyToProgram(it.asmChunk, programChunks, variableAddresses)
|
||||
chunkReplacements += replacement
|
||||
}
|
||||
}
|
||||
block.inlineBinaries.forEach {
|
||||
throw IRParseException("inline binary data not yet supported in the VM") // TODO
|
||||
}
|
||||
}
|
||||
|
||||
pass2translateSyscalls(programChunks)
|
||||
|
@ -42,7 +42,7 @@ class TestVm: FunSpec( {
|
||||
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
|
||||
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
|
||||
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
|
||||
val code = IRCodeChunk(startSub.name, null)
|
||||
val code = IRCodeChunk(startSub.label, null)
|
||||
code += IRInstruction(Opcode.NOP)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=1, value=12345)
|
||||
code += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1=1, value=1000)
|
||||
@ -70,7 +70,7 @@ class TestVm: FunSpec( {
|
||||
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
|
||||
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
|
||||
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
|
||||
val code = IRCodeChunk(startSub.name, null)
|
||||
val code = IRCodeChunk(startSub.label, null)
|
||||
code += IRInstruction(Opcode.BINARYDATA, binaryData = listOf(1u,2u,3u))
|
||||
code += IRInstruction(Opcode.RETURN)
|
||||
startSub += code
|
||||
|
Loading…
x
Reference in New Issue
Block a user