better not(x) replacement by x==0

This commit is contained in:
Irmen de Jong 2022-06-28 03:38:13 +02:00
parent 435d6f6f3f
commit dc82a0fc16
4 changed files with 185 additions and 167 deletions

View File

@ -115,15 +115,9 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
override fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> { override fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
if(expr.operator == "not") { if(expr.operator == "not") {
// To enable simple bitwise and/or/xor/not instructions in the codegen for the logical and/or/xor/not, // not(x) --> x==0
// we wrap the operands in a call to boolean() if required so that they are 0 or 1 as needed. val dt = expr.expression.inferType(program).getOr(DataType.UNDEFINED)
// Making the codegen more generic to do this by itself all the time will generate much larger val replacement = BinaryExpression(expr.expression, "==", NumericLiteral(dt,0.0, expr.position), expr.position)
// code because it is hard to decide there if the value conversion to 0 or 1 is needed or not,
// so a lot of useless checks and conversions are added. Here we can be smarter so the codegen
// can just rely on the correct value of the operands (0 or 1) if they're boolean, and just use bitwise instructions.
// not(x) --> boolean(x)==0
val replacement = BinaryExpression(wrapWithBooleanConversion(expr.expression), "==", NumericLiteral.optimalInteger(0, expr.position), expr.position)
return listOf(IAstModification.ReplaceNodeSafe(expr, replacement, parent)) return listOf(IAstModification.ReplaceNodeSafe(expr, replacement, parent))
} }
return noModifications return noModifications

View File

@ -462,8 +462,7 @@ class TestOptimization: FunSpec({
ubyte z2 ubyte z2
z2 = 255 z2 = 255
ubyte z3 ubyte z3
z3 = 0 z3 = 1
z3 = (boolean(z3)==0)
uword z4 uword z4
z4 = 0 z4 = 0
ubyte z5 ubyte z5
@ -474,30 +473,28 @@ class TestOptimization: FunSpec({
z6 -= 5 z6 -= 5
*/ */
val statements = result.program.entrypoint.statements val statements = result.program.entrypoint.statements
statements.size shouldBe 15 statements.size shouldBe 14
val z1decl = statements[0] as VarDecl val z1decl = statements[0] as VarDecl
val z1init = statements[1] as Assignment val z1init = statements[1] as Assignment
val z2decl = statements[2] as VarDecl val z2decl = statements[2] as VarDecl
val z2init = statements[3] as Assignment val z2init = statements[3] as Assignment
val z3decl = statements[4] as VarDecl val z3decl = statements[4] as VarDecl
val z3init = statements[5] as Assignment val z3init = statements[5] as Assignment
val z3not = statements[6] as Assignment val z4decl = statements[6] as VarDecl
val z4decl = statements[7] as VarDecl val z4init = statements[7] as Assignment
val z4init = statements[8] as Assignment val z5decl = statements[8] as VarDecl
val z5decl = statements[9] as VarDecl val z5init = statements[9] as Assignment
val z5init = statements[10] as Assignment val z5plus = statements[10] as Assignment
val z5plus = statements[11] as Assignment val z6decl = statements[11] as VarDecl
val z6decl = statements[12] as VarDecl val z6init = statements[12] as Assignment
val z6init = statements[13] as Assignment val z6plus = statements[13] as Assignment
val z6plus = statements[14] as Assignment
z1decl.name shouldBe "z1" z1decl.name shouldBe "z1"
z1init.value shouldBe NumericLiteral(DataType.UBYTE, 10.0, Position.DUMMY) z1init.value shouldBe NumericLiteral(DataType.UBYTE, 10.0, Position.DUMMY)
z2decl.name shouldBe "z2" z2decl.name shouldBe "z2"
z2init.value shouldBe NumericLiteral(DataType.UBYTE, 255.0, Position.DUMMY) z2init.value shouldBe NumericLiteral(DataType.UBYTE, 255.0, Position.DUMMY)
z3decl.name shouldBe "z3" z3decl.name shouldBe "z3"
z3init.value shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) z3init.value shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
z3not.value shouldBe instanceOf<BinaryExpression>()
z4decl.name shouldBe "z4" z4decl.name shouldBe "z4"
z4init.value shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY) z4init.value shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
z5decl.name shouldBe "z5" z5decl.name shouldBe "z5"

View File

@ -20,155 +20,181 @@ main {
ubyte ub4 = 0 ubyte ub4 = 0
ubyte bvalue ubyte bvalue
txt.print("bitwise or 14: ") txt.print("const not 126: ")
txt.print_ub(ub1 | ub2 | ub3 | ub4) txt.print_ub(not 129)
txt.nl() txt.nl()
txt.print("bitwise or 142: ") txt.print("const not 255: ")
txt.print_ub(ub1 | ub2 | ub3 | ub4 | 128) txt.print_ub(not 0)
txt.nl() txt.nl()
txt.print("bitwise and 0: ") bvalue = 129
txt.print_ub(ub1 & ub2 & ub3 & ub4) txt.print("bitwise not 126: ")
txt.nl() bvalue = not bvalue
txt.print("bitwise and 8: ") txt.print_ub(bvalue)
txt.print_ub(ub3 & ub3 & 127)
txt.nl()
txt.print("bitwise xor 14: ")
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ ub4)
txt.nl()
txt.print("bitwise xor 6: ")
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ 8)
txt.nl()
txt.print("bitwise not 247: ")
txt.print_ub(~ub3)
txt.nl() txt.nl()
bvalue = 0
txt.print("bitwise not 255: ") txt.print("bitwise not 255: ")
txt.print_ub(~ub4) bvalue = not bvalue
txt.print_ub(bvalue)
txt.nl() txt.nl()
txt.print("not 0: ") ; txt.print("bitwise or 14: ")
bvalue = not ub3 ; txt.print_ub(ub1 | ub2 | ub3 | ub4)
txt.print_ub(bvalue) ; txt.nl()
if not ub1 ; txt.print("bitwise or 142: ")
txt.print(" / fail") ; txt.print_ub(ub1 | ub2 | ub3 | ub4 | 128)
else ; txt.nl()
txt.print(" / ok") ; txt.print("bitwise and 0: ")
txt.nl() ; txt.print_ub(ub1 & ub2 & ub3 & ub4)
; txt.nl()
txt.print("not 1: ") ; txt.print("bitwise and 8: ")
bvalue = not ub4 ; txt.print_ub(ub3 & ub3 & 127)
txt.print_ub(bvalue) ; txt.nl()
if not ub4 ; txt.print("bitwise xor 14: ")
txt.print(" / ok") ; txt.print_ub(ub1 ^ ub2 ^ ub3 ^ ub4)
else ; txt.nl()
txt.print(" / fail") ; txt.print("bitwise xor 6: ")
txt.nl() ; txt.print_ub(ub1 ^ ub2 ^ ub3 ^ 8)
; txt.nl()
bvalue = bvalue and 128 ; txt.print("bitwise not 247: ")
txt.print("bvl 1: ") ; txt.print_ub(~ub3)
txt.print_ub(bvalue) ; txt.nl()
if bvalue and 128 ; txt.print("bitwise not 255: ")
txt.print(" / ok") ; txt.print_ub(~ub4)
else ; txt.nl()
txt.print(" / fail") ;
txt.nl() ; txt.print("not 0: ")
; bvalue = 3 * (ub4 | not (ub3 | ub3 | ub3))
txt.print("and 1: ") ; txt.print_ub(bvalue)
bvalue = ub1 and ub2 and ub3 ; if 3*(ub4 | not (ub1 | ub1 | ub1))
txt.print_ub(bvalue) ; txt.print(" / fail")
if ub1 and ub2 and ub3 ; else
txt.print(" / ok") ; txt.print(" / ok")
else ; txt.nl()
txt.print(" / fail") ;
txt.nl() ; txt.print("not 0: ")
txt.print("and 1: ") ; bvalue = not ub3
bvalue = ub1 and ub2 and ub3 and 64 ; txt.print_ub(bvalue)
txt.print_ub(bvalue) ; if not ub1
if ub1 and ub2 and ub3 and 64 ; txt.print(" / fail")
txt.print(" / ok") ; else
else ; txt.print(" / ok")
txt.print(" / fail") ; txt.nl()
txt.nl() ;
txt.print("and 1: ") ; txt.print("not 1: ")
bvalue = ub1 and ub2 and ub3 and ftrue(99) ; bvalue = not ub4
txt.print_ub(bvalue) ; txt.print_ub(bvalue)
if ub1 and ub2 and ub3 and ftrue(99) ; if not ub4
txt.print(" / ok") ; txt.print(" / ok")
else ; else
txt.print(" / fail") ; txt.print(" / fail")
txt.nl() ; txt.nl()
txt.print("and 0: ") ;
bvalue = ub1 and ub2 and ub3 and ub4 ; bvalue = bvalue and 128
txt.print_ub(bvalue) ; txt.print("bvl 1: ")
if ub1 and ub2 and ub3 and ub4 ; txt.print_ub(bvalue)
txt.print(" / fail") ; if bvalue and 128
else ; txt.print(" / ok")
txt.print(" / ok") ; else
txt.nl() ; txt.print(" / fail")
txt.print("and 0: ") ; txt.nl()
bvalue = ub1 and ub2 and ub3 and ffalse(99) ;
txt.print_ub(bvalue) ; txt.print("and 1: ")
if ub1 and ub2 and ub3 and ffalse(99) ; bvalue = ub1 and ub2 and ub3
txt.print(" / fail") ; txt.print_ub(bvalue)
else ; if ub1 and ub2 and ub3
txt.print(" / ok") ; txt.print(" / ok")
txt.nl() ; else
; txt.print(" / fail")
txt.print(" or 1: ") ; txt.nl()
bvalue = ub1 or ub2 or ub3 or ub4 ; txt.print("and 1: ")
txt.print_ub(bvalue) ; bvalue = ub1 and ub2 and ub3 and 64
if ub1 or ub2 or ub3 or ub4 ; txt.print_ub(bvalue)
txt.print(" / ok") ; if ub1 and ub2 and ub3 and 64
else ; txt.print(" / ok")
txt.print(" / fail") ; else
txt.nl() ; txt.print(" / fail")
txt.print(" or 1: ") ; txt.nl()
bvalue = ub4 or ub4 or ub1 ; txt.print("and 1: ")
txt.print_ub(bvalue) ; bvalue = ub1 and ub2 and ub3 and ftrue(99)
if ub4 or ub4 or ub1 ; txt.print_ub(bvalue)
txt.print(" / ok") ; if ub1 and ub2 and ub3 and ftrue(99)
else ; txt.print(" / ok")
txt.print(" / fail") ; else
txt.nl() ; txt.print(" / fail")
txt.print(" or 1: ") ; txt.nl()
bvalue = ub1 or ub2 or ub3 or ftrue(99) ; txt.print("and 0: ")
txt.print_ub(bvalue) ; bvalue = ub1 and ub2 and ub3 and ub4
if ub1 or ub2 or ub3 or ftrue(99) ; txt.print_ub(bvalue)
txt.print(" / ok") ; if ub1 and ub2 and ub3 and ub4
else ; txt.print(" / fail")
txt.print(" / fail") ; else
txt.nl() ; txt.print(" / ok")
; txt.nl()
txt.print("xor 1: ") ; txt.print("and 0: ")
bvalue = ub1 xor ub2 xor ub3 xor ub4 ; bvalue = ub1 and ub2 and ub3 and ffalse(99)
txt.print_ub(bvalue) ; txt.print_ub(bvalue)
if ub1 xor ub2 xor ub3 xor ub4 ; if ub1 and ub2 and ub3 and ffalse(99)
txt.print(" / ok") ; txt.print(" / fail")
else ; else
txt.print(" / fail") ; txt.print(" / ok")
txt.nl() ; txt.nl()
txt.print("xor 1: ") ;
bvalue = ub1 xor ub2 xor ub3 xor ffalse(99) ; txt.print(" or 1: ")
txt.print_ub(bvalue) ; bvalue = ub1 or ub2 or ub3 or ub4
if ub1 xor ub2 xor ub3 xor ffalse(99) ; txt.print_ub(bvalue)
txt.print(" / ok") ; if ub1 or ub2 or ub3 or ub4
else ; txt.print(" / ok")
txt.print(" / fail") ; else
txt.nl() ; txt.print(" / fail")
; txt.nl()
txt.print("xor 0: ") ; txt.print(" or 1: ")
bvalue = ub1 xor ub2 xor ub3 xor ub4 xor true ; bvalue = ub4 or ub4 or ub1
txt.print_ub(bvalue) ; txt.print_ub(bvalue)
if ub1 xor ub2 xor ub3 xor ub4 xor true ; if ub4 or ub4 or ub1
txt.print(" / fail") ; txt.print(" / ok")
else ; else
txt.print(" / ok") ; txt.print(" / fail")
txt.nl() ; txt.nl()
txt.print("xor 0: ") ; txt.print(" or 1: ")
bvalue = ub1 xor ub2 xor ub3 xor ftrue(99) ; bvalue = ub1 or ub2 or ub3 or ftrue(99)
txt.print_ub(bvalue) ; txt.print_ub(bvalue)
if ub1 xor ub2 xor ub3 xor ftrue(99) ; if ub1 or ub2 or ub3 or ftrue(99)
txt.print(" / fail") ; txt.print(" / ok")
else ; else
txt.print(" / ok") ; txt.print(" / fail")
; txt.nl()
;
; txt.print("xor 1: ")
; bvalue = ub1 xor ub2 xor ub3 xor ub4
; txt.print_ub(bvalue)
; if ub1 xor ub2 xor ub3 xor ub4
; txt.print(" / ok")
; else
; txt.print(" / fail")
; txt.nl()
; txt.print("xor 1: ")
; bvalue = ub1 xor ub2 xor ub3 xor ffalse(99)
; txt.print_ub(bvalue)
; if ub1 xor ub2 xor ub3 xor ffalse(99)
; txt.print(" / ok")
; else
; txt.print(" / fail")
; txt.nl()
;
; txt.print("xor 0: ")
; bvalue = ub1 xor ub2 xor ub3 xor ub4 xor true
; txt.print_ub(bvalue)
; if ub1 xor ub2 xor ub3 xor ub4 xor true
; txt.print(" / fail")
; else
; txt.print(" / ok")
; txt.nl()
; txt.print("xor 0: ")
; bvalue = ub1 xor ub2 xor ub3 xor ftrue(99)
; txt.print_ub(bvalue)
; if ub1 xor ub2 xor ub3 xor ftrue(99)
; txt.print(" / fail")
; else
; txt.print(" / ok")
} }
} }

View File

@ -486,6 +486,7 @@ logical: ``not`` ``and`` ``or`` ``xor``
These operators are the usual logical operations that are part of a logical expression to reason These operators are the usual logical operations that are part of a logical expression to reason
about truths (boolean values). The result of such an expression is a 'boolean' value 'true' or 'false' about truths (boolean values). The result of such an expression is a 'boolean' value 'true' or 'false'
(which in reality is just a byte value of 1 or 0). (which in reality is just a byte value of 1 or 0).
Notice that the expression ``not x`` is equivalent to ``x==0``, and the compiler will treat it as such.
.. note:: .. note::
Unlike most other programming languages, there is no short-cirquit or McCarthy-evaluation Unlike most other programming languages, there is no short-cirquit or McCarthy-evaluation