From 9ef9c243886698311814f87745169343eec199ed Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 25 Nov 2023 01:08:26 +0100 Subject: [PATCH] IR: optimize redundant labels --- .../intermediate/IRPeepholeOptimizer.kt | 38 ++++++++++++++++--- codeGenIntermediate/test/TestIRPeepholeOpt.kt | 16 ++++---- compiler/res/prog8lib/cx16/bmx.p8 | 2 +- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt index 50f949a3f..5c9c6418a 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt @@ -87,7 +87,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { /* Empty Code chunk with label -> If next chunk has no label -> move label to next chunk, remove original - If next chunk has label -> label name should be the same, remove original. Otherwise FOR NOW leave it in place. (TODO: merge both labels into 1) + If next chunk has label -> label name should be the same, remove original. Otherwise merge both labels into 1. If is last chunk -> keep chunk in place because of the label. Empty Code chunk without label -> should not have been generated! ERROR. @@ -96,6 +96,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { val relabelChunks = mutableListOf>() val removeChunks = mutableListOf() + val replaceLabels = mutableMapOf() sub.chunks.withIndex().forEach { (index, chunk) -> if(chunk is IRCodeChunk && chunk.instructions.isEmpty()) { @@ -109,10 +110,18 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { relabelChunks += Pair(index + 1, chunk.label!!) removeChunks += index } else { - if (chunk.label == nextchunk.label) - removeChunks += index - else { - // TODO: merge labels on same chunk + // merge both labels into 1 except if this is the label chunk at the start of the subroutine + if(index>0) { + if (chunk.label == nextchunk.label) + removeChunks += index + else { + removeChunks += index + replaceLabels[chunk.label!!] = nextchunk.label!! + replaceLabels.entries.forEach { (key, value) -> + if (value == chunk.label) + replaceLabels[key] = nextchunk.label!! + } + } } } } @@ -129,6 +138,25 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { sub.chunks[index] = chunk } removeChunks.reversed().forEach { index -> sub.chunks.removeAt(index) } + + sub.chunks.forEach { chunk -> + chunk.instructions.withIndex().forEach { (idx, instr) -> + instr.labelSymbol?.let { + if(instr.opcode in OpcodesThatBranch) { + replaceLabels.forEach { (from, to) -> + if (it == from) { + chunk.instructions[idx] = instr.copy(labelSymbol = to) + } + else { + val actualPrefix = "$from." + if (it.startsWith(actualPrefix)) + chunk.instructions[idx] = instr.copy(labelSymbol = "$to.") + } + } + } + } + } + } } private fun joinChunks(sub: IRSubroutine) { diff --git a/codeGenIntermediate/test/TestIRPeepholeOpt.kt b/codeGenIntermediate/test/TestIRPeepholeOpt.kt index c26eccc03..9ed204739 100644 --- a/codeGenIntermediate/test/TestIRPeepholeOpt.kt +++ b/codeGenIntermediate/test/TestIRPeepholeOpt.kt @@ -53,9 +53,9 @@ class TestIRPeepholeOpt: FunSpec({ test("remove jmp to label below") { val c1 = IRCodeChunk("main.start", null) - c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label") // removed, but chunk stays because of label + c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label") val c2 = IRCodeChunk("label", null) - c2 += IRInstruction(Opcode.JUMP, labelSymbol = "label2") // removed, but chunk stays because of label + c2 += IRInstruction(Opcode.JUMP, labelSymbol = "label2") c2 += IRInstruction(Opcode.NOP) // removed val c3 = IRCodeChunk("label2", null) c3 += IRInstruction(Opcode.JUMP, labelSymbol = "label3") @@ -67,15 +67,13 @@ class TestIRPeepholeOpt: FunSpec({ irProg.chunks().flatMap { it.instructions }.size shouldBe 5 val opt = IRPeepholeOptimizer(irProg) opt.optimize(true, ErrorReporterForTests()) - irProg.chunks().size shouldBe 4 + irProg.chunks().size shouldBe 3 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()[1].label shouldBe "label2" + irProg.chunks()[2].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 + irProg.chunks()[1].isEmpty() shouldBe false + irProg.chunks()[2].isEmpty() shouldBe true val instr = irProg.chunks().flatMap { it.instructions } instr.size shouldBe 2 instr[0].opcode shouldBe Opcode.JUMP diff --git a/compiler/res/prog8lib/cx16/bmx.p8 b/compiler/res/prog8lib/cx16/bmx.p8 index 016c4f100..f83ad184d 100644 --- a/compiler/res/prog8lib/cx16/bmx.p8 +++ b/compiler/res/prog8lib/cx16/bmx.p8 @@ -1,7 +1,7 @@ ; Routines to load and save "BMX" files (commander X16 bitmap format) Version 1. ; Only uncompressed images are supported for now. ; BMX Specification: https://cx16forum.com/forum/viewtopic.php?t=6945 -; TODO: make read_palette() and write_palette() use a palette_buffer_ptr to store palette into system ram instead of directly into vram. +; TODO: make read_palette() and write_palette() use an optional palette_buffer_ptr to store palette into system ram instead of directly into vram. %import diskio