diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 66401eed6..9b64c933c 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -267,14 +267,14 @@ private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions: // keep optimizing expressions and statements until no more steps remain val optsDone1 = programAst.simplifyExpressions() val optsDone2 = programAst.splitBinaryExpressions(compTarget) - val optsDone3 = programAst.optimizeStatements(errors, functions, compTarget) + val optsDone3 = programAst.optimizeStatements(errors, functions, compTarget, ::loadAsmIncludeFile) programAst.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away errors.report() if (optsDone1 + optsDone2 + optsDone3 == 0) break } - val remover = UnusedCodeRemover(programAst, errors, compTarget) + val remover = UnusedCodeRemover(programAst, errors, compTarget, ::loadAsmIncludeFile) remover.visit(programAst) remover.applyModifications() errors.report() @@ -286,7 +286,7 @@ private fun postprocessAst(programAst: Program, errors: IErrorReporter, compiler programAst.variousCleanups() programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget) // check if final tree is still valid errors.report() - val callGraph = CallGraph(programAst) + val callGraph = CallGraph(programAst, ::loadAsmIncludeFile) callGraph.checkRecursiveCalls(errors) errors.report() programAst.verifyFunctionArgTypes() diff --git a/compiler/src/prog8/optimizer/CallGraph.kt b/compiler/src/prog8/optimizer/CallGraph.kt index d1a6b1af6..af41345b1 100644 --- a/compiler/src/prog8/optimizer/CallGraph.kt +++ b/compiler/src/prog8/optimizer/CallGraph.kt @@ -12,7 +12,7 @@ import prog8.ast.expressions.IdentifierReference import prog8.ast.statements.* import prog8.ast.walk.IAstVisitor import prog8.compiler.IErrorReporter -import prog8.compiler.loadAsmIncludeFile +import java.nio.file.Path private val alwaysKeepSubroutines = setOf( Pair("main", "start"), @@ -23,7 +23,7 @@ private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", R private val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE) -class CallGraph(private val program: Program) : IAstVisitor { +class CallGraph(private val program: Program, private val asmFileLoader: (filename: String, source: Path)->String) : IAstVisitor { val imports = mutableMapOf>().withDefault { mutableListOf() } val importedBy = mutableMapOf>().withDefault { mutableListOf() } @@ -80,7 +80,7 @@ class CallGraph(private val program: Program) : IAstVisitor { imports[thisModule] = imports.getValue(thisModule).plus(importedModule) importedBy[importedModule] = importedBy.getValue(importedModule).plus(thisModule) } else if (directive.directive == "%asminclude") { - val asm = loadAsmIncludeFile(directive.args[0].str!!, thisModule.source) + val asm = asmFileLoader(directive.args[0].str!!, thisModule.source) val scope = directive.definingSubroutine() if(scope!=null) { scanAssemblyCode(asm, directive, scope) diff --git a/compiler/src/prog8/optimizer/Extensions.kt b/compiler/src/prog8/optimizer/Extensions.kt index 145139c12..1075629f5 100644 --- a/compiler/src/prog8/optimizer/Extensions.kt +++ b/compiler/src/prog8/optimizer/Extensions.kt @@ -4,6 +4,7 @@ import prog8.ast.IBuiltinFunctions import prog8.ast.Program import prog8.compiler.IErrorReporter import prog8.compiler.target.ICompilationTarget +import java.nio.file.Path internal fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) { @@ -40,8 +41,11 @@ internal fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilati } -internal fun Program.optimizeStatements(errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget): Int { - val optimizer = StatementOptimizer(this, errors, functions, compTarget) +internal fun Program.optimizeStatements(errors: IErrorReporter, + functions: IBuiltinFunctions, + compTarget: ICompilationTarget, + asmFileLoader: (filename: String, source: Path)->String): Int { + val optimizer = StatementOptimizer(this, errors, functions, compTarget, asmFileLoader) optimizer.visit(this) val optimizationCount = optimizer.applyModifications() diff --git a/compiler/src/prog8/optimizer/StatementOptimizer.kt b/compiler/src/prog8/optimizer/StatementOptimizer.kt index 475a0b479..16b1f2046 100644 --- a/compiler/src/prog8/optimizer/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizer/StatementOptimizer.kt @@ -12,17 +12,19 @@ import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstVisitor import prog8.compiler.IErrorReporter import prog8.compiler.target.ICompilationTarget +import java.nio.file.Path import kotlin.math.floor internal class StatementOptimizer(private val program: Program, private val errors: IErrorReporter, private val functions: IBuiltinFunctions, - private val compTarget: ICompilationTarget + private val compTarget: ICompilationTarget, + private val asmFileLoader: (filename: String, source: Path)->String ) : AstWalker() { private val noModifications = emptyList() - private val callgraph = CallGraph(program) + private val callgraph = CallGraph(program, asmFileLoader) override fun after(block: Block, parent: Node): Iterable { if("force_output" !in block.options()) { diff --git a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt index f11880a72..809d1fc03 100644 --- a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt +++ b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt @@ -12,12 +12,16 @@ import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.compiler.IErrorReporter import prog8.compiler.target.ICompilationTarget +import java.nio.file.Path -internal class UnusedCodeRemover(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget): AstWalker() { +internal class UnusedCodeRemover(private val program: Program, + private val errors: IErrorReporter, + private val compTarget: ICompilationTarget, + private val asmFileLoader: (filename: String, source: Path)->String): AstWalker() { override fun before(program: Program, parent: Node): Iterable { - val callgraph = CallGraph(program) + val callgraph = CallGraph(program, asmFileLoader) val removals = mutableListOf() // remove all subroutines that aren't called, or are empty diff --git a/docs/source/todo.rst b/docs/source/todo.rst index c072b5e85..bbe6db484 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -5,8 +5,6 @@ TODO - add sound to the cx16 tehtriz - 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) -- refactor the asmgen into their own submodule? -- refactor the compiler optimizers into their own submodule? - optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2)) - optimize several inner loops in gfx2 (highres 4 color mode) - use the 65c02 bit clear/set/test instructions for single-bit operations @@ -14,6 +12,8 @@ TODO - add modes 2 and 3 to gfx2 (lowres 4 color and 16 color) - add a flood fill routine to gfx2? - add a f_seek() routine for the Cx16 that uses its seek dos api? +- refactor the asmgen into their own submodule? +- refactor the compiler optimizers into their own submodule? - optimizer: detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation - add a compiler option to not remove unused subroutines. this allows for building library programs - make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as 'v_'