From 585009ac5c90cc7d179a7ae624b197317c6ffbed Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 24 Oct 2022 01:57:37 +0200 Subject: [PATCH] ir: fix syscall numbers and more --- .../codegen/intermediate/BuiltinFuncGen.kt | 8 +- docs/source/todo.rst | 6 +- examples/test.p8 | 1 + .../src/prog8/intermediate/IMSyscall.kt | 28 ++--- .../src/prog8/intermediate/IRFileReader.kt | 1 + .../src/prog8/intermediate/IRInstructions.kt | 2 + .../src/prog8/intermediate/IRProgram.kt | 18 ++- virtualmachine/src/prog8/vm/VirtualMachine.kt | 6 +- .../src/prog8/vm/VmProgramLoader.kt | 113 ++++++++++++------ 9 files changed, 119 insertions(+), 64 deletions(-) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index 8ba69bb1f..72fadabe3 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -74,7 +74,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe result += exprGen.translateExpression(call.args[0], 0, -1) result += IRCodeChunk(null, call.position, null).also { it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length) - it += IRInstruction(Opcode.SYSCALL, value = syscall.ordinal) + it += IRInstruction(Opcode.SYSCALL, value = syscall.number) if (resultRegister != 0) it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0) } @@ -97,7 +97,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe result += exprGen.translateExpression(call.args[0], 0, -1) result += IRCodeChunk(null, call.position, null).also { it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length) - it += IRInstruction(Opcode.SYSCALL, value = syscall.ordinal) + it += IRInstruction(Opcode.SYSCALL, value = syscall.number) if (resultRegister != 0) it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0) } @@ -216,7 +216,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe result += exprGen.translateExpression(call.args[0], 0, -1) result += IRCodeChunk(null, call.position, null).also { it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length) - it += IRInstruction(Opcode.SYSCALL, value = syscall.ordinal) + it += IRInstruction(Opcode.SYSCALL, value = syscall.number) } return result } @@ -238,7 +238,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe result += exprGen.translateExpression(call.args[0], 0, -1) result += IRCodeChunk(null, call.position, null).also { it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length) - it += IRInstruction(Opcode.SYSCALL, value = syscall.ordinal) + it += IRInstruction(Opcode.SYSCALL, value = syscall.number) } return result } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index c538e3e3b..61b48fccd 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -4,13 +4,13 @@ TODO For next release ^^^^^^^^^^^^^^^^ - 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 +- maybe?: make sure last %ir chunk in a subroutine ends with a jump or a return instruction - ir: fix unit tests - ir: fix joinChunks() in the IR optimizer - there are WAY too many chunks with 1 instruction in them only +- ir: fix removeWeirdBranches in IR optimizer - 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 +- mention the syntax highlighting files in the readme and the docs, and add note to the IDEA one that it can also be used in Rider ... diff --git a/examples/test.p8 b/examples/test.p8 index 00d207a4e..90e99e5f1 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,6 +1,7 @@ %import textio main { + sub start() { ubyte aa = 42 ubyte bb = 99 diff --git a/intermediate/src/prog8/intermediate/IMSyscall.kt b/intermediate/src/prog8/intermediate/IMSyscall.kt index 28bf2001c..60b3dd57a 100644 --- a/intermediate/src/prog8/intermediate/IMSyscall.kt +++ b/intermediate/src/prog8/intermediate/IMSyscall.kt @@ -4,18 +4,18 @@ package prog8.intermediate // these use the SYSCALL instruction instead. // Note that in the VM these are translated into whatever the corresponding Syscall number in the VM is. -enum class IMSyscall { - SORT_UBYTE, - SORT_BYTE, - SORT_UWORD, - SORT_WORD, - ANY_BYTE, - ANY_WORD, - ANY_FLOAT, - ALL_BYTE, - ALL_WORD, - ALL_FLOAT, - REVERSE_BYTES, - REVERSE_WORDS, - REVERSE_FLOATS +enum class IMSyscall(val number: Int) { + SORT_UBYTE(10000), + SORT_BYTE(10001), + SORT_UWORD(10002), + SORT_WORD(10003), + ANY_BYTE(10004), + ANY_WORD(10005), + ANY_FLOAT(10006), + ALL_BYTE(10007), + ALL_WORD(10008), + ALL_FLOAT(10009), + REVERSE_BYTES(10010), + REVERSE_WORDS(10011), + REVERSE_FLOATS(10012) } \ No newline at end of file diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index 3417d42a8..69124f907 100644 --- a/intermediate/src/prog8/intermediate/IRFileReader.kt +++ b/intermediate/src/prog8/intermediate/IRFileReader.kt @@ -46,6 +46,7 @@ class IRFileReader { program.linkChunks() program.validate() + return program } diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index dbb500495..ebd3514bf 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -357,6 +357,8 @@ val OpcodesThatBranch = setOf( Opcode.JUMP, Opcode.JUMPA, Opcode.RETURN, + Opcode.CALL, + Opcode.SYSCALL, Opcode.BSTCC, Opcode.BSTCS, Opcode.BSTEQ, diff --git a/intermediate/src/prog8/intermediate/IRProgram.kt b/intermediate/src/prog8/intermediate/IRProgram.kt index 92cf6ff80..dce3ece7c 100644 --- a/intermediate/src/prog8/intermediate/IRProgram.kt +++ b/intermediate/src/prog8/intermediate/IRProgram.kt @@ -116,8 +116,10 @@ class IRProgram(val name: String, 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 + if(targetChunk is IRCodeChunk) + it.branchTarget = targetChunk + else + println("TODO: branchTarget to non-codechunk $targetChunk with label ${targetChunk.label}") // TODO } // note: branches with an address value cannot be linked to something... } @@ -147,8 +149,16 @@ class IRProgram(val name: String, 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()) + if (chunk is IRCodeChunk) + require(chunk.instructions.isNotEmpty() || chunk.label!=null) + else + require(chunk.instructions.isEmpty()) + chunk.instructions.forEach { + if(it.labelSymbol!=null && it.opcode in OpcodesThatBranch) { + if(it.branchTarget==null) println("TODO: fix branching instruction to label ${it.labelSymbol} should have branchTarget set") // TODO + // TODO require(it.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 81fc311b3..858cb18bf 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -122,12 +122,14 @@ class VirtualMachine(irProgram: IRProgram) { if(pcIndex>=pcChunk.instructions.size) { pcIndex = 0 if(pcChunk.next==null) - TODO("huh no next chunk in $pcChunk") + TODO("no next chunk in $pcChunk (remove this check)") pcChunk = pcChunk.next!! } } - private inline fun branchTo(i: IRInstruction) { + private fun branchTo(i: IRInstruction) { + if(i.branchTarget==null) + TODO("no branchtarget in $i (remove this check)") pcChunk = i.branchTarget!! pcIndex = 0 } diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index bdbe6a329..ad9fa02d4 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -33,15 +33,24 @@ class VmProgramLoader { } // load rest of the program into the list + val chunkReplacements = mutableListOf>() 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, variableAddresses) } + block.inlineAssembly.forEach { + val replacement = addAssemblyToProgram(it, programChunks, variableAddresses) + if(replacement!=null) + chunkReplacements += replacement + } block.subroutines.forEach { it.chunks.forEach { chunk -> when (chunk) { - is IRInlineAsmChunk -> addAssemblyToProgram(chunk, programChunks, variableAddresses) + is IRInlineAsmChunk -> { + val replacement = addAssemblyToProgram(chunk, programChunks, variableAddresses) + if(replacement!=null) + chunkReplacements += replacement + } is IRInlineBinaryChunk -> TODO("inline binary data not yet supported in the VM") is IRCodeChunk -> programChunks += chunk else -> throw AssemblyError("weird chunk type") @@ -52,11 +61,74 @@ class VmProgramLoader { throw IRParseException("vm currently does not support asmsubs: ${block.asmSubroutines.first().name}") } + pass2translateSyscalls(programChunks) pass2replaceLabelsByProgIndex(programChunks, variableAddresses) + phase2relinkReplacedChunks(chunkReplacements, programChunks) + + programChunks.forEach { + it.instructions.forEach { ins -> + if (ins.labelSymbol != null && ins.opcode !in OpcodesThatBranch) + require(ins.value != null) { "instruction with labelSymbol for a var should have value set to var's memory address" } + } + } return programChunks } + private fun phase2relinkReplacedChunks( + replacements: MutableList>, + programChunks: MutableList + ) { + replacements.forEach { (old, new) -> + programChunks.forEach { chunk -> + if(chunk.next === old) { + chunk.next = new + } + chunk.instructions.forEach { ins -> + if(ins.branchTarget === old) { + ins.branchTarget = new + } else if(ins.branchTarget==null && ins.labelSymbol==new.label) { + ins.branchTarget = new + } + } + } + } + } + + private fun pass2translateSyscalls(chunks: MutableList) { + chunks.forEach { chunk -> + chunk.instructions.withIndex().forEach { (index, ins) -> + if(ins.opcode == Opcode.SYSCALL) { + // convert IR Syscall to VM Syscall + val vmSyscall = when(ins.value!!) { + IMSyscall.SORT_UBYTE.number -> Syscall.SORT_UBYTE + IMSyscall.SORT_BYTE.number -> Syscall.SORT_BYTE + IMSyscall.SORT_UWORD.number -> Syscall.SORT_UWORD + IMSyscall.SORT_WORD.number -> Syscall.SORT_WORD + IMSyscall.ANY_BYTE.number -> Syscall.ANY_BYTE + IMSyscall.ANY_WORD.number -> Syscall.ANY_WORD + IMSyscall.ANY_FLOAT.number -> Syscall.ANY_FLOAT + IMSyscall.ALL_BYTE.number -> Syscall.ALL_BYTE + IMSyscall.ALL_WORD.number -> Syscall.ALL_WORD + IMSyscall.ALL_FLOAT.number -> Syscall.ALL_FLOAT + IMSyscall.REVERSE_BYTES.number -> Syscall.REVERSE_BYTES + IMSyscall.REVERSE_WORDS.number -> Syscall.REVERSE_WORDS + IMSyscall.REVERSE_FLOATS.number -> Syscall.REVERSE_FLOATS + else -> null + } + + if(vmSyscall!=null) + chunk.instructions[index] = ins.copy(value = vmSyscall.ordinal) + } + + val label = ins.labelSymbol + if (label != null && ins.opcode !in OpcodesThatBranch) { + placeholders[Pair(chunk, index)] = label + } + } + } + } + private fun pass2replaceLabelsByProgIndex( chunks: MutableList, variableAddresses: MutableMap @@ -87,40 +159,6 @@ class VmProgramLoader { } } - // TODO replace IM syscalls by their VM Syscall equivalent -// private fun addToProgram( -// instructions: Iterable, -// program: MutableList -// ) { -// 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, allocations: VmVariableAllocator, @@ -232,7 +270,7 @@ class VmProgramLoader { asmChunk: IRInlineAsmChunk, chunks: MutableList, symbolAddresses: MutableMap, - ) { + ): Pair { if(asmChunk.isIR) { val chunk = IRCodeChunk(asmChunk.label, asmChunk.position, null) asmChunk.assembly.lineSequence().forEach { @@ -243,6 +281,7 @@ class VmProgramLoader { ) } chunks += chunk + return Pair(asmChunk, chunk) } else { throw IRParseException("vm currently does not support real inlined assembly (only IR): ${asmChunk.position}") }