From 56fec674c51d384e22f72435c90cd0de961fdcc1 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 12 Apr 2021 23:02:32 +0200 Subject: [PATCH] actually not simplifying if-code generation, leads to larger code at the moment --- .../compiler/BeforeAsmGenerationAstChanger.kt | 38 +++++++++++++------ .../compiler/target/cpu6502/codegen/AsmGen.kt | 2 +- docs/source/todo.rst | 32 ++++++---------- examples/test.p8 | 29 +++++++++++--- 4 files changed, 63 insertions(+), 38 deletions(-) diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index b44b9c7ee..507f5ac6b 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -69,9 +69,11 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I } private val subroutineVariables = mutableListOf>() + private val addedIfConditionVars = mutableSetOf>() override fun before(subroutine: Subroutine, parent: Node): Iterable { subroutineVariables.clear() + addedIfConditionVars.clear() return noModifications } @@ -192,8 +194,11 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I return listOf(IAstModification.ReplaceNode(ifStatement.condition, booleanExpr, ifStatement)) } - return noModifications - // TODO split the conditional expression into separate variables if the operand(s) is not simple. + if((binExpr.left as? NumericLiteralValue)?.number==0) + throw CompilerException("if 0==X should have been swapped to if X==0") + + // split the conditional expression into separate variables if the operand(s) is not simple. + // DISABLED FOR NOW AS IT GENEREATES LARGER CODE IN THE SIMPLE CASES LIKE IF X {...} or IF NOT X {...} // val modifications = mutableListOf() // if(!binExpr.left.isSimple) { // val sub = binExpr.definingSubroutine()!! @@ -202,6 +207,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I // modifications.add(IAstModification.InsertFirst(variable, sub)) // modifications.add(IAstModification.InsertBefore(ifStatement, assignment, parent as INameScope)) // modifications.add(IAstModification.ReplaceNode(binExpr.left, IdentifierReference(listOf(variable.name), binExpr.position), binExpr)) +// addedIfConditionVars.add(Pair(sub, variable.name)) // } // if(!binExpr.right.isSimple) { // val sub = binExpr.definingSubroutine()!! @@ -210,19 +216,29 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I // modifications.add(IAstModification.InsertFirst(variable, sub)) // modifications.add(IAstModification.InsertBefore(ifStatement, assignment, parent as INameScope)) // modifications.add(IAstModification.ReplaceNode(binExpr.right, IdentifierReference(listOf(variable.name), binExpr.position), binExpr)) +// addedIfConditionVars.add(Pair(sub, variable.name)) // } // return modifications + return noModifications } - private fun addIfOperandVar(sub: Subroutine, side: String, operand: Expression): Triple { - val dt = operand.inferType(program).typeOrElse(DataType.STRUCT) - val varname = "prog8_ifvar_${side}_${dt.name.toLowerCase()}" - // TODO check occurrence in sub - val vardecl = VarDecl(VarDeclType.VAR, dt, ZeropageWish.DONTCARE, null, varname, null, null, false, true, operand.position) - val tgt = AssignTarget(IdentifierReference(listOf(varname), operand.position), null, null, operand.position) - val assign = Assignment(tgt, operand, operand.position) - return Triple(vardecl, true, assign) - } +// private fun addIfOperandVar(sub: Subroutine, side: String, operand: Expression): Triple { +// val dt = operand.inferType(program).typeOrElse(DataType.STRUCT) +// val varname = "prog8_ifvar_${side}_${dt.name.toLowerCase()}" +// val tgt = AssignTarget(IdentifierReference(listOf(varname), operand.position), null, null, operand.position) +// val assign = Assignment(tgt, operand, operand.position) +// if(Pair(sub, varname) in addedIfConditionVars) { +// val vardecl = VarDecl(VarDeclType.VAR, dt, ZeropageWish.DONTCARE, null, varname, null, null, false, true, operand.position) +// return Triple(vardecl, false, assign) +// } +// val existing = sub.statements.firstOrNull { it is VarDecl && it.name == varname} as VarDecl? +// return if (existing == null) { +// val vardecl = VarDecl(VarDeclType.VAR, dt, ZeropageWish.DONTCARE, null, varname, null, null, false, true, operand.position) +// Triple(vardecl, true, assign) +// } else { +// Triple(existing, false, assign) +// } +// } override fun after(untilLoop: UntilLoop, parent: Node): Iterable { val binExpr = untilLoop.condition as? BinaryExpression diff --git a/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt index b991c3c85..7b3c5f5e6 100644 --- a/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt @@ -931,7 +931,7 @@ internal class AsmGen(private val program: Program, checkBooleanExpression(stmt.condition) // we require the condition to be of the form 'x ' val booleanCondition = stmt.condition as BinaryExpression - // TODO check this: + // DISABLED FOR NOW: // if(!booleanCondition.left.isSimple || !booleanCondition.right.isSimple) // throw AssemblyError("both operands for if comparison expression should have been simplified") diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 41b9fc305..c33f96647 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,37 +2,29 @@ TODO ==== -- make sure that in if statements, the left and right operand of the comparison is never a complex expression - anymore (only number, variable, addressof or memread) by rewriting if {..} into: - if_eval_left = left - if_eval_right = right - if if_eval_left if_eval_right { ... } (only rewrite operand if x.isSimple() !!) - and then simplify all the codegeneration for if statements. - the variables are allocated in subroutine scope (otherwise irq handler could clobber them) - -- 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) -- optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2)) +- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking) +- get rid of all other TODO's in the code ;-) + + +Low prio +^^^^^^^^ +- optimize several inner loops in gfx2 even further? +- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color)? - add a flood fill routine to gfx2? -- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color) ? - 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 +- refactor the asmgen into own submodule +- refactor the compiler optimizers into own submodule - add a compiler option to not remove unused subroutines. this allows for building library programs. But this won't work with 64tass's .proc ... - make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``v_`` -- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging) -- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking) -- some support for recursive subroutines? - - via %option recursive?: allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters - - Or via a special recursive call operation that copies the current values of all local vars (including arguments) to the stack, replaces the arguments, jsr subroutine, and after returning copy the stack back to the local variables -- get rid of all other TODO's in the code ;-) More optimizations ^^^^^^^^^^^^^^^^^^ Add more compiler optimizations to the existing ones. +- find a way to optimize if-statement codegen so that "if var & %10000" doesn't use stack & subroutine call, but also that the simple case "if X {...}" remains fast +- 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 - further optimize assignment codegeneration, such as the following: - rewrite expression code generator to not use eval stack but a fixed number of predetermined value 'variables' (1 per nesting level?) - binexpr splitting (beware self-referencing expressions and asm code ballooning though) diff --git a/examples/test.p8 b/examples/test.p8 index 8b9cf3685..43b4517f6 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -3,14 +3,31 @@ main { sub start() { - word total + ubyte xx=9 + ubyte yy=9 - repeat 10 { - word wcosa - word zz=1 - total += zz + ; ubyte c_xx = xx+1 + + if xx+1 >= 10 { + txt.print("yes") + } else { + txt.print("error!") + } + if yy+1 >= 10 { + txt.print("yes2") + } else { + txt.print("error2!") } - txt.print_w(total) + if xx+1 >= xx-2 { + txt.print("yes") + } else { + txt.print("error!") + } + if yy+1 >= yy-2 { + txt.print("yes2") + } else { + txt.print("error2!") + } } }