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 {
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<IRCodeChunkBase>(code)
val result = mutableListOf<IRCodeChunkBase>()
result += codeGen.translateNode(assignment)
return result
}

View File

@ -56,8 +56,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
listOf(code)
}
is PtMemoryByte -> {
val code = IRCodeChunk(null, expr.position)
val result = mutableListOf<IRCodeChunkBase>(code)
val result = mutableListOf<IRCodeChunkBase>()
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)

View File

@ -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 {

View File

@ -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<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) {
/*
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

View File

@ -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

View File

@ -263,7 +263,7 @@ class IRFileReader {
line = lines.next()
var chunk = IRCodeChunk(null, Position.DUMMY)
if(line=="<C>") {
chunk = parseCodeChunk(line, lines, null)!!
chunk = parseCodeChunk(line, lines)!!
line = lines.next()
}
if(line!="</INITGLOBALS>")
@ -285,8 +285,8 @@ class IRFileReader {
}
private val blockPattern = Regex("<BLOCK NAME=(.+) ADDRESS=(.+) ALIGN=(.+) POS=(.+)>")
private val inlineAsmPattern = Regex("<INLINEASM IR=(.+) POS=(.+)>")
private val bytesPattern = Regex("<BYTES POS=(.+)>")
private val inlineAsmPattern = Regex("<INLINEASM LABEL=(.*) IR=(.+) POS=(.+)>")
private val bytesPattern = Regex("<BYTES LABEL=(.*) POS=(.+)>")
private val asmsubPattern = Regex("<ASMSUB NAME=(.+) ADDRESS=(.+) CLOBBERS=(.*) RETURNS=(.*) POS=(.+)>")
private val subPattern = Regex("<SUB NAME=(.+) RETURNTYPE=(.+) POS=(.+)>")
private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]")
@ -312,18 +312,19 @@ class IRFileReader {
val sub = parseAsmSubroutine(line, lines)
block += sub
} else if(line.startsWith("<INLINEASM ")) {
val asm = parseInlineAssembly(line, lines, null)
val asm = parseInlineAssembly(line, lines)
block += asm
} else
throw IRParseException("invalid line in BLOCK")
}
}
private fun parseInlineAssembly(startline: String, lines: Iterator<String>, label: String?): IRInlineAsmChunk {
// <INLINEASM IR=true POS=[examples/test.p8: line 8 col 6-9]>
private fun parseInlineAssembly(startline: String, lines: Iterator<String>): IRInlineAsmChunk {
// <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 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<String>()
var line = lines.next()
while(line!="</INLINEASM>") {
@ -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!="</ASMSUB>")
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=="</SUB>")
return sub
val chunk = if(line=="<C>")
parseCodeChunk(line, lines, null)
val chunk = if(line.startsWith("<C"))
parseCodeChunk(line, lines)
else if(line.startsWith("<BYTES "))
parseBinaryBytes(line, lines, null)
parseBinaryBytes(line, lines)
else if(line.startsWith("<INLINEASM "))
parseInlineAssembly(line, lines, null)
parseInlineAssembly(line, lines)
else
throw IRParseException("invalid sub child node")
@ -406,9 +407,10 @@ class IRFileReader {
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 pos = parsePosition(match.groupValues[1])
val label = match.groupValues[1]
val pos = parsePosition(match.groupValues[2])
val bytes = mutableListOf<UByte>()
var line = lines.next()
while(line!="</BYTES>") {
@ -436,13 +438,17 @@ class IRFileReader {
}
}
private fun parseCodeChunk(firstline: String, lines: Iterator<String>, label: String?): IRCodeChunk? {
if(firstline!="<C>") {
private fun parseCodeChunk(firstline: String, lines: Iterator<String>): IRCodeChunk? {
if(!firstline.startsWith("<C")) {
if(firstline=="</SUB>")
return null
else
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)
while(true) {
val line = lines.next()

View File

@ -64,12 +64,14 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
is IRInlineAsmChunk -> writeInlineAsm(chunk)
is IRInlineBinaryChunk -> writeInlineBytes(chunk)
else -> {
if(chunk.label!=null)
out.write("<C LABEL=${chunk.label}>\n")
else
out.write("<C>\n")
if (chunk.instructions.isEmpty())
throw InternalCompilerException("empty code chunk in ${it.name} ${it.position}")
chunk.instructions.forEach { instr ->
numInstr++
out.write(instr.toString())
out.write("\n")
}
out.write("</C>\n")
}
@ -100,7 +102,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
}
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) ->
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("<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("\n</INLINEASM>\n")
}

View File

@ -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(