ir: fix handling of labeled chunks

This commit is contained in:
Irmen de Jong 2022-10-13 00:56:44 +02:00
parent 6fc89607d3
commit a9f9c40d8a
8 changed files with 99 additions and 31 deletions

View File

@ -407,13 +407,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} }
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks { private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
val code = IRCodeChunk(null, target.position)
val assignment = PtAssignment(target.position) val assignment = PtAssignment(target.position)
val assignTarget = PtAssignTarget(target.position) val assignTarget = PtAssignTarget(target.position)
assignTarget.children.add(target) assignTarget.children.add(target)
assignment.children.add(assignTarget) assignment.children.add(assignTarget)
assignment.children.add(PtMachineRegister(register, target.type, target.position)) assignment.children.add(PtMachineRegister(register, target.type, target.position))
val result = mutableListOf<IRCodeChunkBase>(code) val result = mutableListOf<IRCodeChunkBase>()
result += codeGen.translateNode(assignment) result += codeGen.translateNode(assignment)
return result return result
} }

View File

@ -56,8 +56,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
listOf(code) listOf(code)
} }
is PtMemoryByte -> { is PtMemoryByte -> {
val code = IRCodeChunk(null, expr.position) val result = mutableListOf<IRCodeChunkBase>()
val result = mutableListOf<IRCodeChunkBase>(code)
if(expr.address is PtNumber) { if(expr.address is PtNumber) {
val address = (expr.address as PtNumber).number.toInt() val address = (expr.address as PtNumber).number.toInt()
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, value = address), null, expr.position) addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, value = address), null, expr.position)

View File

@ -213,7 +213,7 @@ class IRCodeGen(
} }
internal fun translateNode(node: PtNode): IRCodeChunks { 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 PtScopeVarsDecls -> emptyList() // vars should be looked up via symbol table
is PtVariable -> emptyList() // var 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 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}") is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
else -> TODO("missing codegen for $node") 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 { private fun translate(branch: PtConditionalBranch): IRCodeChunks {

View File

@ -5,6 +5,7 @@ import prog8.intermediate.*
internal class IRPeepholeOptimizer(private val irprog: IRProgram) { internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
fun optimize() { fun optimize() {
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub -> irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
removeEmptyChunks(sub)
joinChunks(sub) joinChunks(sub)
sub.chunks.forEach { chunk -> sub.chunks.forEach { chunk ->
// we don't optimize Inline Asm chunks here. // 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<Pair<Int, String>>()
val removeChunks = mutableListOf<Int>()
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) { private fun joinChunks(sub: IRSubroutine) {
/* /*
Subroutine contains a list of chunks. Subroutine contains a list of chunks.
@ -34,9 +80,10 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
TODO: this has to be changed later... TODO: this has to be changed later...
*/ */
/* if(sub.chunks.isEmpty()) if(sub.chunks.isEmpty())
return return
/*
fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean { fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
if(previous is IRCodeChunk && chunk is IRCodeChunk) { if(previous is IRCodeChunk && chunk is IRCodeChunk) {
return true return true

View File

@ -3,8 +3,9 @@ TODO
For next release 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 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: 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 - ir: fix joinChunks() in the IR optimizer ? Fix TestIRPeepholeOptimizer and TestVm
- vm: program is list of chunks, fix dispatcher - vm: program is list of chunks, fix dispatcher

View File

@ -263,7 +263,7 @@ class IRFileReader {
line = lines.next() line = lines.next()
var chunk = IRCodeChunk(null, Position.DUMMY) var chunk = IRCodeChunk(null, Position.DUMMY)
if(line=="<C>") { if(line=="<C>") {
chunk = parseCodeChunk(line, lines, null)!! chunk = parseCodeChunk(line, lines)!!
line = lines.next() line = lines.next()
} }
if(line!="</INITGLOBALS>") if(line!="</INITGLOBALS>")
@ -285,8 +285,8 @@ class IRFileReader {
} }
private val blockPattern = Regex("<BLOCK NAME=(.+) ADDRESS=(.+) ALIGN=(.+) POS=(.+)>") private val blockPattern = Regex("<BLOCK NAME=(.+) ADDRESS=(.+) ALIGN=(.+) POS=(.+)>")
private val inlineAsmPattern = Regex("<INLINEASM IR=(.+) POS=(.+)>") private val inlineAsmPattern = Regex("<INLINEASM LABEL=(.*) IR=(.+) POS=(.+)>")
private val bytesPattern = Regex("<BYTES POS=(.+)>") private val bytesPattern = Regex("<BYTES LABEL=(.*) POS=(.+)>")
private val asmsubPattern = Regex("<ASMSUB NAME=(.+) ADDRESS=(.+) CLOBBERS=(.*) RETURNS=(.*) POS=(.+)>") private val asmsubPattern = Regex("<ASMSUB NAME=(.+) ADDRESS=(.+) CLOBBERS=(.*) RETURNS=(.*) POS=(.+)>")
private val subPattern = Regex("<SUB NAME=(.+) RETURNTYPE=(.+) POS=(.+)>") private val subPattern = Regex("<SUB NAME=(.+) RETURNTYPE=(.+) POS=(.+)>")
private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]") private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]")
@ -312,18 +312,19 @@ class IRFileReader {
val sub = parseAsmSubroutine(line, lines) val sub = parseAsmSubroutine(line, lines)
block += sub block += sub
} else if(line.startsWith("<INLINEASM ")) { } else if(line.startsWith("<INLINEASM ")) {
val asm = parseInlineAssembly(line, lines, null) val asm = parseInlineAssembly(line, lines)
block += asm block += asm
} else } else
throw IRParseException("invalid line in BLOCK") throw IRParseException("invalid line in BLOCK")
} }
} }
private fun parseInlineAssembly(startline: String, lines: Iterator<String>, label: String?): IRInlineAsmChunk { private fun parseInlineAssembly(startline: String, lines: Iterator<String>): IRInlineAsmChunk {
// <INLINEASM IR=true POS=[examples/test.p8: line 8 col 6-9]> // <INLINEASM LABEL=optional-label IR=true POS=[examples/test.p8: line 8 col 6-9]>
val match = inlineAsmPattern.matchEntire(startline) ?: throw IRParseException("invalid INLINEASM") val match = inlineAsmPattern.matchEntire(startline) ?: throw IRParseException("invalid INLINEASM")
val isIr = match.groupValues[1].toBoolean() val label = match.groupValues[1]
val pos = parsePosition(match.groupValues[2]) val isIr = match.groupValues[2].toBoolean()
val pos = parsePosition(match.groupValues[3])
val asmlines = mutableListOf<String>() val asmlines = mutableListOf<String>()
var line = lines.next() var line = lines.next()
while(line!="</INLINEASM>") { while(line!="</INLINEASM>") {
@ -352,7 +353,7 @@ class IRFileReader {
params += Pair(dt, regsf) params += Pair(dt, regsf)
} }
line = lines.next() line = lines.next()
val asm = parseInlineAssembly(line, lines, null) val asm = parseInlineAssembly(line, lines)
while(line!="</ASMSUB>") while(line!="</ASMSUB>")
line = lines.next() line = lines.next()
val clobberRegs = if(clobbers.isBlank()) emptyList() else clobbers.split(',').map { CpuRegister.valueOf(it) } val clobberRegs = if(clobbers.isBlank()) emptyList() else clobbers.split(',').map { CpuRegister.valueOf(it) }
@ -386,12 +387,12 @@ class IRFileReader {
val line = lines.next() val line = lines.next()
if(line=="</SUB>") if(line=="</SUB>")
return sub return sub
val chunk = if(line=="<C>") val chunk = if(line.startsWith("<C"))
parseCodeChunk(line, lines, null) parseCodeChunk(line, lines)
else if(line.startsWith("<BYTES ")) else if(line.startsWith("<BYTES "))
parseBinaryBytes(line, lines, null) parseBinaryBytes(line, lines)
else if(line.startsWith("<INLINEASM ")) else if(line.startsWith("<INLINEASM "))
parseInlineAssembly(line, lines, null) parseInlineAssembly(line, lines)
else else
throw IRParseException("invalid sub child node") throw IRParseException("invalid sub child node")
@ -406,9 +407,10 @@ class IRFileReader {
return sub return sub
} }
private fun parseBinaryBytes(startline: String, lines: Iterator<String>, label: String?): IRInlineBinaryChunk { private fun parseBinaryBytes(startline: String, lines: Iterator<String>): IRInlineBinaryChunk {
val match = bytesPattern.matchEntire(startline) ?: throw IRParseException("invalid BYTES") 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<UByte>() val bytes = mutableListOf<UByte>()
var line = lines.next() var line = lines.next()
while(line!="</BYTES>") { while(line!="</BYTES>") {
@ -436,13 +438,17 @@ class IRFileReader {
} }
} }
private fun parseCodeChunk(firstline: String, lines: Iterator<String>, label: String?): IRCodeChunk? { private fun parseCodeChunk(firstline: String, lines: Iterator<String>): IRCodeChunk? {
if(firstline!="<C>") { if(!firstline.startsWith("<C")) {
if(firstline=="</SUB>") if(firstline=="</SUB>")
return null return null
else else
throw IRParseException("invalid or empty <C>ODE chunk") throw IRParseException("invalid or empty <C>ODE chunk")
} }
val label = if(firstline.startsWith("<C LABEL="))
firstline.split('=', limit = 2)[1].dropLast(1)
else
null
val chunk = IRCodeChunk(label, Position.DUMMY) val chunk = IRCodeChunk(label, Position.DUMMY)
while(true) { while(true) {
val line = lines.next() val line = lines.next()

View File

@ -64,12 +64,14 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
is IRInlineAsmChunk -> writeInlineAsm(chunk) is IRInlineAsmChunk -> writeInlineAsm(chunk)
is IRInlineBinaryChunk -> writeInlineBytes(chunk) is IRInlineBinaryChunk -> writeInlineBytes(chunk)
else -> { else -> {
if(chunk.label!=null)
out.write("<C LABEL=${chunk.label}>\n")
else
out.write("<C>\n") out.write("<C>\n")
if (chunk.instructions.isEmpty())
throw InternalCompilerException("empty code chunk in ${it.name} ${it.position}")
chunk.instructions.forEach { instr -> chunk.instructions.forEach { instr ->
numInstr++ numInstr++
out.write(instr.toString()) out.write(instr.toString())
out.write("\n")
} }
out.write("</C>\n") out.write("</C>\n")
} }
@ -100,7 +102,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
} }
private fun writeInlineBytes(chunk: IRInlineBinaryChunk) { private fun writeInlineBytes(chunk: IRInlineBinaryChunk) {
out.write("<BYTES POS=${chunk.position}>\n") out.write("<BYTES LABEL=${chunk.label ?: ""} POS=${chunk.position}>\n")
chunk.data.withIndex().forEach {(index, byte) -> chunk.data.withIndex().forEach {(index, byte) ->
out.write(byte.toString(16).padStart(2,'0')) out.write(byte.toString(16).padStart(2,'0'))
if(index and 63 == 63 && index < chunk.data.size-1) 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) { private fun writeInlineAsm(chunk: IRInlineAsmChunk) {
out.write("<INLINEASM IR=${chunk.isIR} POS=${chunk.position}>\n") out.write("<INLINEASM LABEL=${chunk.label ?: ""} IR=${chunk.isIR} POS=${chunk.position}>\n")
out.write(chunk.assembly) out.write(chunk.assembly)
out.write("\n</INLINEASM>\n") out.write("\n</INLINEASM>\n")
} }

View File

@ -72,7 +72,8 @@ class IRProgram(val name: String,
} }
it.subroutines.forEach { sub -> it.subroutines.forEach { sub ->
sub.chunks.forEach { chunk -> 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"} 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( class IRAsmSubroutine(