From a9f9c40d8a4e211a3628706d6bd3514d16513ab0 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 13 Oct 2022 00:56:44 +0200 Subject: [PATCH] ir: fix handling of labeled chunks --- .../codegen/intermediate/BuiltinFuncGen.kt | 3 +- .../codegen/intermediate/ExpressionGen.kt | 3 +- .../prog8/codegen/intermediate/IRCodeGen.kt | 10 +++- .../intermediate/IRPeepholeOptimizer.kt | 49 ++++++++++++++++++- docs/source/todo.rst | 3 +- .../src/prog8/intermediate/IRFileReader.kt | 40 ++++++++------- .../src/prog8/intermediate/IRFileWriter.kt | 12 +++-- .../src/prog8/intermediate/IRProgram.kt | 10 +++- 8 files changed, 99 insertions(+), 31 deletions(-) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index 1c5c76a7f..36fcd0bc1 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -407,13 +407,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe } private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks { - val code = IRCodeChunk(null, target.position) val assignment = PtAssignment(target.position) val assignTarget = PtAssignTarget(target.position) assignTarget.children.add(target) assignment.children.add(assignTarget) assignment.children.add(PtMachineRegister(register, target.type, target.position)) - val result = mutableListOf(code) + val result = mutableListOf() result += codeGen.translateNode(assignment) return result } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index e2c387200..3094a1db4 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -56,8 +56,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { listOf(code) } is PtMemoryByte -> { - val code = IRCodeChunk(null, expr.position) - val result = mutableListOf(code) + val result = mutableListOf() if(expr.address is PtNumber) { val address = (expr.address as PtNumber).number.toInt() addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, value = address), null, expr.position) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index f8c6e3d36..7c2c1a5b0 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -213,7 +213,7 @@ class IRCodeGen( } internal fun translateNode(node: PtNode): IRCodeChunks { - return when(node) { + val chunks = when(node) { is PtScopeVarsDecls -> emptyList() // vars should be looked up via symbol table is PtVariable -> emptyList() // var should be looked up via symbol table is PtMemMapped -> emptyList() // memmapped var should be looked up via symbol table @@ -264,6 +264,14 @@ class IRCodeGen( is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}") else -> TODO("missing codegen for $node") } + + chunks.forEach { chunk -> + require(chunk.isNotEmpty() || chunk.label != null) { + "chunk should have instructions and/or a label" + } + } + + return chunks } private fun translate(branch: PtConditionalBranch): IRCodeChunks { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt index ba4133299..95c83843a 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt @@ -5,6 +5,7 @@ import prog8.intermediate.* internal class IRPeepholeOptimizer(private val irprog: IRProgram) { fun optimize() { irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub -> + removeEmptyChunks(sub) joinChunks(sub) sub.chunks.forEach { chunk -> // we don't optimize Inline Asm chunks here. @@ -27,6 +28,51 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) { } } + private fun removeEmptyChunks(sub: IRSubroutine) { + if(sub.chunks.isEmpty()) + return + + /* + 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: consolidate labels into 1) + Empty Code chunk without label -> + should not have been generated! ERROR. + */ + + + val relabelChunks = mutableListOf>() + val removeChunks = mutableListOf() + + sub.chunks.withIndex().forEach { (index, chunk) -> + require(chunk.isNotEmpty() || chunk.label!=null) { + "chunk should have instructions and/or a label" + } + + if(chunk is IRCodeChunk && chunk.label!=null && chunk.instructions.isEmpty()) { + val nextchunk = sub.chunks[index+1] + if(nextchunk.label==null) { + // can transplant label to next chunk and remove this empty one. + relabelChunks += Pair(index+1, chunk.label!!) + removeChunks += index + } else { + if(chunk.label==nextchunk.label) + removeChunks += index + else { + // TODO: consolidate labels on same chunk + } + } + } + } + + relabelChunks.forEach { (index, label) -> + val chunk = IRCodeChunk(label, sub.chunks[index].position) + chunk.instructions += sub.chunks[index].instructions + sub.chunks[index] = chunk + } + removeChunks.reversed().forEach { index -> sub.chunks.removeAt(index) } + } + private fun joinChunks(sub: IRSubroutine) { /* Subroutine contains a list of chunks. @@ -34,9 +80,10 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) { TODO: this has to be changed later... */ -/* if(sub.chunks.isEmpty()) + if(sub.chunks.isEmpty()) return +/* fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean { if(previous is IRCodeChunk && chunk is IRCodeChunk) { return true diff --git a/docs/source/todo.rst b/docs/source/todo.rst index ab230fb81..aa1715073 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,8 +3,9 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- ir: get rid of IRCodeLabel, make every label start a new code chunk, give those a 'label' property. - ir: fix program to be list of chunks +- ir: fix unit tests +- ir: link all sequential chunks to another (exiting one chunk 'calls' the next chunk) - ir: jump/branch instructions don't link to a PC index anymore, but to the actual chunk with that label - ir: fix joinChunks() in the IR optimizer ? Fix TestIRPeepholeOptimizer and TestVm - vm: program is list of chunks, fix dispatcher diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index 7753b4f91..21e09da4c 100644 --- a/intermediate/src/prog8/intermediate/IRFileReader.kt +++ b/intermediate/src/prog8/intermediate/IRFileReader.kt @@ -263,7 +263,7 @@ class IRFileReader { line = lines.next() var chunk = IRCodeChunk(null, Position.DUMMY) if(line=="") { - chunk = parseCodeChunk(line, lines, null)!! + chunk = parseCodeChunk(line, lines)!! line = lines.next() } if(line!="") @@ -285,8 +285,8 @@ class IRFileReader { } private val blockPattern = Regex("") - private val inlineAsmPattern = Regex("") - private val bytesPattern = Regex("") + private val inlineAsmPattern = Regex("") + private val bytesPattern = Regex("") private val asmsubPattern = Regex("") private val subPattern = Regex("") private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]") @@ -312,18 +312,19 @@ class IRFileReader { val sub = parseAsmSubroutine(line, lines) block += sub } else if(line.startsWith(", label: String?): IRInlineAsmChunk { - // + private fun parseInlineAssembly(startline: String, lines: Iterator): IRInlineAsmChunk { + // val match = inlineAsmPattern.matchEntire(startline) ?: throw IRParseException("invalid INLINEASM") - val isIr = match.groupValues[1].toBoolean() - val pos = parsePosition(match.groupValues[2]) + val label = match.groupValues[1] + val isIr = match.groupValues[2].toBoolean() + val pos = parsePosition(match.groupValues[3]) val asmlines = mutableListOf() var line = lines.next() while(line!="") { @@ -352,7 +353,7 @@ class IRFileReader { params += Pair(dt, regsf) } line = lines.next() - val asm = parseInlineAssembly(line, lines, null) + val asm = parseInlineAssembly(line, lines) while(line!="") line = lines.next() val clobberRegs = if(clobbers.isBlank()) emptyList() else clobbers.split(',').map { CpuRegister.valueOf(it) } @@ -386,12 +387,12 @@ class IRFileReader { val line = lines.next() if(line=="") return sub - val chunk = if(line=="") - parseCodeChunk(line, lines, null) + val chunk = if(line.startsWith(", label: String?): IRInlineBinaryChunk { + private fun parseBinaryBytes(startline: String, lines: Iterator): IRInlineBinaryChunk { val match = bytesPattern.matchEntire(startline) ?: throw IRParseException("invalid BYTES") - val pos = parsePosition(match.groupValues[1]) + val label = match.groupValues[1] + val pos = parsePosition(match.groupValues[2]) val bytes = mutableListOf() var line = lines.next() while(line!="") { @@ -436,13 +438,17 @@ class IRFileReader { } } - private fun parseCodeChunk(firstline: String, lines: Iterator, label: String?): IRCodeChunk? { - if(firstline!="") { + private fun parseCodeChunk(firstline: String, lines: Iterator): IRCodeChunk? { + if(!firstline.startsWith("") return null else throw IRParseException("invalid or empty ODE chunk") } + val label = if(firstline.startsWith("\n") - if (chunk.instructions.isEmpty()) - throw InternalCompilerException("empty code chunk in ${it.name} ${it.position}") + if(chunk.label!=null) + out.write("\n") + else + out.write("\n") chunk.instructions.forEach { instr -> numInstr++ out.write(instr.toString()) + out.write("\n") } out.write("\n") } @@ -100,7 +102,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { } private fun writeInlineBytes(chunk: IRInlineBinaryChunk) { - out.write("\n") + out.write("\n") chunk.data.withIndex().forEach {(index, byte) -> out.write(byte.toString(16).padStart(2,'0')) if(index and 63 == 63 && index < chunk.data.size-1) @@ -110,7 +112,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { } private fun writeInlineAsm(chunk: IRInlineAsmChunk) { - out.write("\n") + out.write("\n") out.write(chunk.assembly) out.write("\n\n") } diff --git a/intermediate/src/prog8/intermediate/IRProgram.kt b/intermediate/src/prog8/intermediate/IRProgram.kt index 365a69896..c1062fbc8 100644 --- a/intermediate/src/prog8/intermediate/IRProgram.kt +++ b/intermediate/src/prog8/intermediate/IRProgram.kt @@ -72,7 +72,8 @@ class IRProgram(val name: String, } it.subroutines.forEach { sub -> sub.chunks.forEach { chunk -> - if (chunk is IRInlineAsmChunk) { require(chunk.instructions.isEmpty()) } + if (chunk is IRCodeChunk) require(chunk.instructions.isNotEmpty() || chunk.label!=null) + else require(chunk.instructions.isEmpty()) } } } @@ -143,7 +144,12 @@ class IRSubroutine(val name: String, require(returnType==null || returnType in NumericDatatypes) {"non-numeric returntype $returnType"} } - operator fun plusAssign(chunk: IRCodeChunkBase) { chunks+= chunk } + operator fun plusAssign(chunk: IRCodeChunkBase) { + require(chunk.isNotEmpty() || chunk.label!=null) { + "chunk should have instructions and/or a label" + } + chunks+= chunk + } } class IRAsmSubroutine(