arithmetic statement optimizations added

This commit is contained in:
Irmen de Jong 2018-09-26 00:58:02 +02:00
parent f85d8edeba
commit 00baec12ab
5 changed files with 267 additions and 50 deletions

View File

@ -23,7 +23,6 @@
_vm_gfx_clearscr(11)
_vm_gfx_text(5, 5, 7, "Calculating Mandelbrot Fractal...")
flt(44)
for pixely in yoffset to yoffset+height-1 {
yy = flt((pixely-yoffset))/height/3.6+0.4

View File

@ -807,6 +807,15 @@ class LiteralValue(val type: DataType,
fun fromBoolean(bool: Boolean, position: Position) =
LiteralValue(DataType.BYTE, bytevalue = if(bool) 1 else 0, position=position)
fun fromNumber(value: Number, type: DataType, position: Position) : LiteralValue {
return when(type) {
DataType.BYTE -> LiteralValue(type, bytevalue = value.toShort(), position = position)
DataType.WORD -> LiteralValue(type, wordvalue = value.toInt(), position = position)
DataType.FLOAT -> LiteralValue(type, floatvalue = value.toDouble(), position = position)
else -> throw FatalAstException("non numeric datatype")
}
}
fun optimalNumeric(value: Number, position: Position): LiteralValue {
val floatval = value.toDouble()
return if(floatval == floor(floatval) && floatval in -32768..65535) {

View File

@ -268,6 +268,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
val constVal = assignment.value.constValue(namespace)
if(constVal!=null) {
checkValueTypeAndRange(targetDatatype, null, assignment.value as LiteralValue)
// todo: fix crash here when assignment value is a functioncall sounch as round()
} else {
val sourceDatatype: DataType? = assignment.value.resultingDatatype(namespace)
if(sourceDatatype==null) {

View File

@ -111,10 +111,10 @@ private fun oneDoubleArgOutputInt(args: List<IExpression>, position: Position, n
if(args.size!=1)
throw SyntaxError("built-in function requires one floating point argument", position)
val constval = args[0].constValue(namespace) ?: throw NotConstArgumentException()
if(constval.type!=DataType.FLOAT)
throw SyntaxError("built-in function requires one floating point argument", position)
val float = constval.asNumericValue?.toDouble()!!
val float: Double = when(constval.type) {
DataType.BYTE, DataType.WORD, DataType.FLOAT -> constval.asNumericValue!!.toDouble()
else -> throw SyntaxError("built-in function requires one floating point argument", position)
}
return numericLiteral(function(float).toInt(), args[0].position)
}

View File

@ -1,57 +1,19 @@
package prog8.optimizing
import prog8.ast.*
import kotlin.math.abs
/*
todo simplify expression terms:
X*0 -> 0
X*1 -> X
X*2 -> X << 1
X*3 -> X + (X << 1)
X*4 -> X << 2
X*5 -> X + (X << 2)
X*6 -> (X<<1) + (X << 2)
X*7 -> X + (X<<1) + (X << 2)
X*8 -> X << 3
X*9 -> X + (X<<3)
X*10 -> (X<<1) + (X<<3)
X/1, X//1, X**1 -> just X
X/2, X//2 -> X >> 1 (if X is byte or word)
X/4, X//4 -> X >> 2 (if X is byte or word)
X/8, X//8 -> X >> 3 (if X is byte or word)
X/16, X//16 -> X >> 4 (if X is byte or word)
X/32, X//32 -> X >> 5 (if X is byte or word)
X/64, X//64 -> X >> 6 (if X is byte or word)
X/128, X//128 -> X >> 7 (if X is byte or word)
X / (n>=256) -> 0 (if x is byte) X >> 8 (if X is word)
X // (n>=256) -> 0 (if x is byte) X >> 8 (if X is word)
X+X -> X << 1
X << n << m -> X << (n+m)
1**X -> 1
0**X -> 0
X*-1 -> unary prefix -X
X**0 -> 1
X**1 -> X
X**2 -> X*X
X**3 -> X*X*X
X**0.5 -> sqrt(X)
X**-1 -> 1.0/X
X**-2 -> 1.0/X/X
X**-3 -> 1.0/X/X/X
X << 0 -> X
X*Y - X -> X*(Y-1)
X*Y - Y -> Y*(X-1)
-X + A -> A - X
-X - A -> -(X+A)
X+ (-A) -> X - A
X % 1 -> constant 0 (if X is byte/word)
X % 2 -> X and 1 (if X is byte/word)
todo expression optimization: remove redundant builtin function calls
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)
*/
@ -122,18 +84,264 @@ class SimplifyExpressions(private val namespace: INameScope) : IAstProcessor {
}
}
"|", "^" -> {
if(leftVal!=null && !leftVal.asBooleanValue)
if(leftVal!=null && !leftVal.asBooleanValue) {
optimizationsDone++
return expr.right
if(rightVal!=null && !rightVal.asBooleanValue)
}
if(rightVal!=null && !rightVal.asBooleanValue) {
optimizationsDone++
return expr.left
}
}
"&" -> {
if(leftVal!=null && !leftVal.asBooleanValue)
if(leftVal!=null && !leftVal.asBooleanValue) {
optimizationsDone++
return constFalse
if(rightVal!=null && !rightVal.asBooleanValue)
}
if(rightVal!=null && !rightVal.asBooleanValue) {
optimizationsDone++
return constFalse
}
}
"*" -> return optimizeMultiplication(expr, leftVal, rightVal)
"/", "//" -> return optimizeDivision(expr, leftVal, rightVal)
"+" -> return optimizeAdd(expr, leftVal, rightVal)
"-" -> return optimizeSub(expr, leftVal, rightVal)
"**" -> return optimizePower(expr, leftVal, rightVal)
}
return expr
}
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
expr.left = expr.right
expr.right = tmp
optimizationsDone++
return ReorderedAssociativeBinaryExpr(expr, expr.right.constValue(namespace), leftVal)
}
return ReorderedAssociativeBinaryExpr(expr, leftVal, expr.right.constValue(namespace))
}
private fun optimizeAdd(pexpr: BinaryExpression, pleftVal: LiteralValue?, prightVal: LiteralValue?): IExpression {
if(pleftVal==null && prightVal==null)
return pexpr
val (expr, _, rightVal) = reorderAssociative(pexpr, pleftVal)
if(rightVal!=null) {
// right value is a constant, see if we can optimize
val rightConst: LiteralValue = rightVal
when(rightConst.asNumericValue?.toDouble()) {
0.0 -> {
// left
optimizationsDone++
return expr.left
}
}
}
// no need to check for left val constant (because of associativity)
return expr
}
private fun optimizeSub(expr: BinaryExpression, leftVal: LiteralValue?, rightVal: LiteralValue?): IExpression {
if(leftVal==null && rightVal==null)
return expr
if(rightVal!=null) {
// right value is a constant, see if we can optimize
val rightConst: LiteralValue = rightVal
when(rightConst.asNumericValue?.toDouble()) {
0.0 -> {
// left
optimizationsDone++
return expr.left
}
}
}
if(leftVal!=null) {
// left value is a constant, see if we can optimize
when(leftVal.asNumericValue?.toDouble()) {
0.0 -> {
// -right
optimizationsDone++
return PrefixExpression("-", expr.right, expr.position)
}
}
}
return expr
}
private fun optimizePower(expr: BinaryExpression, leftVal: LiteralValue?, rightVal: LiteralValue?): IExpression {
if(leftVal==null && rightVal==null)
return expr
if(rightVal!=null) {
// right value is a constant, see if we can optimize
val rightConst: LiteralValue = rightVal
when(rightConst.asNumericValue?.toDouble()) {
-3.0 -> {
// -1/(left*left*left)
optimizationsDone++
return BinaryExpression(LiteralValue(DataType.FLOAT, floatvalue = -1.0, position = expr.position), "/",
BinaryExpression(expr.left, "*", BinaryExpression(expr.left, "*", expr.left, expr.position), expr.position),
expr.position)
}
-2.0 -> {
// -1/(left*left)
optimizationsDone++
return BinaryExpression(LiteralValue(DataType.FLOAT, floatvalue = -1.0, position = expr.position), "/",
BinaryExpression(expr.left, "*", expr.left, expr.position),
expr.position)
}
-1.0 -> {
// -1/left
optimizationsDone++
return BinaryExpression(LiteralValue(DataType.FLOAT, floatvalue = -1.0, position = expr.position), "/",
expr.left, expr.position)
}
0.0 -> {
// 1
optimizationsDone++
return LiteralValue.fromNumber(1, rightConst.type, expr.position)
}
0.5 -> {
// sqrt(left)
optimizationsDone++
return FunctionCall(IdentifierReference(listOf("sqrt"), expr.position), listOf(expr.left), expr.position)
}
1.0 -> {
// left
optimizationsDone++
return expr.left
}
2.0 -> {
// left*left
optimizationsDone++
return BinaryExpression(expr.left, "*", expr.left, expr.position)
}
3.0 -> {
// left*left*left
optimizationsDone++
return BinaryExpression(expr.left, "*", BinaryExpression(expr.left, "*", expr.left, expr.position), expr.position)
}
}
}
if(leftVal!=null) {
// left value is a constant, see if we can optimize
when(leftVal.asNumericValue?.toDouble()) {
-1.0 -> {
// -1
optimizationsDone++
return LiteralValue(DataType.FLOAT, floatvalue = -1.0, position = expr.position)
}
0.0 -> {
// 0
optimizationsDone++
return LiteralValue.fromNumber(0, leftVal.type, expr.position)
}
1.0 -> {
//1
optimizationsDone++
return LiteralValue.fromNumber(1, leftVal.type, expr.position)
}
}
}
return expr
}
private fun optimizeDivision(expr: BinaryExpression, leftVal: LiteralValue?, rightVal: LiteralValue?): IExpression {
if(leftVal==null && rightVal==null)
return expr
if(rightVal!=null) {
// right value is a constant, see if we can optimize
val rightConst: LiteralValue = rightVal
when(rightConst.asNumericValue?.toDouble()) {
-1.0 -> {
// '/' -> -left, '//' -> -ceil(left)
optimizationsDone++
when(expr.operator) {
"/" -> return PrefixExpression("-", expr.left, expr.position)
"//" -> return PrefixExpression("-",
FunctionCall(IdentifierReference(listOf("ceil"), expr.position), listOf(expr.left), expr.position),
expr.position)
}
}
1.0 -> {
// '/' -> left, '//' -> floor(left)
optimizationsDone++
when(expr.operator) {
"/" -> return expr.left
"//" -> return FunctionCall(IdentifierReference(listOf("floor"), expr.position), listOf(expr.left), expr.position)
}
}
}
if (expr.left.resultingDatatype(namespace) == DataType.BYTE) {
if(abs(rightConst.asNumericValue!!.toDouble()) >= 256.0) {
optimizationsDone++
return LiteralValue(DataType.BYTE, 0, position = expr.position)
}
}
else if (expr.left.resultingDatatype(namespace) == DataType.WORD) {
if(abs(rightConst.asNumericValue!!.toDouble()) >= 65536.0) {
optimizationsDone++
return LiteralValue(DataType.BYTE, 0, position = expr.position)
}
}
}
if(leftVal!=null) {
// left value is a constant, see if we can optimize
when(leftVal.asNumericValue?.toDouble()) {
0.0 -> {
// 0
optimizationsDone++
return LiteralValue.fromNumber(0, leftVal.type, expr.position)
}
}
}
return expr
}
private fun optimizeMultiplication(pexpr: BinaryExpression, pleftVal: LiteralValue?, prightVal: LiteralValue?): IExpression {
if(pleftVal==null && prightVal==null)
return pexpr
val (expr, _, rightVal) = reorderAssociative(pexpr, pleftVal)
if(rightVal!=null) {
// right value is a constant, see if we can optimize
val leftValue: IExpression = expr.left
val rightConst: LiteralValue = rightVal
when(rightConst.asNumericValue?.toDouble()) {
-1.0 -> {
// -left
optimizationsDone++
return PrefixExpression("-", leftValue, expr.position)
}
0.0 -> {
// 0
optimizationsDone++
return LiteralValue.fromNumber(0, rightConst.type, expr.position)
}
1.0 -> {
// left
optimizationsDone++
return expr.left
}
}
}
// no need to check for left val constant (because of associativity)
return expr
}
}