From f85d8edeba7de2d6c13f89fdcd029cfd4b6e35ce Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 25 Sep 2018 22:16:32 +0200 Subject: [PATCH] added some more division by zero checks bitwise operator optimizations --- compiler/examples/test.p8 | 23 ++--- compiler/src/prog8/ast/AstChecker.kt | 11 +++ .../prog8/optimizing/ConstExprEvaluator.kt | 28 +++--- .../prog8/optimizing/SimplifyExpressions.kt | 90 ++++++++++++++++--- 4 files changed, 113 insertions(+), 39 deletions(-) diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index faac9d7d8..7e731f1da 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -4,26 +4,15 @@ sub start() -> () { - byte i - - float yy word pixely - word yoffset - word height + pixely = A % 0 ; @todo divide 0 + pixely = A / 0 ; @todo divide 0 + pixely = A // 0 ; @todo divide 0 - ; @todo expression must not result in float but in word - yy = flt(height+1.1) - pixely = height / 100 - ;yy = height- 1.1 - ;yy = height*1.1 - ;yy = height/3.6 - ;yy = height//3.6 - ;yy = height**3.6 - ;yy = height%3.6 - ;yy = height/3.6+0.4 - ;yy = 2/height/3.6+0.4 - ;yy = (pixely-yoffset)/height/3.6+0.4 + pixely |= 1 ; pixely = pixely | 1 + pixely &= 1 ; pixely = pixely & 1 + pixely ^= 1 ; pixely = pixely ^ 1 } } diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 70909e215..dc394e2b4 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -421,6 +421,17 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions: return super.process(literalValue) } + override fun process(expr: BinaryExpression): IExpression { + when(expr.operator){ + "/", "//", "%" -> { + val numeric = expr.right.constValue(namespace)?.asNumericValue?.toDouble() + if(numeric==0.0) + checkResult.add(ExpressionError("division by zero", expr.right.position)) + } + } + return super.process(expr) + } + override fun process(range: RangeExpr): IExpression { fun err(msg: String) { checkResult.add(SyntaxError(msg, range.position)) diff --git a/compiler/src/prog8/optimizing/ConstExprEvaluator.kt b/compiler/src/prog8/optimizing/ConstExprEvaluator.kt index 446ae76de..f8ec205d6 100644 --- a/compiler/src/prog8/optimizing/ConstExprEvaluator.kt +++ b/compiler/src/prog8/optimizing/ConstExprEvaluator.kt @@ -207,27 +207,31 @@ class ConstExprEvaluator { } } + + private fun divideByZeroError(pos: Position): Unit = throw ExpressionError("division by zero", pos) + + private fun divide(left: LiteralValue, right: LiteralValue): LiteralValue { val error = "cannot divide $left by $right" return when { left.asIntegerValue!=null -> when { right.asIntegerValue!=null -> { - if(right.asIntegerValue==0) throw ExpressionError("attempt to divide by zero", left.position) + if(right.asIntegerValue==0) divideByZeroError(right.position) LiteralValue.optimalNumeric(left.asIntegerValue / right.asIntegerValue, left.position) } right.floatvalue!=null -> { - if(right.floatvalue==0.0) throw ExpressionError("attempt to divide by zero", left.position) + if(right.floatvalue==0.0) divideByZeroError(right.position) LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue / right.floatvalue, position = left.position) } else -> throw ExpressionError(error, left.position) } left.floatvalue!=null -> when { right.asIntegerValue!=null -> { - if(right.asIntegerValue==0) throw ExpressionError("attempt to divide by zero", left.position) + if(right.asIntegerValue==0) divideByZeroError(right.position) LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue / right.asIntegerValue, position = left.position) } right.floatvalue!=null -> { - if(right.floatvalue==0.0) throw ExpressionError("attempt to divide by zero", left.position) + if(right.floatvalue==0.0) divideByZeroError(right.position) LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue / right.floatvalue, position = left.position) } else -> throw ExpressionError(error, left.position) @@ -241,22 +245,22 @@ class ConstExprEvaluator { return when { left.asIntegerValue!=null -> when { right.asIntegerValue!=null -> { - if(right.asIntegerValue==0) throw ExpressionError("attempt to divide by zero", left.position) + if(right.asIntegerValue==0) divideByZeroError(right.position) LiteralValue.optimalInteger(left.asIntegerValue / right.asIntegerValue, left.position) } right.floatvalue!=null -> { - if(right.floatvalue==0.0) throw ExpressionError("attempt to divide by zero", left.position) + if(right.floatvalue==0.0) divideByZeroError(right.position) LiteralValue.optimalInteger(left.asIntegerValue / right.floatvalue, left.position) } else -> throw ExpressionError(error, left.position) } left.floatvalue!=null -> when { right.asIntegerValue!=null -> { - if(right.asIntegerValue==0) throw ExpressionError("attempt to divide by zero", left.position) + if(right.asIntegerValue==0) divideByZeroError(right.position) LiteralValue.optimalInteger(left.floatvalue / right.asIntegerValue, left.position) } right.floatvalue!=null -> { - if(right.floatvalue==0.0) throw ExpressionError("attempt to divide by zero", left.position) + if(right.floatvalue==0.0) divideByZeroError(right.position) LiteralValue.optimalInteger(left.floatvalue / right.floatvalue, left.position) } else -> throw ExpressionError(error, left.position) @@ -270,22 +274,22 @@ class ConstExprEvaluator { return when { left.asIntegerValue!=null -> when { right.asIntegerValue!=null -> { - if(right.asIntegerValue==0) throw ExpressionError("attempt to divide by zero", left.position) + if(right.asIntegerValue==0) divideByZeroError(right.position) LiteralValue.optimalNumeric(left.asIntegerValue % right.asIntegerValue, left.position) } right.floatvalue!=null -> { - if(right.floatvalue==0.0) throw ExpressionError("attempt to divide by zero", left.position) + if(right.floatvalue==0.0) divideByZeroError(right.position) LiteralValue(DataType.FLOAT, floatvalue = left.asIntegerValue % right.floatvalue, position = left.position) } else -> throw ExpressionError(error, left.position) } left.floatvalue!=null -> when { right.asIntegerValue!=null -> { - if(right.asIntegerValue==0) throw ExpressionError("attempt to divide by zero", left.position) + if(right.asIntegerValue==0) divideByZeroError(right.position) LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue % right.asIntegerValue, position = left.position) } right.floatvalue!=null -> { - if(right.floatvalue==0.0) throw ExpressionError("attempt to divide by zero", left.position) + if(right.floatvalue==0.0) divideByZeroError(right.position) LiteralValue(DataType.FLOAT, floatvalue = left.floatvalue % right.floatvalue, position = left.position) } else -> throw ExpressionError(error, left.position) diff --git a/compiler/src/prog8/optimizing/SimplifyExpressions.kt b/compiler/src/prog8/optimizing/SimplifyExpressions.kt index c35c84343..4f26b6913 100644 --- a/compiler/src/prog8/optimizing/SimplifyExpressions.kt +++ b/compiler/src/prog8/optimizing/SimplifyExpressions.kt @@ -1,7 +1,6 @@ package prog8.optimizing -import prog8.ast.IAstProcessor -import prog8.ast.INameScope +import prog8.ast.* /* todo simplify expression terms: @@ -43,27 +42,98 @@ import prog8.ast.INameScope X**-2 -> 1.0/X/X X**-3 -> 1.0/X/X/X X << 0 -> X - X | 0 -> X - x & 0 -> 0 - X ^ 0 -> X X*Y - X -> X*(Y-1) -X + A -> A - X -X - A -> -(X+A) X % 1 -> constant 0 (if X is byte/word) - X % 2 -> X and 255 (byte) X and 65535 (word) + X % 2 -> X and 1 (if X is byte/word) todo expression optimization: remove redundant builtin function calls - todo expression optimization: simplify logical expression when a term makes it always true or false (1 or 0) todo expression optimization: optimize some simple multiplications into shifts (A*8 -> A<<3, A/4 -> A>>2) todo optimize addition with self into shift 1 (A+=A -> A<<=1) todo expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call) - todo remove or simplify logical aug assigns like A |= 0, A |= true, A |= false (or perhaps turn them into byte values first?) */ -class SimplifyExpressions(namespace: INameScope) : IAstProcessor { +class SimplifyExpressions(private val namespace: INameScope) : IAstProcessor { var optimizationsDone: Int = 0 - // @todo build this optimizer + override fun process(assignment: Assignment): IStatement { + if(assignment.aug_op!=null) { + throw AstException("augmented assignments should have been converted to normal assignments before this optimizer") + } + return super.process(assignment) + } + + override fun process(expr: BinaryExpression): IExpression { + super.process(expr) + val leftVal = expr.left.constValue(namespace) + val rightVal = expr.right.constValue(namespace) + val constTrue = LiteralValue.fromBoolean(true, expr.position) + val constFalse = LiteralValue.fromBoolean(false, expr.position) + + // simplify logical expressions when a term is constant and determines the outcome + when(expr.operator) { + "or" -> { + if((leftVal!=null && leftVal.asBooleanValue) || (rightVal!=null && rightVal.asBooleanValue)) { + optimizationsDone++ + return constTrue + } + if(leftVal!=null && !leftVal.asBooleanValue) { + optimizationsDone++ + return expr.right + } + if(rightVal!=null && !rightVal.asBooleanValue) { + optimizationsDone++ + return expr.left + } + } + "and" -> { + if((leftVal!=null && !leftVal.asBooleanValue) || (rightVal!=null && !rightVal.asBooleanValue)) { + optimizationsDone++ + return constFalse + } + if(leftVal!=null && leftVal.asBooleanValue) { + optimizationsDone++ + return expr.right + } + if(rightVal!=null && rightVal.asBooleanValue) { + optimizationsDone++ + return expr.left + } + } + "xor" -> { + if(leftVal!=null && !leftVal.asBooleanValue) { + optimizationsDone++ + return expr.right + } + if(rightVal!=null && !rightVal.asBooleanValue) { + optimizationsDone++ + return expr.left + } + if(leftVal!=null && leftVal.asBooleanValue) { + optimizationsDone++ + return PrefixExpression("not", expr.right, expr.right.position) + } + if(rightVal!=null && rightVal.asBooleanValue) { + optimizationsDone++ + return PrefixExpression("not", expr.left, expr.left.position) + } + } + "|", "^" -> { + if(leftVal!=null && !leftVal.asBooleanValue) + return expr.right + if(rightVal!=null && !rightVal.asBooleanValue) + return expr.left + } + "&" -> { + if(leftVal!=null && !leftVal.asBooleanValue) + return constFalse + if(rightVal!=null && !rightVal.asBooleanValue) + return constFalse + } + } + return expr + } } \ No newline at end of file