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 calledBy = mutableMapOf<Subroutine, Set<Node>>().withDefault { setOf() }
private val allIdentifiersAndTargets = mutableMapOf<Pair<IdentifierReference, Position>, Statement>()
private val allAssemblyNodes = mutableListOf<InlineAssembly>()
init {
visit(program)
}
private val usedSubroutines: Set<Subroutine> by lazy {
// TODO also check inline assembly if it uses the subroutine
calledBy.keys
}
private val usedBlocks: Set<Block> by lazy {
// TODO also check inline assembly if it uses the block
val blocksFromSubroutines = usedSubroutines.map { it.definingBlock() }
val blocksFromLibraries = program.allBlocks().filter { it.isInLibrary }
val used = mutableSetOf<Block>()
@ -110,6 +109,10 @@ class CallGraph(private val program: Program) : IAstVisitor {
allIdentifiersAndTargets[Pair(identifier, identifier.position)] = identifier.targetStatement(program)!!
}
override fun visit(inlineAssembly: InlineAssembly) {
allAssemblyNodes.add(inlineAssembly)
}
fun checkRecursiveCalls(errors: IErrorReporter) {
val cycles = recursionCycles()
if(cycles.any()) {
@ -163,9 +166,13 @@ class CallGraph(private val program: Program) : IAstVisitor {
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 {
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)
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
return decl !in allReferencedVardecls && !nameInAssemblyCode(decl.name)
}
fun unused(struct: StructDecl): Boolean {
if(struct.definingBlock() !in usedBlocks)
return false
// TODO implement unused check for struct decls, also check inline asm
return false
}
private fun nameInAssemblyCode(name: String) = allAssemblyNodes.any { it.assembly.contains(name) }
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 VarDecl -> unused(stmt)
is Label -> false // just always output labels
is StructDecl -> unused(stmt)
else -> false
}
}

View File

@ -948,7 +948,7 @@ class WhenChoice(var values: MutableList<Expression>?, // if null, th
class StructDecl(override val name: String,
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

View File

@ -2,8 +2,6 @@
TODO
====
- implement new 'unused' in CallGraph for more node types
- allow inlining of subroutines with params
- 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)