mirror of
https://github.com/irmen/prog8.git
synced 2024-10-07 00:55:33 +00:00
better unused variable removal
This commit is contained in:
parent
69f4a4d4f8
commit
53ac11983b
@ -107,9 +107,30 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
if(decl.type==VarDeclType.VAR) {
|
if(decl.type==VarDeclType.VAR) {
|
||||||
val forceOutput = "force_output" in decl.definingBlock.options()
|
val forceOutput = "force_output" in decl.definingBlock.options()
|
||||||
if (!forceOutput && !decl.autogeneratedDontRemove && !decl.sharedWithAsm && !decl.definingBlock.isInLibrary) {
|
if (!forceOutput && !decl.autogeneratedDontRemove && !decl.sharedWithAsm && !decl.definingBlock.isInLibrary) {
|
||||||
if (callgraph.unused(decl)) {
|
val usages = callgraph.usages(decl)
|
||||||
|
if (usages.isEmpty()) {
|
||||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||||
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
||||||
|
} else {
|
||||||
|
// if all usages are just an assignment to this vardecl
|
||||||
|
// and it is in regular RAM, then remove the var as well including all assignments
|
||||||
|
val assignTargets = usages.mapNotNull {
|
||||||
|
if(it.parent is AssignTarget)
|
||||||
|
it.parent as AssignTarget
|
||||||
|
else if(it.parent.parent is AssignTarget)
|
||||||
|
it.parent.parent as AssignTarget
|
||||||
|
else null
|
||||||
|
}.filter {
|
||||||
|
it.isInRegularRAMof(compTarget.machine)
|
||||||
|
}
|
||||||
|
if(assignTargets.size==usages.size) {
|
||||||
|
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||||
|
return assignTargets.map { it.parent to it.definingScope}.toSet().map {
|
||||||
|
IAstModification.Remove(it.first, it.second)
|
||||||
|
} + listOf(
|
||||||
|
IAstModification.Remove(decl, parent as IStatementContainer)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ class TestCompilerOnRanges: FunSpec({
|
|||||||
|
|
||||||
test("testUByteArrayInitializerWithRange_char_to_char") {
|
test("testUByteArrayInitializerWithRange_char_to_char") {
|
||||||
val platform = Cx16Target
|
val platform = Cx16Target
|
||||||
val result = compileText(platform, true, """
|
val result = compileText(platform, false, """
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte[] cs = @'a' to 'z' ; values are computed at compile time
|
ubyte[] cs = @'a' to 'z' ; values are computed at compile time
|
||||||
|
@ -93,10 +93,10 @@ class TestOptimization: FunSpec({
|
|||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
const ubyte TEST = 10
|
const ubyte TEST = 10
|
||||||
byte x1 = TEST as byte + 1
|
byte @shared x1 = TEST as byte + 1
|
||||||
byte x2 = 1 + TEST as byte
|
byte @shared x2 = 1 + TEST as byte
|
||||||
ubyte y1 = TEST + 1 as byte
|
ubyte @shared y1 = TEST + 1 as byte
|
||||||
ubyte y2 = 1 as byte + TEST
|
ubyte @shared y2 = 1 as byte + TEST
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
@ -215,4 +215,29 @@ class TestOptimization: FunSpec({
|
|||||||
val asm = generateAssembly(result1.program)
|
val asm = generateAssembly(result1.program)
|
||||||
asm.valid shouldBe true
|
asm.valid shouldBe true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("unused variable removal") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
ubyte unused
|
||||||
|
ubyte @shared unused_but_shared ; this one should remain
|
||||||
|
ubyte usedvar_only_written
|
||||||
|
usedvar_only_written=2
|
||||||
|
usedvar_only_written++
|
||||||
|
ubyte usedvar ; and this one too
|
||||||
|
usedvar = msb(usedvar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val result = compileText(C64Target, optimize=true, src, writeAssembly=false).assertSuccess()
|
||||||
|
result.program.entrypoint.statements.size shouldBe 4 // unused_but_shared decl, unused_but_shared=0, usedvar decl, usedvar assign
|
||||||
|
val (decl, assign, decl2, assign2) = result.program.entrypoint.statements
|
||||||
|
decl shouldBe instanceOf<VarDecl>()
|
||||||
|
(decl as VarDecl).name shouldBe "unused_but_shared"
|
||||||
|
assign shouldBe instanceOf<Assignment>()
|
||||||
|
decl2 shouldBe instanceOf<VarDecl>()
|
||||||
|
(decl2 as VarDecl).name shouldBe "usedvar"
|
||||||
|
assign2 shouldBe instanceOf<Assignment>()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@ -173,14 +173,18 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun unused(decl: VarDecl): Boolean {
|
fun unused(decl: VarDecl): Boolean {
|
||||||
|
// Don't check assembly just for occurrences of variables, if they're not used in prog8 itself, just kill them
|
||||||
|
return usages(decl).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun usages(decl: VarDecl): List<IdentifierReference> {
|
||||||
if(decl.type!=VarDeclType.VAR || decl.autogeneratedDontRemove || decl.sharedWithAsm)
|
if(decl.type!=VarDeclType.VAR || decl.autogeneratedDontRemove || decl.sharedWithAsm)
|
||||||
return false
|
return emptyList()
|
||||||
|
|
||||||
if(decl.definingBlock !in usedBlocks)
|
if(decl.definingBlock !in usedBlocks)
|
||||||
return false
|
return emptyList()
|
||||||
|
|
||||||
val allReferencedVardecls = allIdentifiersAndTargets.filter { it.value is VarDecl }.map { it.value }.toSet()
|
return allIdentifiersAndTargets.filter { decl===it.value }.map{ it.key.first }
|
||||||
return decl !in allReferencedVardecls // Don't check assembly just for occurrences of variables, if they're not used in prog8 itself, just kill them
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun nameInAssemblyCode(name: String) = allAssemblyNodes.any { it.assembly.contains(name) }
|
private fun nameInAssemblyCode(name: String) = allAssemblyNodes.any { it.assembly.contains(name) }
|
||||||
|
@ -5,9 +5,6 @@
|
|||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte unused ; TODO FIX : why is this not removed as an unused variable?
|
|
||||||
ubyte @shared unused2
|
|
||||||
|
|
||||||
ubyte bb
|
ubyte bb
|
||||||
uword ww
|
uword ww
|
||||||
ww = not bb or not ww ; TODO WHY DOES THIS USE STACK EVAL
|
ww = not bb or not ww ; TODO WHY DOES THIS USE STACK EVAL
|
||||||
|
Loading…
Reference in New Issue
Block a user