mirror of
https://github.com/irmen/prog8.git
synced 2025-04-07 16:41:46 +00:00
VM now supports indirect jump instruction
This commit is contained in:
parent
4e1686f6e3
commit
040d75dafa
@ -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) }
|
||||
|
@ -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
|
||||
|
||||
...
|
||||
|
@ -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")
|
||||
|
@ -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"),
|
||||
|
@ -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" }
|
||||
}
|
||||
|
||||
|
@ -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) }
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user