VM now supports indirect jump instruction

This commit is contained in:
Irmen de Jong 2024-02-22 11:35:54 +01:00
parent 4e1686f6e3
commit 040d75dafa
7 changed files with 63 additions and 30 deletions

View File

@ -899,6 +899,9 @@ class IRCodeGen(
private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList<IRCodeChunkBase> {
if(isIndirectJump(goto))
TODO("indirect jump after if ${ifElse.position}")
val condition = ifElse.condition as? PtBinaryExpression
val result = mutableListOf<IRCodeChunkBase>()
if(condition==null) {
@ -958,10 +961,14 @@ class IRCodeGen(
}
}
private fun branchInstr(goto: PtJump, branchOpcode: Opcode) = if (goto.address != null)
IRInstruction(branchOpcode, address = goto.address?.toInt())
else
IRInstruction(branchOpcode, labelSymbol = goto.identifier!!.name)
private fun branchInstr(goto: PtJump, branchOpcode: Opcode): IRInstruction {
return if (goto.address != null)
IRInstruction(branchOpcode, address = goto.address?.toInt())
else {
require(!isIndirectJump(goto)) { "indirect jumps cannot be expressed using a branch opcode"}
IRInstruction(branchOpcode, labelSymbol = goto.identifier!!.name)
}
}
private fun ifZeroIntThenJump(
result: MutableList<IRCodeChunkBase>,
@ -1423,11 +1430,8 @@ class IRCodeGen(
chunk += IRInstruction(Opcode.JUMP, address = jump.address!!.toInt())
} else {
if (jump.identifier != null) {
val symbol = symbolTable.lookup(jump.identifier!!.name)
if(symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR) {
val jumpReg = registers.nextFree()
chunk += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = jumpReg, labelSymbol = jump.identifier!!.name)
chunk += IRInstruction(Opcode.JUMPI, reg1 = jumpReg)
if(isIndirectJump(jump)) {
chunk += IRInstruction(Opcode.JUMPI, labelSymbol = jump.identifier!!.name)
} else {
chunk += IRInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.name)
}
@ -1439,6 +1443,13 @@ class IRCodeGen(
return result
}
private fun isIndirectJump(jump: PtJump): Boolean {
if(jump.identifier==null)
return false
val symbol = symbolTable.lookup(jump.identifier!!.name)
return symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR
}
private fun translateGroup(group: List<PtNode>): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
group.forEach { result += translateNode(it) }

View File

@ -1,8 +1,6 @@
TODO
====
make it possible to do indirect jumps in VM? (associate values with labels and if it jumps to a "address value" it looks up the label with that value again?)
replace Takes by Http4k in httpCompilerService project. https://github.com/http4k/examples/blob/master/hello-world/README.md
...

View File

@ -5,14 +5,10 @@
main {
sub start() {
uword @shared pointer
; if cx16.r0L>10
; goto label1
; if cx16.r0L>11
; goto label2
cx16.r0L = 10
pointer = &label2
goto pointer
if cx16.r0L!=0
goto pointer
label1:
txt.print("fail\n")

View File

@ -53,7 +53,7 @@ storezx reg1, address - store zero at memory address, indexed by
CONTROL FLOW
------------
jump location - continue running at instruction at 'location' (label/memory address)
jumpi reg1 - continue running at memory address in reg1 (indirect jump)
jumpi pointervar - continue running at memory address contained in the pointer variable (indirect jump)
preparecall numparams - indicator that the next instructions are the param setup and function call/syscall with <numparams> parameters
calli reg1 - calls a subroutine (without arguments and without return valus) at memory addres in reg1 (indirect jsr)
call label(argument register list) [: resultreg.type]
@ -576,7 +576,7 @@ val instructionFormats = mutableMapOf(
Opcode.STOREZI to InstructionFormat.from("BW,<r1 | F,<r1"),
Opcode.STOREZX to InstructionFormat.from("BW,<r1,>a | F,<r1,>a"),
Opcode.JUMP to InstructionFormat.from("N,<a"),
Opcode.JUMPI to InstructionFormat.from("N,<r1"),
Opcode.JUMPI to InstructionFormat.from("N,<a"),
Opcode.PREPARECALL to InstructionFormat.from("N,<i"),
Opcode.CALLI to InstructionFormat.from("N,<r1"),
Opcode.CALL to InstructionFormat.from("N,call"),

View File

@ -149,7 +149,7 @@ class IRProgram(val name: String,
// link all jump and branching instructions to their target
chunk.instructions.forEach {
if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.RETURN && it.opcode!=Opcode.RETURNR && it.labelSymbol!=null) {
if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.JUMPI && it.opcode!=Opcode.RETURN && it.opcode!=Opcode.RETURNR && it.labelSymbol!=null) {
if(it.labelSymbol.startsWith('$') || it.labelSymbol.first().isDigit()) {
// it's a call to an address (romsub most likely)
require(it.address!=null)
@ -215,7 +215,15 @@ class IRProgram(val name: String,
}
chunk.instructions.withIndex().forEach { (index, instr) ->
if(instr.labelSymbol!=null && instr.opcode in OpcodesThatBranch) {
if(!instr.labelSymbol.startsWith('$') && !instr.labelSymbol.first().isDigit())
if(instr.opcode==Opcode.JUMPI) {
val pointervar = st.lookup(instr.labelSymbol)!!
when(pointervar) {
is IRStStaticVariable -> require(pointervar.dt==DataType.UWORD)
is IRStMemVar -> require(pointervar.dt==DataType.UWORD)
else -> throw AssemblyError("weird pointervar type")
}
}
else if(!instr.labelSymbol.startsWith('$') && !instr.labelSymbol.first().isDigit())
require(instr.branchTarget != null) { "branching instruction to label should have branchTarget set" }
}

View File

@ -37,6 +37,7 @@ class VirtualMachine(irProgram: IRProgram) {
val memory = Memory()
val machinedef = VirtualMachineDefinition()
val program: List<IRCodeChunk>
val artificialLabelAddresses: Map<Int, IRCodeChunk>
val registers = Registers()
val callStack = Stack<CallSiteContext>()
val valueStack = Stack<UByte>() // max 128 entries
@ -52,7 +53,12 @@ class VirtualMachine(irProgram: IRProgram) {
internal var mul16_last_upper = 0u
init {
program = VmProgramLoader().load(irProgram, memory)
val (prg, labelAddr) = VmProgramLoader().load(irProgram, memory)
program = prg
artificialLabelAddresses = mutableMapOf()
labelAddr.forEach { labelname, artificialAddress ->
artificialLabelAddresses[artificialAddress] = program.single { it.label==labelname }
}
require(irProgram.st.getAsmSymbols().isEmpty()) { "virtual machine can't yet process asmsymbols defined on command line" }
reset(false)
}
@ -151,7 +157,7 @@ class VirtualMachine(irProgram: IRProgram) {
else if(i.labelSymbol!=null)
throw IllegalArgumentException("vm program can't jump to system memory address (${i.opcode} ${i.labelSymbol})")
else if(i.reg1!=null)
throw IllegalArgumentException("vm program can't jump to system memory address (${i})")
throw IllegalArgumentException("vm program can't jump to system memory address (${i} = ${registers.getUW(i.reg1!!)})")
else
throw IllegalArgumentException("no branchtarget in $i")
}
@ -179,7 +185,8 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.STOREZM -> InsSTOREZM(ins)
Opcode.STOREZX -> InsSTOREZX(ins)
Opcode.STOREZI -> InsSTOREZI(ins)
Opcode.JUMP, Opcode.JUMPI -> InsJUMP(ins)
Opcode.JUMP -> InsJUMP(ins)
Opcode.JUMPI -> InsJUMPI(ins)
Opcode.PREPARECALL -> nextPc()
Opcode.CALLI -> throw IllegalArgumentException("VM cannot run code from memory")
Opcode.CALL -> InsCALL(ins)
@ -593,6 +600,12 @@ class VirtualMachine(irProgram: IRProgram) {
branchTo(i)
}
private fun InsJUMPI(i: IRInstruction) {
val artificialAddress = memory.getUW(i.address!!).toInt()
pcChunk = artificialLabelAddresses.getValue(artificialAddress)
pcIndex = 0
}
private class SyscallParamValue(var dt: IRDataType?, var value: Comparable<*>?)
private val syscallParams = Array(100) { SyscallParamValue(null, null) }

View File

@ -9,8 +9,9 @@ import prog8.intermediate.*
class VmProgramLoader {
private val placeholders = mutableMapOf<Pair<IRCodeChunk, Int>, String>() // program chunk+index to symbolname
private val subroutines = mutableMapOf<String, IRSubroutine>() // label to subroutine node
private val artificialLabelAddresses = mutableMapOf<String, Int>()
fun load(irProgram: IRProgram, memory: Memory): List<IRCodeChunk> {
fun load(irProgram: IRProgram, memory: Memory): Pair<List<IRCodeChunk>, Map<String, Int>> {
irProgram.validate()
placeholders.clear()
subroutines.clear()
@ -73,7 +74,7 @@ class VmProgramLoader {
}
}
return programChunks
return programChunks to artificialLabelAddresses
}
private fun phase2relinkReplacedChunks(
@ -138,7 +139,7 @@ class VmProgramLoader {
}
val label = ins.labelSymbol
if (label != null && ins.opcode !in OpcodesThatBranch) {
if (label != null && (ins.opcode !in OpcodesThatBranch || ins.opcode==Opcode.JUMPI)) {
placeholders[Pair(chunk, index)] = label
}
}
@ -167,8 +168,14 @@ class VmProgramLoader {
throw IRParseException("placeholder not found in variables nor labels: $label")
else if(instr.opcode in OpcodesThatBranch)
chunk.instructions[line] = instr.copy(branchTarget = target, address = null)
else
throw IRParseException("vm cannot yet load a label address as a value: ${instr}")
else {
var address = artificialLabelAddresses[label]
if(address==null) {
address = 0xa000 + artificialLabelAddresses.size
artificialLabelAddresses[label] = address
}
chunk.instructions[line] = instr.copy(address=address, branchTarget = target)
}
}
} else {
chunk.instructions[line] = instr.copy(address = replacement + offset)