From 00ad285d3fa8d69ef36dafa78ddb9f43ea730de5 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 19 Dec 2018 00:57:57 +0100 Subject: [PATCH] fix array value initialization type conversions --- compiler/src/prog8/ast/AstChecker.kt | 31 ++++++++ .../src/prog8/optimizing/ConstantFolding.kt | 78 +++++++++---------- prog8lib/prog8lib.p8 | 63 ++++++++++++++- 3 files changed, 132 insertions(+), 40 deletions(-) diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 5ac470b3f..134bfac5f 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -1,5 +1,6 @@ package prog8.ast +import com.sun.org.apache.xpath.internal.operations.Bool import prog8.compiler.CompilationOptions import prog8.compiler.HeapValues import prog8.compiler.target.c64.FLOAT_MAX_NEGATIVE @@ -906,6 +907,8 @@ class AstChecker(private val namespace: INameScope, DataType.ARRAY_UB, DataType.ARRAY_B -> { // value may be either a single byte, or a byte arrayspec (of all constant values) if(value.type==targetDt) { + if(!checkArrayValues(value, targetDt)) + return false val arraySpecSize = arrayspec.size() val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize if(arraySpecSize!=null && arraySpecSize>0) { @@ -926,6 +929,8 @@ class AstChecker(private val namespace: INameScope, DataType.ARRAY_UW, DataType.ARRAY_W -> { // value may be either a single word, or a word arrayspec if(value.type==targetDt) { + if(!checkArrayValues(value, targetDt)) + return false val arraySpecSize = arrayspec.size() val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize if(arraySpecSize!=null && arraySpecSize>0) { @@ -946,6 +951,8 @@ class AstChecker(private val namespace: INameScope, DataType.ARRAY_F -> { // value may be either a single float, or a float arrayspec if(value.type==targetDt) { + if(!checkArrayValues(value, targetDt)) + return false val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).doubleArray!!.size val arraySpecSize = arrayspec.size() if(arraySpecSize!=null && arraySpecSize>0) { @@ -975,6 +982,30 @@ class AstChecker(private val namespace: INameScope, return true } + private fun checkArrayValues(value: LiteralValue, type: DataType): Boolean { + val array = heap.get(value.heapId!!) + val correct: Boolean + when(type) { + DataType.ARRAY_UB -> { + correct=array.array!=null && array.array.all { it in 0..255 } + } + DataType.ARRAY_B -> { + correct=array.array!=null && array.array.all { it in -128..127 } + } + DataType.ARRAY_UW -> { + correct=array.array!=null && array.array.all { it in 0..65535 } + } + DataType.ARRAY_W -> { + correct=array.array!=null && array.array.all { it in -32768..32767 } + } + DataType.ARRAY_F -> correct = array.doubleArray!=null + else -> throw AstException("invalid array type $type") + } + if(!correct) + checkResult.add(ExpressionError("array value out of range for type $type", value.position)) + return correct + } + private fun checkAssignmentCompatible(targetDatatype: DataType, sourceDatatype: DataType, sourceValue: IExpression, diff --git a/compiler/src/prog8/optimizing/ConstantFolding.kt b/compiler/src/prog8/optimizing/ConstantFolding.kt index ed5df244f..2149c3a85 100644 --- a/compiler/src/prog8/optimizing/ConstantFolding.kt +++ b/compiler/src/prog8/optimizing/ConstantFolding.kt @@ -32,38 +32,22 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV val result = super.process(decl) if(decl.type==VarDeclType.CONST || decl.type==VarDeclType.VAR) { + val litval = decl.value as? LiteralValue + if(litval!=null && litval.isArray) + fixupArrayTypeOnHeap(decl, litval) when(decl.datatype) { DataType.FLOAT -> { - // vardecl: for float vars, promote constant integer initialization values to floats - val literal = decl.value as? LiteralValue - if (literal != null && literal.type in IntegerDatatypes) { - val newValue = LiteralValue(DataType.FLOAT, floatvalue = literal.asNumericValue!!.toDouble(), position = literal.position) + // vardecl: for scalar float vars, promote constant integer initialization values to floats + if (litval != null && litval.type in IntegerDatatypes) { + val newValue = LiteralValue(DataType.FLOAT, floatvalue = litval.asNumericValue!!.toDouble(), position = litval.position) decl.value = newValue } } DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> { - val litval = decl.value as? LiteralValue if(litval?.type==DataType.FLOAT) errors.add(ExpressionError("arrayspec requires only integers here", litval.position)) val size = decl.arrayspec!!.size() - if(litval!=null && litval.isArray) { - // arrayspec initializer value is an arrayspec already, keep as-is (or convert to WORDs if needed) - if(litval.heapId!=null) { - if(decl.datatype==DataType.ARRAY_UW && litval.type == DataType.ARRAY_UB) { - val array = heap.get(litval.heapId) - if(array.array!=null) { - heap.update(litval.heapId, HeapValues.HeapValue(DataType.ARRAY_UW, null, array.array, null)) - decl.value = LiteralValue(decl.datatype, heapId = litval.heapId, position = litval.position) - } - } else if(decl.datatype==DataType.ARRAY_W && litval.type == DataType.ARRAY_B) { - val array = heap.get(litval.heapId) - if(array.array!=null) { - heap.update(litval.heapId, HeapValues.HeapValue(DataType.ARRAY_W, null, array.array, null)) - decl.value = LiteralValue(decl.datatype, heapId = litval.heapId, position = litval.position) - } - } - } - } else if (size != null) { + if ((litval==null || !litval.isArray) && size != null) { // arrayspec initializer is empty or a single int, and we know the size; create the arrayspec. val fillvalue = if (litval == null) 0 else litval.asIntegerValue ?: 0 when(decl.datatype){ @@ -91,19 +75,8 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV } } DataType.ARRAY_F -> { - val litval = decl.value as? LiteralValue val size = decl.arrayspec!!.size() - if(litval!=null && litval.isArray) { - // arrayspec initializer value is an arrayspec already, make sure to convert to floats - if(litval.heapId!=null) { - val array = heap.get(litval.heapId) - if (array.doubleArray == null) { - val doubleArray = array.array!!.map { it.toDouble() }.toDoubleArray() - heap.update(litval.heapId, HeapValues.HeapValue(DataType.ARRAY_F, null, null, doubleArray)) - decl.value = LiteralValue(decl.datatype, heapId = litval.heapId, position = litval.position) - } - } - } else if (size != null) { + if ((litval==null || !litval.isArray) && size != null) { // arrayspec initializer is empty or a single int, and we know the size; create the arrayspec. val fillvalue = if (litval == null) 0.0 else litval.asNumericValue?.toDouble() ?: 0.0 if(fillvalue< FLOAT_MAX_NEGATIVE || fillvalue> FLOAT_MAX_POSITIVE) @@ -121,6 +94,32 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV return result } + private fun fixupArrayTypeOnHeap(decl: VarDecl, litval: LiteralValue) { + // fix the type of the array value that's on the heap, to match the vardecl. + // notice that checking the bounds of the actual values is not done here, but in the AstChecker later. + if(decl.datatype==litval.type) + return // already correct datatype + val heapId = litval.heapId!! + val array=heap.get(heapId) + when(decl.datatype) { + DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> { + if(array.array!=null) { + heap.update(heapId, HeapValues.HeapValue(decl.datatype, null, array.array, null)) + decl.value = LiteralValue(decl.datatype, heapId=heapId, position = litval.position) + } + } + DataType.ARRAY_F -> { + if(array.array!=null) { + // convert a non-float array to floats + val doubleArray = array.array.map { it.toDouble() }.toDoubleArray() + heap.update(heapId, HeapValues.HeapValue(DataType.ARRAY_F, null, null, doubleArray)) + decl.value = LiteralValue(decl.datatype, heapId = heapId, position = litval.position) + } + } + else -> throw AstException("invalid array vardecl type") + } + } + /** * replace identifiers that refer to const value, with the value itself (if it's a simple type) */ @@ -484,14 +483,15 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV val integerArray = valuesInArray.map{it.toInt()}.toIntArray() val doubleArray = valuesInArray.map{it.toDouble()}.toDoubleArray() val typesInArray: Set = array.mapNotNull { it.resultingDatatype(namespace, heap) }.toSet() + + // Take an educated guess about the array type. + // This may be altered (if needed & if possible) to suit an array declaration type later! + // Also, the check if all values are valid for the given datatype is done later, in the AstChecker. val arrayDt = if(DataType.FLOAT in typesInArray) DataType.ARRAY_F else if(DataType.WORD in typesInArray) { - if(DataType.UWORD in typesInArray) - DataType.ARRAY_F - else - DataType.ARRAY_W + DataType.ARRAY_W } else { val maxValue = integerArray.max()!! val minValue = integerArray.min()!! diff --git a/prog8lib/prog8lib.p8 b/prog8lib/prog8lib.p8 index 843b14e4e..bbf57bd4f 100644 --- a/prog8lib/prog8lib.p8 +++ b/prog8lib/prog8lib.p8 @@ -290,7 +290,7 @@ dec_var_f .proc .pend pop_2_floats_f2_in_fac1 .proc - ; -- pop 2 floats from stack, load the second one in FAC1 + ; -- pop 2 floats from stack, load the second one in FAC1 as well lda #fmath_float2 jsr pop_float @@ -1100,6 +1100,67 @@ _cmp_mod cpy #255 ; modified rts .pend +func_max_ub .proc + inx + rts + .warn "todo func_max_ub" + .pend + +func_max_b .proc + inx + rts + .warn "todo func_max_b" + .pend + +func_max_uw .proc + inx + rts + .warn "todo func_max_uw" + .pend + +func_max_w .proc + inx + rts + .warn "todo func_max_w" + .pend + +func_max_f .proc + inx + rts + .warn "todo func_max_f" + .pend + +func_min_ub .proc + inx + rts + .warn "todo func_min_ub" + .pend + +func_min_b .proc + inx + rts + .warn "todo func_min_b" + .pend + +func_min_uw .proc + inx + rts + .warn "todo func_min_uw" + .pend + +func_min_w .proc + inx + rts + .warn "todo func_min_w" + .pend + +func_min_f .proc + inx + rts + .warn "todo func_min_f" + .pend + + func_len_str .proc ; -- push length of 0-terminated string on stack jsr peek_address