ir: keep order of children in block

This commit is contained in:
Irmen de Jong 2022-11-22 02:04:24 +01:00
parent 77e956a29f
commit c21913a66b
10 changed files with 217 additions and 168 deletions

View File

@ -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

View File

@ -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) ->

View File

@ -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]) {

View File

@ -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(

View File

@ -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!)

View File

@ -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
}}
}

View File

@ -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")
}

View File

@ -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
}

View File

@ -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)

View File

@ -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