From 58400f53bc1908276190dc979e3d5222cbbdc81c Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 13 Dec 2023 21:54:19 +0100 Subject: [PATCH] optimize: flip if true/else blocks if the else block only contains a jump (inverting the condition) --- .../src/prog8/optimizer/StatementOptimizer.kt | 18 +++- compiler/src/prog8/compiler/Compiler.kt | 4 +- .../compiler/astprocessing/BoolRemover.kt | 10 +++ docs/source/todo.rst | 6 +- examples/test.p8 | 83 +++---------------- 5 files changed, 42 insertions(+), 79 deletions(-) diff --git a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt index 197a6d596..b5fd01f35 100644 --- a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt @@ -110,6 +110,22 @@ class StatementOptimizer(private val program: Program, IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer) ) } + + // switch if/else around if the else is just a jump or branch + if(ifElse.elsepart.isNotEmpty() && ifElse.elsepart.statements.size==1) { + val jump = ifElse.elsepart.statements[0] + if(jump is Jump) { + val newTruePart = AnonymousScope(mutableListOf(jump), ifElse.elsepart.position) + val newElsePart = AnonymousScope(ifElse.truepart.statements, ifElse.truepart.position) + val invertedCondition = PrefixExpression("not", ifElse.condition, ifElse.condition.position) + return listOf( + IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse), + IAstModification.ReplaceNode(ifElse.truepart, newTruePart, ifElse), + IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse) + ) + } + } + return noModifications } @@ -460,7 +476,7 @@ class StatementOptimizer(private val program: Program, if(whenStmt.condition.inferType(program).isBool) { if(whenStmt.choices.all { it.values?.size==1 }) { if (whenStmt.choices.all { it.values!!.single().constValue(program)!!.number in arrayOf(0.0, 1.0) }) { - // it's a when statement on booleans that can just be replaced by an if or if..else. + // it's a when statement on booleans that can just be replaced by an if or if-else. if (whenStmt.choices.size == 1) { return if(whenStmt.choices[0].values!![0].constValue(program)!!.number==1.0) { replaceWithIf(whenStmt.condition, whenStmt.choices[0].statements, null) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 9ce0ddb57..4e8041dfa 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -112,9 +112,11 @@ fun compileProgram(args: CompilerArguments): CompilationResult? { program.processAstBeforeAsmGeneration(compilationOptions, args.errors) args.errors.report() - val intermediateAst = IntermediateAstMaker(program, args.errors).transform() // println("*********** COMPILER AST RIGHT BEFORE ASM GENERATION *************") // printProgram(program) + + val intermediateAst = IntermediateAstMaker(program, args.errors).transform() + // println("*********** AST RIGHT BEFORE ASM GENERATION *************") // printAst(intermediateAst, true, ::println) diff --git a/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt b/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt index 44d8c8124..e16edd6cf 100644 --- a/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt +++ b/compiler/src/prog8/compiler/astprocessing/BoolRemover.kt @@ -95,6 +95,16 @@ internal class BoolRemover(val program: Program) : AstWalker() { } return noModifications } + + override fun after(expr: PrefixExpression, parent: Node): Iterable { + if(expr.operator=="not") { + val exprDt = expr.expression.inferType(program).getOrElse { throw FatalAstException("unknown dt") } + val nonBoolDt = if(exprDt==DataType.BOOL) DataType.UBYTE else exprDt + val equalZero = BinaryExpression(expr.expression, "==", NumericLiteral(nonBoolDt, 0.0, expr.expression.position), expr.expression.position) + return listOf(IAstModification.ReplaceNode(expr, equalZero, parent)) + } + return noModifications + } } internal fun wrapWithBooleanCastIfNeeded(expr: Expression, program: Program): Expression? { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 4fe2c5e1e..6952a0d11 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,17 +2,15 @@ TODO ==== -- optimize: flip if true/else blocks if the else block only contains a jump (invert condition!) +- IR (expericodegen): fix code for calling routines that return a boolean in a status register such as Carry flag, it has to store the flag value somewhere -- merge branch optimize-st for some optimizations regardign SymbolTable use +- merge branch optimize-st for some optimizations regarding SymbolTable use - [on branch: call-pointers] allow calling a subroutine via a pointer variable (indirect JSR, optimized form of callfar()) modify programs (shell, paint) that now use callfar - [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 .... -- IR (expericodegen): fix code for calling routines that return a boolean in a status register such as Carry flag, it has to store the flag value somewhere - ... diff --git a/examples/test.p8 b/examples/test.p8 index 098808d5e..18b3fcf14 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,86 +4,23 @@ main { sub start() { - ; expected output: 0000 - cx16.r0L = test_c_clear() - cx16.r1L = test_z_clear() - cx16.r2L = test_n_clear() - cx16.r3L = test_v_clear() - txt.print_ub(cx16.r0L) - txt.print_ub(cx16.r1L) - txt.print_ub(cx16.r2L) - txt.print_ub(cx16.r3L) - txt.nl() - ; expected output: 1111 - cx16.r0L = test_c_set() - cx16.r1L = test_z_set() - cx16.r2L = test_n_set() - cx16.r3L = test_v_set() - txt.print_ub(cx16.r0L) - txt.print_ub(cx16.r1L) - txt.print_ub(cx16.r2L) - txt.print_ub(cx16.r3L) - txt.nl() - - ; exptected output: no2, no3, no5, yes6, no7, yes8 - if not test_c_clear() - goto skip1 + if test_c_set() + txt.print("yes1\n") else - txt.print("no1\n") + goto skip1 + + txt.print("no1\n") skip1: if test_c_clear() - goto skip2 + txt.print("yes2\n") else - txt.print("no2\n") + goto skip2 + + txt.print("no1\n") skip2: - - if not test_c_set() - goto skip3 - else - txt.print("no3\n") - -skip3: - if test_c_set() - goto skip4 - else - txt.print("no4\n") - -skip4: - - if test_c_clear() { - txt.print("yes5\n") - goto skip5 - } - txt.print("no5\n") - -skip5: - if not test_c_clear() { - txt.print("yes6\n") - goto skip6 - } - txt.print("no6\n") - -skip6: - if test_c_clear() - txt.print("yes7\n") - else - txt.print("no7\n") - - if not test_c_clear() - txt.print("yes8\n") - else - txt.print("no8\n") - - - while test_c_clear() { - cx16.r0++ - } - - do { - cx16.r0++ - } until test_c_set() + txt.print("done\n") } asmsub test_c_clear() -> bool @Pc {