mirror of
https://github.com/irmen/prog8.git
synced 2024-11-29 17:50:35 +00:00
ir: fix handling of labeled chunks
This commit is contained in:
parent
6fc89607d3
commit
a9f9c40d8a
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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 -> {
|
||||||
out.write("<C>\n")
|
if(chunk.label!=null)
|
||||||
if (chunk.instructions.isEmpty())
|
out.write("<C LABEL=${chunk.label}>\n")
|
||||||
throw InternalCompilerException("empty code chunk in ${it.name} ${it.position}")
|
else
|
||||||
|
out.write("<C>\n")
|
||||||
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")
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
Loading…
Reference in New Issue
Block a user