remove unused/empty modules

This commit is contained in:
Irmen de Jong 2019-06-21 00:12:22 +02:00
parent b35430214b
commit b374af3526
3 changed files with 116 additions and 2 deletions

View File

@ -496,6 +496,8 @@ class Module(override val name: String,
val source: Path) : Node, INameScope {
override lateinit var parent: Node
lateinit var program: Program
val imports = mutableSetOf<Module>()
val importedBy = mutableSetOf<Module>()
override fun linkParents(parent: Node) {
this.parent=parent
@ -1625,7 +1627,7 @@ class FunctionCallStatement(override var target: IdentifierReference,
override fun process(processor: IAstProcessor) = processor.process(this)
override fun toString(): String {
return "FunctionCall(target=$target, pos=$position)"
return "FunctionCallStatement(target=$target, pos=$position)"
}
}
@ -1690,6 +1692,9 @@ class Subroutine(override val name: String,
override var statements: MutableList<IStatement>,
override val position: Position) : IStatement, INameScope {
override lateinit var parent: Node
val calledBy = mutableSetOf<Subroutine>()
val calls = mutableSetOf<Subroutine>()
val scopedname: String by lazy { makeScopedName(name) }
override fun linkParents(parent: Node) {

View File

@ -0,0 +1,90 @@
package prog8.optimizing
import prog8.ast.*
class CallGraphBuilder(private val program: Program): IAstProcessor {
private val modulesImporting = mutableMapOf<Module, Set<Module>>().withDefault { mutableSetOf() }
private val modulesImportedBy = mutableMapOf<Module, Set<Module>>().withDefault { mutableSetOf() }
private val subroutinesCalling = mutableMapOf<Subroutine, Set<Subroutine>>().withDefault { mutableSetOf() }
private val subroutinesCalledBy = mutableMapOf<Subroutine, Set<Subroutine>>().withDefault { mutableSetOf() }
private fun forAllSubroutines(scope: INameScope, sub: (s: Subroutine) -> Unit) {
fun findSubs(scope: INameScope) {
scope.statements.forEach {
if(it is Subroutine)
sub(it)
if(it is INameScope)
findSubs(it)
}
}
findSubs(scope)
}
override fun process(program: Program) {
super.process(program)
program.modules.forEach {
it.importedBy.clear()
it.imports.clear()
it.importedBy.addAll(modulesImportedBy.getValue(it))
it.imports.addAll(modulesImporting.getValue(it))
if(it.isLibraryModule && it.importedBy.isEmpty())
it.importedBy.add(it) // don't discard auto-imported library module
forAllSubroutines(it) { sub ->
sub.calledBy.clear()
sub.calls.clear()
sub.calledBy.addAll(subroutinesCalledBy.getValue(sub))
sub.calls.addAll(subroutinesCalling.getValue(sub))
}
}
val rootmodule = program.modules.first()
rootmodule.importedBy.add(rootmodule) // don't discard root module
}
override fun process(directive: Directive): IStatement {
if(directive.directive=="%import") {
val importedModule: Module = program.modules.single { it.name==directive.args[0].name }
val thisModule = directive.definingModule()
modulesImporting[thisModule] = modulesImporting.getValue(thisModule).plus(importedModule)
modulesImportedBy[importedModule] = modulesImportedBy.getValue(importedModule).plus(thisModule)
}
return super.process(directive)
}
override fun process(functionCall: FunctionCall): IExpression {
val otherSub = functionCall.target.targetSubroutine(program.namespace)
if(otherSub!=null) {
val thisSub = functionCall.definingScope() as Subroutine
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(thisSub)
}
return super.process(functionCall)
}
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
val otherSub = functionCallStatement.target.targetSubroutine(program.namespace)
if(otherSub!=null) {
val thisSub = functionCallStatement.definingScope() as Subroutine
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(thisSub)
}
return super.process(functionCallStatement)
}
override fun process(jump: Jump): IStatement {
val otherSub = jump.identifier?.targetSubroutine(program.namespace)
if(otherSub!=null) {
val thisSub = jump.definingScope() as Subroutine
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(thisSub)
}
return super.process(jump)
}
}

View File

@ -11,7 +11,7 @@ import kotlin.math.floor
todo: implement usage counters for blocks, variables, subroutines, heap variables. Then:
todo remove unused: subroutines, blocks, modules (in this order)
todo remove unused: subroutines, blocks (in this order)
todo remove unused: variable declarations
todo remove unused strings and arrays from the heap
todo inline subroutines that are called exactly once (regardless of their size)
@ -28,6 +28,24 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
private set
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
override fun process(program: Program) {
val callgraph = CallGraphBuilder(program)
callgraph.process(program)
// remove modules that are not imported, or are empty
val removeModules = mutableSetOf<Module>()
program.modules.forEach {
if(it.importedBy.isEmpty() || it.isEmpty()) {
printWarning("discarding empty or unused module: ${it.name}")
removeModules.add(it)
}
}
program.modules.removeAll(removeModules)
super.process(program)
}
override fun process(block: Block): IStatement {
if(block.statements.isEmpty()) {
// remove empty block
@ -38,6 +56,7 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
}
override fun process(subroutine: Subroutine): IStatement {
println("STMT OPTIMIZE $subroutine ${subroutine.calledBy} ${subroutine.calls}")
super.process(subroutine)
if(subroutine.asmAddress==null) {