ir: fix unused code remover

This commit is contained in:
Irmen de Jong 2022-11-02 22:51:40 +01:00
parent e7408224ac
commit e094785cbd
6 changed files with 105 additions and 41 deletions

View File

@ -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)

View File

@ -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

View File

@ -1,6 +1,5 @@
package prog8.codegen.intermediate
import prog8.code.core.Position
import prog8.intermediate.*
internal class IRPeepholeOptimizer(private val irprog: IRProgram) {

View File

@ -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
}
}

View File

@ -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)
...

View File

@ -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?) {