mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 19:31:36 +00:00
IR: wrong attempt at optimizing register usage by reusing registers inside different code chunks
This commit is contained in:
parent
91e1643627
commit
300e2fe9f8
@ -58,6 +58,10 @@ class IRCodeGen(
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
optimizer.optimize(options.optimize, errors)
|
||||
irProg.validate()
|
||||
|
||||
val regOptimizer = IRRegisterOptimizer(irProg)
|
||||
regOptimizer.optimize()
|
||||
|
||||
return irProg
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,9 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
joinChunks(sub)
|
||||
removeEmptyChunks(sub)
|
||||
joinChunks(sub)
|
||||
|
||||
// TODO also do register optimization step here?
|
||||
|
||||
sub.chunks.withIndex().forEach { (index, chunk1) ->
|
||||
// we don't optimize Inline Asm chunks here.
|
||||
val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null
|
||||
|
@ -0,0 +1,103 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.intermediate.IRProgram
|
||||
|
||||
|
||||
class IRRegisterOptimizer(private val irProg: IRProgram) {
|
||||
fun optimize() {
|
||||
// reuseRegisters()
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: this register re-use renumbering isn't going to work like this,
|
||||
because subroutines will be clobbering the registers that the subroutine
|
||||
which is calling them might be using...
|
||||
|
||||
|
||||
private fun reuseRegisters() {
|
||||
|
||||
fun addToUsage(usage: MutableMap<Pair<Int, IRDataType>, MutableSet<IRCodeChunkBase>>,
|
||||
regnum: Int,
|
||||
dt: IRDataType,
|
||||
chunk: IRCodeChunkBase) {
|
||||
val key = regnum to dt
|
||||
val chunks = usage[key] ?: mutableSetOf()
|
||||
chunks.add(chunk)
|
||||
usage[key] = chunks
|
||||
}
|
||||
|
||||
val usage: MutableMap<Pair<Int, IRDataType>, MutableSet<IRCodeChunkBase>> = mutableMapOf()
|
||||
|
||||
irProg.foreachCodeChunk { chunk ->
|
||||
chunk.usedRegisters().regsTypes.forEach { (regNum, types) ->
|
||||
types.forEach { dt ->
|
||||
addToUsage(usage, regNum, dt, chunk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val registerReplacements = usage.asSequence()
|
||||
.filter { it.value.size==1 }
|
||||
.map { it.key to it.value.iterator().next() }
|
||||
.groupBy({ it.second }, {it.first})
|
||||
.asSequence()
|
||||
.associate { (chunk, registers) ->
|
||||
chunk to registers.withIndex().associate { (index, reg) -> reg to 50000+index }
|
||||
}
|
||||
|
||||
registerReplacements.forEach { replaceRegisters(it.key, it.value) }
|
||||
}
|
||||
|
||||
private fun replaceRegisters(chunk: IRCodeChunkBase, replacements: Map<Pair<Int, IRDataType>, Int>) {
|
||||
val (rF, rI) = replacements.asSequence().partition { it.key.second==IRDataType.FLOAT }
|
||||
val replacementsInt = rI.associate { it.key.first to it.value }
|
||||
val replacementsFloat = rF.associate { it.key.first to it.value }
|
||||
|
||||
fun replaceRegs(fcallArgs: FunctionCallArgs?): FunctionCallArgs? {
|
||||
if(fcallArgs==null)
|
||||
return null
|
||||
val args = if(fcallArgs.arguments.isEmpty()) fcallArgs.arguments else {
|
||||
fcallArgs.arguments.map {
|
||||
FunctionCallArgs.ArgumentSpec(
|
||||
it.name,
|
||||
it.address,
|
||||
FunctionCallArgs.RegSpec(
|
||||
it.reg.dt,
|
||||
if(it.reg.dt==IRDataType.FLOAT)
|
||||
replacementsFloat.getOrDefault(it.reg.registerNum, it.reg.registerNum)
|
||||
else
|
||||
replacementsInt.getOrDefault(it.reg.registerNum, it.reg.registerNum),
|
||||
it.reg.cpuRegister
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
val rt = fcallArgs.returns
|
||||
val returns = if(rt==null) null else {
|
||||
FunctionCallArgs.RegSpec(
|
||||
rt.dt,
|
||||
if(rt.dt==IRDataType.FLOAT)
|
||||
replacementsFloat.getOrDefault(rt.registerNum, rt.registerNum)
|
||||
else
|
||||
replacementsInt.getOrDefault(rt.registerNum, rt.registerNum),
|
||||
rt.cpuRegister
|
||||
)
|
||||
}
|
||||
return FunctionCallArgs(args, returns)
|
||||
}
|
||||
|
||||
fun replaceRegs(instruction: IRInstruction): IRInstruction {
|
||||
val reg1 = replacementsInt.getOrDefault(instruction.reg1, instruction.reg1)
|
||||
val reg2 = replacementsInt.getOrDefault(instruction.reg2, instruction.reg2)
|
||||
val fpReg1 = replacementsFloat.getOrDefault(instruction.fpReg1, instruction.fpReg1)
|
||||
val fpReg2 = replacementsFloat.getOrDefault(instruction.fpReg2, instruction.fpReg2)
|
||||
return instruction.copy(reg1 = reg1, reg2 = reg2, fpReg1 = fpReg1, fpReg2 = fpReg2, fcallArgs = replaceRegs(instruction.fcallArgs))
|
||||
}
|
||||
val newInstructions = chunk.instructions.map {
|
||||
replaceRegs(it)
|
||||
}
|
||||
chunk.instructions.clear()
|
||||
chunk.instructions.addAll(newInstructions)
|
||||
}
|
||||
*/
|
||||
}
|
@ -253,7 +253,7 @@ main {
|
||||
main {
|
||||
sub start() {
|
||||
%ir {{
|
||||
loadr.b r1,r2
|
||||
incm.b $2000
|
||||
return
|
||||
}}
|
||||
}
|
||||
@ -262,7 +262,7 @@ main {
|
||||
val result = compileText(target, false, src, writeAssembly = true)!!
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||
val irSrc = virtfile.readText()
|
||||
irSrc.shouldContain("loadr.b r1,r2")
|
||||
irSrc.shouldContain("incm.b $2000")
|
||||
irSrc.shouldNotContain("INLINEASM")
|
||||
VmRunner().runProgram(irSrc)
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
Fix register renumber issue in vm/sincos, vm/bouncegfx examples
|
||||
IS THE RENUMBERING EVEN GOING TO WORK AT ALL?
|
||||
Subroutines calling each other will start to overwrite the same registers that they now share?
|
||||
|
||||
|
||||
|
||||
For 9.0 major changes
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
- DONE: added 'cbm' block in the syslib module that now contains all CBM compatible kernal routines and variables
|
||||
@ -41,10 +47,6 @@ Compiler:
|
||||
- ir: idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
|
||||
global initialization values are simply a list of LOAD instructions.
|
||||
Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings.
|
||||
- ir: mechanism to determine for chunks which registers are getting input values from "outside"
|
||||
- ir: mechanism to determine for chunks which registers are passing values out? (i.e. are used again in another chunk)
|
||||
- ir: peephole opt: (maybe just integrate this in the variable/register allocator though?) renumber registers in chunks to start with 1 again every time (but keep entry values in mind!)
|
||||
- ir: peephole opt: (maybe just integrate this in the variable/register allocator though?) reuse registers in chunks (but keep result registers in mind that pass values out! and don't renumber registers above SyscallRegisterBase!)
|
||||
- ir: add more optimizations in IRPeepholeOptimizer
|
||||
- ir: for expressions with array indexes that occur multiple times, can we avoid loading them into new virtualregs everytime and just reuse a single virtualreg as indexer? (simple form of common subexpression elimination)
|
||||
- PtAst/IR: more complex common subexpression eliminations
|
||||
|
@ -806,6 +806,37 @@ data class IRInstruction(
|
||||
OperandDirection.READ -> readFpRegsCounts[this.fpReg2!!] = readFpRegsCounts.getValue(this.fpReg2)+1
|
||||
else -> throw IllegalArgumentException("fpReg2 can only be read")
|
||||
}
|
||||
|
||||
if(fcallArgs!=null) {
|
||||
fcallArgs.returns?.let {
|
||||
if(it.dt==IRDataType.FLOAT)
|
||||
writeFpRegsCounts[it.registerNum] = writeFpRegsCounts.getValue(it.registerNum)+1
|
||||
else {
|
||||
writeRegsCounts[it.registerNum] = writeRegsCounts.getValue(it.registerNum) + 1
|
||||
val types = regsTypes[it.registerNum]
|
||||
if(types==null) {
|
||||
regsTypes[it.registerNum] = mutableSetOf(it.dt)
|
||||
} else {
|
||||
types += it.dt
|
||||
regsTypes[it.registerNum] = types
|
||||
}
|
||||
}
|
||||
}
|
||||
fcallArgs.arguments.forEach {
|
||||
if(it.reg.dt==IRDataType.FLOAT)
|
||||
readFpRegsCounts[it.reg.registerNum] = readFpRegsCounts.getValue(it.reg.registerNum)+1
|
||||
else {
|
||||
readRegsCounts[it.reg.registerNum] = readRegsCounts.getValue(it.reg.registerNum) + 1
|
||||
val types = regsTypes[it.reg.registerNum]
|
||||
if(types==null) {
|
||||
regsTypes[it.reg.registerNum] = mutableSetOf(it.reg.dt)
|
||||
} else {
|
||||
types += it.reg.dt
|
||||
regsTypes[it.reg.registerNum] = types
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
|
Loading…
Reference in New Issue
Block a user