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 { val source: Path) : Node, INameScope {
override lateinit var parent: Node override lateinit var parent: Node
lateinit var program: Program lateinit var program: Program
val imports = mutableSetOf<Module>()
val importedBy = mutableSetOf<Module>()
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent=parent this.parent=parent
@ -1625,7 +1627,7 @@ class FunctionCallStatement(override var target: IdentifierReference,
override fun process(processor: IAstProcessor) = processor.process(this) override fun process(processor: IAstProcessor) = processor.process(this)
override fun toString(): String { 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 var statements: MutableList<IStatement>,
override val position: Position) : IStatement, INameScope { override val position: Position) : IStatement, INameScope {
override lateinit var parent: Node override lateinit var parent: Node
val calledBy = mutableSetOf<Subroutine>()
val calls = mutableSetOf<Subroutine>()
val scopedname: String by lazy { makeScopedName(name) } val scopedname: String by lazy { makeScopedName(name) }
override fun linkParents(parent: Node) { 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: 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: variable declarations
todo remove unused strings and arrays from the heap todo remove unused strings and arrays from the heap
todo inline subroutines that are called exactly once (regardless of their size) 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 set
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure } 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 { override fun process(block: Block): IStatement {
if(block.statements.isEmpty()) { if(block.statements.isEmpty()) {
// remove empty block // remove empty block
@ -38,6 +56,7 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
} }
override fun process(subroutine: Subroutine): IStatement { override fun process(subroutine: Subroutine): IStatement {
println("STMT OPTIMIZE $subroutine ${subroutine.calledBy} ${subroutine.calls}")
super.process(subroutine) super.process(subroutine)
if(subroutine.asmAddress==null) { if(subroutine.asmAddress==null) {