mirror of
https://github.com/irmen/prog8.git
synced 2026-04-20 11:17:01 +00:00
first vm peephole optimizer
This commit is contained in:
@@ -68,6 +68,7 @@ internal class AssemblyProgram(override val name: String,
|
||||
|
||||
fun addGlobalInits(chunk: VmCodeChunk) = globalInits.addAll(chunk.lines)
|
||||
fun addBlock(block: VmCodeChunk) = blocks.add(block)
|
||||
fun getBlocks(): List<VmCodeChunk> = blocks
|
||||
}
|
||||
|
||||
internal sealed class VmCodeLine
|
||||
|
||||
@@ -62,6 +62,11 @@ class CodeGen(internal val program: PtProgram,
|
||||
vmprog.addBlock(translate(block))
|
||||
}
|
||||
|
||||
if(options.optimize) {
|
||||
val optimizer = VmPeepholeOptimizer(vmprog, allocations)
|
||||
optimizer.optimize()
|
||||
}
|
||||
|
||||
println("Vm codegen: amount of vm registers=${vmRegisters.peekNext()}")
|
||||
|
||||
return vmprog
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package prog8.codegen.virtual
|
||||
|
||||
import prog8.vm.Instruction
|
||||
import prog8.vm.Opcode
|
||||
|
||||
internal class VmPeepholeOptimizer(private val vmprog: AssemblyProgram, private val allocations: VariableAllocator) {
|
||||
fun optimize() {
|
||||
vmprog.getBlocks().forEach { block ->
|
||||
do {
|
||||
val indexedInstructions = block.lines.withIndex()
|
||||
.filter { it.value is VmCodeInstruction }
|
||||
.map { IndexedValue(it.index, (it.value as VmCodeInstruction).ins)}
|
||||
val changed = optimizeRemoveNops(block, indexedInstructions)
|
||||
|| optimizeDoubleLoadsAndStores(block, indexedInstructions)
|
||||
// TODO other optimizations:
|
||||
// useless arithmethic (div/mul by 1, add/sub 0, ...)
|
||||
// useless logical (bitwise (x)or 0, bitwise and by ffff, shl followed by shr or vice versa (no carry)... )
|
||||
// jump/branch to label immediately below
|
||||
// branch instructions with reg1==reg2
|
||||
// conditional set instructions with reg1==reg2
|
||||
// push followed by pop to same target, or different target replace with load
|
||||
// double sec, clc
|
||||
// sec+clc or clc+sec
|
||||
// move complex optimizations such as unused registers, ...
|
||||
} while(changed)
|
||||
}
|
||||
}
|
||||
|
||||
private fun optimizeRemoveNops(block: VmCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if (ins.opcode == Opcode.NOP) {
|
||||
changed = true
|
||||
block.lines.removeAt(idx)
|
||||
}
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun optimizeDoubleLoadsAndStores(block: VmCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
|
||||
var changed = false
|
||||
indexedInstructions.forEach { (idx, ins) ->
|
||||
|
||||
// TODO: detect multiple loads to the same target, only keep first
|
||||
// TODO: detect multiple stores to the same target, only keep first
|
||||
// TODO: detect multiple ffrom/fto to the same target, only keep first
|
||||
// TODO: detect multiple sequential rnd with same reg1, only keep one
|
||||
// TODO: double same xors/nots/negs, remove the pair completely as they cancel out
|
||||
// TODO: multiple same ands, ors, only keep first
|
||||
}
|
||||
return changed
|
||||
}
|
||||
}
|
||||
|
||||
private interface ICodeChange { // TODO not used? remove?
|
||||
fun perform(block: VmCodeChunk)
|
||||
|
||||
class Remove(val idx: Int): ICodeChange {
|
||||
override fun perform(block: VmCodeChunk) {
|
||||
block.lines.removeAt(idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user