diff --git a/compiler/src/prog8/optimizer/CallGraph.kt b/compiler/src/prog8/optimizer/CallGraph.kt index 1c02482ed..4b533d7ff 100644 --- a/compiler/src/prog8/optimizer/CallGraph.kt +++ b/compiler/src/prog8/optimizer/CallGraph.kt @@ -3,7 +3,9 @@ package prog8.optimizer import prog8.ast.Module import prog8.ast.Node import prog8.ast.Program +import prog8.ast.base.DataType import prog8.ast.base.Position +import prog8.ast.base.VarDeclType import prog8.ast.expressions.AddressOf import prog8.ast.expressions.FunctionCall import prog8.ast.expressions.IdentifierReference @@ -18,7 +20,7 @@ class CallGraph(private val program: Program) : IAstVisitor { val importedBy = mutableMapOf>().withDefault { setOf() } val calls = mutableMapOf>().withDefault { setOf() } val calledBy = mutableMapOf>().withDefault { setOf() } - private val allIdentifiers = mutableSetOf() + private val allIdentifiersAndTargets = mutableMapOf, Statement>() init { visit(program) @@ -35,9 +37,9 @@ class CallGraph(private val program: Program) : IAstVisitor { val blocksFromLibraries = program.allBlocks().filter { it.isInLibrary } val used = mutableSetOf() - allIdentifiers.forEach { - if(it.definingBlock() in blocksFromSubroutines) { - val target = it.targetStatement(program)!!.definingBlock() + allIdentifiersAndTargets.forEach { + if(it.key.first.definingBlock() in blocksFromSubroutines) { + val target = it.value.definingBlock() used.add(target) } } @@ -46,7 +48,7 @@ class CallGraph(private val program: Program) : IAstVisitor { } private val usedModules: Set by lazy { - program.modules.toSet() // TODO + usedBlocks.map { it.definingModule() }.toSet() } override fun visit(directive: Directive) { @@ -105,7 +107,7 @@ class CallGraph(private val program: Program) : IAstVisitor { } override fun visit(identifier: IdentifierReference) { - allIdentifiers.add(identifier) + allIdentifiersAndTargets[Pair(identifier, identifier.position)] = identifier.targetStatement(program)!! } fun checkRecursiveCalls(errors: IErrorReporter) { @@ -166,11 +168,24 @@ class CallGraph(private val program: Program) : IAstVisitor { fun unused(block: Block) = block !in usedBlocks fun unused(decl: VarDecl): Boolean { - return false // TODO implement unused check for vardecls, also check inline asm + if(decl.type!=VarDeclType.VAR || decl.datatype==DataType.STRUCT || decl.autogeneratedDontRemove || decl.parent is StructDecl) + return false + + if(decl.definingBlock() !in usedBlocks) + return false + + // TODO also check inline asm for references to the var + val allReferencedVardecls = allIdentifiersAndTargets.filter { it.value is VarDecl }.map { it.value }.toSet() + + return decl !in allReferencedVardecls } fun unused(struct: StructDecl): Boolean { - return false // TODO implement unused check for struct decls, also check inline asm + if(struct.definingBlock() !in usedBlocks) + return false + + // TODO implement unused check for struct decls, also check inline asm + return false } inline fun unused(label: Label) = false // just always output labels diff --git a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt index c82dfdb84..c8dc5ac4b 100644 --- a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt +++ b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt @@ -105,8 +105,8 @@ internal class UnusedCodeRemover(private val program: Program, override fun after(decl: VarDecl, parent: Node): Iterable { val forceOutput = "force_output" in decl.definingBlock().options() - if(!forceOutput && callgraph.unused(decl)) { - if(decl.type == VarDeclType.VAR) + if(!forceOutput && !decl.autogeneratedDontRemove && callgraph.unused(decl)) { + if(decl.type == VarDeclType.VAR && !decl.definingBlock().isInLibrary) errors.warn("removing unused variable '${decl.name}'", decl.position) return listOf(IAstModification.Remove(decl, decl.definingScope())) diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index 39604f8c2..910d60096 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -727,7 +727,7 @@ data class IdentifierReference(val nameInSource: List, override val posi fun targetVarDecl(program: Program): VarDecl? = targetStatement(program) as? VarDecl fun targetSubroutine(program: Program): Subroutine? = targetStatement(program) as? Subroutine - override fun equals(other: Any?) = other is IdentifierReference && other.nameInSource==nameInSource + override fun equals(other: Any?) = other is IdentifierReference && other.nameInSource==nameInSource // NOTE: only compare by the name, not the position! override fun hashCode() = nameInSource.hashCode() override fun linkParents(parent: Node) { diff --git a/compilerAst/src/prog8/ast/walk/AstWalker.kt b/compilerAst/src/prog8/ast/walk/AstWalker.kt index aa05b7c57..41db120d5 100644 --- a/compilerAst/src/prog8/ast/walk/AstWalker.kt +++ b/compilerAst/src/prog8/ast/walk/AstWalker.kt @@ -160,7 +160,6 @@ abstract class AstWalker { open fun after(whileLoop: WhileLoop, parent: Node): Iterable = noModifications protected val modifications = mutableListOf>() - // private val modificationsReplacedNodes = mutableSetOf>() private fun track(mods: Iterable, node: Node, parent: Node) { for (it in mods) { @@ -176,12 +175,23 @@ abstract class AstWalker { } fun applyModifications(): Int { + // check if there are double removes, keep only the last one + val removals = modifications.filter { it.first is IAstModification.Remove } + if(removals.size>0) { + val doubles = removals.groupBy { (it.first as IAstModification.Remove).node }.filter { it.value.size>1 } + doubles.forEach { + for(doubleRemove in it.value.dropLast(1)) { + if(!modifications.removeIf { mod-> mod.first === doubleRemove.first }) + throw FatalAstException("ast remove problem") + } + } + } + modifications.forEach { it.first.perform() } val amount = modifications.size modifications.clear() -// modificationsReplacedNodes.clear() return amount }