callgraph

This commit is contained in:
Irmen de Jong 2021-04-05 18:50:46 +02:00
parent 619fa9b65e
commit 530d03d284
5 changed files with 62 additions and 42 deletions

View File

@ -71,7 +71,6 @@ internal fun Program.moveMainAndStartToFirst() {
val directives = modules[0].statements.filterIsInstance<Directive>()
val start = this.entrypoint()
if(start!=null) {
val mod = start.definingModule()
val block = start.definingBlock()
if(!modules.remove(mod))
@ -96,4 +95,3 @@ internal fun Program.moveMainAndStartToFirst() {
modules[0].statements.add(0, directive)
}
}
}

View File

@ -6,6 +6,7 @@ import prog8.ast.Program
import prog8.ast.base.Position
import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.FunctionCall
import prog8.ast.expressions.IdentifierReference
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.compiler.IErrorReporter
@ -17,11 +18,37 @@ class CallGraph(private val program: Program) : IAstVisitor {
val importedBy = mutableMapOf<Module, Set<Module>>().withDefault { setOf() }
val calls = mutableMapOf<Subroutine, Set<Subroutine>>().withDefault { setOf() }
val calledBy = mutableMapOf<Subroutine, Set<Node>>().withDefault { setOf() }
private val allIdentifiers = mutableSetOf<IdentifierReference>()
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>()
allIdentifiers.forEach {
if(it.definingBlock() in blocksFromSubroutines) {
val target = it.targetStatement(program)!!.definingBlock()
used.add(target)
}
}
used + blocksFromLibraries + program.entrypoint().definingBlock()
}
private val usedModules: Set<Module> by lazy {
program.modules.toSet() // TODO
}
override fun visit(directive: Directive) {
val thisModule = directive.definingModule()
if (directive.directive == "%import") {
@ -77,6 +104,10 @@ class CallGraph(private val program: Program) : IAstVisitor {
super.visit(jump)
}
override fun visit(identifier: IdentifierReference) {
allIdentifiers.add(identifier)
}
fun checkRecursiveCalls(errors: IErrorReporter) {
val cycles = recursionCycles()
if(cycles.any()) {
@ -128,13 +159,11 @@ class CallGraph(private val program: Program) : IAstVisitor {
return false
}
fun unused(sub: Subroutine): Boolean {
return calledBy[sub].isNullOrEmpty() // TODO also check inline assembly if it uses the subroutine
}
fun unused(module: Module) = module !in usedModules
fun unused(block: Block): Boolean {
return false // TODO implement unused check for Block, also check inline asm
}
fun unused(sub: Subroutine) = sub !in usedSubroutines
fun unused(block: Block) = block !in usedBlocks
fun unused(decl: VarDecl): Boolean {
return false // TODO implement unused check for vardecls, also check inline asm
@ -144,10 +173,6 @@ class CallGraph(private val program: Program) : IAstVisitor {
return false // TODO implement unused check for struct decls, also check inline asm
}
fun unused(module: Module): Boolean {
return false // TODO
}
inline fun unused(label: Label) = false // just always output labels
fun unused(stmt: ISymbolStatement): Boolean {

View File

@ -283,15 +283,13 @@ class Program(val name: String,
}
}
fun entrypoint(): Subroutine? {
fun entrypoint(): Subroutine {
val mainBlocks = allBlocks().filter { it.name=="main" }
if(mainBlocks.size > 1)
throw FatalAstException("more than one 'main' block")
return if(mainBlocks.isEmpty()) {
null
} else {
mainBlocks[0].subScope("start") as Subroutine?
}
if(mainBlocks.isEmpty())
throw FatalAstException("no 'main' block")
return mainBlocks[0].subScope("start") as Subroutine
}
fun internString(string: StringLiteralValue): List<String> {

View File

@ -720,7 +720,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
fun targetStatement(program: Program) =
if(nameInSource.size==1 && nameInSource[0] in program.builtinFunctions.names)
BuiltinFunctionStatementPlaceholder(nameInSource[0], position)
BuiltinFunctionStatementPlaceholder(nameInSource[0], position, parent)
else
program.namespace.lookup(nameInSource, this)

View File

@ -35,8 +35,7 @@ sealed class Statement : Node {
}
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : Statement() {
override var parent: Node = ParentSentinel
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position, override var parent: Node) : Statement() {
override fun linkParents(parent: Node) {}
override fun accept(visitor: IAstVisitor) = throw FatalAstException("should not iterate over this node")
override fun accept(visitor: AstWalker, parent: Node) = throw FatalAstException("should not iterate over this node")