mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
ir/vm fix chunk linkage
This commit is contained in:
parent
cfa7258ff4
commit
b718b12083
@ -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")
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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?
|
||||
|
||||
...
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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()
|
||||
|
@ -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) {
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user