diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 8ce9b74d7..417d8c5e4 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -63,9 +63,9 @@ class IRCodeGen( // for instance when a piece of inlined assembly references them. val replacements = mutableListOf>() irProg.blocks.asSequence().flatMap { it.subroutines }.flatMap { it.chunks }.forEach { chunk -> - chunk.lines.withIndex().forEach { - (lineIndex, line) -> if(line is IRInstruction) { - val symbolExpr = line.labelSymbol + chunk.instructions.withIndex().forEach { + (idx, instr) -> if(instr is IRInstruction) { + val symbolExpr = instr.labelSymbol if(symbolExpr!=null) { val symbol: String val index: UInt @@ -79,7 +79,7 @@ class IRCodeGen( } val target = symbolTable.flat[symbol.split('.')] if (target is StMemVar) { - replacements.add(Triple(chunk, lineIndex, target.address+index)) + replacements.add(Triple(chunk, idx, target.address+index)) } } } @@ -87,8 +87,8 @@ class IRCodeGen( } replacements.forEach { - val old = it.first.lines[it.second] as IRInstruction - it.first.lines[it.second] = IRInstruction( + val old = it.first.instructions[it.second] as IRInstruction + it.first.instructions[it.second] = IRInstruction( old.opcode, old.type, old.reg1, diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt index 41da392ef..c8ddfc688 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt @@ -10,7 +10,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) { // we don't optimize Inline Asm chunks here. if(chunk is IRCodeChunk) { do { - val indexedInstructions = chunk.lines.withIndex() + val indexedInstructions = chunk.instructions.withIndex() .filter { it.value is IRInstruction } .map { IndexedValue(it.index, it.value as IRInstruction) } val changed = removeNops(chunk, indexedInstructions) @@ -51,7 +51,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) { chunks += sub.chunks[0] for(ix in 1 until sub.chunks.size) { if(mayJoin(chunks.last(), sub.chunks[ix])) - chunks.last().lines += sub.chunks[ix].lines + chunks.last().instructions += sub.chunks[ix].instructions else chunks += sub.chunks[ix] } @@ -64,15 +64,15 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) { var changed = false indexedInstructions.reversed().forEach { (idx, ins) -> if(ins.opcode== Opcode.PUSH) { - if(idx < chunk.lines.size-1) { - val insAfter = chunk.lines[idx+1] as? IRInstruction + if(idx < chunk.instructions.size-1) { + val insAfter = chunk.instructions[idx+1] as? IRInstruction if(insAfter!=null && insAfter.opcode == Opcode.POP) { if(ins.reg1==insAfter.reg1) { - chunk.lines.removeAt(idx) - chunk.lines.removeAt(idx) + chunk.instructions.removeAt(idx) + chunk.instructions.removeAt(idx) } else { - chunk.lines[idx] = IRInstruction(Opcode.LOADR, ins.type, reg1=insAfter.reg1, reg2=ins.reg1) - chunk.lines.removeAt(idx+1) + chunk.instructions[idx] = IRInstruction(Opcode.LOADR, ins.type, reg1=insAfter.reg1, reg2=ins.reg1) + chunk.instructions.removeAt(idx+1) } changed = true } @@ -88,18 +88,18 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) { var changed = false indexedInstructions.reversed().forEach { (idx, ins) -> if(ins.opcode== Opcode.SEC || ins.opcode== Opcode.CLC) { - if(idx < chunk.lines.size-1) { - val insAfter = chunk.lines[idx+1] as? IRInstruction + if(idx < chunk.instructions.size-1) { + val insAfter = chunk.instructions[idx+1] as? IRInstruction if(insAfter?.opcode == ins.opcode) { - chunk.lines.removeAt(idx) + chunk.instructions.removeAt(idx) changed = true } else if(ins.opcode== Opcode.SEC && insAfter?.opcode== Opcode.CLC) { - chunk.lines.removeAt(idx) + chunk.instructions.removeAt(idx) changed = true } else if(ins.opcode== Opcode.CLC && insAfter?.opcode== Opcode.SEC) { - chunk.lines.removeAt(idx) + chunk.instructions.removeAt(idx) changed = true } } @@ -114,19 +114,19 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) { val labelSymbol = ins.labelSymbol if(ins.opcode== Opcode.JUMP && labelSymbol!=null) { // remove jump/branch to label immediately below - if(idx < chunk.lines.size-1) { - val label = chunk.lines[idx+1] as? IRCodeLabel + if(idx < chunk.instructions.size-1) { + val label = chunk.instructions[idx+1] as? IRCodeLabel if(label?.name == labelSymbol) { - chunk.lines.removeAt(idx) + chunk.instructions.removeAt(idx) changed = true } } } // remove useless RETURN if(ins.opcode == Opcode.RETURN && idx>0) { - val previous = chunk.lines[idx-1] as? IRInstruction + val previous = chunk.instructions[idx-1] as? IRInstruction if(previous?.opcode in setOf(Opcode.JUMP, Opcode.JUMPA, Opcode.RETURN)) { - chunk.lines.removeAt(idx) + chunk.instructions.removeAt(idx) changed = true } } @@ -141,47 +141,47 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) { when (ins.opcode) { Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> { if (ins.value == 1) { - chunk.lines.removeAt(idx) + chunk.instructions.removeAt(idx) changed = true } } Opcode.ADD, Opcode.SUB -> { if (ins.value == 1) { - chunk.lines[idx] = IRInstruction( + chunk.instructions[idx] = IRInstruction( if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC, ins.type, ins.reg1 ) changed = true } else if (ins.value == 0) { - chunk.lines.removeAt(idx) + chunk.instructions.removeAt(idx) changed = true } } Opcode.AND -> { if (ins.value == 0) { - chunk.lines[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0) + chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0) changed = true } else if (ins.value == 255 && ins.type == IRDataType.BYTE) { - chunk.lines.removeAt(idx) + chunk.instructions.removeAt(idx) changed = true } else if (ins.value == 65535 && ins.type == IRDataType.WORD) { - chunk.lines.removeAt(idx) + chunk.instructions.removeAt(idx) changed = true } } Opcode.OR -> { if (ins.value == 0) { - chunk.lines.removeAt(idx) + chunk.instructions.removeAt(idx) changed = true } else if ((ins.value == 255 && ins.type == IRDataType.BYTE) || (ins.value == 65535 && ins.type == IRDataType.WORD)) { - chunk.lines[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value) + chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value) changed = true } } Opcode.XOR -> { if (ins.value == 0) { - chunk.lines.removeAt(idx) + chunk.instructions.removeAt(idx) changed = true } } @@ -196,7 +196,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) { indexedInstructions.reversed().forEach { (idx, ins) -> if (ins.opcode == Opcode.NOP) { changed = true - chunk.lines.removeAt(idx) + chunk.instructions.removeAt(idx) } } return changed diff --git a/codeGenIntermediate/test/TestIRPeepholeOpt.kt b/codeGenIntermediate/test/TestIRPeepholeOpt.kt index ca50f1561..23c8a9dc1 100644 --- a/codeGenIntermediate/test/TestIRPeepholeOpt.kt +++ b/codeGenIntermediate/test/TestIRPeepholeOpt.kt @@ -6,12 +6,12 @@ import prog8.codegen.intermediate.IRPeepholeOptimizer import prog8.intermediate.* class TestIRPeepholeOpt: FunSpec({ - fun makeIRProgram(lines: List): IRProgram { + fun makeIRProgram(instructions: List): IRProgram { val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY) val chunk = IRCodeChunk(Position.DUMMY) - for(line in lines) - chunk += line + for(instr in instructions) + chunk += instr sub += chunk block += sub val target = VMTarget() @@ -30,7 +30,7 @@ class TestIRPeepholeOpt: FunSpec({ return prog } - fun IRProgram.lines(): List = this.blocks.flatMap { it.subroutines }.flatMap { it.chunks }.flatMap { it.lines } + fun IRProgram.instructions(): List = this.blocks.flatMap { it.subroutines }.flatMap { it.chunks }.flatMap { it.instructions } test("remove nops") { val irProg = makeIRProgram(listOf( @@ -38,10 +38,10 @@ class TestIRPeepholeOpt: FunSpec({ IRInstruction(Opcode.NOP), IRInstruction(Opcode.NOP) )) - irProg.lines().size shouldBe 3 + irProg.instructions().size shouldBe 3 val opt = IRPeepholeOptimizer(irProg) opt.optimize() - irProg.lines().size shouldBe 1 + irProg.instructions().size shouldBe 1 } test("remove jmp to label below") { @@ -55,10 +55,10 @@ class TestIRPeepholeOpt: FunSpec({ IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=1), IRCodeLabel("label3") )) - irProg.lines().size shouldBe 8 + irProg.instructions().size shouldBe 8 val opt = IRPeepholeOptimizer(irProg) opt.optimize() - val lines = irProg.lines() + val lines = irProg.instructions() lines.size shouldBe 5 (lines[0] as IRCodeLabel).name shouldBe "label" (lines[1] as IRCodeLabel).name shouldBe "label2" @@ -76,10 +76,10 @@ class TestIRPeepholeOpt: FunSpec({ IRInstruction(Opcode.CLC), IRInstruction(Opcode.CLC) )) - irProg.lines().size shouldBe 6 + irProg.instructions().size shouldBe 6 val opt = IRPeepholeOptimizer(irProg) opt.optimize() - val lines = irProg.lines() + val lines = irProg.instructions() lines.size shouldBe 1 (lines[0] as IRInstruction).opcode shouldBe Opcode.CLC } @@ -91,10 +91,10 @@ class TestIRPeepholeOpt: FunSpec({ IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=99), IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=222) )) - irProg.lines().size shouldBe 4 + irProg.instructions().size shouldBe 4 val opt = IRPeepholeOptimizer(irProg) opt.optimize() - val lines = irProg.lines() + val lines = irProg.instructions() lines.size shouldBe 1 (lines[0] as IRInstruction).opcode shouldBe Opcode.LOADR (lines[0] as IRInstruction).reg1 shouldBe 222 @@ -114,10 +114,10 @@ class TestIRPeepholeOpt: FunSpec({ IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 0), IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 0) )) - irProg.lines().size shouldBe 10 + irProg.instructions().size shouldBe 10 val opt = IRPeepholeOptimizer(irProg) opt.optimize() - val lines = irProg.lines() + val lines = irProg.instructions() lines.size shouldBe 4 } @@ -126,10 +126,10 @@ class TestIRPeepholeOpt: FunSpec({ IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 1), IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 1) )) - irProg.lines().size shouldBe 2 + irProg.instructions().size shouldBe 2 val opt = IRPeepholeOptimizer(irProg) opt.optimize() - val lines = irProg.lines() + val lines = irProg.instructions() lines.size shouldBe 2 (lines[0] as IRInstruction).opcode shouldBe Opcode.INC (lines[1] as IRInstruction).opcode shouldBe Opcode.DEC @@ -146,10 +146,10 @@ class TestIRPeepholeOpt: FunSpec({ IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 1), IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 1) )) - irProg.lines().size shouldBe 8 + irProg.instructions().size shouldBe 8 val opt = IRPeepholeOptimizer(irProg) opt.optimize() - val lines = irProg.lines() + val lines = irProg.instructions() lines.size shouldBe 4 } @@ -160,10 +160,10 @@ class TestIRPeepholeOpt: FunSpec({ IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 255), IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, value = 65535) )) - irProg.lines().size shouldBe 4 + irProg.instructions().size shouldBe 4 val opt = IRPeepholeOptimizer(irProg) opt.optimize() - val lines = irProg.lines() + val lines = irProg.instructions() lines.size shouldBe 4 (lines[0] as IRInstruction).opcode shouldBe Opcode.LOAD (lines[1] as IRInstruction).opcode shouldBe Opcode.LOAD diff --git a/docs/source/todo.rst b/docs/source/todo.rst index e3dd1f001..3e20573fb 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,6 +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 joinChunks() in the IR optimizer ? + ... @@ -19,8 +22,6 @@ Compiler: - create BSS section in output program and put StStaticVariables in there with bss=true. Don't forget to add init code to zero out everything that was put in bss. If array in bss->only zero ONCE! So requires self-modifying code - ir: Jumps go to a code block rather than a specific address(label) -> also helps future dead code elimination? -- ir: the above means that every label introduces a new code block. This eliminates the use of actual labels altogether during execution/translation. -- ir: joinChunks() in the IR optimizer should be changed accordingly - ir: add more optimizations in IRPeepholeOptimizer - ir: how to remove all unused subroutines? (the 6502 assembly codegen relies on 64tass solve this for us) - see if we can let for loops skip the loop if end writeInlineBytes(chunk) else -> { out.write("\n") - if (chunk.lines.isEmpty()) + if (chunk.instructions.isEmpty()) throw InternalCompilerException("empty code chunk in ${it.name} ${it.position}") - chunk.lines.forEach { line -> - numLines++ - out.writeLine(line) + chunk.instructions.forEach { instr -> + numInstr++ + out.writeLine(instr) } out.write("\n") } diff --git a/intermediate/src/prog8/intermediate/IRProgram.kt b/intermediate/src/prog8/intermediate/IRProgram.kt index 69ad39495..2194ce533 100644 --- a/intermediate/src/prog8/intermediate/IRProgram.kt +++ b/intermediate/src/prog8/intermediate/IRProgram.kt @@ -55,7 +55,7 @@ class IRProgram(val name: String, val globalInits = mutableListOf() val blocks = mutableListOf() - fun addGlobalInits(chunk: IRCodeChunk) = globalInits.addAll(chunk.lines) + fun addGlobalInits(chunk: IRCodeChunk) = globalInits.addAll(chunk.instructions) fun addBlock(block: IRBlock) { require(blocks.all { it.name != block.name}) { "duplicate block ${block.name} ${block.position}" } blocks.add(block) @@ -68,11 +68,11 @@ class IRProgram(val name: String, fun validate() { blocks.forEach { it.inlineAssembly.forEach { chunk -> - require(chunk.lines.isEmpty()) + require(chunk.instructions.isEmpty()) } it.subroutines.forEach { sub -> sub.chunks.forEach { chunk -> - if (chunk is IRInlineAsmChunk) { require(chunk.lines.isEmpty()) } + if (chunk is IRInlineAsmChunk) { require(chunk.instructions.isEmpty()) } } } } @@ -176,7 +176,7 @@ sealed class IRCodeLine class IRCodeLabel(val name: String): IRCodeLine() abstract class IRCodeChunkBase(val position: Position) { - val lines = mutableListOf() + val instructions = mutableListOf() abstract fun isEmpty(): Boolean abstract fun isNotEmpty(): Boolean @@ -185,14 +185,14 @@ abstract class IRCodeChunkBase(val position: Position) { class IRCodeChunk(position: Position): IRCodeChunkBase(position) { - override fun isEmpty() = lines.isEmpty() - override fun isNotEmpty() = lines.isNotEmpty() + override fun isEmpty() = instructions.isEmpty() + override fun isNotEmpty() = instructions.isNotEmpty() override fun usedRegisters(): RegistersUsed { val inputRegs = mutableMapOf().withDefault { 0 } val inputFpRegs = mutableMapOf().withDefault { 0 } val outputRegs = mutableMapOf().withDefault { 0 } val outputFpRegs = mutableMapOf().withDefault { 0 } - lines.forEach { + instructions.forEach { if(it is IRInstruction) it.addUsedRegistersCounts(inputRegs, outputRegs, inputFpRegs, outputFpRegs) } @@ -200,11 +200,11 @@ class IRCodeChunk(position: Position): IRCodeChunkBase(position) { } operator fun plusAssign(line: IRCodeLine) { - lines.add(line) + instructions.add(line) } operator fun plusAssign(chunk: IRCodeChunkBase) { - lines.addAll(chunk.lines) + instructions.addAll(chunk.instructions) } } diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index d8eaff316..6771b4afc 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -39,7 +39,7 @@ class VmProgramLoader { when (chunk) { is IRInlineAsmChunk -> addAssemblyToProgram(chunk, program, symbolAddresses) is IRInlineBinaryChunk -> program += IRInstruction(Opcode.BINARYDATA, binaryData = chunk.data) - else -> addToProgram(chunk.lines, program, symbolAddresses) + else -> addToProgram(chunk.instructions, program, symbolAddresses) } } } @@ -187,11 +187,11 @@ class VmProgramLoader { } private fun addToProgram( - lines: Iterable, + instructions: Iterable, program: MutableList, symbolAddresses: MutableMap ) { - lines.map { + instructions.map { when(it) { is IRInstruction -> { it.labelSymbol?.let { symbol -> placeholders[program.size]=symbol } diff --git a/virtualmachine/test/TestVm.kt b/virtualmachine/test/TestVm.kt index bbd5413d3..fdbb69ff2 100644 --- a/virtualmachine/test/TestVm.kt +++ b/virtualmachine/test/TestVm.kt @@ -61,8 +61,8 @@ class TestVm: FunSpec( { vm.memory.getUW(1000) shouldBe 12345u vm.callStack.shouldBeEmpty() vm.valueStack.shouldBeEmpty() - vm.pc shouldBe code.lines.size-1 - vm.stepCount shouldBe code.lines.size + vm.pc shouldBe code.instructions.size-1 + vm.stepCount shouldBe code.instructions.size } test("vm asmsub not supported") {