fix: boolean values of terms in logical expressions are now properly evaluated

This commit is contained in:
Irmen de Jong
2022-06-26 23:55:34 +02:00
parent a6d0ea347c
commit bb1cda0916
8 changed files with 98 additions and 26 deletions

View File

@@ -702,10 +702,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
when (val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)) { when (val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)) {
in ByteDatatypes -> { in ByteDatatypes -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.A, dt==DataType.BYTE) assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.A, dt==DataType.BYTE)
asmgen.out(" beq + | lda #1")
asmgen.out("+")
} }
in WordDatatypes -> { in WordDatatypes -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY, dt==DataType.WORD) assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY, dt==DataType.WORD)
asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1") asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1")
asmgen.out(" beq + | lda #1")
asmgen.out("+")
} }
DataType.FLOAT -> { DataType.FLOAT -> {
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.FAC1, true) assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.FAC1, true)

View File

@@ -368,12 +368,20 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
} }
VmDataType.WORD -> { VmDataType.WORD -> {
val msbReg = codeGen.vmRegisters.nextFree() val msbReg = codeGen.vmRegisters.nextFree()
val skipLabel = codeGen.createLabelName()
code += exprGen.translateExpression(call.args.single(), resultRegister, -1) code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += VmCodeInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = msbReg, reg2=resultRegister) code += VmCodeInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = msbReg, reg2=resultRegister)
code += VmCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg) 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 -> { else -> {
val skipLabel = codeGen.createLabelName()
code += exprGen.translateExpression(call.args.single(), resultRegister, -1) 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 return code

View File

@@ -249,7 +249,7 @@ internal class StatementReorderer(val program: Program,
} }
else if(expr.operator in LogicalOperators) { else if(expr.operator in LogicalOperators) {
fun wrapped(expr: Expression): Expression { fun wrapped(expr: Expression): Expression {
return if(expr.inferType(program).isBytes) return if(expr is IFunctionCall && expr.target.nameInSource==listOf("boolean"))
expr expr
else else
FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position) 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 // rewrite in-place assignment expressions a bit so that the assignment target usually is the leftmost operand
val binExpr = assignment.value as? BinaryExpression val binExpr = assignment.value as? BinaryExpression
if(binExpr!=null) { 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) { if(binExpr.left isSameAs assignment.target) {
// A = A <operator> 5, unchanged // A = A <operator> 5, unchanged
return noModifications return noModifications
@@ -425,6 +436,56 @@ internal class StatementReorderer(val program: Program,
checkUnusedReturnValues(functionCallStatement, function, errors) checkUnusedReturnValues(functionCallStatement, function, errors)
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options) return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
} }
private fun mcCarthyAndAssignment(andExpr: BinaryExpression, assignment: Assignment, assignmentParent: Node): Iterable<IAstModification> {
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<IAstModification> {
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<Expression> = emptyList()): List<Expression> {
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<IAstModification> {
// 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))
}
} }

View File

@@ -233,14 +233,5 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> { override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options) return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
} }
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
if(functionCallExpr.target.nameInSource==listOf("boolean")) {
if(functionCallExpr.args[0].inferType(program).isBytes) {
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
}
}
return noModifications
}
} }

View File

@@ -255,17 +255,10 @@ private fun builtinBoolean(args: List<Expression>, position: Position, program:
if (args.size != 1) if (args.size != 1)
throw SyntaxError("boolean requires one argument", position) throw SyntaxError("boolean requires one argument", position)
val constvalue = args[0].constValue(program) ?: throw NotConstArgumentException() val constvalue = args[0].constValue(program) ?: throw NotConstArgumentException()
return if(constvalue.number==0.0) return if(constvalue.type==DataType.FLOAT)
NumericLiteral(DataType.UBYTE, 0.0, args[0].position)
else if(constvalue.type==DataType.UBYTE)
constvalue
else if(constvalue.type==DataType.FLOAT)
NumericLiteral(DataType.UBYTE, constvalue.number.sign, args[0].position) NumericLiteral(DataType.UBYTE, constvalue.number.sign, args[0].position)
else { else
val value = constvalue.number.toInt() NumericLiteral.fromBoolean(constvalue.number!=0.0, args[0].position)
val byteValue = ((value ushr 8) or value) and 255
NumericLiteral(DataType.UBYTE, byteValue.toDouble(), args[0].position)
}
} }
private fun builtinSgn(args: List<Expression>, position: Position, program: Program): NumericLiteral { private fun builtinSgn(args: List<Expression>, position: Position, program: Program): NumericLiteral {

View File

@@ -777,7 +777,7 @@ Miscellaneous
boolean(x) boolean(x)
Returns a byte value representing the boolean (truthy) value of x, where x can be any numeric type. 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) 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! Compare the integer value x to integer value y. Doesn't return a value or boolean result, only sets the processor's status bits!

View File

@@ -4,6 +4,7 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- add McCarthy evaluation to shortcircuit and/or expressions. Both conditional expressions and assignments! - add McCarthy evaluation to shortcircuit and/or expressions. Both conditional expressions and assignments!
StatementReorder.after(assignment).
- add some more optimizations in vmPeepholeOptimizer - add some more optimizations in vmPeepholeOptimizer
- vm Instruction needs to know what the read-registers/memory are, and what the write-register/memory is. - 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. this info is needed for more advanced optimizations and later code generation steps.

View File

@@ -48,10 +48,24 @@ main {
sub start() { sub start() {
ubyte value ubyte value
uword wvalue uword wvalue
byte svalue = -99
wvalue = 3*abs(svalue) +abs(svalue)+abs(svalue)+abs(svalue) ubyte ub1 = 11
txt.print_uw(wvalue) 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): ") txt.print("short and with false (word): ")
wvalue = funcw() and funcFalseWord() and funcw() and funcw() wvalue = funcw() and funcFalseWord() and funcw() and funcw()
@@ -72,7 +86,7 @@ main {
txt.nl() txt.nl()
txt.print("and with false: ") 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.print_ub(value)
txt.nl() txt.nl()
txt.print("and with true: ") txt.print("and with true: ")
@@ -80,7 +94,7 @@ main {
txt.print_ub(value) txt.print_ub(value)
txt.nl() txt.nl()
txt.print("or with false: ") 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.print_ub(value)
txt.nl() txt.nl()
txt.print("or with true: ") txt.print("or with true: ")