diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 26d6888d1..7c618494f 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -899,6 +899,9 @@ class IRCodeGen( private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList { + if(isIndirectJump(goto)) + TODO("indirect jump after if ${ifElse.position}") + val condition = ifElse.condition as? PtBinaryExpression val result = mutableListOf() 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, @@ -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): IRCodeChunks { val result = mutableListOf() group.forEach { result += translateNode(it) } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index ade38b4fa..a21bd55e6 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -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 ... diff --git a/examples/test.p8 b/examples/test.p8 index 7b145a8c3..de407d781 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -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") diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index a9b44eebf..0b353caa4 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -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 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,a | F,a"), Opcode.JUMP to InstructionFormat.from("N, 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" } } diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index cf979bb44..422075dd4 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -37,6 +37,7 @@ class VirtualMachine(irProgram: IRProgram) { val memory = Memory() val machinedef = VirtualMachineDefinition() val program: List + val artificialLabelAddresses: Map val registers = Registers() val callStack = Stack() val valueStack = Stack() // 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) } diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index bffcbfa5f..e0c636ae8 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -9,8 +9,9 @@ import prog8.intermediate.* class VmProgramLoader { private val placeholders = mutableMapOf, String>() // program chunk+index to symbolname private val subroutines = mutableMapOf() // label to subroutine node + private val artificialLabelAddresses = mutableMapOf() - fun load(irProgram: IRProgram, memory: Memory): List { + fun load(irProgram: IRProgram, memory: Memory): Pair, Map> { 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)