From 619fa9b65e84cea2a037a5cadecb47e4e66ec3ff Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 5 Apr 2021 17:57:07 +0200 Subject: [PATCH] callgraph --- compiler/src/prog8/compiler/Compiler.kt | 6 +- compiler/src/prog8/optimizer/CallGraph.kt | 86 ++----------------- .../src/prog8/optimizer/UnusedCodeRemover.kt | 8 +- compilerAst/src/prog8/ast/AstToplevel.kt | 6 +- 4 files changed, 17 insertions(+), 89 deletions(-) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 199bf8a67..09c660748 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -265,7 +265,7 @@ private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions: // optimize the parse tree println("Optimizing...") - val remover = UnusedCodeRemover(programAst, errors, compTarget, ::loadAsmIncludeFile) + val remover = UnusedCodeRemover(programAst, errors, compTarget) remover.visit(programAst) remover.applyModifications() @@ -286,7 +286,7 @@ private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions: if(errors.noErrors()) { inliner.applyModifications() inliner.fixCallsToInlinedSubroutines() - val remover2 = UnusedCodeRemover(programAst, errors, compTarget, ::loadAsmIncludeFile) + val remover2 = UnusedCodeRemover(programAst, errors, compTarget) remover2.visit(programAst) remover2.applyModifications() } @@ -300,7 +300,7 @@ private fun postprocessAst(programAst: Program, errors: IErrorReporter, compiler programAst.variousCleanups(errors) programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget) // check if final tree is still valid errors.report() - val callGraph = CallGraph(programAst, ::loadAsmIncludeFile) + val callGraph = CallGraph(programAst) callGraph.checkRecursiveCalls(errors) errors.report() programAst.verifyFunctionArgTypes() diff --git a/compiler/src/prog8/optimizer/CallGraph.kt b/compiler/src/prog8/optimizer/CallGraph.kt index e9766d609..5bf68f9d5 100644 --- a/compiler/src/prog8/optimizer/CallGraph.kt +++ b/compiler/src/prog8/optimizer/CallGraph.kt @@ -9,50 +9,25 @@ import prog8.ast.expressions.FunctionCall import prog8.ast.statements.* import prog8.ast.walk.IAstVisitor import prog8.compiler.IErrorReporter -import java.nio.file.Path - -private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr|bra)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE) -private val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE) -class CallGraph(private val program: Program, private val asmFileLoader: (filename: String, source: Path)->String) : IAstVisitor { +class CallGraph(private val program: Program) : IAstVisitor { - val imports = mutableMapOf>().withDefault { mutableListOf() } - val importedBy = mutableMapOf>().withDefault { mutableListOf() } - val calls = mutableMapOf>().withDefault { mutableListOf() } - val calledBy = mutableMapOf>().withDefault { mutableListOf() } + val imports = mutableMapOf>().withDefault { setOf() } + val importedBy = mutableMapOf>().withDefault { setOf() } + val calls = mutableMapOf>().withDefault { setOf() } + val calledBy = mutableMapOf>().withDefault { setOf() } init { visit(program) } - override fun visit(program: Program) { - super.visit(program) - - program.modules.forEach { - it.importedBy.clear() - it.imports.clear() - - it.importedBy.addAll(importedBy.getValue(it)) - it.imports.addAll(imports.getValue(it)) - } - - val rootmodule = program.modules.first() - rootmodule.importedBy.add(rootmodule) // don't discard root module - } - override fun visit(directive: Directive) { val thisModule = directive.definingModule() if (directive.directive == "%import") { val importedModule: Module = program.modules.single { it.name == directive.args[0].name } imports[thisModule] = imports.getValue(thisModule).plus(importedModule) importedBy[importedModule] = importedBy.getValue(importedModule).plus(thisModule) - } else if (directive.directive == "%asminclude") { - val asm = asmFileLoader(directive.args[0].str!!, thisModule.source) - val scope = directive.definingSubroutine() - if(scope!=null) { - scanAssemblyCode(asm, directive, scope) - } } super.visit(directive) @@ -102,53 +77,6 @@ class CallGraph(private val program: Program, private val asmFileLoader: (filena super.visit(jump) } - override fun visit(inlineAssembly: InlineAssembly) { - // parse inline asm for subroutine calls (jmp, jsr, bra) - val scope = inlineAssembly.definingSubroutine() - scanAssemblyCode(inlineAssembly.assembly, inlineAssembly, scope) - super.visit(inlineAssembly) - } - - private fun scanAssemblyCode(asm: String, context: Statement, scope: Subroutine?) { - asm.lines().forEach { line -> - val matches = asmJumpRx.matchEntire(line) - if (matches != null) { - val jumptarget = matches.groups[2]?.value - if (jumptarget != null && (jumptarget[0].isLetter() || jumptarget[0] == '_')) { - val node = program.namespace.lookup(jumptarget.split('.'), context) - if (node is Subroutine) { - if(scope!=null) - calls[scope] = calls.getValue(scope).plus(node) - calledBy[node] = calledBy.getValue(node).plus(context) - } else if (jumptarget.contains('.')) { - // maybe only the first part already refers to a subroutine - val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context) - if (node2 is Subroutine) { - if(scope!=null) - calls[scope] = calls.getValue(scope).plus(node2) - calledBy[node2] = calledBy.getValue(node2).plus(context) - } - } - } - } else { - val matches2 = asmRefRx.matchEntire(line) - if (matches2 != null) { - val target = matches2.groups[2]?.value - if (target != null && (target[0].isLetter() || target[0] == '_')) { - if (target.contains('.')) { - val node = program.namespace.lookup(listOf(target.substringBefore('.')), context) - if (node is Subroutine) { - if(scope!=null) - calls[scope] = calls.getValue(scope).plus(node) - calledBy[node] = calledBy.getValue(node).plus(context) - } - } - } - } - } - } - } - fun checkRecursiveCalls(errors: IErrorReporter) { val cycles = recursionCycles() if(cycles.any()) { @@ -216,6 +144,10 @@ class CallGraph(private val program: Program, private val asmFileLoader: (filena 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 { diff --git a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt index 24b81a561..c82dfdb84 100644 --- a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt +++ b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt @@ -14,18 +14,16 @@ import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.compiler.IErrorReporter import prog8.compiler.target.ICompilationTarget -import java.nio.file.Path internal class UnusedCodeRemover(private val program: Program, private val errors: IErrorReporter, - private val compTarget: ICompilationTarget, - asmFileLoader: (filename: String, source: Path)->String): AstWalker() { + private val compTarget: ICompilationTarget): AstWalker() { - private val callgraph = CallGraph(program, asmFileLoader) + private val callgraph = CallGraph(program) override fun before(module: Module, parent: Node): Iterable { - return if (!module.isLibraryModule && (module.importedBy.isEmpty() || module.containsNoCodeNorVars())) + return if (!module.isLibraryModule && (module.containsNoCodeNorVars() || callgraph.unused(module))) listOf(IAstModification.Remove(module, module.definingScope())) else noModifications diff --git a/compilerAst/src/prog8/ast/AstToplevel.kt b/compilerAst/src/prog8/ast/AstToplevel.kt index ccfab468f..6cf435cb9 100644 --- a/compilerAst/src/prog8/ast/AstToplevel.kt +++ b/compilerAst/src/prog8/ast/AstToplevel.kt @@ -274,9 +274,9 @@ class Program(val name: String, init { // insert a container module for all interned strings later if(modules.firstOrNull()?.name != internedStringsModuleName) { - val internedStringsModule = Module(internedStringsModuleName, mutableListOf(), Position.DUMMY, false, Path.of("")) + val internedStringsModule = Module(internedStringsModuleName, mutableListOf(), Position.DUMMY, true, Path.of("")) modules.add(0, internedStringsModule) - val block = Block(internedStringsModuleName, null, mutableListOf(), false, Position.DUMMY) + val block = Block(internedStringsModuleName, null, mutableListOf(), true, Position.DUMMY) internedStringsModule.statements.add(block) internedStringsModule.linkParents(this) internedStringsModule.program = this @@ -339,8 +339,6 @@ class Module(override val name: String, override lateinit var parent: Node lateinit var program: Program - val importedBy = mutableListOf() - val imports = mutableSetOf() val loadAddress: Int by lazy { val address = (statements.singleOrNull { it is Directive && it.directive == "%address" } as? Directive)?.args?.single()?.int ?: 0