diff --git a/compiler/src/prog8/compiler/astprocessing/NotExpressionAndIfComparisonExprChanger.kt b/compiler/src/prog8/compiler/astprocessing/NotExpressionAndIfComparisonExprChanger.kt index 3967dfa84..f59a7871b 100644 --- a/compiler/src/prog8/compiler/astprocessing/NotExpressionAndIfComparisonExprChanger.kt +++ b/compiler/src/prog8/compiler/astprocessing/NotExpressionAndIfComparisonExprChanger.kt @@ -48,6 +48,34 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val return listOf(IAstModification.ReplaceNode(expr, notExpr, parent)) } + + // applying De Morgan's laws proved beneficial for the code generator, + // when the code has one outer 'not' instead of two inner ones. + if(expr.operator=="or" || expr.operator=="and") { + val newOper = if(expr.operator=="or") "and" else "or" + val leftP = expr.left as? PrefixExpression + val rightP = expr.right as? PrefixExpression + if(leftP!=null && leftP.operator=="not" && rightP!=null && rightP.operator=="not") { + // (not a) or (not b) --> not(a and b) + // (not a) and (not b) --> not(a or b) + val inner = BinaryExpression(leftP.expression, newOper, rightP.expression, expr.position) + val notExpr = PrefixExpression("not", inner, expr.position) + return listOf(IAstModification.ReplaceNode(expr, notExpr, parent)) + } + val leftB = expr.left as? BinaryExpression + val rightB = expr.right as? BinaryExpression + if(leftB!=null && leftB.operator=="==" && (leftB.right as? NumericLiteral)?.number==0.0 + && rightB!=null && rightB.operator=="==" && (rightB.right as? NumericLiteral)?.number==0.0) { + // a==0 or b==0 --> (a!=0 and b!=0)==0 + // a==0 and b==0 --> (a!=0 or b!=0)==0 + leftB.operator = "!=" + rightB.operator = "!=" + val inner = BinaryExpression(leftB, newOper, rightB, expr.position) + val notExpr = BinaryExpression(inner, "==", NumericLiteral.optimalInteger(0, expr.position), expr.position) + return listOf(IAstModification.ReplaceNode(expr, notExpr, parent)) + } + } + return noModifications } diff --git a/compiler/test/TestOptimization.kt b/compiler/test/TestOptimization.kt index c31b0bc7a..01c4e7939 100644 --- a/compiler/test/TestOptimization.kt +++ b/compiler/test/TestOptimization.kt @@ -307,8 +307,8 @@ class TestOptimization: FunSpec({ a1 = not a1 ; a1 = a1==0 a1 = not not a1 ; a1 = a1, so removed totally a1 = not not not a1 ; a1 = a1==0 - a1 = not a1 or not a2 ; a1 = a1==0 or a2==0 - a1 = not a1 and not a2 ; a1 = a1==0 and a2==0 + a1 = not a1 or not a2 ; a1 = (a1 and a2)==0 + a1 = not a1 and not a2 ; a1 = (a1 or a2)==0 } } """ @@ -324,8 +324,12 @@ class TestOptimization: FunSpec({ value1.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) value2.operator shouldBe "==" value2.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) - value3.operator shouldBe "or" - value4.operator shouldBe "and" + value3.operator shouldBe "==" + value3.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) + (value3.left as BinaryExpression).operator shouldBe "and" + value4.operator shouldBe "==" + value4.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) + (value4.left as BinaryExpression).operator shouldBe "or" } test("various 'not' operator rewrites with optimizations") { @@ -337,8 +341,8 @@ class TestOptimization: FunSpec({ a1 = not a1 ; a1 = a1==0 a1 = not not a1 ; a1 = a1, so removed totally a1 = not not not a1 ; a1 = a1==0 - a1 = not a1 or not a2 ; a1 = a1==0 or a2==0 - a1 = not a1 and not a2 ; a1 = a1==0 and a2==0 + a1 = not a1 or not a2 ; a1 = (a1 and a2)==0 + a1 = not a1 and not a2 ; a1 = (a1 or a2)==0 } } """ @@ -354,8 +358,12 @@ class TestOptimization: FunSpec({ value1.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) value2.operator shouldBe "==" value2.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) - value3.operator shouldBe "or" - value4.operator shouldBe "and" + value3.operator shouldBe "==" + value3.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) + (value3.left as BinaryExpression).operator shouldBe "and" + value4.operator shouldBe "==" + value4.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) + (value4.left as BinaryExpression).operator shouldBe "or" } test("asmgen correctly deals with float typecasting in augmented assignment") { diff --git a/examples/test.p8 b/examples/test.p8 index 3829af4ba..23555f35c 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,18 +4,31 @@ main { sub start() { - ubyte @shared rnr = $a0 - txt.print_ub(rnr>=$33) - txt.print_ub(rnr>=$66) - txt.print_ub(rnr>=$99) - txt.print_ub(rnr>=$cc) - txt.nl() + ; is optimizing this useful? : not a1 or not a2 -> not(a1 and a2) likewise for and. + bool @shared a1 = true + bool @shared a2 + bool @shared a4 - ubyte wordNr = (rnr >= $33) as ubyte + (rnr >= $66) as ubyte + (rnr >= $99) as ubyte + (rnr >= $CC) as ubyte - txt.print_uw(wordNr) - txt.nl() - wordNr = 100 - (rnr >= $33) - (rnr >= $66) - (rnr >= $99) - (rnr >= $CC) - txt.print_uw(wordNr) - txt.nl() + if a1==0 and a2==0 + cx16.r0++ + + if (a1!=0 or a2!=0)==0 + cx16.r0++ + + if a1==0 or a2==0 + cx16.r0++ + + if (a1!=0 and a2!=0)==0 + cx16.r0++ + + +; if not a1 or not a2 +; cx16.r0++ +; if not (a1 and a2) +; cx16.r0++ +; if not a1 and not a2 +; cx16.r0++ +; if not (a1 or a2) +; cx16.r0++ } }