apply De Morgan's laws for logical not, results in smaller code

This commit is contained in:
Irmen de Jong 2024-01-04 23:45:46 +01:00
parent 8ba5a0d90c
commit 8a0e650511
3 changed files with 69 additions and 20 deletions

View File

@ -48,6 +48,34 @@ internal class NotExpressionAndIfComparisonExprChanger(val program: Program, val
return listOf(IAstModification.ReplaceNode(expr, notExpr, parent)) 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 return noModifications
} }

View File

@ -307,8 +307,8 @@ class TestOptimization: FunSpec({
a1 = not a1 ; a1 = a1==0 a1 = not a1 ; a1 = a1==0
a1 = not not a1 ; a1 = a1, so removed totally a1 = not not a1 ; a1 = a1, so removed totally
a1 = not not not a1 ; a1 = a1==0 a1 = not not not a1 ; a1 = a1==0
a1 = not a1 or not a2 ; a1 = a1==0 or a2==0 a1 = not a1 or not a2 ; a1 = (a1 and a2)==0
a1 = not a1 and not a2 ; a1 = a1==0 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) value1.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
value2.operator shouldBe "==" value2.operator shouldBe "=="
value2.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) value2.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
value3.operator shouldBe "or" value3.operator shouldBe "=="
value4.operator shouldBe "and" 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") { test("various 'not' operator rewrites with optimizations") {
@ -337,8 +341,8 @@ class TestOptimization: FunSpec({
a1 = not a1 ; a1 = a1==0 a1 = not a1 ; a1 = a1==0
a1 = not not a1 ; a1 = a1, so removed totally a1 = not not a1 ; a1 = a1, so removed totally
a1 = not not not a1 ; a1 = a1==0 a1 = not not not a1 ; a1 = a1==0
a1 = not a1 or not a2 ; a1 = a1==0 or a2==0 a1 = not a1 or not a2 ; a1 = (a1 and a2)==0
a1 = not a1 and not a2 ; a1 = a1==0 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) value1.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
value2.operator shouldBe "==" value2.operator shouldBe "=="
value2.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) value2.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
value3.operator shouldBe "or" value3.operator shouldBe "=="
value4.operator shouldBe "and" 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") { test("asmgen correctly deals with float typecasting in augmented assignment") {

View File

@ -4,18 +4,31 @@
main { main {
sub start() { sub start() {
ubyte @shared rnr = $a0 ; is optimizing this useful? : not a1 or not a2 -> not(a1 and a2) likewise for and.
txt.print_ub(rnr>=$33) bool @shared a1 = true
txt.print_ub(rnr>=$66) bool @shared a2
txt.print_ub(rnr>=$99) bool @shared a4
txt.print_ub(rnr>=$cc)
txt.nl()
ubyte wordNr = (rnr >= $33) as ubyte + (rnr >= $66) as ubyte + (rnr >= $99) as ubyte + (rnr >= $CC) as ubyte if a1==0 and a2==0
txt.print_uw(wordNr) cx16.r0++
txt.nl()
wordNr = 100 - (rnr >= $33) - (rnr >= $66) - (rnr >= $99) - (rnr >= $CC) if (a1!=0 or a2!=0)==0
txt.print_uw(wordNr) cx16.r0++
txt.nl()
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++
} }
} }