callgraph improved unused node checking

This commit is contained in:
Irmen de Jong 2021-04-05 20:45:18 +02:00
parent 4c1eb1b12a
commit 3e3b0bcd8b
3 changed files with 14 additions and 18 deletions

View File

@ -21,18 +21,17 @@ class CallGraph(private val program: Program) : IAstVisitor {
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 allIdentifiersAndTargets = mutableMapOf<Pair<IdentifierReference, Position>, Statement>() private val allIdentifiersAndTargets = mutableMapOf<Pair<IdentifierReference, Position>, Statement>()
private val allAssemblyNodes = mutableListOf<InlineAssembly>()
init { init {
visit(program) visit(program)
} }
private val usedSubroutines: Set<Subroutine> by lazy { private val usedSubroutines: Set<Subroutine> by lazy {
// TODO also check inline assembly if it uses the subroutine
calledBy.keys calledBy.keys
} }
private val usedBlocks: Set<Block> by lazy { private val usedBlocks: Set<Block> by lazy {
// TODO also check inline assembly if it uses the block
val blocksFromSubroutines = usedSubroutines.map { it.definingBlock() } val blocksFromSubroutines = usedSubroutines.map { it.definingBlock() }
val blocksFromLibraries = program.allBlocks().filter { it.isInLibrary } val blocksFromLibraries = program.allBlocks().filter { it.isInLibrary }
val used = mutableSetOf<Block>() val used = mutableSetOf<Block>()
@ -110,6 +109,10 @@ class CallGraph(private val program: Program) : IAstVisitor {
allIdentifiersAndTargets[Pair(identifier, identifier.position)] = identifier.targetStatement(program)!! allIdentifiersAndTargets[Pair(identifier, identifier.position)] = identifier.targetStatement(program)!!
} }
override fun visit(inlineAssembly: InlineAssembly) {
allAssemblyNodes.add(inlineAssembly)
}
fun checkRecursiveCalls(errors: IErrorReporter) { fun checkRecursiveCalls(errors: IErrorReporter) {
val cycles = recursionCycles() val cycles = recursionCycles()
if(cycles.any()) { if(cycles.any()) {
@ -163,9 +166,13 @@ class CallGraph(private val program: Program) : IAstVisitor {
fun unused(module: Module) = module !in usedModules fun unused(module: Module) = module !in usedModules
fun unused(sub: Subroutine) = sub !in usedSubroutines fun unused(sub: Subroutine): Boolean {
return sub !in usedSubroutines && !nameInAssemblyCode(sub.name)
}
fun unused(block: Block) = block !in usedBlocks fun unused(block: Block): Boolean {
return block !in usedBlocks && !nameInAssemblyCode(block.name)
}
fun unused(decl: VarDecl): Boolean { fun unused(decl: VarDecl): Boolean {
if(decl.type!=VarDeclType.VAR || decl.datatype==DataType.STRUCT || decl.autogeneratedDontRemove || decl.parent is StructDecl) if(decl.type!=VarDeclType.VAR || decl.datatype==DataType.STRUCT || decl.autogeneratedDontRemove || decl.parent is StructDecl)
@ -174,19 +181,11 @@ class CallGraph(private val program: Program) : IAstVisitor {
if(decl.definingBlock() !in usedBlocks) if(decl.definingBlock() !in usedBlocks)
return false return false
// TODO also check inline asm for references to the var
val allReferencedVardecls = allIdentifiersAndTargets.filter { it.value is VarDecl }.map { it.value }.toSet() val allReferencedVardecls = allIdentifiersAndTargets.filter { it.value is VarDecl }.map { it.value }.toSet()
return decl !in allReferencedVardecls && !nameInAssemblyCode(decl.name)
return decl !in allReferencedVardecls
} }
fun unused(struct: StructDecl): Boolean { private fun nameInAssemblyCode(name: String) = allAssemblyNodes.any { it.assembly.contains(name) }
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
@ -196,7 +195,6 @@ class CallGraph(private val program: Program) : IAstVisitor {
is Block -> unused(stmt) is Block -> unused(stmt)
is VarDecl -> unused(stmt) is VarDecl -> unused(stmt)
is Label -> false // just always output labels is Label -> false // just always output labels
is StructDecl -> unused(stmt)
else -> false else -> false
} }
} }

View File

@ -948,7 +948,7 @@ class WhenChoice(var values: MutableList<Expression>?, // if null, th
class StructDecl(override val name: String, class StructDecl(override val name: String,
override var statements: MutableList<Statement>, // actually, only vardecls here override var statements: MutableList<Statement>, // actually, only vardecls here
override val position: Position): Statement(), INameScope, ISymbolStatement { override val position: Position): Statement(), INameScope {
override lateinit var parent: Node override lateinit var parent: Node

View File

@ -2,8 +2,6 @@
TODO TODO
==== ====
- implement new 'unused' in CallGraph for more node types
- allow inlining of subroutines with params - allow inlining of subroutines with params
- optimize several inner loops in gfx2 - optimize several inner loops in gfx2
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine) - hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)