diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt index 6c88761e3..b81b34abb 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt @@ -24,6 +24,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { private fun optimizeOnlyJoinChunks() { irprog.blocks.asSequence().flatMap { it.children.filterIsInstance() }.forEach { sub -> + joinChunks(sub) removeEmptyChunks(sub) joinChunks(sub) } @@ -32,6 +33,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { private fun peepholeOptimize() { irprog.blocks.asSequence().flatMap { it.children.filterIsInstance() }.forEach { sub -> + joinChunks(sub) removeEmptyChunks(sub) joinChunks(sub) sub.chunks.withIndex().forEach { (index, chunk1) -> @@ -112,7 +114,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { if(sub.chunks.isEmpty()) return - fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean { + fun mayJoinCodeChunks(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean { if(chunk.label!=null) return false if(previous is IRCodeChunk && chunk is IRCodeChunk) { @@ -129,12 +131,39 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { chunks += sub.chunks[0] for(ix in 1 until sub.chunks.size) { val lastChunk = chunks.last() - if(mayJoin(lastChunk, sub.chunks[ix])) { - lastChunk.instructions += sub.chunks[ix].instructions - lastChunk.next = sub.chunks[ix].next + val candidate = sub.chunks[ix] + when(candidate) { + is IRCodeChunk -> { + if(mayJoinCodeChunks(lastChunk, candidate)) { + lastChunk.instructions += candidate.instructions + lastChunk.next = candidate.next + } + else + chunks += candidate + } + is IRInlineAsmChunk -> { + if(candidate.label!=null) + chunks += candidate + else if(lastChunk.isEmpty()) { + val label = lastChunk.label + if(label!=null) + chunks += IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next) + else + chunks += candidate + } + } + is IRInlineBinaryChunk -> { + if(candidate.label!=null) + chunks += candidate + else if(lastChunk.isEmpty()) { + val label = lastChunk.label + if(label!=null) + chunks += IRInlineBinaryChunk(label, candidate.data, candidate.next) + else + chunks += candidate + } + } } - else - chunks += sub.chunks[ix] } sub.chunks.clear() sub.chunks += chunks diff --git a/intermediate/src/prog8/intermediate/IRFileWriter.kt b/intermediate/src/prog8/intermediate/IRFileWriter.kt index 5521f04bb..1cb02ad92 100644 --- a/intermediate/src/prog8/intermediate/IRFileWriter.kt +++ b/intermediate/src/prog8/intermediate/IRFileWriter.kt @@ -140,7 +140,6 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { private fun writeInlineBytes(chunk: IRInlineBinaryChunk) { xml.writeStartElement("BYTES") chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) } - xml.writeCharacters("\n") chunk.data.withIndex().forEach {(index, byte) -> xml.writeCharacters(byte.toString(16).padStart(2,'0')) if(index and 63 == 63 && index < chunk.data.size-1) diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index a2921842e..bd0f26195 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -213,7 +213,6 @@ msig [b, w] reg1, reg2 - reg1 becomes the most significant by concat [b, w] reg1, reg2 - reg1 = concatenated lsb/lsw of reg1 (as lsb) and lsb/lsw of reg2 (as msb) into word or int (int not yet implemented; requires 32bits regs) push [b, w, f] reg1 - push value in reg1 on the stack pop [b, w, f] reg1 - pop value from stack into reg1 -binarydata - 'instruction' to hold inlined binary data bytes */ enum class Opcode { @@ -371,8 +370,7 @@ enum class Opcode { POP, MSIG, CONCAT, - BREAKPOINT, - BINARYDATA + BREAKPOINT } val OpcodesThatJump = setOf( @@ -650,7 +648,6 @@ val instructionFormats = mutableMapOf( Opcode.CLC to InstructionFormat.from("N"), Opcode.SEC to InstructionFormat.from("N"), Opcode.BREAKPOINT to InstructionFormat.from("N"), - Opcode.BINARYDATA to InstructionFormat.from("N"), ) @@ -664,9 +661,8 @@ data class IRInstruction( val immediate: Int?=null, // 0-$ff or $ffff if word val immediateFp: Float?=null, val address: Int?=null, // 0-$ffff - val labelSymbol: String?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!) - val binaryData: Collection?=null, - var branchTarget: IRCodeChunkBase? = null // will be linked after loading + val labelSymbol: String?=null, // symbolic label name as alternative to address (so only for Branch/jump/call Instructions!) + var branchTarget: IRCodeChunkBase? = null // Will be linked after loading in IRProgram.linkChunks()! This is the chunk that the branch labelSymbol points to. ) { // reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT) // This knowledge is useful in IL assembly optimizers to see how registers are used. @@ -683,10 +679,6 @@ data class IRInstruction( require(fpReg2==null || fpReg2 in 0..65536) {"fpReg2 out of bounds"} if(reg1!=null && reg2!=null) require(reg1!=reg2) {"reg1 must not be same as reg2"} // note: this is ok for fpRegs as these are always the same type - require((opcode==Opcode.BINARYDATA && binaryData!=null) || (opcode!=Opcode.BINARYDATA && binaryData==null)) { - "binarydata inconsistency" - } - val formats = instructionFormats.getValue(opcode) require (type != null || formats.containsKey(null)) { "missing type" } diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index ee4537f67..f90ad90f0 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -284,7 +284,6 @@ class VirtualMachine(irProgram: IRProgram) { Opcode.BREAKPOINT -> InsBREAKPOINT() Opcode.CLC -> { statusCarry = false; nextPc() } Opcode.SEC -> { statusCarry = true; nextPc() } - Opcode.BINARYDATA -> TODO("BINARYDATA not yet supported in VM") Opcode.LOADCPU -> InsLOADCPU(ins) Opcode.STORECPU -> InsSTORECPU(ins) Opcode.STOREZCPU -> InsSTOREZCPU(ins) diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index 14ac964e1..aa9546439 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -46,7 +46,7 @@ class VmProgramLoader { is IRCodeChunk -> programChunks += child is IRInlineAsmChunk -> { val replacement = addAssemblyToProgram(child, programChunks, variableAddresses) - chunkReplacements += replacement + chunkReplacements += Pair(child, replacement) } is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO is IRSubroutine -> { @@ -55,7 +55,7 @@ class VmProgramLoader { when (chunk) { is IRInlineAsmChunk -> { val replacement = addAssemblyToProgram(chunk, programChunks, variableAddresses) - chunkReplacements += replacement + chunkReplacements += Pair(chunk, replacement) } is IRInlineBinaryChunk -> throw IRParseException("inline binary data not yet supported in the VM") // TODO is IRCodeChunk -> programChunks += chunk @@ -339,7 +339,7 @@ class VmProgramLoader { asmChunk: IRInlineAsmChunk, chunks: MutableList, symbolAddresses: MutableMap, - ): Pair { + ): IRCodeChunk { if(asmChunk.isIR) { val chunk = IRCodeChunk(asmChunk.label, asmChunk.next) asmChunk.assembly.lineSequence().forEach { @@ -350,7 +350,7 @@ class VmProgramLoader { ) } chunks += chunk - return Pair(asmChunk, chunk) + return chunk } else { throw IRParseException("vm currently does not support real inlined assembly (only IR): ${asmChunk.label}") } diff --git a/virtualmachine/test/TestVm.kt b/virtualmachine/test/TestVm.kt index 24ab28a57..ec8956d86 100644 --- a/virtualmachine/test/TestVm.kt +++ b/virtualmachine/test/TestVm.kt @@ -70,22 +70,6 @@ class TestVm: FunSpec( { vm.stepCount shouldBe code.instructions.size } - test("vm asmbinary not supported") { - val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) - val block = IRBlock("testmain", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY) - val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY) - val code = IRCodeChunk(startSub.label, null) - code += IRInstruction(Opcode.BINARYDATA, binaryData = listOf(1u,2u,3u)) - code += IRInstruction(Opcode.RETURN) - startSub += code - block += startSub - program.addBlock(block) - val vm = VirtualMachine(program) - shouldThrowWithMessage("An operation is not implemented: BINARYDATA not yet supported in VM") { - vm.run() - } - } - test("asmsub not supported in vm even with IR") { val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) val block = IRBlock("main", null, false, false, IRBlock.BlockAlignment.NONE, Position.DUMMY)