mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
ir: fix unused code remover
This commit is contained in:
parent
e7408224ac
commit
e094785cbd
@ -597,7 +597,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||
result += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||
val factor = constFactorRight.number.toFloat()
|
||||
result += codeGen.divideByConstFloat(resultFpRegister, factor, binExpr.position)
|
||||
result += codeGen.divideByConstFloat(resultFpRegister, factor)
|
||||
} else {
|
||||
val rightResultFpReg = codeGen.registers.nextFreeFloat()
|
||||
result += translateExpression(binExpr.left, -1, resultFpRegister)
|
||||
@ -612,7 +612,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||
result += translateExpression(binExpr.left, resultRegister, -1)
|
||||
val factor = constFactorRight.number.toInt()
|
||||
result += codeGen.divideByConst(vmDt, resultRegister, factor, signed, binExpr.position)
|
||||
result += codeGen.divideByConst(vmDt, resultRegister, factor, signed)
|
||||
} else {
|
||||
val rightResultReg = codeGen.registers.nextFree()
|
||||
if(binExpr.right is PtNumber) {
|
||||
@ -642,7 +642,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||
val factor = constFactorRight.number.toFloat()
|
||||
result += codeGen.divideByConstFloatInplace(knownAddress, symbol, factor, operand.position)
|
||||
result += codeGen.divideByConstFloatInplace(knownAddress, symbol, factor)
|
||||
} else {
|
||||
val operandFpReg = codeGen.registers.nextFreeFloat()
|
||||
result += translateExpression(operand, -1, operandFpReg)
|
||||
@ -663,7 +663,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
} else {
|
||||
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||
val factor = constFactorRight.number.toInt()
|
||||
result += codeGen.divideByConstInplace(vmDt, knownAddress, symbol, factor, signed, operand.position)
|
||||
result += codeGen.divideByConstInplace(vmDt, knownAddress, symbol, factor, signed)
|
||||
} else {
|
||||
val operandReg = codeGen.registers.nextFree()
|
||||
result += translateExpression(operand, operandReg, -1)
|
||||
@ -742,7 +742,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
} else {
|
||||
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
|
||||
val factor = constFactorRight.number.toInt()
|
||||
result += codeGen.multiplyByConstInplace(vmDt, knownAddress, symbol, factor, constFactorRight.position)
|
||||
result += codeGen.multiplyByConstInplace(vmDt, knownAddress, symbol, factor)
|
||||
} else {
|
||||
val operandReg = codeGen.registers.nextFree()
|
||||
result += translateExpression(operand, operandReg, -1)
|
||||
|
@ -57,13 +57,12 @@ class IRCodeGen(
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
optimizer.optimize()
|
||||
|
||||
// TODO FIX & REENABLE:
|
||||
// val remover = IRUnusedCodeRemover(irProg, errors)
|
||||
// do {
|
||||
// val numRemoved = remover.optimize()
|
||||
// } while(numRemoved>0 && errors.noErrors())
|
||||
//
|
||||
// errors.report()
|
||||
val remover = IRUnusedCodeRemover(irProg, errors)
|
||||
do {
|
||||
val numRemoved = remover.optimize()
|
||||
} while(numRemoved>0 && errors.noErrors())
|
||||
|
||||
errors.report()
|
||||
|
||||
irProg.linkChunks() // re-link
|
||||
}
|
||||
@ -710,7 +709,7 @@ class IRCodeGen(
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun multiplyByConstInplace(dt: IRDataType, knownAddress: Int?, symbol: String?, factor: Int, position: Position): IRCodeChunk {
|
||||
internal fun multiplyByConstInplace(dt: IRDataType, knownAddress: Int?, symbol: String?, factor: Int): IRCodeChunk {
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(factor==1)
|
||||
return code
|
||||
@ -749,7 +748,7 @@ class IRCodeGen(
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun divideByConstFloat(fpReg: Int, factor: Float, position: Position): IRCodeChunk {
|
||||
internal fun divideByConstFloat(fpReg: Int, factor: Float): IRCodeChunk {
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(factor==1f)
|
||||
return code
|
||||
@ -761,7 +760,7 @@ class IRCodeGen(
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun divideByConstFloatInplace(knownAddress: Int?, symbol: String?, factor: Float, position: Position): IRCodeChunk {
|
||||
internal fun divideByConstFloatInplace(knownAddress: Int?, symbol: String?, factor: Float): IRCodeChunk {
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(factor==1f)
|
||||
return code
|
||||
@ -783,7 +782,7 @@ class IRCodeGen(
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun divideByConst(dt: IRDataType, reg: Int, factor: Int, signed: Boolean, position: Position): IRCodeChunk {
|
||||
internal fun divideByConst(dt: IRDataType, reg: Int, factor: Int, signed: Boolean): IRCodeChunk {
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(factor==1)
|
||||
return code
|
||||
@ -812,7 +811,7 @@ class IRCodeGen(
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun divideByConstInplace(dt: IRDataType, knownAddress: Int?, symbol: String?, factor: Int, signed: Boolean, position: Position): IRCodeChunk {
|
||||
internal fun divideByConstInplace(dt: IRDataType, knownAddress: Int?, symbol: String?, factor: Int, signed: Boolean): IRCodeChunk {
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(factor==1)
|
||||
return code
|
||||
|
@ -1,6 +1,5 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.core.Position
|
||||
import prog8.intermediate.*
|
||||
|
||||
internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
|
@ -1,36 +1,96 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.SourceCode.Companion.libraryFilePrefix
|
||||
import prog8.intermediate.IRCodeChunkBase
|
||||
import prog8.intermediate.IRProgram
|
||||
|
||||
|
||||
internal class IRUnusedCodeRemover(private val irprog: IRProgram, private val errors: IErrorReporter) {
|
||||
fun optimize(): Int {
|
||||
val linkedChunks = mutableSetOf<IRCodeChunkBase>()
|
||||
var numRemoved = 0
|
||||
var numRemoved = removeSimpleUnlinked() + removeUnreachable()
|
||||
|
||||
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
|
||||
sub.chunks.forEach { chunk ->
|
||||
if(chunk.next!=null)
|
||||
linkedChunks += chunk.next!!
|
||||
chunk.instructions.forEach {
|
||||
if(it.branchTarget!=null)
|
||||
linkedChunks += it.branchTarget!!
|
||||
}
|
||||
if(chunk.label=="main.start")
|
||||
linkedChunks += chunk
|
||||
}
|
||||
}
|
||||
|
||||
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
|
||||
sub.chunks.reversed().forEach { chunk ->
|
||||
if(chunk !in linkedChunks) {
|
||||
sub.chunks.remove(chunk)
|
||||
// remove empty subs
|
||||
irprog.blocks.forEach { block ->
|
||||
block.subroutines.reversed().forEach { sub ->
|
||||
if(sub.isEmpty()) {
|
||||
if(!sub.position.file.startsWith(libraryFilePrefix))
|
||||
errors.warn("unused subroutine ${sub.name}", sub.position)
|
||||
block.subroutines.remove(sub)
|
||||
numRemoved++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove empty blocks
|
||||
irprog.blocks.reversed().forEach { block ->
|
||||
if(block.isEmpty()) {
|
||||
irprog.blocks.remove(block)
|
||||
numRemoved++
|
||||
}
|
||||
}
|
||||
|
||||
return numRemoved
|
||||
}
|
||||
|
||||
private fun removeUnreachable(): Int {
|
||||
val reachable = mutableSetOf(irprog.blocks.single { it.name=="main" }.subroutines.single { it.name=="main.start" }.chunks.first())
|
||||
|
||||
fun grow() {
|
||||
val new = mutableSetOf<IRCodeChunkBase>()
|
||||
reachable.forEach {
|
||||
it.next?.let { next -> new += next }
|
||||
it.instructions.forEach { it.branchTarget?.let { target -> new += target} }
|
||||
}
|
||||
reachable += new
|
||||
}
|
||||
|
||||
var previousCount = reachable.size
|
||||
while(true) {
|
||||
grow()
|
||||
if(reachable.size<=previousCount)
|
||||
break
|
||||
previousCount = reachable.size
|
||||
}
|
||||
|
||||
return removeUnlinkedChunks(reachable)
|
||||
}
|
||||
|
||||
private fun removeSimpleUnlinked(): Int {
|
||||
val linkedChunks = mutableSetOf<IRCodeChunkBase>()
|
||||
|
||||
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
|
||||
sub.chunks.forEach { chunk ->
|
||||
chunk.next?.let { next -> linkedChunks += next }
|
||||
chunk.instructions.forEach { it.branchTarget?.let { target -> linkedChunks += target } }
|
||||
if (chunk.label == "main.start")
|
||||
linkedChunks += chunk
|
||||
}
|
||||
}
|
||||
|
||||
return removeUnlinkedChunks(linkedChunks)
|
||||
}
|
||||
|
||||
private fun removeUnlinkedChunks(
|
||||
linkedChunks: MutableSet<IRCodeChunkBase>
|
||||
): Int {
|
||||
var numRemoved = 0
|
||||
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
|
||||
sub.chunks.reversed().forEach { chunk ->
|
||||
if (chunk !in linkedChunks) {
|
||||
if (chunk === sub.chunks[0]) {
|
||||
if (chunk.instructions.isNotEmpty()) {
|
||||
// don't remove the first chunk of the sub itself because it has to have the name of the sub as label
|
||||
chunk.instructions.clear()
|
||||
numRemoved++
|
||||
}
|
||||
} else {
|
||||
sub.chunks.remove(chunk)
|
||||
numRemoved++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return numRemoved
|
||||
}
|
||||
}
|
@ -3,10 +3,6 @@ TODO
|
||||
|
||||
For next release
|
||||
^^^^^^^^^^^^^^^^
|
||||
- fix unused chunk remover that now causes code execution error (IRCodeGen should enable it)
|
||||
- ir: improve dead code elimination by checking chunk linkage. Does this solve the next issue?:
|
||||
- ir: how to remove all unused subroutines? (the 6502 assembly codegen relies on 64tass solve this for us)
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
@ -215,6 +215,13 @@ class IRBlock(
|
||||
}
|
||||
operator fun plusAssign(sub: IRAsmSubroutine) { asmSubroutines += sub }
|
||||
operator fun plusAssign(asm: IRInlineAsmChunk) { inlineAssembly += asm }
|
||||
|
||||
fun isEmpty(): Boolean {
|
||||
val noAsm = inlineAssembly.isEmpty() || inlineAssembly.all { it.isEmpty() }
|
||||
val noSubs = subroutines.isEmpty() || subroutines.all { it.isEmpty() }
|
||||
val noAsmSubs = asmSubroutines.isEmpty() || asmSubroutines.all { it.isEmpty() }
|
||||
return noAsm && noSubs && noAsmSubs
|
||||
}
|
||||
}
|
||||
|
||||
class IRSubroutine(val name: String,
|
||||
@ -241,6 +248,8 @@ class IRSubroutine(val name: String,
|
||||
}
|
||||
chunks+= chunk
|
||||
}
|
||||
|
||||
fun isEmpty(): Boolean = chunks.isEmpty() || chunks.all { it.isEmpty() }
|
||||
}
|
||||
|
||||
class IRAsmSubroutine(
|
||||
@ -260,6 +269,7 @@ class IRAsmSubroutine(
|
||||
private val registersUsed by lazy { registersUsedInAssembly(asmChunk.isIR, asmChunk.assembly) }
|
||||
|
||||
fun usedRegisters() = registersUsed
|
||||
fun isEmpty(): Boolean = asmChunk.isEmpty()
|
||||
}
|
||||
|
||||
sealed class IRCodeChunkBase(val label: String?, var next: IRCodeChunkBase?) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user