simple subroutine inlining

This commit is contained in:
Irmen de Jong 2019-06-23 02:49:37 +02:00
parent 560047adee
commit a6c3251668
8 changed files with 214 additions and 88 deletions

View File

@ -48,6 +48,7 @@ private fun compileMain(args: Array<String>) {
var writeVmCode = false var writeVmCode = false
var writeAssembly = true var writeAssembly = true
var optimize = true var optimize = true
var optimizeInlining = true
for (arg in args) { for (arg in args) {
if(arg=="-emu") if(arg=="-emu")
emulatorToStart = "x64" emulatorToStart = "x64"
@ -59,6 +60,8 @@ private fun compileMain(args: Array<String>) {
writeAssembly = false writeAssembly = false
else if(arg=="-noopt") else if(arg=="-noopt")
optimize = false optimize = false
else if(arg=="-nooptinline")
optimizeInlining = false
else if(!arg.startsWith("-")) else if(!arg.startsWith("-"))
moduleFile = arg moduleFile = arg
else else
@ -118,7 +121,7 @@ private fun compileMain(args: Array<String>) {
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.optimizeStatements() val optsDone2 = programAst.optimizeStatements(optimizeInlining)
if (optsDone1 + optsDone2 == 0) if (optsDone1 + optsDone2 == 0)
break break
} }
@ -230,7 +233,8 @@ private fun usage() {
System.err.println(" [-writevm] write intermediate vm code to a file as well") System.err.println(" [-writevm] write intermediate vm code to a file as well")
System.err.println(" [-noasm] don't create assembly code") System.err.println(" [-noasm] don't create assembly code")
System.err.println(" [-vm] launch the prog8 virtual machine instead of the compiler") System.err.println(" [-vm] launch the prog8 virtual machine instead of the compiler")
System.err.println(" [-noopt] don't perform optimizations") System.err.println(" [-noopt] don't perform any optimizations")
System.err.println(" [-nooptinline] don't perform subroutine inlining optimizations")
System.err.println(" modulefile main module file to compile") System.err.println(" modulefile main module file to compile")
exitProcess(1) exitProcess(1)
} }

View File

@ -191,14 +191,14 @@ interface IAstProcessor {
fun process(ifStatement: IfStatement): IStatement { fun process(ifStatement: IfStatement): IStatement {
ifStatement.condition = ifStatement.condition.process(this) ifStatement.condition = ifStatement.condition.process(this)
ifStatement.truepart = ifStatement.truepart.process(this) ifStatement.truepart = ifStatement.truepart.process(this) as AnonymousScope
ifStatement.elsepart = ifStatement.elsepart.process(this) ifStatement.elsepart = ifStatement.elsepart.process(this) as AnonymousScope
return ifStatement return ifStatement
} }
fun process(branchStatement: BranchStatement): IStatement { fun process(branchStatement: BranchStatement): IStatement {
branchStatement.truepart = branchStatement.truepart.process(this) branchStatement.truepart = branchStatement.truepart.process(this) as AnonymousScope
branchStatement.elsepart = branchStatement.elsepart.process(this) branchStatement.elsepart = branchStatement.elsepart.process(this) as AnonymousScope
return branchStatement return branchStatement
} }
@ -240,19 +240,19 @@ interface IAstProcessor {
fun process(forLoop: ForLoop): IStatement { fun process(forLoop: ForLoop): IStatement {
forLoop.loopVar?.process(this) forLoop.loopVar?.process(this)
forLoop.iterable = forLoop.iterable.process(this) forLoop.iterable = forLoop.iterable.process(this)
forLoop.body = forLoop.body.process(this) forLoop.body = forLoop.body.process(this) as AnonymousScope
return forLoop return forLoop
} }
fun process(whileLoop: WhileLoop): IStatement { fun process(whileLoop: WhileLoop): IStatement {
whileLoop.condition = whileLoop.condition.process(this) whileLoop.condition = whileLoop.condition.process(this)
whileLoop.body = whileLoop.body.process(this) whileLoop.body = whileLoop.body.process(this) as AnonymousScope
return whileLoop return whileLoop
} }
fun process(repeatLoop: RepeatLoop): IStatement { fun process(repeatLoop: RepeatLoop): IStatement {
repeatLoop.untilCondition = repeatLoop.untilCondition.process(this) repeatLoop.untilCondition = repeatLoop.untilCondition.process(this)
repeatLoop.body = repeatLoop.body.process(this) repeatLoop.body = repeatLoop.body.process(this) as AnonymousScope
return repeatLoop return repeatLoop
} }
@ -274,7 +274,7 @@ interface IAstProcessor {
return assignTarget return assignTarget
} }
fun process(scope: AnonymousScope): AnonymousScope { fun process(scope: AnonymousScope): IStatement {
scope.statements = scope.statements.asSequence().map { it.process(this) }.toMutableList() scope.statements = scope.statements.asSequence().map { it.process(this) }.toMutableList()
return scope return scope
} }
@ -513,8 +513,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 importedBy = mutableListOf<Module>()
val imports = mutableSetOf<Module>() val imports = mutableSetOf<Module>()
val importedBy = mutableSetOf<Module>()
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent=parent this.parent=parent
@ -1695,9 +1695,10 @@ class Subroutine(override val name: String,
val isAsmSubroutine: Boolean, val isAsmSubroutine: Boolean,
override var statements: MutableList<IStatement>, override var statements: MutableList<IStatement>,
override val position: Position) : IStatement, INameScope { override val position: Position) : IStatement, INameScope {
var keepAlways: Boolean = false var keepAlways: Boolean = false
override lateinit var parent: Node override lateinit var parent: Node
val calledBy = mutableSetOf<INameScope>() val calledBy = mutableListOf<Node>()
val calls = mutableSetOf<Subroutine>() val calls = mutableSetOf<Subroutine>()
val scopedname: String by lazy { makeScopedName(name) } val scopedname: String by lazy { makeScopedName(name) }

View File

@ -51,8 +51,7 @@ fun Program.checkIdentifiers() {
private class AstIdentifiersChecker(private val namespace: INameScope) : IAstProcessor { private class AstIdentifiersChecker(private val namespace: INameScope) : IAstProcessor {
private val checkResult: MutableList<AstException> = mutableListOf() private val checkResult: MutableList<AstException> = mutableListOf()
var blocks: MutableMap<String, Block> = mutableMapOf() private var blocks: MutableMap<String, Block> = mutableMapOf()
private set
fun result(): List<AstException> { fun result(): List<AstException> {
return checkResult return checkResult

View File

@ -224,10 +224,10 @@ private class VarInitValueAndAddressOfCreator(private val namespace: INameScope)
// Also takes care to insert AddressOf (&) expression where required (string params to a UWORD function param etc). // Also takes care to insert AddressOf (&) expression where required (string params to a UWORD function param etc).
private val vardeclsToAdd = mutableMapOf<INameScope, MutableMap<String, VarDecl>>() private val vardeclsToAdd = mutableMapOf<INameScope, MutableMap<String, VarDecl>>()
override fun process(module: Module) { override fun process(module: Module) {
vardeclsToAdd.clear()
super.process(module) super.process(module)
// add any new vardecls to the various scopes // add any new vardecls to the various scopes
@ -254,8 +254,9 @@ private class VarInitValueAndAddressOfCreator(private val namespace: INameScope)
} }
else else
declvalue declvalue
val identifierName = listOf(decl.name) // // TODO this was: (scoped name) decl.scopedname.split(".")
return VariableInitializationAssignment( return VariableInitializationAssignment(
AssignTarget(null, IdentifierReference(decl.scopedname.split("."), decl.position), null, null, decl.position), AssignTarget(null, IdentifierReference(identifierName, decl.position), null, null, decl.position),
null, null,
value, value,
decl.position decl.position

View File

@ -4,12 +4,16 @@ import prog8.ast.*
import prog8.compiler.loadAsmIncludeFile import prog8.compiler.loadAsmIncludeFile
class CallGraphBuilder(private val program: Program): IAstProcessor { class CallGraph(private val program: Program): IAstProcessor {
private val modulesImporting = mutableMapOf<Module, Set<Module>>().withDefault { mutableSetOf() } private val modulesImporting = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
private val modulesImportedBy = mutableMapOf<Module, Set<Module>>().withDefault { mutableSetOf() } private val modulesImportedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
private val subroutinesCalling = mutableMapOf<INameScope, Set<Subroutine>>().withDefault { mutableSetOf() } private val subroutinesCalling = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
private val subroutinesCalledBy = mutableMapOf<Subroutine, Set<INameScope>>().withDefault { mutableSetOf() } private val subroutinesCalledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
init {
process(program)
}
fun forAllSubroutines(scope: INameScope, sub: (s: Subroutine) -> Unit) { fun forAllSubroutines(scope: INameScope, sub: (s: Subroutine) -> Unit) {
fun findSubs(scope: INameScope) { fun findSubs(scope: INameScope) {
@ -67,7 +71,7 @@ class CallGraphBuilder(private val program: Program): IAstProcessor {
if(otherSub!=null) { if(otherSub!=null) {
functionCall.definingSubroutine()?.let { thisSub -> functionCall.definingSubroutine()?.let { thisSub ->
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub) subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(thisSub) subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCall)
} }
} }
return super.process(functionCall) return super.process(functionCall)
@ -78,7 +82,7 @@ class CallGraphBuilder(private val program: Program): IAstProcessor {
if(otherSub!=null) { if(otherSub!=null) {
functionCallStatement.definingSubroutine()?.let { thisSub -> functionCallStatement.definingSubroutine()?.let { thisSub ->
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub) subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(thisSub) subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCallStatement)
} }
} }
return super.process(functionCallStatement) return super.process(functionCallStatement)
@ -89,7 +93,7 @@ class CallGraphBuilder(private val program: Program): IAstProcessor {
if(otherSub!=null) { if(otherSub!=null) {
jump.definingSubroutine()?.let { thisSub -> jump.definingSubroutine()?.let { thisSub ->
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub) subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(thisSub) subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(jump)
} }
} }
return super.process(jump) return super.process(jump)
@ -102,7 +106,7 @@ class CallGraphBuilder(private val program: Program): IAstProcessor {
return super.process(inlineAssembly) return super.process(inlineAssembly)
} }
private fun scanAssemblyCode(asm: String, context: Node, scope: INameScope) { private fun scanAssemblyCode(asm: String, context: IStatement, scope: INameScope) {
val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE) val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE) val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
asm.lines().forEach { line -> asm.lines().forEach { line ->
@ -113,13 +117,13 @@ class CallGraphBuilder(private val program: Program): IAstProcessor {
val node = program.namespace.lookup(jumptarget.split('.'), context) val node = program.namespace.lookup(jumptarget.split('.'), context)
if (node is Subroutine) { if (node is Subroutine) {
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node) subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
subroutinesCalledBy[node] = subroutinesCalledBy.getValue(node).plus(scope) subroutinesCalledBy[node] = subroutinesCalledBy.getValue(node).plus(context)
} else if(jumptarget.contains('.')) { } else if(jumptarget.contains('.')) {
// maybe only the first part already refers to a subroutine // maybe only the first part already refers to a subroutine
val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context) val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context)
if (node2 is Subroutine) { if (node2 is Subroutine) {
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node2) subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node2)
subroutinesCalledBy[node2] = subroutinesCalledBy.getValue(node2).plus(scope) subroutinesCalledBy[node2] = subroutinesCalledBy.getValue(node2).plus(context)
} }
} }
} }
@ -131,7 +135,7 @@ class CallGraphBuilder(private val program: Program): IAstProcessor {
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context) val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
if (node is Subroutine) { if (node is Subroutine) {
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node) subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
subroutinesCalledBy[node] = subroutinesCalledBy.getValue(node).plus(scope) subroutinesCalledBy[node] = subroutinesCalledBy.getValue(node).plus(context)
} }
} }
} }

View File

@ -1,7 +1,6 @@
package prog8.optimizing package prog8.optimizing
import prog8.ast.AstException import prog8.ast.*
import prog8.ast.Program
import prog8.parser.ParsingFailedError import prog8.parser.ParsingFailedError
@ -27,12 +26,16 @@ fun Program.constantFold() {
} }
fun Program.optimizeStatements(): Int { fun Program.optimizeStatements(optimizeInlining: Boolean): Int {
val optimizer = StatementOptimizer(this) val optimizer = StatementOptimizer(this, optimizeInlining)
optimizer.process(this) optimizer.process(this)
for(stmt in optimizer.statementsToRemove) { for(scope in optimizer.scopesToFlatten.reversed()) {
val scope=stmt.definingScope() val namescope = scope.parent as INameScope
scope.remove(stmt) val idx = namescope.statements.indexOf(scope as IStatement)
if(idx>=0) {
namescope.statements[idx] = NopStatement(scope.position)
namescope.statements.addAll(idx, scope.statements)
}
} }
modules.forEach { it.linkParents() } // re-link in final configuration modules.forEach { it.linkParents() } // re-link in final configuration

View File

@ -8,28 +8,88 @@ 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 labels, variables (locals and heap), blocks. Remove if count is zero.
todo: implement usage counters for variables (locals and heap), blocks. Remove if count is zero.
todo inline subroutines that are called exactly once (regardless of their size)
todo inline subroutines that are only called a few times (max 3?) (if < 20 statements)
todo inline all subroutines that are "very small" (0-3 statements)
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
*/ */
class StatementOptimizer(private val program: Program) : IAstProcessor { class StatementOptimizer(private val program: Program, private val optimizeInlining: Boolean) : IAstProcessor {
var optimizationsDone: Int = 0 var optimizationsDone: Int = 0
private set private set
var statementsToRemove = mutableListOf<IStatement>() var scopesToFlatten = mutableListOf<INameScope>()
private set
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure } private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
companion object {
private var generatedLabelSequenceNumber = 0
}
override fun process(program: Program) { override fun process(program: Program) {
val callgraph = CallGraphBuilder(program) val callgraph = CallGraph(program)
callgraph.process(program) removeUnusedCode(callgraph)
if(optimizeInlining) {
inlineSubroutines(callgraph)
}
super.process(program)
}
private fun inlineSubroutines(callgraph: CallGraph) {
val entrypoint = program.entrypoint()
program.modules.forEach {
callgraph.forAllSubroutines(it) { sub ->
if(sub!==entrypoint && !sub.isAsmSubroutine) {
if (sub.statements.size <= 3) {
sub.calledBy.toList().forEach { caller -> inlineSubroutine(sub, caller) }
} else if (sub.calledBy.size==1 && sub.statements.size < 50) {
inlineSubroutine(sub, sub.calledBy[0])
} else if(sub.calledBy.size<=3 && sub.statements.size < 10) {
sub.calledBy.toList().forEach { caller -> inlineSubroutine(sub, caller) }
}
}
}
}
}
private fun inlineSubroutine(sub: Subroutine, caller: Node) {
// if the sub is called multiple times from the same scope, we can't inline (would result in duplicate definitions)
// (unless we add a sequence number to all vars/labels and references to them in the inlined code, but I skip that for now)
val scope = caller.definingScope()
if(sub.calledBy.count { it.definingScope()===scope } > 1)
return
if(caller !is IFunctionCall || caller !is IStatement || sub.statements.any { it is Subroutine })
return
if(sub.parameters.isEmpty() && sub.returntypes.isEmpty()) {
// sub without params and without return value can be easily inlined
val parent = caller.parent as INameScope
val inlined = AnonymousScope(sub.statements.toMutableList(), caller.position)
parent.statements[parent.statements.indexOf(caller)] = inlined
// replace return statements in the inlined sub by a jump to the end of it
var endlabel = inlined.statements.last() as? Label
if(endlabel==null) {
endlabel = makeLabel("_prog8_auto_sub_end", inlined.statements.last().position)
inlined.statements.add(endlabel)
endlabel.parent = inlined
}
val returns = inlined.statements.withIndex().filter { iv -> iv.value is Return }.map { iv -> Pair(iv.index, iv.value as Return)}
for(returnIdx in returns) {
assert(returnIdx.second.values.isEmpty())
val jump = Jump(null, IdentifierReference(listOf(endlabel.name), returnIdx.second.position), null, returnIdx.second.position)
inlined.statements[returnIdx.first] = jump
}
inlined.linkParents(caller.parent)
sub.calledBy.remove(caller) // if there are no callers left, the sub will be removed automatically later
optimizationsDone++
} else {
// TODO inline subroutine that has params or returnvalues or both
}
}
private fun makeLabel(name: String, position: Position): Label {
generatedLabelSequenceNumber++
return Label("${name}_$generatedLabelSequenceNumber", position)
}
private fun removeUnusedCode(callgraph: CallGraph) {
// TODO remove unused variables (local and global) // 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
@ -37,12 +97,12 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
val entrypoint = program.entrypoint() val entrypoint = program.entrypoint()
program.modules.forEach { program.modules.forEach {
callgraph.forAllSubroutines(it) { sub -> callgraph.forAllSubroutines(it) { sub ->
if(sub !== entrypoint && !sub.keepAlways && (sub.calledBy.isEmpty() || (sub.containsNoCodeNorVars() && !sub.isAsmSubroutine))) if (sub !== entrypoint && !sub.keepAlways && (sub.calledBy.isEmpty() || (sub.containsNoCodeNorVars() && !sub.isAsmSubroutine)))
removeSubroutines.add(sub) removeSubroutines.add(sub)
} }
} }
if(removeSubroutines.isNotEmpty()) { if (removeSubroutines.isNotEmpty()) {
removeSubroutines.forEach { removeSubroutines.forEach {
it.definingScope().statements.remove(it) it.definingScope().statements.remove(it)
} }
@ -55,7 +115,7 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
removeBlocks.add(block) removeBlocks.add(block)
} }
if(removeBlocks.isNotEmpty()) { if (removeBlocks.isNotEmpty()) {
removeBlocks.forEach { it.definingScope().statements.remove(it) } removeBlocks.forEach { it.definingScope().statements.remove(it) }
} }
@ -66,18 +126,16 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
removeModules.add(it) removeModules.add(it)
} }
if(removeModules.isNotEmpty()) { if (removeModules.isNotEmpty()) {
println("[debug] removing ${removeModules.size} empty/unused modules") println("[debug] removing ${removeModules.size} empty/unused modules")
program.modules.removeAll(removeModules) program.modules.removeAll(removeModules)
} }
super.process(program)
} }
override fun process(block: Block): IStatement { override fun process(block: Block): IStatement {
if(block.containsNoCodeNorVars()) { if(block.containsNoCodeNorVars()) {
optimizationsDone++ optimizationsDone++
statementsToRemove.add(block) return NopStatement(block.position)
} }
return super.process(block) return super.process(block)
} }
@ -88,7 +146,7 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
if(subroutine.asmAddress==null) { if(subroutine.asmAddress==null) {
if(subroutine.containsNoCodeNorVars()) { if(subroutine.containsNoCodeNorVars()) {
optimizationsDone++ optimizationsDone++
statementsToRemove.add(subroutine) return NopStatement(subroutine.position)
} }
} }
@ -161,8 +219,8 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
val functionName = functionCallStatement.target.nameInSource[0] val functionName = functionCallStatement.target.nameInSource[0]
if (functionName in pureBuiltinFunctions) { if (functionName in pureBuiltinFunctions) {
printWarning("statement has no effect (function return value is discarded)", functionCallStatement.position) printWarning("statement has no effect (function return value is discarded)", functionCallStatement.position)
statementsToRemove.add(functionCallStatement) optimizationsDone++
return functionCallStatement return NopStatement(functionCallStatement.position)
} }
} }
@ -238,9 +296,8 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
super.process(ifStatement) super.process(ifStatement)
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars()) { if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars()) {
statementsToRemove.add(ifStatement)
optimizationsDone++ optimizationsDone++
return ifStatement return NopStatement(ifStatement.position)
} }
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsCodeOrVars()) { if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsCodeOrVars()) {
@ -273,16 +330,14 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
super.process(forLoop) super.process(forLoop)
if(forLoop.body.containsNoCodeNorVars()) { if(forLoop.body.containsNoCodeNorVars()) {
// remove empty for loop // remove empty for loop
statementsToRemove.add(forLoop)
optimizationsDone++ optimizationsDone++
return forLoop return NopStatement(forLoop.position)
} else if(forLoop.body.statements.size==1) { } else if(forLoop.body.statements.size==1) {
val loopvar = forLoop.body.statements[0] as? VarDecl val loopvar = forLoop.body.statements[0] as? VarDecl
if(loopvar!=null && loopvar.name==forLoop.loopVar?.nameInSource?.singleOrNull()) { if(loopvar!=null && loopvar.name==forLoop.loopVar?.nameInSource?.singleOrNull()) {
// remove empty for loop // remove empty for loop
statementsToRemove.add(forLoop)
optimizationsDone++ optimizationsDone++
return forLoop return NopStatement(forLoop.position)
} }
} }
@ -392,6 +447,17 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
return first return first
} }
} }
// if the jump is to the next statement, remove the jump
val scope = jump.definingScope()
val label = jump.identifier?.targetStatement(scope)
if(label!=null) {
if(scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1) {
optimizationsDone++
return NopStatement(jump.position)
}
}
return jump return jump
} }
@ -405,7 +471,7 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
optimizationsDone++ optimizationsDone++
return NopStatement(assignment.position) return NopStatement(assignment.position)
} }
val targetDt = target.determineDatatype(program, assignment)!! val targetDt = target.determineDatatype(program, assignment)
val bexpr=assignment.value as? BinaryExpression val bexpr=assignment.value as? BinaryExpression
if(bexpr!=null) { if(bexpr!=null) {
val cv = bexpr.right.constValue(program)?.asNumericValue?.toDouble() val cv = bexpr.right.constValue(program)?.asNumericValue?.toDouble()
@ -528,14 +594,29 @@ class StatementOptimizer(private val program: Program) : IAstProcessor {
return super.process(assignment) return super.process(assignment)
} }
override fun process(scope: AnonymousScope): AnonymousScope { override fun process(scope: AnonymousScope): IStatement {
val linesToRemove = deduplicateAssignments(scope.statements) val linesToRemove = deduplicateAssignments(scope.statements)
if(linesToRemove.isNotEmpty()) { if(linesToRemove.isNotEmpty()) {
linesToRemove.reversed().forEach{scope.statements.removeAt(it)} linesToRemove.reversed().forEach{scope.statements.removeAt(it)}
} }
if(scope.parent is INameScope) {
scopesToFlatten.add(scope) // get rid of the anonymous scope
}
return super.process(scope) return super.process(scope)
} }
override fun process(label: Label): IStatement {
// remove duplicate labels
val stmts = label.definingScope().statements
val startIdx = stmts.indexOf(label)
if(startIdx<(stmts.size-1) && stmts[startIdx+1] == label)
return NopStatement(label.position)
return super.process(label)
}
private fun same(target: AssignTarget, value: IExpression): Boolean { private fun same(target: AssignTarget, value: IExpression): Boolean {
return when { return when {
target.memoryAddress!=null -> false target.memoryAddress!=null -> false

View File

@ -1,28 +1,61 @@
%zeropage basicsafe %zeropage basicsafe
%option enable_floats
%import c64flt
~ main { ~ main {
float[] fa = [1.1,2.2,3.3]
ubyte[] uba = [10,2,3,4]
byte[] ba = [-10,2,3,4]
uword[] uwa = [100,20,30,40]
word[] wa = [-100,20,30,40]
sub start() { sub start() {
float a greeting()
a=avg([1,2,3,4])
c64flt.print_f(a) ubyte square = stuff.function(12)
c64.CHROUT('\n')
a=avg([100,200,300,400]) c64scr.print_ub(square)
c64flt.print_f(a)
c64.CHROUT('\n')
a=avg([1.1,2.2,3.3,4.4])
c64flt.print_f(a)
c64.CHROUT('\n') c64.CHROUT('\n')
stuff.name()
stuff.name()
stuff.bye()
abs(4)
abs(4)
abs(4)
abs(4)
abs(4)
foobar()
foobar()
foobar()
foobar()
if(false) {
} else {
}
} }
sub foobar() {
}
sub greeting() {
c64scr.print("hello\n")
}
}
~ stuff {
sub function(ubyte v) -> ubyte {
return v*v
}
sub name() {
c64scr.print("name\n")
}
sub bye() {
c64scr.print("bye\n")
}
} }