ir: ensure that block and sub labels are also on the first chunk in said block/sub

This commit is contained in:
Irmen de Jong 2022-10-23 17:29:03 +02:00
parent 76428b16f0
commit 30ee65fd14
13 changed files with 212 additions and 198 deletions

View File

@ -42,7 +42,7 @@ class PtNodeGroup : PtNode(Position.DUMMY) {
}
abstract class PtNamedNode(val name: String, position: Position): PtNode(position) {
sealed class PtNamedNode(val name: String, position: Position): PtNode(position) {
val scopedName: List<String> by lazy {
var namedParent: PtNode = this.parent
if(namedParent is PtProgram)

View File

@ -1,43 +0,0 @@
package prog8.codegen.intermediate
import prog8.code.core.AssemblyError
import prog8.intermediate.*
internal class IRChunkLinker(private val irprog: IRProgram) {
private val labeledChunks = irprog.blocks.flatMap { it.subroutines }.flatMap { it.chunks }.associateBy { it.label }
fun link() {
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
sub.chunks.withIndex().forEach { (index, chunk) ->
if(chunk is IRCodeChunk) {
// link sequential chunks
val jump = chunk.instructions.lastOrNull()?.opcode
if (jump == null || jump !in setOf(Opcode.JUMP, Opcode.JUMPA, Opcode.RETURN)) {
// no jump at the end, so link to next chunk (if it exists)
if(index<sub.chunks.size-1) {
val nextChunk = sub.chunks[index + 1]
if (nextChunk is IRCodeChunk)
chunk.next = nextChunk
else
throw AssemblyError("code chunk flows into following non-code chunk")
}
}
// link all jump and branching instructions to their target
chunk.instructions.forEach {
if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.RETURN && it.labelSymbol!=null) {
val targetChunk = labeledChunks.getValue(it.labelSymbol)
require(targetChunk is IRCodeChunk) { "target $targetChunk with label ${targetChunk.label} has to be a code chunk" }
it.branchTarget = targetChunk
}
// note: branches with an address value cannot be linked to something...
}
}
}
}
}
}

View File

@ -49,19 +49,59 @@ class IRCodeGen(
irProg.addBlock(translate(block))
replaceMemoryMappedVars(irProg)
IRChunkLinker(irProg).link()
ensureFirstChunkLabels(irProg)
irProg.linkChunks()
if(options.optimize) {
val optimizer = IRPeepholeOptimizer(irProg)
optimizer.optimize()
IRChunkLinker(irProg).link() // re-link
irProg.linkChunks() // re-link
}
irProg.validate()
return irProg
}
private fun ensureFirstChunkLabels(irProg: IRProgram) {
// make sure that first chunks in Blocks and Subroutines share the name of the block/sub as label.
irProg.blocks.forEach { block ->
if(block.inlineAssembly.isNotEmpty()) {
val first = block.inlineAssembly.first()
if(first.label==null) {
val replacement = IRInlineAsmChunk(block.name, first.assembly, first.isIR, first.position)
block.inlineAssembly.removeAt(0)
block.inlineAssembly.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 ->
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.position, first.next)
replacement.instructions += first.instructions
replacement
}
is IRInlineAsmChunk -> IRInlineAsmChunk(sub.name, first.assembly, first.isIR, first.position)
is IRInlineBinaryChunk -> IRInlineBinaryChunk(sub.name, first.data, first.position)
else -> throw AssemblyError("invalid chunk")
}
sub.chunks.removeAt(0)
sub.chunks.add(0, replacement)
} else if(first.label != sub.name) {
val next = if(first is IRCodeChunk) first else null
sub.chunks.add(0, IRCodeChunk(sub.name, sub.position, next))
}
}
}
}
}
private fun replaceMemoryMappedVars(irProg: IRProgram) {
// replace memory mapped variable symbols with the memory address directly.
// note: we do still export the memory mapped symbols so a code generator can use those
@ -309,7 +349,7 @@ class IRCodeGen(
val labeledFirstChunk = when(val first=chunks[0]) {
is IRCodeChunk -> {
val newChunk = IRCodeChunk(label, first.position, null)
first.instructions.forEach { newChunk += it }
newChunk.instructions += first.instructions
newChunk
}
is IRInlineAsmChunk -> {

View File

@ -2,7 +2,6 @@ import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.codegen.intermediate.IRChunkLinker
import prog8.codegen.intermediate.IRPeepholeOptimizer
import prog8.intermediate.*
@ -25,6 +24,7 @@ class TestIRPeepholeOpt: FunSpec({
)
val prog = IRProgram("test", IRSymbolTable(null), options, target)
prog.addBlock(block)
prog.linkChunks()
prog.validate()
return prog
}
@ -44,7 +44,6 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.NOP)
))
irProg.chunks().single().instructions.size shouldBe 3
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
irProg.chunks().single().instructions.size shouldBe 1
@ -64,7 +63,6 @@ class TestIRPeepholeOpt: FunSpec({
irProg.chunks().size shouldBe 4
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
irProg.chunks().size shouldBe 3
@ -87,7 +85,6 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.CLC)
))
irProg.chunks().single().instructions.size shouldBe 6
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val instr = irProg.chunks().single().instructions
@ -103,7 +100,6 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=222)
))
irProg.chunks().single().instructions.size shouldBe 4
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val instr = irProg.chunks().single().instructions
@ -127,7 +123,6 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 0)
))
irProg.chunks().single().instructions.size shouldBe 10
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
irProg.chunks().single().instructions.size shouldBe 4
@ -139,7 +134,6 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 1)
))
irProg.chunks().single().instructions.size shouldBe 2
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val instr = irProg.chunks().single().instructions
@ -160,7 +154,6 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 1)
))
irProg.chunks().single().instructions.size shouldBe 8
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
irProg.chunks().single().instructions.size shouldBe 4
@ -174,7 +167,6 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, value = 65535)
))
irProg.chunks().single().instructions.size shouldBe 4
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val instr = irProg.chunks().single().instructions

View File

@ -3,13 +3,13 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- ir: check that block and sub labels are also on the first chunk in said block/sub
- ir: link all sequential chunks to another (exiting one chunk 'calls' the next chunk)
- vm: program is list of chunks, fix dispatcher
- ir: see TODO replace IM syscalls by their VM Syscall equivalent
- ir: fix JUMP, RETURN and all branching instructions to reference a chunk + index instead of just a single programcounter index
- ir: fix removeWeirdBranches in IR optimizer
- ir: fix unit tests
- ir: fix joinChunks() in the IR optimizer
- vm: program is list of chunks, fix dispatcher
- ir: fix joinChunks() in the IR optimizer - there are WAY too many chunks with 1 instruction in them only
- ir: next in IRCodeChunk can also be a Asm Chunk which can have next as well
- add cx16diskio.vload_raw() to load headerless files into vram
...

View File

@ -44,6 +44,7 @@ class IRFileReader {
program.addGlobalInits(initGlobals)
blocks.forEach{ program.addBlock(it) }
program.linkChunks()
program.validate()
return program
}

View File

@ -25,10 +25,8 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
out.write("\n<INITGLOBALS>\n")
if(!irProgram.options.dontReinitGlobals) {
out.write("<C>\n")
// note: this a block of code that loads values and stores them into the global variables to reset their values.
irProgram.globalInits.forEach { out.write(it.toString()) }
out.write("</C>\n")
writeCodeChunk(irProgram.globalInits)
}
out.write("</INITGLOBALS>\n")
writeBlocks()
@ -63,18 +61,8 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
when (chunk) {
is IRInlineAsmChunk -> writeInlineAsm(chunk)
is IRInlineBinaryChunk -> writeInlineBytes(chunk)
else -> {
if(chunk.label!=null)
out.write("<C LABEL=${chunk.label}>\n")
else
out.write("<C>\n")
chunk.instructions.forEach { instr ->
numInstr++
out.write(instr.toString())
out.write("\n")
}
out.write("</C>\n")
}
is IRCodeChunk -> writeCodeChunk(chunk)
else -> throw InternalCompilerException("invalid chunk")
}
}
out.write("</SUB>\n")
@ -101,6 +89,19 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
}
}
private fun writeCodeChunk(chunk: IRCodeChunk) {
if(chunk.label!=null)
out.write("<C LABEL=${chunk.label}>\n")
else
out.write("<C>\n")
chunk.instructions.forEach { instr ->
numInstr++
out.write(instr.toString())
out.write("\n")
}
out.write("</C>\n")
}
private fun writeInlineBytes(chunk: IRInlineBinaryChunk) {
out.write("<BYTES LABEL=${chunk.label ?: ""} POS=${chunk.position}>\n")
chunk.data.withIndex().forEach {(index, byte) ->

View File

@ -353,60 +353,6 @@ enum class Opcode {
BINARYDATA
}
val OpcodesWithAddress = setOf(
Opcode.LOADM,
Opcode.LOADX,
Opcode.LOADIX,
Opcode.STOREM,
Opcode.STOREX,
Opcode.STOREIX,
Opcode.STOREZM,
Opcode.STOREZX,
Opcode.JUMP,
Opcode.JUMPA,
Opcode.CALL,
Opcode.INCM,
Opcode.DECM,
Opcode.NEGM,
Opcode.ADDM,
Opcode.SUBM,
Opcode.MULM,
Opcode.DIVM,
Opcode.DIVSM,
Opcode.INVM,
Opcode.ORM,
Opcode.XORM,
Opcode.ANDM,
Opcode.ASRM,
Opcode.LSRM,
Opcode.LSLM,
Opcode.LSLNM,
Opcode.LSRNM,
Opcode.ASRNM,
Opcode.ROLM,
Opcode.RORM,
Opcode.ROXLM,
Opcode.ROXRM,
Opcode.BSTCC,
Opcode.BSTCS,
Opcode.BSTEQ,
Opcode.BSTNE,
Opcode.BSTNEG,
Opcode.BSTPOS,
Opcode.BZ,
Opcode.BNZ,
Opcode.BEQ,
Opcode.BNE,
Opcode.BLT,
Opcode.BLTS,
Opcode.BLE,
Opcode.BLES,
Opcode.BGT,
Opcode.BGTS,
Opcode.BGE,
Opcode.BGES
)
val OpcodesThatBranch = setOf(
Opcode.JUMP,
Opcode.JUMPA,
@ -681,7 +627,7 @@ data class IRInstruction(
require(reg2==null || reg2 in 0..65536) {"reg2 out of bounds"}
require(fpReg1==null || fpReg1 in 0..65536) {"fpReg1 out of bounds"}
require(fpReg2==null || fpReg2 in 0..65536) {"fpReg2 out of bounds"}
if(value!=null && opcode !in OpcodesWithAddress) {
if(value!=null) {
when (type) {
IRDataType.BYTE -> require(value in -128..255) {"value out of range for byte: $value"}
IRDataType.WORD -> require(value in -32768..65535) {"value out of range for word: $value"}

View File

@ -52,10 +52,13 @@ class IRProgram(val name: String,
val encoding: IStringEncoding) {
val asmSymbols = mutableMapOf<String, String>()
val globalInits = mutableListOf<IRInstruction>()
val globalInits = IRCodeChunk(null, Position.DUMMY, null)
val blocks = mutableListOf<IRBlock>()
fun addGlobalInits(chunk: IRCodeChunk) = globalInits.addAll(chunk.instructions)
fun addGlobalInits(chunk: IRCodeChunk) {
globalInits += chunk
}
fun addBlock(block: IRBlock) {
require(blocks.all { it.name != block.name}) { "duplicate block ${block.name} ${block.position}" }
blocks.add(block)
@ -65,15 +68,84 @@ class IRProgram(val name: String,
asmSymbols += symbolDefs
}
fun linkChunks() {
val labeledChunks = blocks.flatMap { it.subroutines }.flatMap { it.chunks }.associateBy { it.label }
if(globalInits.isNotEmpty()) {
if(globalInits.next==null) {
val firstBlock = blocks.firstOrNull()
if(firstBlock!=null) {
if(firstBlock.inlineAssembly.isNotEmpty()) {
TODO("link to inline assembly block")
// irprog.globalInits.next = firstBlock.inlineAssembly.first()
} else if(firstBlock.subroutines.isNotEmpty()) {
val firstSub = firstBlock.subroutines.first()
if(firstSub.chunks.isNotEmpty()) {
val firstChunk = firstSub.chunks.first()
when(firstChunk) {
is IRCodeChunk -> globalInits.next = firstChunk
else -> TODO("link to other type of chunk")
}
}
}
}
}
}
blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
sub.chunks.withIndex().forEach { (index, chunk) ->
when (chunk) {
is IRCodeChunk -> {
// link sequential chunks
val jump = chunk.instructions.lastOrNull()?.opcode
if (jump == null || jump !in setOf(Opcode.JUMP, Opcode.JUMPA, Opcode.RETURN)) {
// no jump at the end, so link to next chunk (if it exists)
if(index<sub.chunks.size-1) {
val nextChunk = sub.chunks[index + 1]
if (nextChunk is IRCodeChunk)
chunk.next = nextChunk
else
throw AssemblyError("code chunk flows into following non-code chunk")
} else {
TODO("???")
}
}
// link all jump and branching instructions to their target
chunk.instructions.forEach {
if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.RETURN && it.labelSymbol!=null) {
val targetChunk = labeledChunks.getValue(it.labelSymbol)
require(targetChunk is IRCodeChunk) { "target $targetChunk with label ${targetChunk.label} has to be a code chunk" }
it.branchTarget = targetChunk
}
// note: branches with an address value cannot be linked to something...
}
}
is IRInlineAsmChunk -> {
// TODO("link next of asm chunk")
}
is IRInlineBinaryChunk -> {
// TODO("link next of binary chunk")
}
else -> throw AssemblyError("invalid chunk")
}
}
}
}
fun validate() {
// TODO: check that block and sub labels are also on the first chunk in said block/sub
blocks.forEach {
it.inlineAssembly.forEach { chunk ->
blocks.forEach { block ->
if(block.inlineAssembly.isNotEmpty()) {
require(block.inlineAssembly.first().label == block.name) { "first block chunk should have block name as its label" }
}
block.inlineAssembly.forEach { chunk ->
require(chunk.instructions.isEmpty())
}
it.subroutines.forEach { sub ->
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)
else require(chunk.instructions.isEmpty())
@ -95,7 +167,7 @@ class IRProgram(val name: String,
usedRegisters.outputFpRegs.forEach{ (reg, count) -> outputFpRegs[reg] = outputFpRegs.getValue(reg) + count }
}
globalInits.forEach { it.addUsedRegistersCounts(inputRegs, outputRegs, inputFpRegs, outputFpRegs) }
globalInits.instructions.forEach { it.addUsedRegistersCounts(inputRegs, outputRegs, inputFpRegs, outputFpRegs) }
blocks.forEach {
it.inlineAssembly.forEach { chunk -> addUsed(chunk.usedRegisters()) }
it.subroutines.flatMap { sub->sub.chunks }.forEach { chunk -> addUsed(chunk.usedRegisters()) }
@ -112,9 +184,9 @@ class IRBlock(
val alignment: BlockAlignment,
val position: Position
) {
val inlineAssembly = mutableListOf<IRInlineAsmChunk>()
val subroutines = mutableListOf<IRSubroutine>()
val asmSubroutines = mutableListOf<IRAsmSubroutine>()
val inlineAssembly = mutableListOf<IRInlineAsmChunk>()
enum class BlockAlignment {
NONE,
@ -177,7 +249,7 @@ class IRAsmSubroutine(
fun usedRegisters() = registersUsed
}
abstract class IRCodeChunkBase(val label: String?, val position: Position) {
sealed class IRCodeChunkBase(val label: String?, val position: Position) {
val instructions = mutableListOf<IRInstruction>()
abstract fun isEmpty(): Boolean
@ -187,7 +259,7 @@ abstract class IRCodeChunkBase(val label: String?, val position: Position) {
class IRCodeChunk(label: String?,
position: Position,
var next: IRCodeChunk?): IRCodeChunkBase(label, position) {
var next: IRCodeChunk?): IRCodeChunkBase(label, position) { // TODO next can also be InlineAsmChunk!! which can also have a next again.
override fun isEmpty() = instructions.isEmpty()
override fun isNotEmpty() = instructions.isNotEmpty()

View File

@ -206,7 +206,7 @@ fun parseIRCodeLine(line: String, location: Pair<IRCodeChunk, Int>?, placeholder
throw IRParseException("invalid reg1 for $line")
if(format.reg2==OperandDirection.UNUSED && reg2!=null)
throw IRParseException("invalid reg2 for $line")
if(value!=null && opcode !in OpcodesWithAddress) {
if(value!=null) {
when (type) {
IRDataType.BYTE -> {
if (value < -128 || value > 255)

View File

@ -75,7 +75,7 @@ load.b r1,42
<SUB NAME=main.start RETURNTYPE=null POS=[examples/test.p8: line 4 col 6-8]>
<PARAMS>
</PARAMS>
<C>
<C LABEL=main.start>
return
</C>
</SUB>
@ -86,7 +86,7 @@ return
<PARAMS>
uword sys.wait.jiffies
</PARAMS>
<INLINEASM LABEL= IR=true POS=[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]>
<INLINEASM LABEL=sys.wait IR=true POS=[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]>
loadm.w r0,sys.wait.jiffies
syscall 13
</INLINEASM>

View File

@ -117,10 +117,12 @@ class VirtualMachine(irProgram: IRProgram) {
}
}
private inline fun nextPc() {
private fun nextPc() {
pcIndex ++
if(pcIndex>=pcChunk.instructions.size) {
pcIndex = 0
if(pcChunk.next==null)
TODO("huh no next chunk in $pcChunk")
pcChunk = pcChunk.next!!
}
}

View File

@ -1,5 +1,6 @@
package prog8.vm
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.intermediate.*
@ -11,14 +12,14 @@ class VmProgramLoader {
placeholders.clear()
irProgram.validate()
val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
val symbolAddresses = allocations.allocations.toMutableMap()
val variableAddresses = allocations.allocations.toMutableMap()
val programChunks = mutableListOf<IRCodeChunk>()
varsToMemory(irProgram, allocations, symbolAddresses, memory)
varsToMemory(irProgram, allocations, variableAddresses, memory)
if(!irProgram.options.dontReinitGlobals) {
if(irProgram.globalInits.isNotEmpty())
addToProgram(irProgram.globalInits, programChunks, symbolAddresses)
programChunks += irProgram.globalInits
}
// make sure that if there is a "main.start" entrypoint, we jump to it
@ -31,18 +32,19 @@ class VmProgramLoader {
}
}
// TODO load rest of the program
// load rest of the program into the list
irProgram.blocks.forEach { block ->
if(block.address!=null)
throw IRParseException("blocks cannot have a load address for vm: ${block.name}")
block.inlineAssembly.forEach { addAssemblyToProgram(it, programChunks, symbolAddresses) }
block.inlineAssembly.forEach { addAssemblyToProgram(it, programChunks, variableAddresses) }
block.subroutines.forEach {
it.chunks.forEach { chunk ->
when (chunk) {
is IRInlineAsmChunk -> addAssemblyToProgram(chunk, programChunks, symbolAddresses)
is IRInlineAsmChunk -> addAssemblyToProgram(chunk, programChunks, variableAddresses)
is IRInlineBinaryChunk -> TODO("inline binary data not yet supported in the VM")
else -> addToProgram(chunk.instructions, programChunks, symbolAddresses)
is IRCodeChunk -> programChunks += chunk
else -> throw AssemblyError("weird chunk type")
}
}
}
@ -50,33 +52,34 @@ class VmProgramLoader {
throw IRParseException("vm currently does not support asmsubs: ${block.asmSubroutines.first().name}")
}
pass2replaceLabelsByProgIndex(programChunks, symbolAddresses)
programChunks.asSequence().flatMap { it.instructions }.forEach {
if(it.opcode in OpcodesWithAddress && it.value==null) {
throw IRParseException("instruction missing numeric value, label not replaced? $it")
}
}
pass2replaceLabelsByProgIndex(programChunks, variableAddresses)
return programChunks
}
private fun pass2replaceLabelsByProgIndex(
chunks: MutableList<IRCodeChunk>,
symbolAddresses: MutableMap<String, Int>
variableAddresses: MutableMap<String, Int>
) {
for((ref, label) in placeholders) {
val (chunk, line) = ref
val replacement = symbolAddresses[label]
val replacement = variableAddresses[label]
if(replacement==null) {
// it could be an address + index: symbol+42
if('+' in label) {
val (symbol, indexStr) = label.split('+')
val index = indexStr.toInt()
val address = symbolAddresses.getValue(symbol) + index
val address = variableAddresses.getValue(symbol) + index
chunk.instructions[line] = chunk.instructions[line].copy(value = address)
} else {
throw IRParseException("placeholder not found in labels: $label")
// placeholder is not a variable, so it must be a label of a code chunk instead
val target: IRCodeChunk? = chunks.firstOrNull { it.label==label }
if(target==null)
throw IRParseException("placeholder not found in variables nor labels: $label")
else {
require(chunk.instructions[line].opcode in OpcodesThatBranch)
chunk.instructions[line] = chunk.instructions[line].copy(branchTarget = target, value = null)
}
}
} else {
chunk.instructions[line] = chunk.instructions[line].copy(value = replacement)
@ -84,39 +87,39 @@ class VmProgramLoader {
}
}
private fun addToProgram(
instructions: Iterable<IRInstruction>,
program: MutableList<IRCodeChunk>,
symbolAddresses: MutableMap<String, Int>
) {
val chunk = IRCodeChunk(null, Position.DUMMY, null)
instructions.map {
it.labelSymbol?.let { symbol -> placeholders[Pair(chunk, chunk.instructions.size)]=symbol }
if(it.opcode==Opcode.SYSCALL) {
// convert IR Syscall to VM Syscall
val vmSyscall = when(it.value!!) {
IMSyscall.SORT_UBYTE.ordinal -> Syscall.SORT_UBYTE
IMSyscall.SORT_BYTE.ordinal -> Syscall.SORT_BYTE
IMSyscall.SORT_UWORD.ordinal -> Syscall.SORT_UWORD
IMSyscall.SORT_WORD.ordinal -> Syscall.SORT_WORD
IMSyscall.ANY_BYTE.ordinal -> Syscall.ANY_BYTE
IMSyscall.ANY_WORD.ordinal -> Syscall.ANY_WORD
IMSyscall.ANY_FLOAT.ordinal -> Syscall.ANY_FLOAT
IMSyscall.ALL_BYTE.ordinal -> Syscall.ALL_BYTE
IMSyscall.ALL_WORD.ordinal -> Syscall.ALL_WORD
IMSyscall.ALL_FLOAT.ordinal -> Syscall.ALL_FLOAT
IMSyscall.REVERSE_BYTES.ordinal -> Syscall.REVERSE_BYTES
IMSyscall.REVERSE_WORDS.ordinal -> Syscall.REVERSE_WORDS
IMSyscall.REVERSE_FLOATS.ordinal -> Syscall.REVERSE_FLOATS
else -> throw IRParseException("invalid IM syscall number $it")
}
chunk += it.copy(value=vmSyscall.ordinal)
} else {
chunk += it
}
}
program += chunk
}
// TODO replace IM syscalls by their VM Syscall equivalent
// private fun addToProgram(
// instructions: Iterable<IRInstruction>,
// program: MutableList<IRCodeChunk>
// ) {
// val chunk = IRCodeChunk(null, Position.DUMMY, null)
// instructions.map {
// it.labelSymbol?.let { symbol -> placeholders[Pair(chunk, chunk.instructions.size)]=symbol }
// if(it.opcode==Opcode.SYSCALL) {
// // convert IR Syscall to VM Syscall
// val vmSyscall = when(it.value!!) {
// IMSyscall.SORT_UBYTE.ordinal -> Syscall.SORT_UBYTE
// IMSyscall.SORT_BYTE.ordinal -> Syscall.SORT_BYTE
// IMSyscall.SORT_UWORD.ordinal -> Syscall.SORT_UWORD
// IMSyscall.SORT_WORD.ordinal -> Syscall.SORT_WORD
// IMSyscall.ANY_BYTE.ordinal -> Syscall.ANY_BYTE
// IMSyscall.ANY_WORD.ordinal -> Syscall.ANY_WORD
// IMSyscall.ANY_FLOAT.ordinal -> Syscall.ANY_FLOAT
// IMSyscall.ALL_BYTE.ordinal -> Syscall.ALL_BYTE
// IMSyscall.ALL_WORD.ordinal -> Syscall.ALL_WORD
// IMSyscall.ALL_FLOAT.ordinal -> Syscall.ALL_FLOAT
// IMSyscall.REVERSE_BYTES.ordinal -> Syscall.REVERSE_BYTES
// IMSyscall.REVERSE_WORDS.ordinal -> Syscall.REVERSE_WORDS
// IMSyscall.REVERSE_FLOATS.ordinal -> Syscall.REVERSE_FLOATS
// else -> throw IRParseException("invalid IM syscall number $it")
// }
// chunk += it.copy(value=vmSyscall.ordinal)
// } else {
// chunk += it
// }
// }
// program += chunk
// }
private fun varsToMemory(
program: IRProgram,