diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 4b8f5195d..94be4b026 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -69,7 +69,7 @@ class IRCodeGen( if(block.inlineAssembly.isNotEmpty()) { val first = block.inlineAssembly.first() if(first.label==null) { - val replacement = IRInlineAsmChunk(block.name, first.assembly, first.isIR, first.position) + val replacement = IRInlineAsmChunk(block.name, first.assembly, first.isIR, first.position, first.next) block.inlineAssembly.removeAt(0) block.inlineAssembly.add(0, replacement) } else if(first.label != block.name) { @@ -87,8 +87,8 @@ class IRCodeGen( 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) + is IRInlineAsmChunk -> IRInlineAsmChunk(sub.name, first.assembly, first.isIR, first.position, first.next) + is IRInlineBinaryChunk -> IRInlineBinaryChunk(sub.name, first.data, first.position, first.next) else -> throw AssemblyError("invalid chunk") } sub.chunks.removeAt(0) @@ -278,12 +278,12 @@ class IRCodeGen( listOf(chunk) } is PtConditionalBranch -> translate(node) - is PtInlineAssembly -> listOf(IRInlineAsmChunk(null, node.assembly, node.isIR, node.position)) + is PtInlineAssembly -> listOf(IRInlineAsmChunk(null, node.assembly, node.isIR, node.position, null)) is PtIncludeBinary -> { val data = node.file.readBytes() .drop(node.offset?.toInt() ?: 0) .take(node.length?.toInt() ?: Int.MAX_VALUE) - listOf(IRInlineBinaryChunk(null, data.map { it.toUByte() }, node.position)) + listOf(IRInlineBinaryChunk(null, data.map { it.toUByte() }, node.position, null)) } is PtAddressOf, is PtContainmentCheck, @@ -348,15 +348,15 @@ class IRCodeGen( val newChunks = chunks.drop(1).toMutableList() val labeledFirstChunk = when(val first=chunks[0]) { is IRCodeChunk -> { - val newChunk = IRCodeChunk(label, first.position, null) + val newChunk = IRCodeChunk(label, first.position, first.next) newChunk.instructions += first.instructions newChunk } is IRInlineAsmChunk -> { - IRInlineAsmChunk(label, first.assembly, first.isIR, first.position) + IRInlineAsmChunk(label, first.assembly, first.isIR, first.position, first.next) } is IRInlineBinaryChunk -> { - IRInlineBinaryChunk(label, first.data, first.position) + IRInlineBinaryChunk(label, first.data, first.position, first.next) } else -> { throw AssemblyError("invalid chunk") @@ -1077,7 +1077,7 @@ class IRCodeGen( ) } is PtInlineAssembly -> { - irBlock += IRInlineAsmChunk(null, child.assembly, child.isIR, child.position) + irBlock += IRInlineAsmChunk(null, child.assembly, child.isIR, child.position, null) } else -> TODO("weird child node $child") } diff --git a/codeGenIntermediate/test/TestIRPeepholeOpt.kt b/codeGenIntermediate/test/TestIRPeepholeOpt.kt index 1defde54e..012da4eac 100644 --- a/codeGenIntermediate/test/TestIRPeepholeOpt.kt +++ b/codeGenIntermediate/test/TestIRPeepholeOpt.kt @@ -7,6 +7,7 @@ import prog8.intermediate.* class TestIRPeepholeOpt: FunSpec({ fun makeIRProgram(chunks: List): IRProgram { + require(chunks.first().label=="main.start") val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY) chunks.forEach { sub += it } @@ -30,7 +31,7 @@ class TestIRPeepholeOpt: FunSpec({ } fun makeIRProgram(instructions: List): IRProgram { - val chunk = IRCodeChunk(null, Position.DUMMY, null) + val chunk = IRCodeChunk("main.start", Position.DUMMY, null) instructions.forEach { chunk += it } return makeIRProgram(listOf(chunk)) } @@ -50,10 +51,10 @@ class TestIRPeepholeOpt: FunSpec({ } test("remove jmp to label below") { - val c1 = IRCodeChunk(null, Position.DUMMY, null) - c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label") // removed + val c1 = IRCodeChunk("main.start", Position.DUMMY, null) + c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label") // removed, but chunk stays because of label val c2 = IRCodeChunk("label", Position.DUMMY, null) - c2 += IRInstruction(Opcode.JUMP, labelSymbol = "label2") // removed + c2 += IRInstruction(Opcode.JUMP, labelSymbol = "label2") // removed, but chunk stays because of label c2 += IRInstruction(Opcode.NOP) // removed val c3 = IRCodeChunk("label2", Position.DUMMY, null) c3 += IRInstruction(Opcode.JUMP, labelSymbol = "label3") @@ -65,10 +66,15 @@ class TestIRPeepholeOpt: FunSpec({ irProg.chunks().flatMap { it.instructions }.size shouldBe 5 val opt = IRPeepholeOptimizer(irProg) opt.optimize() - irProg.chunks().size shouldBe 3 - irProg.chunks()[0].label shouldBe "label" - irProg.chunks()[1].label shouldBe "label2" - irProg.chunks()[2].label shouldBe "label3" + irProg.chunks().size shouldBe 4 + irProg.chunks()[0].label shouldBe "main.start" + irProg.chunks()[1].label shouldBe "label" + irProg.chunks()[2].label shouldBe "label2" + irProg.chunks()[3].label shouldBe "label3" + irProg.chunks()[0].isEmpty() shouldBe true + irProg.chunks()[1].isEmpty() shouldBe true + irProg.chunks()[2].isEmpty() shouldBe false + irProg.chunks()[3].isEmpty() shouldBe true val instr = irProg.chunks().flatMap { it.instructions } instr.size shouldBe 2 instr[0].opcode shouldBe Opcode.JUMP diff --git a/docs/source/todo.rst b/docs/source/todo.rst index b8102ae04..480161eb6 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,11 +3,8 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- ir: fix linkChunks() in IRProgram and addAssemblyToProgram() next pointer tracking -- vm: program is list of chunks, fix dispatcher - ir: fix unit tests - ir: fix removeWeirdBranches in IR optimizer -- ir: next in IRCodeChunk can also be a Asm Chunk which can have next as well - update diagram in technical.rst? ... diff --git a/examples/test.p8 b/examples/test.p8 index 0b5ffa868..d99c57e09 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,16 +1,23 @@ -; %import textio + %import textio main { sub start() { - ; should get a return after the nop %ir {{ nop }} + ; should flow into next statements -; ubyte aa = 42 -; ubyte bb = 99 -; aa += bb -; txt.print_ub(aa) + ubyte aa = 42 + ubyte bb = 99 + aa += bb + txt.print_ub(aa) + txt.spc() + aa += bb + txt.print_ub(aa) + txt.spc() + aa += bb + txt.print_ub(aa) + txt.nl() } } diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index 69124f907..2e1990742 100644 --- a/intermediate/src/prog8/intermediate/IRFileReader.kt +++ b/intermediate/src/prog8/intermediate/IRFileReader.kt @@ -333,7 +333,7 @@ class IRFileReader { asmlines.add(line) line = lines.next() } - return IRInlineAsmChunk(label, asmlines.joinToString("\n"), isIr, pos) + return IRInlineAsmChunk(label, asmlines.joinToString("\n"), isIr, pos, null) } private fun parseAsmSubroutine(startline: String, lines: Iterator): IRAsmSubroutine { @@ -421,7 +421,7 @@ class IRFileReader { } line = lines.next() } - return IRInlineBinaryChunk(label, bytes, pos) + return IRInlineBinaryChunk(label, bytes, pos, null) } private fun parseParameters(lines: Iterator): List { diff --git a/intermediate/src/prog8/intermediate/IRProgram.kt b/intermediate/src/prog8/intermediate/IRProgram.kt index 4fb7897c3..edadb1dc3 100644 --- a/intermediate/src/prog8/intermediate/IRProgram.kt +++ b/intermediate/src/prog8/intermediate/IRProgram.kt @@ -111,8 +111,6 @@ class IRProgram(val name: String, chunk.next = next else throw AssemblyError("code chunk flows into following non-code chunk") - } else { - TODO("???") } } @@ -131,12 +129,12 @@ class IRProgram(val name: String, is IRInlineAsmChunk -> { val next = nextChunk() if(next!=null) { - // TODO if chunk doesn't end in a jump or return statement, flow continues into the next chunk + val lastInstr = chunk.instructions.lastOrNull() + if(lastInstr==null || lastInstr.opcode !in OpcodesThatJump) + chunk.next = next } } - is IRInlineBinaryChunk -> { - // TODO("link next of binary chunk") - } + is IRInlineBinaryChunk -> { } else -> throw AssemblyError("invalid chunk") } } @@ -266,7 +264,7 @@ class IRAsmSubroutine( fun usedRegisters() = registersUsed } -sealed class IRCodeChunkBase(val label: String?, val position: Position) { +sealed class IRCodeChunkBase(val label: String?, val position: Position, var next: IRCodeChunkBase?) { val instructions = mutableListOf() abstract fun isEmpty(): Boolean @@ -274,9 +272,7 @@ sealed class IRCodeChunkBase(val label: String?, val position: Position) { abstract fun usedRegisters(): RegistersUsed } -class IRCodeChunk(label: String?, - position: Position, - var next: IRCodeChunk?): IRCodeChunkBase(label, position) { // TODO next can also be InlineAsmChunk!! which can also have a next again. +class IRCodeChunk(label: String?, position: Position, next: IRCodeChunkBase?): IRCodeChunkBase(label, position, next) { override fun isEmpty() = instructions.isEmpty() override fun isNotEmpty() = instructions.isNotEmpty() @@ -298,7 +294,11 @@ class IRCodeChunk(label: String?, } } -class IRInlineAsmChunk(label: String?, val assembly: String, val isIR: Boolean, position: Position): IRCodeChunkBase(label, position) { +class IRInlineAsmChunk(label: String?, + val assembly: String, + val isIR: Boolean, + position: Position, + next: IRCodeChunkBase?): IRCodeChunkBase(label, position, next) { // note: no instructions, asm is in the property override fun isEmpty() = assembly.isBlank() override fun isNotEmpty() = assembly.isNotBlank() @@ -312,7 +312,10 @@ class IRInlineAsmChunk(label: String?, val assembly: String, val isIR: Boolean, override fun usedRegisters() = registersUsed } -class IRInlineBinaryChunk(label: String?, val data: Collection, position: Position): IRCodeChunkBase(label, position) { +class IRInlineBinaryChunk(label: String?, + val data: Collection, + position: Position, + next: IRCodeChunkBase?): IRCodeChunkBase(label, position, next) { // note: no instructions, data is in the property override fun isEmpty() = data.isEmpty() override fun isNotEmpty() = data.isNotEmpty() diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index b505dacc7..603379c0d 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -101,15 +101,7 @@ class VirtualMachine(irProgram: IRProgram) { var left=count while(left>0) { if(pcIndex >= pcChunk.instructions.size) { - if(pcChunk.next!=null) { - // go to next chunk - pcChunk = pcChunk.next!! - pcIndex = 0 - } else { - // end of program reached - exit() - break - } + stepNextChunk() } stepCount++ dispatch(pcChunk.instructions[pcIndex]) @@ -117,14 +109,26 @@ class VirtualMachine(irProgram: IRProgram) { } } + private fun stepNextChunk() { + val nextChunk = pcChunk.next + when (nextChunk) { + is IRCodeChunk -> { + pcChunk = nextChunk + pcIndex = 0 + } + null -> { + exit() // end of program reached + } + else -> { + throw IllegalArgumentException("VM cannot run code from non-code chunk $nextChunk") + } + } + } + private fun nextPc() { pcIndex ++ - if(pcIndex>=pcChunk.instructions.size) { - pcIndex = 0 - if(pcChunk.next==null) - TODO("no next chunk in $pcChunk (remove this check)") - pcChunk = pcChunk.next!! - } + if(pcIndex>=pcChunk.instructions.size) + stepNextChunk() } private fun branchTo(i: IRInstruction) { diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index 0d87aa776..17a419160 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -272,7 +272,7 @@ class VmProgramLoader { symbolAddresses: MutableMap, ): Pair { if(asmChunk.isIR) { - val chunk = IRCodeChunk(asmChunk.label, asmChunk.position, null) // TODO keep track of the chunk's next pointer + val chunk = IRCodeChunk(asmChunk.label, asmChunk.position, asmChunk.next) asmChunk.assembly.lineSequence().forEach { val parsed = parseIRCodeLine(it.trim(), Pair(chunk, chunk.instructions.size), placeholders) parsed.fold( diff --git a/virtualmachine/test/TestVm.kt b/virtualmachine/test/TestVm.kt index fcc5bc1bc..a170d1f57 100644 --- a/virtualmachine/test/TestVm.kt +++ b/virtualmachine/test/TestVm.kt @@ -42,7 +42,7 @@ class TestVm: FunSpec( { val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY) - val code = IRCodeChunk(null, Position.DUMMY, null) + val code = IRCodeChunk(startSub.name, Position.DUMMY, null) code += IRInstruction(Opcode.NOP) code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=1, value=12345) code += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1=1, value=1000) @@ -70,7 +70,7 @@ class TestVm: FunSpec( { val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY) - val code = IRCodeChunk(null, Position.DUMMY, null) + val code = IRCodeChunk(startSub.name, Position.DUMMY, null) code += IRInstruction(Opcode.BINARYDATA, binaryData = listOf(1u,2u,3u)) code += IRInstruction(Opcode.RETURN) startSub += code