ir/vm fix chunk linkage

This commit is contained in:
Irmen de Jong 2022-10-25 23:51:22 +02:00
parent cfa7258ff4
commit b718b12083
9 changed files with 75 additions and 58 deletions

View File

@ -69,7 +69,7 @@ class IRCodeGen(
if(block.inlineAssembly.isNotEmpty()) {
val first = block.inlineAssembly.first()
if(first.label==null) {
val replacement = IRInlineAsmChunk(block.name, first.assembly, first.isIR, first.position)
val replacement = IRInlineAsmChunk(block.name, first.assembly, first.isIR, first.position, first.next)
block.inlineAssembly.removeAt(0)
block.inlineAssembly.add(0, replacement)
} else if(first.label != block.name) {
@ -87,8 +87,8 @@ class IRCodeGen(
replacement.instructions += first.instructions
replacement
}
is IRInlineAsmChunk -> IRInlineAsmChunk(sub.name, first.assembly, first.isIR, first.position)
is IRInlineBinaryChunk -> IRInlineBinaryChunk(sub.name, first.data, first.position)
is IRInlineAsmChunk -> IRInlineAsmChunk(sub.name, first.assembly, first.isIR, first.position, first.next)
is IRInlineBinaryChunk -> IRInlineBinaryChunk(sub.name, first.data, first.position, first.next)
else -> throw AssemblyError("invalid chunk")
}
sub.chunks.removeAt(0)
@ -278,12 +278,12 @@ class IRCodeGen(
listOf(chunk)
}
is PtConditionalBranch -> translate(node)
is PtInlineAssembly -> listOf(IRInlineAsmChunk(null, node.assembly, node.isIR, node.position))
is PtInlineAssembly -> listOf(IRInlineAsmChunk(null, node.assembly, node.isIR, node.position, null))
is PtIncludeBinary -> {
val data = node.file.readBytes()
.drop(node.offset?.toInt() ?: 0)
.take(node.length?.toInt() ?: Int.MAX_VALUE)
listOf(IRInlineBinaryChunk(null, data.map { it.toUByte() }, node.position))
listOf(IRInlineBinaryChunk(null, data.map { it.toUByte() }, node.position, null))
}
is PtAddressOf,
is PtContainmentCheck,
@ -348,15 +348,15 @@ class IRCodeGen(
val newChunks = chunks.drop(1).toMutableList()
val labeledFirstChunk = when(val first=chunks[0]) {
is IRCodeChunk -> {
val newChunk = IRCodeChunk(label, first.position, null)
val newChunk = IRCodeChunk(label, first.position, first.next)
newChunk.instructions += first.instructions
newChunk
}
is IRInlineAsmChunk -> {
IRInlineAsmChunk(label, first.assembly, first.isIR, first.position)
IRInlineAsmChunk(label, first.assembly, first.isIR, first.position, first.next)
}
is IRInlineBinaryChunk -> {
IRInlineBinaryChunk(label, first.data, first.position)
IRInlineBinaryChunk(label, first.data, first.position, first.next)
}
else -> {
throw AssemblyError("invalid chunk")
@ -1077,7 +1077,7 @@ class IRCodeGen(
)
}
is PtInlineAssembly -> {
irBlock += IRInlineAsmChunk(null, child.assembly, child.isIR, child.position)
irBlock += IRInlineAsmChunk(null, child.assembly, child.isIR, child.position, null)
}
else -> TODO("weird child node $child")
}

View File

@ -7,6 +7,7 @@ import prog8.intermediate.*
class TestIRPeepholeOpt: FunSpec({
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
require(chunks.first().label=="main.start")
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
chunks.forEach { sub += it }
@ -30,7 +31,7 @@ class TestIRPeepholeOpt: FunSpec({
}
fun makeIRProgram(instructions: List<IRInstruction>): IRProgram {
val chunk = IRCodeChunk(null, Position.DUMMY, null)
val chunk = IRCodeChunk("main.start", Position.DUMMY, null)
instructions.forEach { chunk += it }
return makeIRProgram(listOf(chunk))
}
@ -50,10 +51,10 @@ class TestIRPeepholeOpt: FunSpec({
}
test("remove jmp to label below") {
val c1 = IRCodeChunk(null, Position.DUMMY, null)
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label") // removed
val c1 = IRCodeChunk("main.start", Position.DUMMY, null)
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label") // removed, but chunk stays because of label
val c2 = IRCodeChunk("label", Position.DUMMY, null)
c2 += IRInstruction(Opcode.JUMP, labelSymbol = "label2") // removed
c2 += IRInstruction(Opcode.JUMP, labelSymbol = "label2") // removed, but chunk stays because of label
c2 += IRInstruction(Opcode.NOP) // removed
val c3 = IRCodeChunk("label2", Position.DUMMY, null)
c3 += IRInstruction(Opcode.JUMP, labelSymbol = "label3")
@ -65,10 +66,15 @@ class TestIRPeepholeOpt: FunSpec({
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
irProg.chunks().size shouldBe 3
irProg.chunks()[0].label shouldBe "label"
irProg.chunks()[1].label shouldBe "label2"
irProg.chunks()[2].label shouldBe "label3"
irProg.chunks().size shouldBe 4
irProg.chunks()[0].label shouldBe "main.start"
irProg.chunks()[1].label shouldBe "label"
irProg.chunks()[2].label shouldBe "label2"
irProg.chunks()[3].label shouldBe "label3"
irProg.chunks()[0].isEmpty() shouldBe true
irProg.chunks()[1].isEmpty() shouldBe true
irProg.chunks()[2].isEmpty() shouldBe false
irProg.chunks()[3].isEmpty() shouldBe true
val instr = irProg.chunks().flatMap { it.instructions }
instr.size shouldBe 2
instr[0].opcode shouldBe Opcode.JUMP

View File

@ -3,11 +3,8 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- ir: fix linkChunks() in IRProgram and addAssemblyToProgram() next pointer tracking
- vm: program is list of chunks, fix dispatcher
- ir: fix unit tests
- ir: fix removeWeirdBranches in IR optimizer
- ir: next in IRCodeChunk can also be a Asm Chunk which can have next as well
- update diagram in technical.rst?
...

View File

@ -1,16 +1,23 @@
; %import textio
%import textio
main {
sub start() {
; should get a return after the nop
%ir {{
nop
}}
; should flow into next statements
; ubyte aa = 42
; ubyte bb = 99
; aa += bb
; txt.print_ub(aa)
ubyte aa = 42
ubyte bb = 99
aa += bb
txt.print_ub(aa)
txt.spc()
aa += bb
txt.print_ub(aa)
txt.spc()
aa += bb
txt.print_ub(aa)
txt.nl()
}
}

View File

@ -333,7 +333,7 @@ class IRFileReader {
asmlines.add(line)
line = lines.next()
}
return IRInlineAsmChunk(label, asmlines.joinToString("\n"), isIr, pos)
return IRInlineAsmChunk(label, asmlines.joinToString("\n"), isIr, pos, null)
}
private fun parseAsmSubroutine(startline: String, lines: Iterator<String>): IRAsmSubroutine {
@ -421,7 +421,7 @@ class IRFileReader {
}
line = lines.next()
}
return IRInlineBinaryChunk(label, bytes, pos)
return IRInlineBinaryChunk(label, bytes, pos, null)
}
private fun parseParameters(lines: Iterator<String>): List<IRSubroutine.IRParam> {

View File

@ -111,8 +111,6 @@ class IRProgram(val name: String,
chunk.next = next
else
throw AssemblyError("code chunk flows into following non-code chunk")
} else {
TODO("???")
}
}
@ -131,12 +129,12 @@ class IRProgram(val name: String,
is IRInlineAsmChunk -> {
val next = nextChunk()
if(next!=null) {
// TODO if chunk doesn't end in a jump or return statement, flow continues into the next chunk
val lastInstr = chunk.instructions.lastOrNull()
if(lastInstr==null || lastInstr.opcode !in OpcodesThatJump)
chunk.next = next
}
}
is IRInlineBinaryChunk -> {
// TODO("link next of binary chunk")
}
is IRInlineBinaryChunk -> { }
else -> throw AssemblyError("invalid chunk")
}
}
@ -266,7 +264,7 @@ class IRAsmSubroutine(
fun usedRegisters() = registersUsed
}
sealed class IRCodeChunkBase(val label: String?, val position: Position) {
sealed class IRCodeChunkBase(val label: String?, val position: Position, var next: IRCodeChunkBase?) {
val instructions = mutableListOf<IRInstruction>()
abstract fun isEmpty(): Boolean
@ -274,9 +272,7 @@ sealed class IRCodeChunkBase(val label: String?, val position: Position) {
abstract fun usedRegisters(): RegistersUsed
}
class IRCodeChunk(label: String?,
position: Position,
var next: IRCodeChunk?): IRCodeChunkBase(label, position) { // TODO next can also be InlineAsmChunk!! which can also have a next again.
class IRCodeChunk(label: String?, position: Position, next: IRCodeChunkBase?): IRCodeChunkBase(label, position, next) {
override fun isEmpty() = instructions.isEmpty()
override fun isNotEmpty() = instructions.isNotEmpty()
@ -298,7 +294,11 @@ class IRCodeChunk(label: String?,
}
}
class IRInlineAsmChunk(label: String?, val assembly: String, val isIR: Boolean, position: Position): IRCodeChunkBase(label, position) {
class IRInlineAsmChunk(label: String?,
val assembly: String,
val isIR: Boolean,
position: Position,
next: IRCodeChunkBase?): IRCodeChunkBase(label, position, next) {
// note: no instructions, asm is in the property
override fun isEmpty() = assembly.isBlank()
override fun isNotEmpty() = assembly.isNotBlank()
@ -312,7 +312,10 @@ class IRInlineAsmChunk(label: String?, val assembly: String, val isIR: Boolean,
override fun usedRegisters() = registersUsed
}
class IRInlineBinaryChunk(label: String?, val data: Collection<UByte>, position: Position): IRCodeChunkBase(label, position) {
class IRInlineBinaryChunk(label: String?,
val data: Collection<UByte>,
position: Position,
next: IRCodeChunkBase?): IRCodeChunkBase(label, position, next) {
// note: no instructions, data is in the property
override fun isEmpty() = data.isEmpty()
override fun isNotEmpty() = data.isNotEmpty()

View File

@ -101,15 +101,7 @@ class VirtualMachine(irProgram: IRProgram) {
var left=count
while(left>0) {
if(pcIndex >= pcChunk.instructions.size) {
if(pcChunk.next!=null) {
// go to next chunk
pcChunk = pcChunk.next!!
pcIndex = 0
} else {
// end of program reached
exit()
break
}
stepNextChunk()
}
stepCount++
dispatch(pcChunk.instructions[pcIndex])
@ -117,14 +109,26 @@ class VirtualMachine(irProgram: IRProgram) {
}
}
private fun stepNextChunk() {
val nextChunk = pcChunk.next
when (nextChunk) {
is IRCodeChunk -> {
pcChunk = nextChunk
pcIndex = 0
}
null -> {
exit() // end of program reached
}
else -> {
throw IllegalArgumentException("VM cannot run code from non-code chunk $nextChunk")
}
}
}
private fun nextPc() {
pcIndex ++
if(pcIndex>=pcChunk.instructions.size) {
pcIndex = 0
if(pcChunk.next==null)
TODO("no next chunk in $pcChunk (remove this check)")
pcChunk = pcChunk.next!!
}
if(pcIndex>=pcChunk.instructions.size)
stepNextChunk()
}
private fun branchTo(i: IRInstruction) {

View File

@ -272,7 +272,7 @@ class VmProgramLoader {
symbolAddresses: MutableMap<String, Int>,
): Pair<IRCodeChunkBase, IRCodeChunk> {
if(asmChunk.isIR) {
val chunk = IRCodeChunk(asmChunk.label, asmChunk.position, null) // TODO keep track of the chunk's next pointer
val chunk = IRCodeChunk(asmChunk.label, asmChunk.position, asmChunk.next)
asmChunk.assembly.lineSequence().forEach {
val parsed = parseIRCodeLine(it.trim(), Pair(chunk, chunk.instructions.size), placeholders)
parsed.fold(

View File

@ -42,7 +42,7 @@ class TestVm: FunSpec( {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
val code = IRCodeChunk(null, Position.DUMMY, null)
val code = IRCodeChunk(startSub.name, Position.DUMMY, null)
code += IRInstruction(Opcode.NOP)
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=1, value=12345)
code += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1=1, value=1000)
@ -70,7 +70,7 @@ class TestVm: FunSpec( {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
val code = IRCodeChunk(null, Position.DUMMY, null)
val code = IRCodeChunk(startSub.name, Position.DUMMY, null)
code += IRInstruction(Opcode.BINARYDATA, binaryData = listOf(1u,2u,3u))
code += IRInstruction(Opcode.RETURN)
startSub += code