diff --git a/compiler/examples/mandelbrot.p8 b/compiler/examples/mandelbrot.p8 index ce614474c..6096ee6c7 100644 --- a/compiler/examples/mandelbrot.p8 +++ b/compiler/examples/mandelbrot.p8 @@ -27,15 +27,15 @@ 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 + xx = flt((pixelx-xoffset))/width/3.0+0.2 x = 0.0 y = 0.0 xsquared = 0 ysquared = 0 iter = 0 - while (iter<32 and xsquared+ysquared<4) { - y = x*y*2 + yy + while (iter<32 and xsquared+ysquared<4.0) { + y = x*y*2.0 + yy x = xsquared - ysquared + xx xsquared = x*x ysquared = y*y diff --git a/compiler/examples/swirl.p8 b/compiler/examples/swirl.p8 index 6c3beabe0..c90c86f85 100644 --- a/compiler/examples/swirl.p8 +++ b/compiler/examples/swirl.p8 @@ -25,9 +25,9 @@ } sub screenx(x: float) -> word { - return floor(x * width/4.1) + width // 2 + return floor(x * flt(width)/4.1) + width // 2 ; @todo const-fold x*y/z } sub screeny(y: float) -> word { - return floor(y * height/4.1) + height // 2 + return floor(y * flt(height)/4.1) + height // 2 ; @todo const-fold x*y/z } } diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index c1dd636f0..ca523732e 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -4,22 +4,18 @@ ~ main { - X=0 - - word mainvar = 44*Y - - return - - sub start() { - A=99 + const byte b1 = 20//7 + const word w1 = 20//7 + const float f1 = 20/7 - byte bvar = 4*X - word wvar = 44*XY - float fvar = 128.34+XY - - A=100 + _vm_write_num(b1) + _vm_write_char('\n') + _vm_write_num(w1) + _vm_write_char('\n') + _vm_write_num(f1) + _vm_write_char('\n') return } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 5ba21dc8a..d490cc986 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -521,12 +521,43 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, stackvmProg.instr(Opcode.NOP) } - private fun checkForFloatPrecisionProblem(left: IExpression, right: IExpression) { - val leftDt = left.resultingDatatype(namespace, heap) - val rightDt = right.resultingDatatype(namespace, heap) - if (leftDt == DataType.BYTE || leftDt == DataType.WORD) { - if(rightDt==DataType.FLOAT) - printWarning("byte or word value implicitly converted to float. Suggestion: use explicit flt() conversion or revert to byte/word arithmetic", left.position) + private fun commonDatatype(leftDt: DataType, rightDt: DataType, leftpos: Position, rightpos: Position): DataType { + // byte + byte -> byte + // byte + word -> word + // word + byte -> word + // word + word -> word + // a combination with a float will be float (but give a warning about this!) + + val floatWarning = "byte or word value implicitly converted to float. Suggestion: use explicit flt() conversion, a float number, or revert to byte/word arithmetic" + + return when(leftDt) { + DataType.BYTE -> { + when(rightDt) { + DataType.BYTE -> DataType.BYTE + DataType.WORD -> DataType.WORD + DataType.FLOAT -> { + printWarning(floatWarning, leftpos) + DataType.FLOAT + } + else -> throw CompilerException("non-numeric datatype $rightDt") + } + } + DataType.WORD -> { + when(rightDt) { + DataType.BYTE, DataType.WORD -> DataType.WORD + DataType.FLOAT -> { + printWarning(floatWarning, leftpos) + DataType.FLOAT + } + else -> throw CompilerException("non-numeric datatype $rightDt") + } + } + DataType.FLOAT -> { + if(rightDt!=DataType.FLOAT) + printWarning(floatWarning, rightpos) + DataType.FLOAT + } + else -> throw CompilerException("non-numeric datatype $leftDt") } } @@ -541,10 +572,16 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, translatePrefixOperator(expr.operator, expr.expression.resultingDatatype(namespace, heap)) } is BinaryExpression -> { - checkForFloatPrecisionProblem(expr.left, expr.right) + val leftDt = expr.left.resultingDatatype(namespace, heap)!! + val rightDt = expr.right.resultingDatatype(namespace, heap)!! + val commonDt = commonDatatype(leftDt, rightDt, expr.left.position, expr.right.position) translate(expr.left) + if(leftDt!=commonDt) + convertType(leftDt, commonDt) translate(expr.right) - translateBinaryOperator(expr.operator, expr.left.resultingDatatype(namespace, heap), expr.right.resultingDatatype(namespace, heap)) + if(rightDt!=commonDt) + convertType(rightDt, commonDt) + translateBinaryOperator(expr.operator, commonDt) } is FunctionCall -> { val target = expr.target.targetStatement(namespace) @@ -586,6 +623,33 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } + private fun convertType(givenDt: DataType, targetDt: DataType) { + // only WIDENS a type, never NARROWS + if(givenDt==targetDt) + return + if(givenDt!=DataType.BYTE && givenDt!=DataType.WORD && givenDt!=DataType.FLOAT) + throw CompilerException("converting a non-numeric $givenDt") + if(targetDt!=DataType.BYTE && targetDt!=DataType.WORD && targetDt!=DataType.FLOAT) + throw CompilerException("converting to non-numeric $targetDt") + when(givenDt) { + DataType.BYTE -> when(targetDt) { + DataType.WORD -> stackvmProg.instr(Opcode.B2WORD) + DataType.FLOAT -> stackvmProg.instr(Opcode.B2FLOAT) + else -> {} + } + DataType.WORD -> when(targetDt) { + DataType.BYTE -> throw CompilerException("narrowing type") + DataType.FLOAT -> stackvmProg.instr(Opcode.W2FLOAT) + else -> {} + } + DataType.FLOAT -> when(targetDt) { + DataType.BYTE, DataType.WORD -> throw CompilerException("narrowing type") + else -> {} + } + else -> {} + } + } + private fun translate(identifierRef: IdentifierReference) { val target = identifierRef.targetStatement(namespace) when (target) { @@ -720,17 +784,13 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, stackvmProg.instr(Opcode.CALL, callLabel=subroutine.scopedname) } - private fun translateBinaryOperator(operator: String, leftDt: DataType?, rightDt: DataType?) { - if(leftDt==null || rightDt==null) - throw CompilerException("left and/or right operand datatype not known") + private fun translateBinaryOperator(operator: String, dt: DataType) { val validDt = setOf(DataType.BYTE, DataType.WORD, DataType.FLOAT) - if(leftDt !in validDt || rightDt !in validDt) - throw CompilerException("invalid datatype(s) for operand(s)") - if(leftDt!=rightDt) - throw CompilerException("operands have different datatypes") + if(dt !in validDt) + throw CompilerException("invalid datatype for operator: $dt") val opcode = when(operator) { "+" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.ADD_B DataType.WORD -> Opcode.ADD_W DataType.FLOAT -> Opcode.ADD_F @@ -738,7 +798,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } "-" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.SUB_B DataType.WORD -> Opcode.SUB_W DataType.FLOAT -> Opcode.SUB_F @@ -746,7 +806,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } "*" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.MUL_B DataType.WORD -> Opcode.MUL_W DataType.FLOAT -> Opcode.MUL_F @@ -754,7 +814,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } "/" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.DIV_B DataType.WORD -> Opcode.DIV_W DataType.FLOAT -> Opcode.DIV_F @@ -762,7 +822,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } "//" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.FLOORDIV_B DataType.WORD -> Opcode.FLOORDIV_W DataType.FLOAT -> Opcode.FLOORDIV_F @@ -770,7 +830,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } "%" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.REMAINDER_B DataType.WORD -> Opcode.REMAINDER_W DataType.FLOAT -> Opcode.REMAINDER_F @@ -778,7 +838,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } "**" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.POW_B DataType.WORD -> Opcode.POW_W DataType.FLOAT -> Opcode.POW_F @@ -786,49 +846,49 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } "&" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.BITAND DataType.WORD -> Opcode.BITAND_W else -> throw CompilerException("only byte/word possible") } } "|" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.BITOR DataType.WORD -> Opcode.BITOR_W else -> throw CompilerException("only byte/word possible") } } "^" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.BITXOR DataType.WORD -> Opcode.BITXOR_W else -> throw CompilerException("only byte/word possible") } } "and" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.AND DataType.WORD -> Opcode.AND_W else -> throw CompilerException("only byte/word possible") } } "or" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.OR DataType.WORD -> Opcode.OR_W else -> throw CompilerException("only byte/word possible") } } "xor" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.XOR DataType.WORD -> Opcode.XOR_W else -> throw CompilerException("only byte/word possible") } } "<" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.LESS DataType.WORD -> Opcode.LESS_W DataType.FLOAT -> Opcode.LESS_F @@ -836,7 +896,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } ">" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.GREATER DataType.WORD -> Opcode.GREATER_W DataType.FLOAT -> Opcode.GREATER_F @@ -844,7 +904,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } "<=" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.LESSEQ DataType.WORD -> Opcode.LESSEQ_W DataType.FLOAT -> Opcode.LESSEQ_F @@ -852,7 +912,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } ">=" -> { - when(leftDt) { + when(dt) { DataType.BYTE -> Opcode.GREATEREQ DataType.WORD -> Opcode.GREATEREQ_W DataType.FLOAT -> Opcode.GREATEREQ_F @@ -860,7 +920,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } "==" -> { - when (leftDt) { + when (dt) { DataType.BYTE -> Opcode.EQUAL DataType.WORD -> Opcode.EQUAL_W DataType.FLOAT -> Opcode.EQUAL_F @@ -868,7 +928,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } "!=" -> { - when (leftDt) { + when (dt) { DataType.BYTE -> Opcode.NOTEQUAL DataType.WORD -> Opcode.NOTEQUAL_W DataType.FLOAT -> Opcode.NOTEQUAL_F diff --git a/compiler/src/prog8/optimizing/ConstExprEvaluator.kt b/compiler/src/prog8/optimizing/ConstExprEvaluator.kt index 2e729ae32..1647c6e8d 100644 --- a/compiler/src/prog8/optimizing/ConstExprEvaluator.kt +++ b/compiler/src/prog8/optimizing/ConstExprEvaluator.kt @@ -215,7 +215,8 @@ class ConstExprEvaluator { left.asIntegerValue!=null -> when { right.asIntegerValue!=null -> { if(right.asIntegerValue==0) divideByZeroError(right.position) - LiteralValue.optimalNumeric(left.asIntegerValue / right.asIntegerValue, left.position) + val result = left.asIntegerValue.toDouble() / right.asIntegerValue.toDouble() + LiteralValue.optimalNumeric(result, left.position) } right.floatvalue!=null -> { if(right.floatvalue==0.0) divideByZeroError(right.position) @@ -273,7 +274,7 @@ class ConstExprEvaluator { left.asIntegerValue!=null -> when { right.asIntegerValue!=null -> { if(right.asIntegerValue==0) divideByZeroError(right.position) - LiteralValue.optimalNumeric(left.asIntegerValue % right.asIntegerValue, left.position) + LiteralValue.optimalNumeric(left.asIntegerValue.toDouble() % right.asIntegerValue.toDouble(), left.position) } right.floatvalue!=null -> { if(right.floatvalue==0.0) divideByZeroError(right.position) diff --git a/compiler/src/prog8/optimizing/SimplifyExpressions.kt b/compiler/src/prog8/optimizing/SimplifyExpressions.kt index b7a1b77a4..24d24a828 100644 --- a/compiler/src/prog8/optimizing/SimplifyExpressions.kt +++ b/compiler/src/prog8/optimizing/SimplifyExpressions.kt @@ -14,7 +14,6 @@ import kotlin.math.abs 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: common (sub) expression elimination (turn common expressions into single subroutine call) */