refactoring unused code removal and noModification

This commit is contained in:
Irmen de Jong 2021-04-04 15:52:10 +02:00
parent 49036abbaf
commit 374e2b311d
21 changed files with 197 additions and 275 deletions

View File

@ -14,8 +14,6 @@ import prog8.compiler.target.ICompilationTarget
internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() { internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
private val noModifications = emptyList<IAstModification>()
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
subroutineVariables.add(decl.name to decl) subroutineVariables.add(decl.name to decl)
if (decl.value == null && !decl.autogeneratedDontRemove && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) { if (decl.value == null && !decl.autogeneratedDontRemove && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {

View File

@ -264,11 +264,16 @@ private fun processAst(programAst: Program, errors: IErrorReporter, compilerOpti
private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget, options: CompilationOptions) { private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget, options: CompilationOptions) {
// optimize the parse tree // optimize the parse tree
println("Optimizing...") println("Optimizing...")
val remover = UnusedCodeRemover(programAst, errors, compTarget, ::loadAsmIncludeFile)
remover.visit(programAst)
remover.applyModifications()
while (true) { while (true) {
// keep optimizing expressions and statements until no more steps remain // keep optimizing expressions and statements until no more steps remain
val optsDone1 = programAst.simplifyExpressions() val optsDone1 = programAst.simplifyExpressions()
val optsDone2 = programAst.splitBinaryExpressions(compTarget) val optsDone2 = programAst.splitBinaryExpressions(compTarget)
val optsDone3 = programAst.optimizeStatements(errors, functions, compTarget, ::loadAsmIncludeFile) val optsDone3 = programAst.optimizeStatements(errors, functions, compTarget)
programAst.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away programAst.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
errors.report() errors.report()
if (optsDone1 + optsDone2 + optsDone3 == 0) if (optsDone1 + optsDone2 + optsDone3 == 0)
@ -281,9 +286,9 @@ private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions:
if(errors.noErrors()) { if(errors.noErrors()) {
inliner.applyModifications() inliner.applyModifications()
inliner.fixCallsToInlinedSubroutines() inliner.fixCallsToInlinedSubroutines()
val remover = UnusedCodeRemover(programAst, errors, compTarget, ::loadAsmIncludeFile) val remover2 = UnusedCodeRemover(programAst, errors, compTarget, ::loadAsmIncludeFile)
remover.visit(programAst) remover2.visit(programAst)
remover.applyModifications() remover2.applyModifications()
} }
errors.report() errors.report()

View File

@ -14,7 +14,6 @@ import prog8.ast.walk.IAstModification
internal class AstVariousTransforms(private val program: Program) : AstWalker() { internal class AstVariousTransforms(private val program: Program) : AstWalker() {
private val noModifications = emptyList<IAstModification>()
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
// is it a struct variable? then define all its struct members as mangled names, // is it a struct variable? then define all its struct members as mangled names,

View File

@ -13,7 +13,6 @@ import prog8.ast.walk.IAstModification
internal class LiteralsToAutoVars(private val program: Program) : AstWalker() { internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
private val noModifications = emptyList<IAstModification>()
override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> { override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
if(string.parent !is VarDecl && string.parent !is WhenChoice) { if(string.parent !is VarDecl && string.parent !is WhenChoice) {

View File

@ -26,7 +26,6 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
// - sorts the choices in when statement. // - sorts the choices in when statement.
// - insert AddressOf (&) expression where required (string params to a UWORD function param etc). // - insert AddressOf (&) expression where required (string params to a UWORD function param etc).
private val noModifications = emptyList<IAstModification>()
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option") private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
override fun after(module: Module, parent: Node): Iterable<IAstModification> { override fun after(module: Module, parent: Node): Iterable<IAstModification> {

View File

@ -18,8 +18,6 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk
* (this includes function call arguments) * (this includes function call arguments)
*/ */
private val noModifications = emptyList<IAstModification>()
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
val declValue = decl.value val declValue = decl.value
if(decl.type==VarDeclType.VAR && declValue!=null && decl.struct==null) { if(decl.type==VarDeclType.VAR && declValue!=null && decl.struct==null) {

View File

@ -13,7 +13,6 @@ import prog8.compiler.IErrorReporter
internal class VariousCleanups(val errors: IErrorReporter): AstWalker() { internal class VariousCleanups(val errors: IErrorReporter): AstWalker() {
private val noModifications = emptyList<IAstModification>()
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> { override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
return listOf(IAstModification.Remove(nopStatement, parent as INameScope)) return listOf(IAstModification.Remove(nopStatement, parent as INameScope))

View File

@ -12,7 +12,6 @@ import prog8.compiler.target.ICompilationTarget
internal class BinExprSplitter(private val program: Program, private val compTarget: ICompilationTarget) : AstWalker() { internal class BinExprSplitter(private val program: Program, private val compTarget: ICompilationTarget) : AstWalker() {
private val noModifications = emptyList<IAstModification>()
// override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { // override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
// TODO somehow if we do this, the resulting code for some programs (cube3d.p8) gets hundreds of bytes larger...: // TODO somehow if we do this, the resulting code for some programs (cube3d.p8) gets hundreds of bytes larger...:

View File

@ -1,24 +1,16 @@
package prog8.optimizer package prog8.optimizer
import prog8.ast.INameScope
import prog8.ast.Module import prog8.ast.Module
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.DataType
import prog8.ast.base.ParentSentinel
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.expressions.AddressOf import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.FunctionCall import prog8.ast.expressions.FunctionCall
import prog8.ast.expressions.IdentifierReference
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.compiler.IErrorReporter import prog8.compiler.IErrorReporter
import java.nio.file.Path import java.nio.file.Path
private val alwaysKeepSubroutines = setOf(
Pair("main", "start")
)
private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr|bra)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE) 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) private val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
@ -30,25 +22,10 @@ class CallGraph(private val program: Program, private val asmFileLoader: (filena
val calls = mutableMapOf<Subroutine, List<Subroutine>>().withDefault { mutableListOf() } val calls = mutableMapOf<Subroutine, List<Subroutine>>().withDefault { mutableListOf() }
val calledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() } val calledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
// TODO add dataflow graph: what statements use what variables - can be used to eliminate unused vars
val usedSymbols = mutableSetOf<Statement>()
init { init {
visit(program) visit(program)
} }
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 visit(program: Program) { override fun visit(program: Program) {
super.visit(program) super.visit(program)
@ -64,15 +41,6 @@ class CallGraph(private val program: Program, private val asmFileLoader: (filena
rootmodule.importedBy.add(rootmodule) // don't discard root module rootmodule.importedBy.add(rootmodule) // don't discard root module
} }
override fun visit(block: Block) {
if (block.definingModule().isLibraryModule) {
// make sure the block is not removed
addNodeAndParentScopes(block)
}
super.visit(block)
}
override fun visit(directive: Directive) { override fun visit(directive: Directive) {
val thisModule = directive.definingModule() val thisModule = directive.definingModule()
if (directive.directive == "%import") { if (directive.directive == "%import") {
@ -90,44 +58,6 @@ class CallGraph(private val program: Program, private val asmFileLoader: (filena
super.visit(directive) super.visit(directive)
} }
override fun visit(identifier: IdentifierReference) {
// track symbol usage
val target = identifier.targetStatement(program)
if (target != null) {
addNodeAndParentScopes(target)
}
super.visit(identifier)
}
private fun addNodeAndParentScopes(stmt: Statement) {
usedSymbols.add(stmt)
var node: Node = stmt
do {
if (node is INameScope && node is Statement) {
usedSymbols.add(node)
}
node = node.parent
} while (node !is Module && node !is ParentSentinel)
}
override fun visit(subroutine: Subroutine) {
if (Pair(subroutine.definingScope().name, subroutine.name) in alwaysKeepSubroutines
|| subroutine.definingModule().isLibraryModule) {
// make sure the entrypoint is mentioned in the used symbols
addNodeAndParentScopes(subroutine)
}
super.visit(subroutine)
}
override fun visit(decl: VarDecl) {
if (decl.autogeneratedDontRemove || decl.datatype==DataType.STRUCT)
addNodeAndParentScopes(decl)
else if(decl.parent is Block && decl.definingModule().isLibraryModule)
addNodeAndParentScopes(decl)
super.visit(decl)
}
override fun visit(functionCall: FunctionCall) { override fun visit(functionCall: FunctionCall) {
val otherSub = functionCall.target.targetSubroutine(program) val otherSub = functionCall.target.targetSubroutine(program)
if (otherSub != null) { if (otherSub != null) {
@ -172,11 +102,6 @@ class CallGraph(private val program: Program, private val asmFileLoader: (filena
super.visit(jump) super.visit(jump)
} }
override fun visit(structDecl: StructDecl) {
usedSymbols.add(structDecl)
usedSymbols.addAll(structDecl.statements)
}
override fun visit(inlineAssembly: InlineAssembly) { override fun visit(inlineAssembly: InlineAssembly) {
// parse inline asm for subroutine calls (jmp, jsr, bra) // parse inline asm for subroutine calls (jmp, jsr, bra)
val scope = inlineAssembly.definingSubroutine() val scope = inlineAssembly.definingSubroutine()
@ -274,4 +199,14 @@ class CallGraph(private val program: Program, private val asmFileLoader: (filena
recStack[sub] = false recStack[sub] = false
return false return false
} }
fun unused(stmt: ISymbolStatement): Boolean {
if(stmt is Subroutine)
return calledBy[stmt].isNullOrEmpty() // TODO also check inline assembly if it uses the subroutine
// TODO implement algorithm to check usages of other things:
// stmt can be: Block, Label, VarDecl (including ParameterVarDecl), Subroutine, StructDecl.
// NOTE also check inline assembly blocks that may reference a name.
return false
}
} }

View File

@ -14,7 +14,6 @@ import kotlin.math.pow
internal class ConstantFoldingOptimizer(private val program: Program, private val compTarget: ICompilationTarget) : AstWalker() { internal class ConstantFoldingOptimizer(private val program: Program, private val compTarget: ICompilationTarget) : AstWalker() {
private val noModifications = emptyList<IAstModification>()
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> { override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
// @( &thing ) --> thing // @( &thing ) --> thing

View File

@ -15,7 +15,6 @@ import prog8.compiler.target.ICompilationTarget
// Fix up the literal value's type to match that of the vardecl // Fix up the literal value's type to match that of the vardecl
internal class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() { internal class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
private val noModifications = emptyList<IAstModification>()
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
try { try {
@ -40,7 +39,6 @@ internal class VarConstantValueTypeAdjuster(private val program: Program, privat
// and the array var initializer values and sizes. // and the array var initializer values and sizes.
// This is needed because further constant optimizations depend on those. // This is needed because further constant optimizations depend on those.
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() { internal class ConstantIdentifierReplacer(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
private val noModifications = emptyList<IAstModification>()
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> { override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
// replace identifiers that refer to const value, with the value itself // replace identifiers that refer to const value, with the value itself

View File

@ -25,7 +25,6 @@ import kotlin.math.pow
internal class ExpressionSimplifier(private val program: Program) : AstWalker() { internal class ExpressionSimplifier(private val program: Program) : AstWalker() {
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet() private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet() private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
private val noModifications = emptyList<IAstModification>()
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> { override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
val mods = mutableListOf<IAstModification>() val mods = mutableListOf<IAstModification>()

View File

@ -4,7 +4,6 @@ import prog8.ast.IBuiltinFunctions
import prog8.ast.Program import prog8.ast.Program
import prog8.compiler.IErrorReporter import prog8.compiler.IErrorReporter
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
import java.nio.file.Path
internal fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) { internal fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) {
@ -43,9 +42,8 @@ internal fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilati
internal fun Program.optimizeStatements(errors: IErrorReporter, internal fun Program.optimizeStatements(errors: IErrorReporter,
functions: IBuiltinFunctions, functions: IBuiltinFunctions,
compTarget: ICompilationTarget, compTarget: ICompilationTarget): Int {
asmFileLoader: (filename: String, source: Path)->String): Int { val optimizer = StatementOptimizer(this, errors, functions, compTarget)
val optimizer = StatementOptimizer(this, errors, functions, compTarget, asmFileLoader)
optimizer.visit(this) optimizer.visit(this)
val optimizationCount = optimizer.applyModifications() val optimizationCount = optimizer.applyModifications()

View File

@ -12,7 +12,6 @@ import prog8.ast.walk.IAstModification
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.compiler.IErrorReporter import prog8.compiler.IErrorReporter
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
import java.nio.file.Path
import kotlin.math.floor import kotlin.math.floor
internal const val retvarName = "prog8_retval" internal const val retvarName = "prog8_retval"
@ -21,29 +20,10 @@ internal const val retvarName = "prog8_retval"
internal class StatementOptimizer(private val program: Program, internal class StatementOptimizer(private val program: Program,
private val errors: IErrorReporter, private val errors: IErrorReporter,
private val functions: IBuiltinFunctions, private val functions: IBuiltinFunctions,
private val compTarget: ICompilationTarget, private val compTarget: ICompilationTarget) : AstWalker() {
asmFileLoader: (filename: String, source: Path)->String
) : AstWalker() {
private val noModifications = emptyList<IAstModification>()
private val callgraph = CallGraph(program, asmFileLoader)
private val subsThatNeedReturnVariable = mutableSetOf<Triple<INameScope, DataType, Position>>() private val subsThatNeedReturnVariable = mutableSetOf<Triple<INameScope, DataType, Position>>()
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
if("force_output" !in block.options()) {
if (block.containsNoCodeNorVars()) {
if(block.name != program.internedStringsModuleName)
errors.warn("removing empty block '${block.name}'", block.position)
return listOf(IAstModification.Remove(block, parent as INameScope))
}
if (block !in callgraph.usedSymbols) {
errors.warn("removing unused block '${block.name}'", block.position)
return listOf(IAstModification.Remove(block, parent as INameScope))
}
}
return noModifications
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> { override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
for(returnvar in subsThatNeedReturnVariable) { for(returnvar in subsThatNeedReturnVariable) {
@ -51,38 +31,6 @@ internal class StatementOptimizer(private val program: Program,
returnvar.first.statements.add(0, decl) returnvar.first.statements.add(0, decl)
} }
subsThatNeedReturnVariable.clear() subsThatNeedReturnVariable.clear()
val forceOutput = "force_output" in subroutine.definingBlock().options()
if(subroutine.asmAddress==null && !forceOutput) {
if(subroutine.containsNoCodeNorVars() && !subroutine.inline) {
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
val removals = callgraph.calledBy.getValue(subroutine).map {
IAstModification.Remove(it, it.definingScope())
}.toMutableList()
removals += IAstModification.Remove(subroutine, subroutine.definingScope())
return removals
}
}
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
if(!subroutine.isAsmSubroutine) {
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope()))
}
}
return noModifications
}
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
val forceOutput = "force_output" in decl.definingBlock().options()
if(decl !in callgraph.usedSymbols && !forceOutput) {
if(decl.type == VarDeclType.VAR)
errors.warn("removing unused variable '${decl.name}'", decl.position)
return listOf(IAstModification.Remove(decl, decl.definingScope()))
}
return noModifications return noModifications
} }

View File

@ -4,8 +4,12 @@ import prog8.ast.IFunctionCall
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.expressions.* import prog8.ast.expressions.FunctionCall
import prog8.ast.statements.* import prog8.ast.expressions.IdentifierReference
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.statements.Return
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.compiler.CompilationOptions import prog8.compiler.CompilationOptions
@ -13,7 +17,6 @@ import prog8.compiler.IErrorReporter
internal class SubroutineInliner(private val program: Program, val errors: IErrorReporter, private val compilerOptions: CompilationOptions): AstWalker() { internal class SubroutineInliner(private val program: Program, val errors: IErrorReporter, private val compilerOptions: CompilationOptions): AstWalker() {
private val noModifications = emptyList<IAstModification>()
private var callsToInlinedSubroutines = mutableListOf<Pair<IFunctionCall, Node>>() private var callsToInlinedSubroutines = mutableListOf<Pair<IFunctionCall, Node>>()
fun fixCallsToInlinedSubroutines() { fun fixCallsToInlinedSubroutines() {

View File

@ -1,8 +1,10 @@
package prog8.optimizer package prog8.optimizer
import prog8.ast.INameScope import prog8.ast.INameScope
import prog8.ast.Module
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.BinaryExpression import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.FunctionCall import prog8.ast.expressions.FunctionCall
import prog8.ast.expressions.PrefixExpression import prog8.ast.expressions.PrefixExpression
@ -18,39 +20,17 @@ import java.nio.file.Path
internal class UnusedCodeRemover(private val program: Program, internal class UnusedCodeRemover(private val program: Program,
private val errors: IErrorReporter, private val errors: IErrorReporter,
private val compTarget: ICompilationTarget, private val compTarget: ICompilationTarget,
private val asmFileLoader: (filename: String, source: Path)->String): AstWalker() { asmFileLoader: (filename: String, source: Path)->String): AstWalker() {
override fun before(program: Program, parent: Node): Iterable<IAstModification> { private val callgraph = CallGraph(program, asmFileLoader)
val callgraph = CallGraph(program, asmFileLoader)
val removals = mutableListOf<IAstModification>()
// remove all subroutines that aren't called, or are empty override fun before(module: Module, parent: Node): Iterable<IAstModification> {
// NOTE: part of this is also done already in the StatementOptimizer return if (!module.isLibraryModule && (module.importedBy.isEmpty() || module.containsNoCodeNorVars()))
val entrypoint = program.entrypoint() listOf(IAstModification.Remove(module, module.definingScope()))
program.modules.forEach { else
callgraph.forAllSubroutines(it) { sub -> noModifications
val forceOutput = "force_output" in sub.definingBlock().options()
if (sub !== entrypoint && !forceOutput && !sub.isAsmSubroutine && (callgraph.calledBy[sub].isNullOrEmpty() || sub.containsNoCodeNorVars())) {
removals.add(IAstModification.Remove(sub, sub.definingScope()))
}
}
}
program.modules.flatMap { it.statements }.filterIsInstance<Block>().forEach { block ->
if (block.containsNoCodeNorVars() && "force_output" !in block.options())
removals.add(IAstModification.Remove(block, block.definingScope()))
}
// remove modules that are not imported, or are empty (unless it's a library modules)
program.modules.forEach {
if (!it.isLibraryModule && (it.importedBy.isEmpty() || it.containsNoCodeNorVars()))
removals.add(IAstModification.Remove(it, it.definingScope()))
}
return removals
} }
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> { override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
reportUnreachable(breakStmt, parent as INameScope) reportUnreachable(breakStmt, parent as INameScope)
return emptyList() return emptyList()
@ -85,15 +65,58 @@ internal class UnusedCodeRemover(private val program: Program,
} }
override fun after(block: Block, parent: Node): Iterable<IAstModification> { override fun after(block: Block, parent: Node): Iterable<IAstModification> {
if("force_output" !in block.options()) {
if (block.containsNoCodeNorVars()) {
if(block.name != program.internedStringsModuleName)
errors.warn("removing unused block '${block.name}'", block.position)
return listOf(IAstModification.Remove(block, parent as INameScope))
}
if(callgraph.unused(block)) {
errors.warn("removing unused block '${block.name}'", block.position)
return listOf(IAstModification.Remove(block, parent as INameScope))
}
}
val removeDoubleAssignments = deduplicateAssignments(block.statements) val removeDoubleAssignments = deduplicateAssignments(block.statements)
return removeDoubleAssignments.map { IAstModification.Remove(it, block) } return removeDoubleAssignments.map { IAstModification.Remove(it, block) }
} }
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> { override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
val forceOutput = "force_output" in subroutine.definingBlock().options()
if (subroutine !== program.entrypoint() && !forceOutput && !subroutine.inline && !subroutine.isAsmSubroutine) {
if(callgraph.unused(subroutine)) {
if(!subroutine.definingModule().isLibraryModule)
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope()))
}
if(subroutine.containsNoCodeNorVars()) {
if(!subroutine.definingModule().isLibraryModule)
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
val removals = mutableListOf(IAstModification.Remove(subroutine, subroutine.definingScope()))
callgraph.calledBy[subroutine]?.let {
for(node in it)
removals.add(IAstModification.Remove(node, node.definingScope()))
}
return removals
}
}
val removeDoubleAssignments = deduplicateAssignments(subroutine.statements) val removeDoubleAssignments = deduplicateAssignments(subroutine.statements)
return removeDoubleAssignments.map { IAstModification.Remove(it, subroutine) } return removeDoubleAssignments.map { IAstModification.Remove(it, subroutine) }
} }
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
val forceOutput = "force_output" in decl.definingBlock().options()
if(!forceOutput && callgraph.unused(decl)) {
if(decl.type == VarDeclType.VAR)
errors.warn("removing unused variable '${decl.name}'", decl.position)
return listOf(IAstModification.Remove(decl, decl.definingScope()))
}
return noModifications
}
private fun deduplicateAssignments(statements: List<Statement>): List<Assignment> { private fun deduplicateAssignments(statements: List<Statement>): List<Assignment> {
// removes 'duplicate' assignments that assign the same target directly after another // removes 'duplicate' assignments that assign the same target directly after another
val linesToRemove = mutableListOf<Assignment>() val linesToRemove = mutableListOf<Assignment>()

View File

@ -7,6 +7,10 @@ import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
interface ISymbolStatement {
val name: String
}
sealed class Statement : Node { sealed class Statement : Node {
abstract fun accept(visitor: IAstVisitor) abstract fun accept(visitor: IAstVisitor)
abstract fun accept(visitor: AstWalker, parent: Node) abstract fun accept(visitor: AstWalker, parent: Node)
@ -48,7 +52,7 @@ class Block(override val name: String,
val address: Int?, val address: Int?,
override var statements: MutableList<Statement>, override var statements: MutableList<Statement>,
val isInLibrary: Boolean, val isInLibrary: Boolean,
override val position: Position) : Statement(), INameScope { override val position: Position) : Statement(), INameScope, ISymbolStatement {
override lateinit var parent: Node override lateinit var parent: Node
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
@ -95,7 +99,7 @@ data class DirectiveArg(val str: String?, val name: String?, val int: Int?, over
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here") override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
} }
data class Label(val name: String, override val position: Position) : Statement() { data class Label(override val name: String, override val position: Position) : Statement(), ISymbolStatement {
override lateinit var parent: Node override lateinit var parent: Node
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
@ -153,17 +157,16 @@ enum class ZeropageWish {
NOT_IN_ZEROPAGE NOT_IN_ZEROPAGE
} }
open class VarDecl(val type: VarDeclType, open class VarDecl(val type: VarDeclType,
private val declaredDatatype: DataType, private val declaredDatatype: DataType,
val zeropage: ZeropageWish, val zeropage: ZeropageWish,
var arraysize: ArrayIndex?, var arraysize: ArrayIndex?,
val name: String, override val name: String,
private val structName: String?, private val structName: String?,
var value: Expression?, var value: Expression?,
val isArray: Boolean, val isArray: Boolean,
val autogeneratedDontRemove: Boolean, val autogeneratedDontRemove: Boolean,
override val position: Position) : Statement() { override val position: Position) : Statement(), ISymbolStatement {
override lateinit var parent: Node override lateinit var parent: Node
var struct: StructDecl? = null // set later (because at parse time, we only know the name) var struct: StructDecl? = null // set later (because at parse time, we only know the name)
private set private set
@ -281,7 +284,6 @@ open class VarDecl(val type: VarDeclType,
class ParameterVarDecl(name: String, declaredDatatype: DataType, position: Position) class ParameterVarDecl(name: String, declaredDatatype: DataType, position: Position)
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, null, false, true, position) : VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, null, false, true, position)
class ArrayIndex(var indexExpr: Expression, class ArrayIndex(var indexExpr: Expression,
override val position: Position) : Node { override val position: Position) : Node {
override lateinit var parent: Node override lateinit var parent: Node
@ -484,7 +486,6 @@ data class AssignTarget(var identifier: IdentifierReference?,
fun copy() = AssignTarget(identifier?.copy(), arrayindexed?.copy(), memoryAddress?.copy(), position) fun copy() = AssignTarget(identifier?.copy(), arrayindexed?.copy(), memoryAddress?.copy(), position)
} }
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() { class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() {
override lateinit var parent: Node override lateinit var parent: Node
@ -611,7 +612,6 @@ class NopStatement(override val position: Position): Statement() {
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
} }
class AsmGenInfo { class AsmGenInfo {
// This class contains various attributes that influence the assembly code generator. // This class contains various attributes that influence the assembly code generator.
// Conceptually it should be part of any INameScope. // Conceptually it should be part of any INameScope.
@ -637,7 +637,7 @@ class Subroutine(override val name: String,
val isAsmSubroutine: Boolean, val isAsmSubroutine: Boolean,
val inline: Boolean, val inline: Boolean,
override var statements: MutableList<Statement>, override var statements: MutableList<Statement>,
override val position: Position) : Statement(), INameScope { override val position: Position) : Statement(), INameScope, ISymbolStatement {
constructor(name: String, parameters: List<SubroutineParameter>, returntypes: List<DataType>, statements: MutableList<Statement>, inline: Boolean, position: Position) constructor(name: String, parameters: List<SubroutineParameter>, returntypes: List<DataType>, statements: MutableList<Statement>, inline: Boolean, position: Position)
: this(name, parameters, returntypes, emptyList(), determineReturnRegisters(returntypes), emptySet(), null, false, inline, statements, position) : this(name, parameters, returntypes, emptyList(), determineReturnRegisters(returntypes), emptySet(), null, false, inline, statements, position)
@ -701,7 +701,6 @@ class Subroutine(override val name: String,
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it || " bra" in it || "\tbra" in it} .count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it || " bra" in it || "\tbra" in it}
} }
open class SubroutineParameter(val name: String, open class SubroutineParameter(val name: String,
val type: DataType, val type: DataType,
override val position: Position) : Node { override val position: Position) : Node {
@ -948,10 +947,9 @@ class WhenChoice(var values: MutableList<Expression>?, // if null, th
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
} }
class StructDecl(override val name: String, class StructDecl(override val name: String,
override var statements: MutableList<Statement>, // actually, only vardecls here override var statements: MutableList<Statement>, // actually, only vardecls here
override val position: Position): Statement(), INameScope { override val position: Position): Statement(), INameScope, ISymbolStatement {
override lateinit var parent: Node override lateinit var parent: Node

View File

@ -76,86 +76,88 @@ interface IAstModification {
abstract class AstWalker { abstract class AstWalker {
open fun before(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = emptyList() protected val noModifications = emptyList<IAstModification>()
open fun before(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(assignTarget: AssignTarget, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(block: Block, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(inlineAssembly: InlineAssembly, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(jump: Jump, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(label: Label, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(module: Module, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = emptyList() open fun before(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = noModifications
open fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> = emptyList() open fun before(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
open fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> = emptyList() open fun before(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> = noModifications
open fun after(assignTarget: AssignTarget, parent: Node): Iterable<IAstModification> = emptyList() open fun before(assignTarget: AssignTarget, parent: Node): Iterable<IAstModification> = noModifications
open fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> = emptyList() open fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> = noModifications
open fun after(block: Block, parent: Node): Iterable<IAstModification> = emptyList() open fun before(block: Block, parent: Node): Iterable<IAstModification> = noModifications
open fun after(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList() open fun before(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = noModifications
open fun after(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList() open fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> = noModifications
open fun after(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList() open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = noModifications
open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList() open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = noModifications
open fun after(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList() open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = noModifications
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList() open fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = noModifications
open fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList() open fun before(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = noModifications
open fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList() open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = noModifications
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList() open fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = noModifications
open fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList() open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = noModifications
open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList() open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = noModifications
open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList() open fun before(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> = noModifications
open fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> = emptyList() open fun before(inlineAssembly: InlineAssembly, parent: Node): Iterable<IAstModification> = noModifications
open fun after(inlineAssembly: InlineAssembly, parent: Node): Iterable<IAstModification> = emptyList() open fun before(jump: Jump, parent: Node): Iterable<IAstModification> = noModifications
open fun after(jump: Jump, parent: Node): Iterable<IAstModification> = emptyList() open fun before(label: Label, parent: Node): Iterable<IAstModification> = noModifications
open fun after(label: Label, parent: Node): Iterable<IAstModification> = emptyList() open fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> = noModifications
open fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> = emptyList() open fun before(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> = noModifications
open fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> = emptyList() open fun before(module: Module, parent: Node): Iterable<IAstModification> = noModifications
open fun after(module: Module, parent: Node): Iterable<IAstModification> = emptyList() open fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = noModifications
open fun after(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = emptyList() open fun before(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
open fun after(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = emptyList() open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = noModifications
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList() open fun before(program: Program, parent: Node): Iterable<IAstModification> = noModifications
open fun after(program: Program, parent: Node): Iterable<IAstModification> = emptyList() open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = noModifications
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList() open fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = noModifications
open fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = emptyList() open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = noModifications
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList() open fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = noModifications
open fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList() open fun before(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
open fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList() open fun before(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = noModifications
open fun after(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList() open fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = noModifications
open fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList() open fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = noModifications
open fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList() open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList() open fun before(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> = noModifications
open fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> = emptyList() open fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = noModifications
open fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
open fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> = noModifications
open fun after(assignTarget: AssignTarget, parent: Node): Iterable<IAstModification> = noModifications
open fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> = noModifications
open fun after(block: Block, parent: Node): Iterable<IAstModification> = noModifications
open fun after(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = noModifications
open fun after(breakStmt: Break, parent: Node): Iterable<IAstModification> = noModifications
open fun after(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = noModifications
open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = noModifications
open fun after(directive: Directive, parent: Node): Iterable<IAstModification> = noModifications
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = noModifications
open fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = noModifications
open fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = noModifications
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = noModifications
open fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = noModifications
open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = noModifications
open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = noModifications
open fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> = noModifications
open fun after(inlineAssembly: InlineAssembly, parent: Node): Iterable<IAstModification> = noModifications
open fun after(jump: Jump, parent: Node): Iterable<IAstModification> = noModifications
open fun after(label: Label, parent: Node): Iterable<IAstModification> = noModifications
open fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> = noModifications
open fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> = noModifications
open fun after(module: Module, parent: Node): Iterable<IAstModification> = noModifications
open fun after(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = noModifications
open fun after(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = noModifications
open fun after(program: Program, parent: Node): Iterable<IAstModification> = noModifications
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = noModifications
open fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = noModifications
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = noModifications
open fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = noModifications
open fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
open fun after(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = noModifications
open fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = noModifications
open fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = noModifications
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
open fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> = noModifications
open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
protected val modifications = mutableListOf<Triple<IAstModification, Node, Node>>() protected val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
// private val modificationsReplacedNodes = mutableSetOf<Pair<Node, Position>>() // private val modificationsReplacedNodes = mutableSetOf<Pair<Node, Position>>()

View File

@ -129,7 +129,7 @@ Directives
take care of that yourself. The program will just start running from whatever state the machine is in when the take care of that yourself. The program will just start running from whatever state the machine is in when the
program was launched. program was launched.
- ``force_output`` (in a block) will force the block to be outputted in the final program. - ``force_output`` (in a block) will force the block to be outputted in the final program.
Can be useful to make sure some data is generated that would otherwise be discarded because it's not referenced (such as sprite data). Can be useful to make sure some data is generated that would otherwise be discarded because the compiler thinks it's not referenced (such as sprite data)
- ``align_word`` (in a block) will make the assembler align the start address of this block on a word boundary in memory (so, an even memory address). - ``align_word`` (in a block) will make the assembler align the start address of this block on a word boundary in memory (so, an even memory address).
- ``align_page`` (in a block) will make the assembler align the start address of this block on a page boundary in memory (so, the LSB of the address is 0). - ``align_page`` (in a block) will make the assembler align the start address of this block on a page boundary in memory (so, the LSB of the address is 0).

View File

@ -2,6 +2,10 @@
TODO TODO
==== ====
- fix crash parent node mismatch with -noopt (textelite)
- implement new 'unused' in CallGraph for more node types
- allow inlining of subroutines with params - allow inlining of subroutines with params
- optimize several inner loops in gfx2 - optimize several inner loops in gfx2
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine) - hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)

View File

@ -1,4 +1,3 @@
%import textio
%zeropage basicsafe %zeropage basicsafe
main { main {
@ -9,6 +8,26 @@ main {
; Comment here ; Comment here
4,5,6 ] 4,5,6 ]
txt.print_ub(len(array)) ubyte zz = len(array)
ubyte foobar
empty2()
%asm {{
lda foobar
}}
}
sub nix() {
}
sub empty2() {
}
}
derp {
sub nix2() {
ubyte zz
zz++
} }
} }