mirror of
https://github.com/irmen/prog8.git
synced 2024-08-19 14:29:17 +00:00
remove unused/empty modules
This commit is contained in:
parent
b35430214b
commit
b374af3526
@ -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) {
|
||||
|
90
compiler/src/prog8/optimizing/CallGraphBuilder.kt
Normal file
90
compiler/src/prog8/optimizing/CallGraphBuilder.kt
Normal 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)
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user