callgraph

This commit is contained in:
Irmen de Jong 2021-04-05 20:32:30 +02:00
parent 530d03d284
commit 4c1eb1b12a
4 changed files with 38 additions and 13 deletions

View File

@ -3,7 +3,9 @@ package prog8.optimizer
import prog8.ast.Module import prog8.ast.Module
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.DataType
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.AddressOf import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.FunctionCall import prog8.ast.expressions.FunctionCall
import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.IdentifierReference
@ -18,7 +20,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
val importedBy = mutableMapOf<Module, Set<Module>>().withDefault { setOf() } val importedBy = mutableMapOf<Module, Set<Module>>().withDefault { setOf() }
val calls = mutableMapOf<Subroutine, Set<Subroutine>>().withDefault { setOf() } val calls = mutableMapOf<Subroutine, Set<Subroutine>>().withDefault { setOf() }
val calledBy = mutableMapOf<Subroutine, Set<Node>>().withDefault { setOf() } val calledBy = mutableMapOf<Subroutine, Set<Node>>().withDefault { setOf() }
private val allIdentifiers = mutableSetOf<IdentifierReference>() private val allIdentifiersAndTargets = mutableMapOf<Pair<IdentifierReference, Position>, Statement>()
init { init {
visit(program) visit(program)
@ -35,9 +37,9 @@ class CallGraph(private val program: Program) : IAstVisitor {
val blocksFromLibraries = program.allBlocks().filter { it.isInLibrary } val blocksFromLibraries = program.allBlocks().filter { it.isInLibrary }
val used = mutableSetOf<Block>() val used = mutableSetOf<Block>()
allIdentifiers.forEach { allIdentifiersAndTargets.forEach {
if(it.definingBlock() in blocksFromSubroutines) { if(it.key.first.definingBlock() in blocksFromSubroutines) {
val target = it.targetStatement(program)!!.definingBlock() val target = it.value.definingBlock()
used.add(target) used.add(target)
} }
} }
@ -46,7 +48,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
} }
private val usedModules: Set<Module> by lazy { private val usedModules: Set<Module> by lazy {
program.modules.toSet() // TODO usedBlocks.map { it.definingModule() }.toSet()
} }
override fun visit(directive: Directive) { override fun visit(directive: Directive) {
@ -105,7 +107,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
} }
override fun visit(identifier: IdentifierReference) { override fun visit(identifier: IdentifierReference) {
allIdentifiers.add(identifier) allIdentifiersAndTargets[Pair(identifier, identifier.position)] = identifier.targetStatement(program)!!
} }
fun checkRecursiveCalls(errors: IErrorReporter) { fun checkRecursiveCalls(errors: IErrorReporter) {
@ -166,11 +168,24 @@ class CallGraph(private val program: Program) : IAstVisitor {
fun unused(block: Block) = block !in usedBlocks fun unused(block: Block) = block !in usedBlocks
fun unused(decl: VarDecl): Boolean { 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 { 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 inline fun unused(label: Label) = false // just always output labels

View File

@ -105,8 +105,8 @@ internal class UnusedCodeRemover(private val program: Program,
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
val forceOutput = "force_output" in decl.definingBlock().options() val forceOutput = "force_output" in decl.definingBlock().options()
if(!forceOutput && callgraph.unused(decl)) { if(!forceOutput && !decl.autogeneratedDontRemove && callgraph.unused(decl)) {
if(decl.type == VarDeclType.VAR) if(decl.type == VarDeclType.VAR && !decl.definingBlock().isInLibrary)
errors.warn("removing unused variable '${decl.name}'", decl.position) errors.warn("removing unused variable '${decl.name}'", decl.position)
return listOf(IAstModification.Remove(decl, decl.definingScope())) return listOf(IAstModification.Remove(decl, decl.definingScope()))

View File

@ -727,7 +727,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
fun targetVarDecl(program: Program): VarDecl? = targetStatement(program) as? VarDecl fun targetVarDecl(program: Program): VarDecl? = targetStatement(program) as? VarDecl
fun targetSubroutine(program: Program): Subroutine? = targetStatement(program) as? Subroutine 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 hashCode() = nameInSource.hashCode()
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {

View File

@ -160,7 +160,6 @@ abstract class AstWalker {
open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
protected val modifications = mutableListOf<Triple<IAstModification, Node, Node>>() protected val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
// private val modificationsReplacedNodes = mutableSetOf<Pair<Node, Position>>()
private fun track(mods: Iterable<IAstModification>, node: Node, parent: Node) { private fun track(mods: Iterable<IAstModification>, node: Node, parent: Node) {
for (it in mods) { for (it in mods) {
@ -176,12 +175,23 @@ abstract class AstWalker {
} }
fun applyModifications(): Int { 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 { modifications.forEach {
it.first.perform() it.first.perform()
} }
val amount = modifications.size val amount = modifications.size
modifications.clear() modifications.clear()
// modificationsReplacedNodes.clear()
return amount return amount
} }