From e67c05c274cbc86d2507e1a328f6b867a37f39f8 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 1 Nov 2022 00:15:39 +0100 Subject: [PATCH] ir: fix asmsub contents not appearing in IR file --- .../prog8/codegen/intermediate/IRCodeGen.kt | 16 ++++++-- .../intermediate/IRPeepholeOptimizer.kt | 2 + .../intermediate/IRUnusedCodeRemover.kt | 38 +++++++++++++++++++ compiler/test/vm/TestCompilerVirtual.kt | 2 +- docs/source/todo.rst | 3 +- .../src/prog8/intermediate/IRProgram.kt | 4 +- .../src/prog8/vm/VmProgramLoader.kt | 16 +++++--- virtualmachine/test/TestVm.kt | 23 +++++++++-- 8 files changed, 89 insertions(+), 15 deletions(-) create mode 100644 codeGenIntermediate/src/prog8/codegen/intermediate/IRUnusedCodeRemover.kt diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 912f0f4e5..51cb3aa35 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -56,7 +56,16 @@ class IRCodeGen( if(options.optimize) { val optimizer = IRPeepholeOptimizer(irProg) optimizer.optimize() - irProg.linkChunks() // re-link + + // TODO FIX & REENABLE: +// val remover = IRUnusedCodeRemover(irProg, errors) +// do { +// val numRemoved = remover.optimize() +// } while(numRemoved>0 && errors.noErrors()) +// +// errors.report() + + irProg.linkChunks() // re-link } irProg.validate() @@ -249,6 +258,7 @@ class IRCodeGen( sub.retvalRegisters, sub.inline, sub.position) + renamedSub.add(sub.children.single()) parent.children.remove(sub) parent.add(renamedSub) } @@ -1082,9 +1092,9 @@ class IRCodeGen( irBlock += sub } is PtAsmSub -> { - val assemblyChild = if(child.children.isEmpty()) null else (child.children.single() as PtInlineAssembly) + val assemblyChild = child.children.single() as PtInlineAssembly val asmChunk = IRInlineAsmChunk( - child.name, assemblyChild?.assembly ?: "", assemblyChild?.isIR==true, child.position, null + child.name, assemblyChild.assembly, assemblyChild.isIR, child.position, null ) irBlock += IRAsmSubroutine( child.name, diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt index 58cba81a5..72a1e415e 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt @@ -27,6 +27,8 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) { } removeEmptyChunks(sub) } + + irprog.linkChunks() // re-link } private fun removeEmptyChunks(sub: IRSubroutine) { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRUnusedCodeRemover.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRUnusedCodeRemover.kt new file mode 100644 index 000000000..62e721541 --- /dev/null +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRUnusedCodeRemover.kt @@ -0,0 +1,38 @@ +package prog8.codegen.intermediate + +import prog8.code.core.IErrorReporter +import prog8.code.core.SourceCode.Companion.libraryFilePrefix +import prog8.intermediate.* + +internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val errors: IErrorReporter) { + fun optimize(): Int { + val linkedChunks = mutableSetOf() + var numRemoved = 0 + + irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub -> + sub.chunks.forEach { chunk -> + if(chunk.next!=null) + linkedChunks += chunk.next!! + chunk.instructions.forEach { + if(it.branchTarget!=null) + linkedChunks += it.branchTarget!! + } + if(chunk.label=="main.start") + linkedChunks += chunk + } + } + + irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub -> + sub.chunks.reversed().forEach { chunk -> + if(chunk !in linkedChunks) { + if(!chunk.position.file.startsWith(libraryFilePrefix)) + errors.warn("unreachable code", chunk.position) + sub.chunks.remove(chunk) + numRemoved++ + } + } + } + + return numRemoved + } +} \ No newline at end of file diff --git a/compiler/test/vm/TestCompilerVirtual.kt b/compiler/test/vm/TestCompilerVirtual.kt index 29c8d9c52..383def318 100644 --- a/compiler/test/vm/TestCompilerVirtual.kt +++ b/compiler/test/vm/TestCompilerVirtual.kt @@ -240,6 +240,6 @@ main { val exc = shouldThrow { VmRunner().runProgram(virtfile.readText()) } - exc.message shouldContain("does not support asmsubs") + exc.message shouldContain("does not support non-IR asmsubs") } }) \ No newline at end of file diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 9d99d5b81..a0ed14b0f 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,7 +3,8 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- ir: asmsub contents remains blank in IR file ? +- ir: position of chunk of text.print("abc") seems to point to the string arg instead of the call +- fix unused chunk remover that now causes code execution error (IRCodeGen should enable it) - ir: improve dead code elimination by checking chunk linkage. Does this solve the next issue?: - ir: how to remove all unused subroutines? (the 6502 assembly codegen relies on 64tass solve this for us) diff --git a/intermediate/src/prog8/intermediate/IRProgram.kt b/intermediate/src/prog8/intermediate/IRProgram.kt index d1a570446..a325449b5 100644 --- a/intermediate/src/prog8/intermediate/IRProgram.kt +++ b/intermediate/src/prog8/intermediate/IRProgram.kt @@ -107,8 +107,10 @@ class IRProgram(val name: String, if(next!=null) { if (next is IRCodeChunk && chunk.instructions.lastOrNull()?.opcode !in OpcodesThatJump) chunk.next = next + else if(next is IRInlineAsmChunk) + chunk.next = next else - throw AssemblyError("code chunk flows into following non-code chunk") + throw AssemblyError("code chunk flows into following non-executable chunk") } } diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index 17a419160..50582ef83 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -40,16 +40,14 @@ class VmProgramLoader { block.inlineAssembly.forEach { val replacement = addAssemblyToProgram(it, programChunks, variableAddresses) - if(replacement!=null) - chunkReplacements += replacement + chunkReplacements += replacement } block.subroutines.forEach { it.chunks.forEach { chunk -> when (chunk) { is IRInlineAsmChunk -> { val replacement = addAssemblyToProgram(chunk, programChunks, variableAddresses) - if(replacement!=null) - chunkReplacements += replacement + chunkReplacements += replacement } is IRInlineBinaryChunk -> TODO("inline binary data not yet supported in the VM") is IRCodeChunk -> programChunks += chunk @@ -57,8 +55,14 @@ class VmProgramLoader { } } } - if(block.asmSubroutines.any()) - throw IRParseException("vm currently does not support asmsubs: ${block.asmSubroutines.first().name}") + block.asmSubroutines.forEach { + if(!it.asmChunk.isIR) + throw IRParseException("vm currently does not support non-IR asmsubs: ${block.asmSubroutines.first().name}") + else { + val replacement = addAssemblyToProgram(it.asmChunk, programChunks, variableAddresses) + chunkReplacements += replacement + } + } } pass2translateSyscalls(programChunks) diff --git a/virtualmachine/test/TestVm.kt b/virtualmachine/test/TestVm.kt index 2061e17f9..d687318c1 100644 --- a/virtualmachine/test/TestVm.kt +++ b/virtualmachine/test/TestVm.kt @@ -82,7 +82,7 @@ class TestVm: FunSpec( { } } - test("vm asmsub not supported") { + test("non-IR asmsub not supported in vm") { val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val startSub = IRAsmSubroutine( @@ -91,16 +91,33 @@ class TestVm: FunSpec( { emptySet(), emptyList(), emptyList(), - IRInlineAsmChunk("main.asmstart", "inlined asm here", true, Position.DUMMY, null), + IRInlineAsmChunk("main.asmstart", "inlined asm here", false, Position.DUMMY, null), Position.DUMMY ) block += startSub program.addBlock(block) - shouldThrowWithMessage("vm currently does not support asmsubs: main.asmstart") { + shouldThrowWithMessage("vm currently does not support non-IR asmsubs: main.asmstart") { VirtualMachine(program) } } + test("IR asmsub ok in vm") { + val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget()) + val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) + val startSub = IRAsmSubroutine( + "main.start", + 0x2000u, + emptySet(), + emptyList(), + emptyList(), + IRInlineAsmChunk("main.start", "return", true, Position.DUMMY, null), + Position.DUMMY + ) + block += startSub + program.addBlock(block) + VirtualMachine(program).run() + } + test("vmrunner") { val runner = VmRunner() val irSource="""