From bb1cda09164c294fcd6bcce6312f7e751e5e6a37 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 26 Jun 2022 23:55:34 +0200 Subject: [PATCH] fix: boolean values of terms in logical expressions are now properly evaluated --- .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 4 ++ .../prog8/codegen/virtual/BuiltinFuncGen.kt | 8 +++ .../astprocessing/StatementReorderer.kt | 63 ++++++++++++++++++- .../compiler/astprocessing/VariousCleanups.kt | 9 --- .../src/prog8/compiler/BuiltinFunctions.kt | 13 +--- docs/source/programming.rst | 2 +- docs/source/todo.rst | 1 + examples/test.p8 | 24 +++++-- 8 files changed, 98 insertions(+), 26 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index d646180eb..118b94931 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -702,10 +702,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, when (val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)) { in ByteDatatypes -> { assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.A, dt==DataType.BYTE) + asmgen.out(" beq + | lda #1") + asmgen.out("+") } in WordDatatypes -> { assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY, dt==DataType.WORD) asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1") + asmgen.out(" beq + | lda #1") + asmgen.out("+") } DataType.FLOAT -> { assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.FAC1, true) diff --git a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt index e174a3938..e67c771b8 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt @@ -368,12 +368,20 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: } VmDataType.WORD -> { val msbReg = codeGen.vmRegisters.nextFree() + val skipLabel = codeGen.createLabelName() code += exprGen.translateExpression(call.args.single(), resultRegister, -1) code += VmCodeInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = msbReg, reg2=resultRegister) code += VmCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg) + code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=resultRegister, labelSymbol = skipLabel) + code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value = 1) + code += VmCodeLabel(skipLabel) } else -> { + val skipLabel = codeGen.createLabelName() code += exprGen.translateExpression(call.args.single(), resultRegister, -1) + code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=resultRegister, labelSymbol = skipLabel) + code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value = 1) + code += VmCodeLabel(skipLabel) } } return code diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index 4a1e98092..3c0b0d2ce 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -249,7 +249,7 @@ internal class StatementReorderer(val program: Program, } else if(expr.operator in LogicalOperators) { fun wrapped(expr: Expression): Expression { - return if(expr.inferType(program).isBytes) + return if(expr is IFunctionCall && expr.target.nameInSource==listOf("boolean")) expr else FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position) @@ -320,6 +320,17 @@ internal class StatementReorderer(val program: Program, // rewrite in-place assignment expressions a bit so that the assignment target usually is the leftmost operand val binExpr = assignment.value as? BinaryExpression if(binExpr!=null) { +// if (binExpr.operator == "and") { +// val leftBinExpr = binExpr.left as? BinaryExpression +// if (leftBinExpr?.operator == "and") +// return mcCarthyAndAssignment(binExpr, assignment, parent) +// } +// if (binExpr.operator == "or") { +// val leftBinExpr = binExpr.left as? BinaryExpression +// if (leftBinExpr?.operator == "or") +// return mcCarthyOrAssignment(binExpr, assignment, parent) +// } + if(binExpr.left isSameAs assignment.target) { // A = A 5, unchanged return noModifications @@ -425,6 +436,56 @@ internal class StatementReorderer(val program: Program, checkUnusedReturnValues(functionCallStatement, function, errors) return tryReplaceCallWithGosub(functionCallStatement, parent, program, options) } + + private fun mcCarthyAndAssignment(andExpr: BinaryExpression, assignment: Assignment, assignmentParent: Node): Iterable { + val andTerms = findTerms(andExpr, "and") + if(andTerms.any { it.constValue(program)?.asBooleanValue == false }) { + errors.warn("expression is always false", andExpr.position) + return listOf(IAstModification.ReplaceNode(andExpr, NumericLiteral.fromBoolean(false, andExpr.position), assignment)) + } + + // TODO: + // repeat for all terms: + // assign term to target + // add if: if target==0: goto done + // done:. + + return noModifications + } + + private fun mcCarthyOrAssignment(orExpr: BinaryExpression, assignment: Assignment, assignmentParent: Node): Iterable { + val andTerms = findTerms(orExpr, "or") + if(andTerms.any { it.constValue(program)?.asBooleanValue == true }) { + errors.warn("expression is always true", orExpr.position) + return listOf(IAstModification.ReplaceNode(orExpr, NumericLiteral.fromBoolean(true, orExpr.position), assignment)) + } + + // TODO: + // repeat for all terms: + // assign term to target + // add if: if target!=0: goto done + // done:. + + return noModifications + } + + private fun findTerms(expr: BinaryExpression, operator: String, terms: List = emptyList()): List { + if(expr.operator!=operator) + return listOf(expr) + terms + val leftBinExpr = expr.left as? BinaryExpression ?: return listOf(expr.left, expr.right) + terms + return findTerms(leftBinExpr, operator, listOf(expr.right)+terms) + } + + private fun mcCarthyAndExpression(e1: Expression, e2: Expression, e3: Expression, origAndExpression: BinaryExpression, parent: Node): Iterable { + // TODO + println("AND EXPRESSION:") + println(" $e1") + println(" $e2") + println(" $e3") + println(" (orig) $origAndExpression") + val replacement = NumericLiteral.fromBoolean(false, origAndExpression.position) + return listOf(IAstModification.ReplaceNode(origAndExpression, replacement, parent)) + } } diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index 57842a093..31babc35f 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -233,14 +233,5 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable { return tryReplaceCallWithGosub(functionCallStatement, parent, program, options) } - - override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable { - if(functionCallExpr.target.nameInSource==listOf("boolean")) { - if(functionCallExpr.args[0].inferType(program).isBytes) { - return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent)) - } - } - return noModifications - } } diff --git a/compilerAst/src/prog8/compiler/BuiltinFunctions.kt b/compilerAst/src/prog8/compiler/BuiltinFunctions.kt index 71d102616..3a7923204 100644 --- a/compilerAst/src/prog8/compiler/BuiltinFunctions.kt +++ b/compilerAst/src/prog8/compiler/BuiltinFunctions.kt @@ -255,17 +255,10 @@ private fun builtinBoolean(args: List, position: Position, program: if (args.size != 1) throw SyntaxError("boolean requires one argument", position) val constvalue = args[0].constValue(program) ?: throw NotConstArgumentException() - return if(constvalue.number==0.0) - NumericLiteral(DataType.UBYTE, 0.0, args[0].position) - else if(constvalue.type==DataType.UBYTE) - constvalue - else if(constvalue.type==DataType.FLOAT) + return if(constvalue.type==DataType.FLOAT) NumericLiteral(DataType.UBYTE, constvalue.number.sign, args[0].position) - else { - val value = constvalue.number.toInt() - val byteValue = ((value ushr 8) or value) and 255 - NumericLiteral(DataType.UBYTE, byteValue.toDouble(), args[0].position) - } + else + NumericLiteral.fromBoolean(constvalue.number!=0.0, args[0].position) } private fun builtinSgn(args: List, position: Position, program: Program): NumericLiteral { diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 97d6045c8..6947c28f3 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -777,7 +777,7 @@ Miscellaneous boolean(x) Returns a byte value representing the boolean (truthy) value of x, where x can be any numeric type. - This means it returns 0 (false) when x equals 0, and a value other than 0 otherwise. + This means it returns 0 (false) when x equals 0, and 1 otherwise. cmp(x,y) Compare the integer value x to integer value y. Doesn't return a value or boolean result, only sets the processor's status bits! diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 94fd7e253..d13e9b0c0 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -4,6 +4,7 @@ TODO For next release ^^^^^^^^^^^^^^^^ - add McCarthy evaluation to shortcircuit and/or expressions. Both conditional expressions and assignments! + StatementReorder.after(assignment). - add some more optimizations in vmPeepholeOptimizer - vm Instruction needs to know what the read-registers/memory are, and what the write-register/memory is. this info is needed for more advanced optimizations and later code generation steps. diff --git a/examples/test.p8 b/examples/test.p8 index 2c40ae5ed..a0abf244e 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -48,10 +48,24 @@ main { sub start() { ubyte value uword wvalue - byte svalue = -99 - wvalue = 3*abs(svalue) +abs(svalue)+abs(svalue)+abs(svalue) - txt.print_uw(wvalue) + ubyte ub1 = 11 + ubyte ub2 = 22 + ubyte ub3 = 33 + ubyte ub4 = 44 + ubyte ub5 = 55 + + ub4 = 42 + txt.print("and with bytes: ") + ub5 = ub1 and ub2 and ub3 and ub4 and ub5 ; TODO FIX !! should be True (!=0) + txt.print_ub(ub5) + txt.nl() + + ub4 = 42 + txt.print("or with bytes: ") + ub5 = ub1 or ub2 or ub3 or ub4 or ub5 ; TODO FIX!! should be False (0) + txt.print_ub(ub5) + txt.nl() txt.print("short and with false (word): ") wvalue = funcw() and funcFalseWord() and funcw() and funcw() @@ -72,7 +86,7 @@ main { txt.nl() txt.print("and with false: ") - value = func1(25) and func2(25) and funcFalse() and func3(25) and func4(25) + value = func1(25) and func2(25) and funcFalse() and false and func3(25) and func4(25) txt.print_ub(value) txt.nl() txt.print("and with true: ") @@ -80,7 +94,7 @@ main { txt.print_ub(value) txt.nl() txt.print("or with false: ") - value = func1(25) or func2(25) or funcFalse() or func3(25) or func4(25) + value = func1(25) or func2(25) or funcFalse() or true or func3(25) or func4(25) txt.print_ub(value) txt.nl() txt.print("or with true: ")