From 5e729e21fface5966ae8e1705bca129c5fbf0191 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 27 Sep 2018 01:35:35 +0200 Subject: [PATCH] added some more constant foldings --- compiler/examples/mandelbrot.p8 | 20 ++-- compiler/examples/test.p8 | 18 ++-- compiler/src/prog8/ast/AST.kt | 2 +- .../prog8/optimizing/ConstExprEvaluator.kt | 4 + .../src/prog8/optimizing/ConstantFolding.kt | 99 ++++++++++++++++++- .../prog8/optimizing/SimplifyExpressions.kt | 1 - 6 files changed, 124 insertions(+), 20 deletions(-) diff --git a/compiler/examples/mandelbrot.p8 b/compiler/examples/mandelbrot.p8 index 7e86c6fbc..15eef20b0 100644 --- a/compiler/examples/mandelbrot.p8 +++ b/compiler/examples/mandelbrot.p8 @@ -14,8 +14,8 @@ float yy float x float y - float xsq - float ysq + float xsquared + float ysquared byte iter word plotx byte ploty @@ -24,21 +24,21 @@ _vm_gfx_text(5, 5, 7, "Calculating Mandelbrot Fractal...") for pixely in yoffset to yoffset+height-1 { - yy = flt((pixely-yoffset))/height/3.6+0.4 ; @todo why is /height/3.6 not const-folded??? + yy = flt((pixely-yoffset))/height/3.6+0.4 for pixelx in xoffset to xoffset+width-1 { - xx = flt((pixelx-xoffset))/width/3+0.2 ; @todo why is /width/3 not const-folded??? + xx = flt((pixelx-xoffset))/width/3+0.2 x = 0.0 y = 0.0 - xsq = 0 - ysq = 0 + xsquared = 0 + ysquared = 0 iter = 0 - while (iter<32 and xsq+ysq<4) { + while (iter<32 and xsquared+ysquared<4) { y = x*y*2 + yy - x = xsq - ysq + xx - xsq = x*x - ysq = y*y + x = xsquared - ysquared + xx + xsquared = x*x + ysquared = y*y iter++ } diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index 7e731f1da..5e840063f 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -4,15 +4,21 @@ sub start() -> () { + const word yoffset=100 + const float height=20.2 word pixely + float yy + float v - pixely = A % 0 ; @todo divide 0 - pixely = A / 0 ; @todo divide 0 - pixely = A // 0 ; @todo divide 0 + yy = 11.0-(v-22.0) + yy = 11.0-(22.0-v) + yy = (v-22.0)-11.0 + yy = (22.0-v)-11.0 - pixely |= 1 ; pixely = pixely | 1 - pixely &= 1 ; pixely = pixely & 1 - pixely ^= 1 ; pixely = pixely ^ 1 + yy = 11.0/(v/22.0) + yy = 11.0/(22.0/v) + yy = (v/22.0)/11.0 + yy = (22.0/v)/11.0 } } diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index cb9bce8d3..9407272f5 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -709,7 +709,7 @@ class PrefixExpression(val operator: String, var expression: IExpression, overri } -class BinaryExpression(var left: IExpression, val operator: String, var right: IExpression, override val position: Position) : IExpression { +class BinaryExpression(var left: IExpression, var operator: String, var right: IExpression, override val position: Position) : IExpression { override lateinit var parent: Node override fun linkParents(parent: Node) { diff --git a/compiler/src/prog8/optimizing/ConstExprEvaluator.kt b/compiler/src/prog8/optimizing/ConstExprEvaluator.kt index fdcdc8010..cde1842ad 100644 --- a/compiler/src/prog8/optimizing/ConstExprEvaluator.kt +++ b/compiler/src/prog8/optimizing/ConstExprEvaluator.kt @@ -3,6 +3,10 @@ package prog8.optimizing import prog8.ast.* import kotlin.math.pow + +val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=") + + class ConstExprEvaluator { fun evaluate(left: LiteralValue, operator: String, right: LiteralValue): IExpression { diff --git a/compiler/src/prog8/optimizing/ConstantFolding.kt b/compiler/src/prog8/optimizing/ConstantFolding.kt index d73619efc..9ef5e09c5 100644 --- a/compiler/src/prog8/optimizing/ConstantFolding.kt +++ b/compiler/src/prog8/optimizing/ConstantFolding.kt @@ -171,14 +171,49 @@ class ConstantFolding(private val namespace: INameScope) : IAstProcessor { * Try to process a binary expression. * Compile-time constant sub expressions will be evaluated on the spot. * For instance, "9 * (4 + 2)" will be optimized into the integer literal 54. + * + * More complex: dealing with associative operators: + * if our operator is +,-,*,/ THEN: + * if one of our operands is a const, THEN: + * if the other operand is also a binary expression THEN: + * if its operator is in the same group as our operator (+ and -, * and /) THEN: + * if one of its operands is a const THEN: + * reorder the expression by lifting out the calculation on the 2 consts. (so that it can be const-folded later) */ override fun process(expr: BinaryExpression): IExpression { return try { super.process(expr) - - val evaluator = ConstExprEvaluator() val leftconst = expr.left.constValue(namespace) val rightconst = expr.right.constValue(namespace) + + // check associative operators and const operands + if(setOf("+", "-", "*", "/").contains(expr.operator)) { + val subExpr: BinaryExpression? = when { + leftconst!=null -> expr.right as? BinaryExpression + rightconst!=null -> expr.left as? BinaryExpression + else -> null + } + if(subExpr!=null) { + val sameGroupOperator = + when (expr.operator) { + "+", "-" -> subExpr.operator == "+" || subExpr.operator == "-" + "*", "/" -> subExpr.operator == "*" || subExpr.operator == "/" + else -> false + } + if (sameGroupOperator) { + val subleftconst = subExpr.left.constValue(namespace) + val subrightconst = subExpr.right.constValue(namespace) + if (subleftconst != null || subrightconst != null) { + // reorder! + return reorderAssociativeConst(expr, subExpr, leftconst!=null, rightconst!=null, subleftconst!=null, subrightconst!=null) + } + } + } + } + + + // const fold when both operands are a const + val evaluator = ConstExprEvaluator() return when { leftconst != null && rightconst != null -> { optimizationsDone++ @@ -192,6 +227,66 @@ class ConstantFolding(private val namespace: INameScope) : IAstProcessor { } } + private fun reorderAssociativeConst(expr: BinaryExpression, + subExpr: BinaryExpression, + leftIsConst: Boolean, + rightIsConst: Boolean, + subleftIsConst: Boolean, + subrightIsConst: Boolean): IExpression { + + if(expr.operator==subExpr.operator) { + // both operators are the same. + + // If + or *, we can simply swap the const of expr and Var in subexpr. + if(expr.operator=="+" || expr.operator=="*") { + if(leftIsConst) { + if(subleftIsConst) + expr.left = subExpr.right.also { subExpr.right = expr.left } + else + expr.left = subExpr.left.also { subExpr.left = expr.left } + } else { + if(subleftIsConst) + expr.right = subExpr.right.also {subExpr.right = expr.right } + else + expr.right = subExpr.left.also { subExpr.left = expr.right } + } + optimizationsDone++ + return expr + } + + // If - or /, we simetimes must reorder more, and flip operators (- -> +, / -> *) + if(expr.operator=="-" || expr.operator=="/") { + optimizationsDone++ + if(leftIsConst) { + if(subleftIsConst) { + val tmp = subExpr.right + subExpr.right = subExpr.left + subExpr.left = expr.left + expr.left = tmp + expr.operator = if(expr.operator=="-") "+" else "*" + } else + return BinaryExpression( + BinaryExpression(expr.left, if(expr.operator=="-") "+" else "*", subExpr.right, subExpr.position), + expr.operator, subExpr.left, expr.position) + } else { + if(subleftIsConst) + expr.right = subExpr.right.also {subExpr.right = expr.right} + else + return BinaryExpression( + subExpr.left, expr.operator, + BinaryExpression(expr.right, if(expr.operator=="-") "+" else "*", subExpr.right, subExpr.position), + expr.position) + } + return expr + } + + // todo: other combinations of operators and constants + return expr + } else { + return expr // TODO reorder when operators are not identical + } + } + override fun process(range: RangeExpr): IExpression { range.from = range.from.process(this) range.to = range.to.process(this) diff --git a/compiler/src/prog8/optimizing/SimplifyExpressions.kt b/compiler/src/prog8/optimizing/SimplifyExpressions.kt index 50bff685d..7d50d010b 100644 --- a/compiler/src/prog8/optimizing/SimplifyExpressions.kt +++ b/compiler/src/prog8/optimizing/SimplifyExpressions.kt @@ -115,7 +115,6 @@ class SimplifyExpressions(private val namespace: INameScope) : IAstProcessor { private data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: LiteralValue?, val rightVal: LiteralValue?) private fun reorderAssociative(expr: BinaryExpression, leftVal: LiteralValue?): ReorderedAssociativeBinaryExpr { - val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=") if(associativeOperators.contains(expr.operator) && leftVal!=null) { // swap left and right so that right is always the constant val tmp = expr.left