diff --git a/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt b/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt index 7283ebc1a..9bbd21a63 100644 --- a/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt +++ b/codeOptimizers/src/prog8/optimizer/ExpressionSimplifier.kt @@ -193,6 +193,16 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() { } } + // boolvar & 1 --> boolvar + // boolvar & 2 --> false + if(expr.operator=="&" && rightDt in IntegerDatatypes && (leftDt == DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) { + if(rightVal?.number==1.0) { + return listOf(IAstModification.ReplaceNode(expr, expr.left, parent)) + } else if(rightVal?.number!=null && (rightVal.number.toInt() and 1)==0) { + return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(false, expr.position), parent)) + } + } + // simplify when a term is constant and directly determines the outcome val constFalse = NumericLiteral.fromBoolean(false, expr.position) val newExpr: Expression? = when (expr.operator) { diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 1b68e2d4f..4c36145a2 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -462,6 +462,11 @@ internal class AstChecker(private val program: Program, if(numvalue!=null && targetDt.isKnown) checkValueTypeAndRange(targetDt.getOr(DataType.UNDEFINED), numvalue) + if(assignment.isAugmentable && targetDt istype DataType.BOOL) { + val operator = (assignment.value as? BinaryExpression)?.operator + if(operator in setOf("-", "*", "/", "%")) + errors.err("can't use boolean operand with this operator $operator", assignment.position) + } super.visit(assignment) } @@ -890,6 +895,31 @@ internal class AstChecker(private val program: Program, } } + if(leftDt==DataType.BOOL || rightDt==DataType.BOOL || + (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true || + (expr.right as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true) { + if(expr.operator in setOf("<", "<=", ">", ">=")) { + errors.err("can't use boolean operand with this comparison operator", expr.position) + } + if(expr.operator in setOf("-", "*", "/", "%") && (leftDt==DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) { + errors.err("can't use boolean operand with this operator", expr.left.position) + } + if(expr.operator=="+" && (leftDt==DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) { + val rightNum = expr.right.constValue(program)?.number ?: 0.0 + if(rightNum > 1.0) + errors.err("can't use boolean operand with this operator", expr.left.position) + } + if(expr.operator == "==" || expr.operator == "!=") { + val leftNum = expr.left.constValue(program)?.number ?: 0.0 + val rightNum = expr.right.constValue(program)?.number ?: 0.0 + if(leftNum>1.0 || rightNum>1.0 || leftNum<0.0 || rightNum<0.0) { + errors.warn("expression is always false", expr.position) + } + } + if((expr.operator == "/" || expr.operator == "%") && ( rightDt==DataType.BOOL || (expr.right as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) { + errors.err("can't use boolean operand with this operator", expr.right.position) + } + } } override fun visit(typecast: TypecastExpression) { @@ -1121,6 +1151,11 @@ internal class AstChecker(private val program: Program, } } // else if(postIncrDecr.target.memoryAddress != null) { } // a memory location can always be ++/-- + + if(postIncrDecr.target.inferType(program) istype DataType.BOOL) { + errors.err("can't use boolean operand with this operator", postIncrDecr.position) + } + super.visit(postIncrDecr) } diff --git a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt index 6bfc6e0d1..9c32e3d54 100644 --- a/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt +++ b/compiler/src/prog8/compiler/astprocessing/BeforeAsmAstChanger.kt @@ -137,6 +137,22 @@ internal class BeforeAsmAstChanger(val program: Program, override fun after(subroutine: Subroutine, parent: Node): Iterable { + // replace BOOL return type and parameters by UBYTE + if(subroutine.returntypes.any { it==DataType.BOOL } || subroutine.parameters.any {it.type==DataType.BOOL}) { + val newReturnTypes = subroutine.returntypes.map { + if(it==DataType.BOOL) DataType.UBYTE else it + } + val newParams = subroutine.parameters.map { + if(it.type==DataType.BOOL) SubroutineParameter(it.name, DataType.UBYTE, it.position) else it + }.toMutableList() + val newSubroutine = Subroutine(subroutine.name, newParams, newReturnTypes, + subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers, + subroutine.asmAddress, subroutine.isAsmSubroutine, subroutine.inline, subroutine.statements, + subroutine.position) + return listOf(IAstModification.ReplaceNode(subroutine, newSubroutine, parent)) + } + + // Most code generation targets only support subroutine inlining on asmsub subroutines // So we reset the flag here to be sure it doesn't cause problems down the line in the codegen. if(!subroutine.isAsmSubroutine && options.compTarget.name!=VMTarget.NAME) diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index 30fca9da4..ae628543f 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -222,23 +222,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(subroutine: Subroutine, parent: Node): Iterable { - - if(subroutine.returntypes.any { it==DataType.BOOL } || subroutine.parameters.any {it.type==DataType.BOOL}) { - val newReturnTypes = subroutine.returntypes.map { - if(it==DataType.BOOL) DataType.UBYTE else it - } - val newParams = subroutine.parameters.map { - if(it.type==DataType.BOOL) SubroutineParameter(it.name, DataType.UBYTE, it.position) else it - }.toMutableList() - val newSubroutine = Subroutine(subroutine.name, newParams, newReturnTypes, - subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers, - subroutine.asmAddress, subroutine.isAsmSubroutine, subroutine.inline, subroutine.statements, - subroutine.position) - return listOf(IAstModification.ReplaceNode(subroutine, newSubroutine, parent)) - } - return noModifications - } } diff --git a/compilerAst/src/prog8/ast/Extensions.kt b/compilerAst/src/prog8/ast/Extensions.kt index 96266da83..8c451fe61 100644 --- a/compilerAst/src/prog8/ast/Extensions.kt +++ b/compilerAst/src/prog8/ast/Extensions.kt @@ -14,7 +14,7 @@ import prog8.code.core.ZeropageWish fun Program.getTempVar(dt: DataType, altNames: Boolean=false): Pair, VarDecl> { val tmpvarName = if(altNames) { when (dt) { - DataType.UBYTE -> listOf("prog8_lib", "tempvar_ub2") + DataType.UBYTE, DataType.BOOL -> listOf("prog8_lib", "tempvar_ub2") DataType.BYTE -> listOf("prog8_lib", "tempvar_b2") DataType.UWORD -> listOf("prog8_lib", "tempvar_uw2") DataType.WORD -> listOf("prog8_lib", "tempvar_w2") @@ -23,7 +23,7 @@ fun Program.getTempVar(dt: DataType, altNames: Boolean=false): Pair } } else { when (dt) { - DataType.UBYTE -> listOf("prog8_lib", "tempvar_ub") + DataType.UBYTE, DataType.BOOL -> listOf("prog8_lib", "tempvar_ub") DataType.BYTE -> listOf("prog8_lib", "tempvar_b") DataType.UWORD -> listOf("prog8_lib", "tempvar_uw") DataType.WORD -> listOf("prog8_lib", "tempvar_w") diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 648361f95..4fbb399ea 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,9 +3,8 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- add compiler error when using boolean with operators <,<=,>,>= (== and != are ok!) -- add compiler error when using boolean on lhs of operators -,*,/,% (+ is ok!) -- add compiler error when using boolean on rhs operators /,% +- add ARRAY_OF_BOOL array type + - test various scenarios of using bool type vs byte actually produces tighter code make unit test of them? @@ -24,7 +23,6 @@ Future Things and Ideas ^^^^^^^^^^^^^^^^^^^^^^^ Compiler: -- bool data type? see below - 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 2594bfa48..4884f0c7c 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,23 +5,20 @@ main { bool boolvalue1 = true bool boolvalue2 = false - ubyte ubvalue1 = true - ubyte ubvalue2 = false + ubyte @shared ubvalue1 = true + ubyte @shared ubvalue2 = false + + sub thing(bool b1, bool b2) -> bool { + return b1 and b2 + } sub start() { - if ubvalue1<44 or ubvalue1>99 - txt.print("0\n") - if boolvalue1 or boolvalue2 - txt.print("1\n") - - if boolvalue1 and boolvalue2 - txt.print("2\n") - -; if ubvalue1 or ubvalu2 -; txt.print("3\n") -; -; if ubvalue1 and ubvalu2 -; txt.print("4\n") + ubvalue1 = boolvalue1 & 1 + ubvalue2 = 1 & boolvalue1 + ubvalue1 = boolvalue1 & 0 + ubvalue2 = boolvalue1 & 8 + boolvalue1 = boolvalue2 & 1 ==0 + ; boolvar & 1 -> boolvar, (boolvar & 1 == 0) -> not boolvar } }