mirror of
https://github.com/irmen/prog8.git
synced 2025-01-13 10:29:52 +00:00
remove unused variables, subroutines, blocks
This commit is contained in:
parent
ff1294207e
commit
d83f49d84f
@ -186,11 +186,17 @@ interface IAstProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun process(functionCall: FunctionCall): IExpression {
|
fun process(functionCall: FunctionCall): IExpression {
|
||||||
|
val newtarget = functionCall.target.process(this)
|
||||||
|
if(newtarget is IdentifierReference)
|
||||||
|
functionCall.target = newtarget
|
||||||
functionCall.arglist = functionCall.arglist.map { it.process(this) }.toMutableList()
|
functionCall.arglist = functionCall.arglist.map { it.process(this) }.toMutableList()
|
||||||
return functionCall
|
return functionCall
|
||||||
}
|
}
|
||||||
|
|
||||||
fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
||||||
|
val newtarget = functionCallStatement.target.process(this)
|
||||||
|
if(newtarget is IdentifierReference)
|
||||||
|
functionCallStatement.target = newtarget
|
||||||
functionCallStatement.arglist = functionCallStatement.arglist.map { it.process(this) }.toMutableList()
|
functionCallStatement.arglist = functionCallStatement.arglist.map { it.process(this) }.toMutableList()
|
||||||
return functionCallStatement
|
return functionCallStatement
|
||||||
}
|
}
|
||||||
@ -202,6 +208,12 @@ interface IAstProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun process(jump: Jump): IStatement {
|
fun process(jump: Jump): IStatement {
|
||||||
|
if(jump.identifier!=null) {
|
||||||
|
val ident = jump.identifier.process(this)
|
||||||
|
if(ident is IdentifierReference && ident!==jump.identifier) {
|
||||||
|
return Jump(null, ident, null, jump.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
return jump
|
return jump
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +242,7 @@ interface IAstProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun process(literalValue: LiteralValue): LiteralValue {
|
fun process(literalValue: LiteralValue): LiteralValue {
|
||||||
if(literalValue.arrayvalue!=null && literalValue.heapId==null) {
|
if(literalValue.arrayvalue!=null) {
|
||||||
for(av in literalValue.arrayvalue.withIndex()) {
|
for(av in literalValue.arrayvalue.withIndex()) {
|
||||||
val newvalue = av.value.process(this)
|
val newvalue = av.value.process(this)
|
||||||
literalValue.arrayvalue[av.index] = newvalue
|
literalValue.arrayvalue[av.index] = newvalue
|
||||||
@ -316,7 +328,7 @@ interface IAstProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun process(addressOf: AddressOf): IExpression {
|
fun process(addressOf: AddressOf): IExpression {
|
||||||
process(addressOf.identifier)
|
addressOf.identifier.process(this)
|
||||||
return addressOf
|
return addressOf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,6 +400,12 @@ interface IStatement : Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val expensiveToInline: Boolean
|
val expensiveToInline: Boolean
|
||||||
|
|
||||||
|
fun definingBlock(): Block {
|
||||||
|
if(this is Block)
|
||||||
|
return this
|
||||||
|
return findParentNode<Block>(this)!!
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -494,7 +512,7 @@ interface INameScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private object ParentSentinel : Node {
|
object ParentSentinel : Node {
|
||||||
override val position = Position("<<sentinel>>", 0, 0, 0)
|
override val position = Position("<<sentinel>>", 0, 0, 0)
|
||||||
override var parent: Node = this
|
override var parent: Node = this
|
||||||
override fun linkParents(parent: Node) {}
|
override fun linkParents(parent: Node) {}
|
||||||
@ -744,6 +762,7 @@ class VarDecl(val type: VarDeclType,
|
|||||||
val name: String,
|
val name: String,
|
||||||
var value: IExpression?,
|
var value: IExpression?,
|
||||||
val isArray: Boolean,
|
val isArray: Boolean,
|
||||||
|
val autoGenerated: Boolean,
|
||||||
override val position: Position) : IStatement {
|
override val position: Position) : IStatement {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
override val expensiveToInline
|
||||||
@ -787,7 +806,7 @@ class VarDecl(val type: VarDeclType,
|
|||||||
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue=0.0, position=position)
|
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue=0.0, position=position)
|
||||||
else -> throw FatalAstException("can only set a default value for a numeric type")
|
else -> throw FatalAstException("can only set a default value for a numeric type")
|
||||||
}
|
}
|
||||||
val decl = VarDecl(type, declaredDatatype, zeropage, arraysize, name, constValue, isArray, position)
|
val decl = VarDecl(type, declaredDatatype, zeropage, arraysize, name, constValue, isArray, true, position)
|
||||||
if(parent!=null)
|
if(parent!=null)
|
||||||
decl.linkParents(parent)
|
decl.linkParents(parent)
|
||||||
return decl
|
return decl
|
||||||
@ -1665,7 +1684,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
?: throw UndefinedSymbolError(this)
|
?: throw UndefinedSymbolError(this)
|
||||||
val vardecl = node as? VarDecl
|
val vardecl = node as? VarDecl
|
||||||
if(vardecl==null) {
|
if(vardecl==null) {
|
||||||
throw ExpressionError("name must be a constant, instead of: ${node::class.simpleName}", position)
|
return null
|
||||||
} else if(vardecl.type!=VarDeclType.CONST) {
|
} else if(vardecl.type!=VarDeclType.CONST) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -2115,6 +2134,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
|
|||||||
it.identifier().text,
|
it.identifier().text,
|
||||||
null,
|
null,
|
||||||
it.ARRAYSIG()!=null || it.arrayindex()!=null,
|
it.ARRAYSIG()!=null || it.arrayindex()!=null,
|
||||||
|
false,
|
||||||
it.toPosition())
|
it.toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2127,6 +2147,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
|
|||||||
vd.identifier().text,
|
vd.identifier().text,
|
||||||
it.expression().toAst(),
|
it.expression().toAst(),
|
||||||
vd.ARRAYSIG()!=null || vd.arrayindex()!=null,
|
vd.ARRAYSIG()!=null || vd.arrayindex()!=null,
|
||||||
|
false,
|
||||||
it.toPosition())
|
it.toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2140,6 +2161,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
|
|||||||
vd.identifier().text,
|
vd.identifier().text,
|
||||||
cvarinit.expression().toAst(),
|
cvarinit.expression().toAst(),
|
||||||
vd.ARRAYSIG()!=null || vd.arrayindex()!=null,
|
vd.ARRAYSIG()!=null || vd.arrayindex()!=null,
|
||||||
|
false,
|
||||||
cvarinit.toPosition())
|
cvarinit.toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2153,6 +2175,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
|
|||||||
vd.identifier().text,
|
vd.identifier().text,
|
||||||
mvarinit.expression().toAst(),
|
mvarinit.expression().toAst(),
|
||||||
vd.ARRAYSIG()!=null || vd.arrayindex()!=null,
|
vd.ARRAYSIG()!=null || vd.arrayindex()!=null,
|
||||||
|
false,
|
||||||
mvarinit.toPosition())
|
mvarinit.toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +90,7 @@ private class AstChecker(private val program: Program,
|
|||||||
is InlineAssembly -> true
|
is InlineAssembly -> true
|
||||||
is INameScope -> true
|
is INameScope -> true
|
||||||
is VariableInitializationAssignment -> true
|
is VariableInitializationAssignment -> true
|
||||||
|
is NopStatement -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
@ -138,7 +138,8 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
|
|||||||
subroutine.parameters
|
subroutine.parameters
|
||||||
.filter { it.name !in namesInSub }
|
.filter { it.name !in namesInSub }
|
||||||
.forEach {
|
.forEach {
|
||||||
val vardecl = VarDecl(VarDeclType.VAR, it.type, false, null, it.name, null, false, subroutine.position)
|
val vardecl = VarDecl(VarDeclType.VAR, it.type, false, null, it.name, null,
|
||||||
|
isArray = false, autoGenerated = true, position = subroutine.position)
|
||||||
vardecl.linkParents(subroutine)
|
vardecl.linkParents(subroutine)
|
||||||
subroutine.statements.add(0, vardecl)
|
subroutine.statements.add(0, vardecl)
|
||||||
}
|
}
|
||||||
@ -176,7 +177,8 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
|
|||||||
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first())
|
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first())
|
||||||
if(existing==null) {
|
if(existing==null) {
|
||||||
// create the local scoped for loop variable itself
|
// create the local scoped for loop variable itself
|
||||||
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, forLoop.zeropage, null, varName, null, false, forLoop.loopVar.position)
|
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, forLoop.zeropage, null, varName, null,
|
||||||
|
isArray = false, autoGenerated = true, position = forLoop.loopVar.position)
|
||||||
vardecl.linkParents(forLoop.body)
|
vardecl.linkParents(forLoop.body)
|
||||||
forLoop.body.statements.add(0, vardecl)
|
forLoop.body.statements.add(0, vardecl)
|
||||||
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
||||||
@ -188,7 +190,8 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
|
|||||||
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(listOf(ForLoop.iteratorLoopcounterVarname), forLoop.body.statements.first())
|
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(listOf(ForLoop.iteratorLoopcounterVarname), forLoop.body.statements.first())
|
||||||
if(existing==null) {
|
if(existing==null) {
|
||||||
// create loop iteration counter variable (without value, to avoid an assignment)
|
// create loop iteration counter variable (without value, to avoid an assignment)
|
||||||
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, true, null, ForLoop.iteratorLoopcounterVarname, null, false, forLoop.loopVar.position)
|
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, true, null, ForLoop.iteratorLoopcounterVarname, null,
|
||||||
|
isArray = false, autoGenerated = true, position = forLoop.loopVar.position)
|
||||||
vardecl.linkParents(forLoop.body)
|
vardecl.linkParents(forLoop.body)
|
||||||
forLoop.body.statements.add(0, vardecl)
|
forLoop.body.statements.add(0, vardecl)
|
||||||
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
||||||
@ -236,7 +239,8 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
|
|||||||
if(literalValue.heapId!=null && literalValue.parent !is VarDecl) {
|
if(literalValue.heapId!=null && literalValue.parent !is VarDecl) {
|
||||||
// a literal value that's not declared as a variable, which refers to something on the heap.
|
// a literal value that's not declared as a variable, which refers to something on the heap.
|
||||||
// we need to introduce an auto-generated variable for this to be able to refer to the value!
|
// we need to introduce an auto-generated variable for this to be able to refer to the value!
|
||||||
val variable = VarDecl(VarDeclType.VAR, literalValue.type, false, null, "$autoHeapValuePrefix${literalValue.heapId}", literalValue, false, literalValue.position)
|
val variable = VarDecl(VarDeclType.VAR, literalValue.type, false, null, "$autoHeapValuePrefix${literalValue.heapId}", literalValue,
|
||||||
|
isArray = false, autoGenerated = false, position = literalValue.position)
|
||||||
anonymousVariablesFromHeap[variable.name] = Pair(literalValue, variable)
|
anonymousVariablesFromHeap[variable.name] = Pair(literalValue, variable)
|
||||||
}
|
}
|
||||||
return super.process(literalValue)
|
return super.process(literalValue)
|
||||||
|
@ -412,7 +412,8 @@ private class VarInitValueAndAddressOfCreator(private val namespace: INameScope)
|
|||||||
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
||||||
arglist[argparam.first.index] = pointerExpr
|
arglist[argparam.first.index] = pointerExpr
|
||||||
// add a vardecl so that the autovar can be resolved in later lookups
|
// add a vardecl so that the autovar can be resolved in later lookups
|
||||||
val variable = VarDecl(VarDeclType.VAR, strvalue.type, false, null, autoVarName, strvalue, false, strvalue.position)
|
val variable = VarDecl(VarDeclType.VAR, strvalue.type, false, null, autoVarName, strvalue,
|
||||||
|
isArray = false, autoGenerated = false, position=strvalue.position)
|
||||||
addVarDecl(strvalue.definingScope(), variable)
|
addVarDecl(strvalue.definingScope(), variable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,9 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
|
|||||||
runtimeVariables.define(program.namespace, Register.Y.name, RuntimeValue(DataType.UBYTE, 0))
|
runtimeVariables.define(program.namespace, Register.Y.name, RuntimeValue(DataType.UBYTE, 0))
|
||||||
|
|
||||||
val globalpos = Position("<<global>>", 0, 0, 0)
|
val globalpos = Position("<<global>>", 0, 0, 0)
|
||||||
val vdA = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, Register.A.name, LiteralValue.optimalInteger(0, globalpos), false, globalpos)
|
val vdA = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, Register.A.name, LiteralValue.optimalInteger(0, globalpos), isArray = false, autoGenerated = true, position = globalpos)
|
||||||
val vdX = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, Register.X.name, LiteralValue.optimalInteger(255, globalpos), false, globalpos)
|
val vdX = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, Register.X.name, LiteralValue.optimalInteger(255, globalpos), isArray = false, autoGenerated = true, position = globalpos)
|
||||||
val vdY = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, Register.Y.name, LiteralValue.optimalInteger(0, globalpos), false, globalpos)
|
val vdY = VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, Register.Y.name, LiteralValue.optimalInteger(0, globalpos), isArray = false, autoGenerated = true, position = globalpos)
|
||||||
vdA.linkParents(program.namespace)
|
vdA.linkParents(program.namespace)
|
||||||
vdX.linkParents(program.namespace)
|
vdX.linkParents(program.namespace)
|
||||||
vdY.linkParents(program.namespace)
|
vdY.linkParents(program.namespace)
|
||||||
|
@ -6,10 +6,11 @@ import prog8.compiler.loadAsmIncludeFile
|
|||||||
|
|
||||||
class CallGraph(private val program: Program): IAstProcessor {
|
class CallGraph(private val program: Program): IAstProcessor {
|
||||||
|
|
||||||
private val modulesImporting = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val modulesImporting = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
private val modulesImportedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val modulesImportedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
private val subroutinesCalling = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
|
val subroutinesCalling = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
|
||||||
private val subroutinesCalledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
val subroutinesCalledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
||||||
|
val usedSymbols = mutableSetOf<IStatement>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
process(program)
|
process(program)
|
||||||
@ -51,6 +52,15 @@ class CallGraph(private val program: Program): IAstProcessor {
|
|||||||
rootmodule.importedBy.add(rootmodule) // don't discard root module
|
rootmodule.importedBy.add(rootmodule) // don't discard root module
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun process(block: Block): IStatement {
|
||||||
|
if(block.definingModule().isLibraryModule) {
|
||||||
|
// make sure the block is not removed
|
||||||
|
addNodeAndParentScopes(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.process(block)
|
||||||
|
}
|
||||||
|
|
||||||
override fun process(directive: Directive): IStatement {
|
override fun process(directive: Directive): IStatement {
|
||||||
val thisModule = directive.definingModule()
|
val thisModule = directive.definingModule()
|
||||||
if(directive.directive=="%import") {
|
if(directive.directive=="%import") {
|
||||||
@ -66,6 +76,43 @@ class CallGraph(private val program: Program): IAstProcessor {
|
|||||||
return super.process(directive)
|
return super.process(directive)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun process(identifier: IdentifierReference): IExpression {
|
||||||
|
// track symbol usage
|
||||||
|
val target = identifier.targetStatement(this.program.namespace)
|
||||||
|
if(target!=null) {
|
||||||
|
addNodeAndParentScopes(target)
|
||||||
|
}
|
||||||
|
return super.process(identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addNodeAndParentScopes(stmt: IStatement) {
|
||||||
|
usedSymbols.add(stmt)
|
||||||
|
var node: Node=stmt
|
||||||
|
do {
|
||||||
|
if(node is INameScope && node is IStatement) {
|
||||||
|
usedSymbols.add(node)
|
||||||
|
}
|
||||||
|
node=node.parent
|
||||||
|
} while (node !is Module && node !is ParentSentinel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun process(subroutine: Subroutine): IStatement {
|
||||||
|
if((subroutine.name=="start" && subroutine.definingScope().name=="main")
|
||||||
|
|| subroutine.name==initvarsSubName || subroutine.definingModule().isLibraryModule) {
|
||||||
|
// make sure the entrypoint is mentioned in the used symbols
|
||||||
|
addNodeAndParentScopes(subroutine)
|
||||||
|
}
|
||||||
|
return super.process(subroutine)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun process(decl: VarDecl): IStatement {
|
||||||
|
if(decl.autoGenerated || (decl.definingModule().isLibraryModule && decl.type!=VarDeclType.VAR)) {
|
||||||
|
// make sure autogenerated vardecls are in the used symbols
|
||||||
|
addNodeAndParentScopes(decl)
|
||||||
|
}
|
||||||
|
return super.process(decl)
|
||||||
|
}
|
||||||
|
|
||||||
override fun process(functionCall: FunctionCall): IExpression {
|
override fun process(functionCall: FunctionCall): IExpression {
|
||||||
val otherSub = functionCall.target.targetSubroutine(program.namespace)
|
val otherSub = functionCall.target.targetSubroutine(program.namespace)
|
||||||
if(otherSub!=null) {
|
if(otherSub!=null) {
|
||||||
|
@ -8,7 +8,6 @@ import kotlin.math.floor
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
todo: subroutines with 1 or 2 byte args or 1 word arg can be converted to asm sub calling convention (args in registers)
|
todo: subroutines with 1 or 2 byte args or 1 word arg can be converted to asm sub calling convention (args in registers)
|
||||||
todo: implement usage counters for variables (locals and heap), blocks. Remove if count is zero.
|
|
||||||
todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to) + print warning about this
|
todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to) + print warning about this
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -18,13 +17,13 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
var scopesToFlatten = mutableListOf<INameScope>()
|
var scopesToFlatten = mutableListOf<INameScope>()
|
||||||
|
|
||||||
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
|
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
|
||||||
|
private val callgraph = CallGraph(program)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private var generatedLabelSequenceNumber = 0
|
private var generatedLabelSequenceNumber = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun process(program: Program) {
|
override fun process(program: Program) {
|
||||||
val callgraph = CallGraph(program)
|
|
||||||
removeUnusedCode(callgraph)
|
removeUnusedCode(callgraph)
|
||||||
if(optimizeInlining) {
|
if(optimizeInlining) {
|
||||||
inlineSubroutines(callgraph)
|
inlineSubroutines(callgraph)
|
||||||
@ -90,8 +89,6 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun removeUnusedCode(callgraph: CallGraph) {
|
private fun removeUnusedCode(callgraph: CallGraph) {
|
||||||
// TODO remove unused variables (local and global)
|
|
||||||
|
|
||||||
// remove all subroutines that aren't called, or are empty
|
// remove all subroutines that aren't called, or are empty
|
||||||
val removeSubroutines = mutableSetOf<Subroutine>()
|
val removeSubroutines = mutableSetOf<Subroutine>()
|
||||||
val entrypoint = program.entrypoint()
|
val entrypoint = program.entrypoint()
|
||||||
@ -109,9 +106,8 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
}
|
}
|
||||||
|
|
||||||
val removeBlocks = mutableSetOf<Block>()
|
val removeBlocks = mutableSetOf<Block>()
|
||||||
// TODO remove blocks that have no incoming references
|
|
||||||
program.modules.flatMap { it.statements }.filterIsInstance<Block>().forEach { block ->
|
program.modules.flatMap { it.statements }.filterIsInstance<Block>().forEach { block ->
|
||||||
if (block.containsNoCodeNorVars())
|
if (block.containsNoCodeNorVars() && "force_output" !in block.options())
|
||||||
removeBlocks.add(block)
|
removeBlocks.add(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +115,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
removeBlocks.forEach { it.definingScope().remove(it) }
|
removeBlocks.forEach { it.definingScope().remove(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove modules that are not imported, or are empty
|
// remove modules that are not imported, or are empty (unless it's a library modules)
|
||||||
val removeModules = mutableSetOf<Module>()
|
val removeModules = mutableSetOf<Module>()
|
||||||
program.modules.forEach {
|
program.modules.forEach {
|
||||||
if (!it.isLibraryModule && (it.importedBy.isEmpty() || it.containsNoCodeNorVars()))
|
if (!it.isLibraryModule && (it.importedBy.isEmpty() || it.containsNoCodeNorVars()))
|
||||||
@ -127,24 +123,34 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (removeModules.isNotEmpty()) {
|
if (removeModules.isNotEmpty()) {
|
||||||
println("[debug] removing ${removeModules.size} empty/unused modules")
|
|
||||||
program.modules.removeAll(removeModules)
|
program.modules.removeAll(removeModules)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun process(block: Block): IStatement {
|
override fun process(block: Block): IStatement {
|
||||||
if(block.containsNoCodeNorVars()) {
|
if("force_output" !in block.options()) {
|
||||||
optimizationsDone++
|
if (block.containsNoCodeNorVars()) {
|
||||||
return NopStatement(block.position)
|
optimizationsDone++
|
||||||
|
printWarning("removing empty block '${block.name}'", block.position)
|
||||||
|
return NopStatement(block.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block !in callgraph.usedSymbols) {
|
||||||
|
optimizationsDone++
|
||||||
|
printWarning("removing unused block '${block.name}'", block.position)
|
||||||
|
return NopStatement(block.position) // remove unused block
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.process(block)
|
return super.process(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun process(subroutine: Subroutine): IStatement {
|
override fun process(subroutine: Subroutine): IStatement {
|
||||||
super.process(subroutine)
|
super.process(subroutine)
|
||||||
|
val forceOutput = "force_output" in subroutine.definingBlock().options()
|
||||||
if(subroutine.asmAddress==null) {
|
if(subroutine.asmAddress==null && !forceOutput) {
|
||||||
if(subroutine.containsNoCodeNorVars()) {
|
if(subroutine.containsNoCodeNorVars()) {
|
||||||
|
printWarning("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(subroutine.position)
|
return NopStatement(subroutine.position)
|
||||||
}
|
}
|
||||||
@ -167,9 +173,27 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
||||||
|
printWarning("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
||||||
|
optimizationsDone++
|
||||||
|
return NopStatement(subroutine.position) // remove unused subroutine
|
||||||
|
}
|
||||||
|
|
||||||
return subroutine
|
return subroutine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun process(decl: VarDecl): IStatement {
|
||||||
|
val forceOutput = "force_output" in decl.definingBlock().options()
|
||||||
|
if(decl !in callgraph.usedSymbols && !forceOutput) {
|
||||||
|
if(decl.type!=VarDeclType.CONST)
|
||||||
|
printWarning("removing unused variable '${decl.name}'", decl.position)
|
||||||
|
optimizationsDone++
|
||||||
|
return NopStatement(decl.position) // remove unused variable
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.process(decl)
|
||||||
|
}
|
||||||
|
|
||||||
private fun deduplicateAssignments(statements: List<IStatement>): MutableList<Int> {
|
private fun deduplicateAssignments(statements: List<IStatement>): MutableList<Int> {
|
||||||
// removes 'duplicate' assignments that assign the isSameAs target
|
// removes 'duplicate' assignments that assign the isSameAs target
|
||||||
val linesToRemove = mutableListOf<Int>()
|
val linesToRemove = mutableListOf<Int>()
|
||||||
|
@ -6,21 +6,19 @@
|
|||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
foo(1)
|
ubyte x = 99
|
||||||
}
|
|
||||||
|
|
||||||
sub foo(ubyte param1) {
|
return
|
||||||
|
|
||||||
sub subsub() {
|
startqqq:
|
||||||
|
|
||||||
}
|
sub startzzz() {
|
||||||
|
if_cc goto startqqq
|
||||||
sub param1() {
|
c64.EXTCOL++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
; for ubyte y in 0 to 3 {
|
; for ubyte y in 0 to 3 {
|
||||||
; for ubyte x in 0 to 10 {
|
; for ubyte x in 0 to 10 {
|
||||||
; ubyte product = x*y
|
; ubyte product = x*y
|
||||||
|
Loading…
x
Reference in New Issue
Block a user