From 4f8d4a95854a8434c8f908cd34ae22ed76dd24d0 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 6 Mar 2021 19:01:16 +0100 Subject: [PATCH] use memcopy to assign arrays --- .../astprocessing/StatementReorderer.kt | 104 +++++--------- docs/source/programming.rst | 7 +- docs/source/todo.rst | 3 +- examples/test.p8 | 131 ++++++------------ 4 files changed, 88 insertions(+), 157 deletions(-) diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index d3fc33fdd..7cf4bb39b 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -268,32 +268,31 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport override fun before(assignment: Assignment, parent: Node): Iterable { val valueType = assignment.value.inferType(program) val targetType = assignment.target.inferType(program) - var assignments = emptyList() if(targetType.istype(DataType.STRUCT) && (valueType.istype(DataType.STRUCT) || valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes )) { - assignments = if (assignment.value is ArrayLiteralValue) { - flattenStructAssignmentFromStructLiteral(assignment) // 'structvar = [ ..... ] ' + val assignments = if (assignment.value is ArrayLiteralValue) { + throw FatalAstException("array literal should have been translated to a variable at "+assignment.position.toString()) } else { - flattenStructAssignmentFromIdentifier(assignment) // 'structvar1 = structvar2' + flattenStructAssignmentFromIdentifier(assignment) + } + + if(assignments.isNotEmpty()) { + val modifications = mutableListOf() + val scope = assignment.definingScope() + assignments.reversed().mapTo(modifications) { IAstModification.InsertAfter(assignment, it, scope) } + modifications.add(IAstModification.Remove(assignment, scope)) + return modifications } } if(targetType.typeOrElse(DataType.STRUCT) in ArrayDatatypes && valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes ) { - assignments = if (assignment.value is ArrayLiteralValue) { - flattenArrayAssignmentFromArrayLiteral(assignment) // 'arrayvar = [ ..... ] ' + if (assignment.value is ArrayLiteralValue) { + throw FatalAstException("array literal should have been translated to a variable at "+assignment.position.toString()) } else { - flattenArrayAssignmentFromIdentifier(assignment) // 'arrayvar1 = arrayvar2' + return copyArrayValue(assignment) } } - if(assignments.isNotEmpty()) { - val modifications = mutableListOf() - val scope = assignment.definingScope() - assignments.reversed().mapTo(modifications) { IAstModification.InsertAfter(assignment, it, scope) } - modifications.add(IAstModification.Remove(assignment, scope)) - return modifications - } - return noModifications } @@ -346,66 +345,37 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport return noModifications } - private fun flattenArrayAssignmentFromArrayLiteral(assign: Assignment): List { + private fun copyArrayValue(assign: Assignment): List { val identifier = assign.target.identifier!! val targetVar = identifier.targetVarDecl(program)!! - val alv = assign.value as? ArrayLiteralValue - return flattenArrayAssign(targetVar, alv, identifier, assign.position) - } - private fun flattenArrayAssignmentFromIdentifier(assign: Assignment): List { - val identifier = assign.target.identifier!! - val targetVar = identifier.targetVarDecl(program)!! + if(targetVar.arraysize==null) + errors.err("array has no defined size", assign.position) + val sourceIdent = assign.value as IdentifierReference val sourceVar = sourceIdent.targetVarDecl(program)!! if(!sourceVar.isArray) { - errors.err("value must be an array", sourceIdent.position) + errors.err("value must be an array", sourceIdent.position) + } else { + if (sourceVar.arraysize!!.constIndex() != targetVar.arraysize!!.constIndex()) + errors.err("element count mismatch", assign.position) + if (sourceVar.datatype != targetVar.datatype) + errors.err("element type mismatch", assign.position) + } + + if(!errors.isEmpty()) return emptyList() - } - val alv = sourceVar.value as? ArrayLiteralValue - return flattenArrayAssign(targetVar, alv, identifier, assign.position) - } - private fun flattenArrayAssign(targetVar: VarDecl, alv: ArrayLiteralValue?, identifier: IdentifierReference, position: Position): List { - if(targetVar.arraysize==null) { - errors.err("array has no defined size", identifier.position) - return emptyList() - } - - if(alv==null || alv.value.size != targetVar.arraysize!!.constIndex()) { - errors.err("element count mismatch", position) - return emptyList() - } - - // TODO use memcopy instead of individual assignments after certain amount of elements - // TODO what does assigning a struct var use? - return alv.value.mapIndexed { index, value -> - val idx = ArrayIndexedExpression(identifier, ArrayIndex(NumericLiteralValue(DataType.UBYTE, index, position), position), position) - Assignment(AssignTarget(null, idx, null, position), value, value.position) - } - } - - private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment): List { - val identifier = structAssignment.target.identifier!! - val identifierName = identifier.nameInSource.single() - val targetVar = identifier.targetVarDecl(program)!! - val struct = targetVar.struct!! - - val slv = structAssignment.value as? ArrayLiteralValue - if(slv==null || slv.value.size != struct.numberOfElements) { - errors.err("element count mismatch", structAssignment.position) - return emptyList() - } - - return struct.statements.zip(slv.value).map { (targetDecl, sourceValue) -> - targetDecl as VarDecl - val mangled = mangledStructMemberName(identifierName, targetDecl.name) - val idref = IdentifierReference(listOf(mangled), structAssignment.position) - val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position), - sourceValue, sourceValue.position) - assign.linkParents(structAssignment) - assign - } + val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), assign.position), + mutableListOf( + AddressOf(sourceIdent, assign.position), + AddressOf(identifier, assign.position), + NumericLiteralValue.optimalInteger(targetVar.arraysize!!.constIndex()!!, assign.position) + ), + true, + assign.position + ) + return listOf(IAstModification.ReplaceNode(assign, memcopy, assign.parent)) } private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment): List { diff --git a/docs/source/programming.rst b/docs/source/programming.rst index d316c580c..0196f0ae7 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -757,11 +757,12 @@ all(x) 1 ('true') if all of the values in the array value x are 'true' (not zero), else 0 ('false') len(x) - Number of values in the array value x, or the number of characters in a string (excluding the size or 0-byte). + Number of values in the array value x, or the number of characters in a string (excluding the 0-byte). Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte. See sizeof(). Note: lengths of strings and arrays are determined at compile-time! If your program modifies the actual - length of the string during execution, the value of len(string) may no longer be correct! - (use strlen function if you want to dynamically determine the length) + length of the string during execution, the value of len(s) may no longer be correct! + (use the ``string.length`` routine if you want to dynamically determine the length by counting to the + first 0-byte) max(x) Maximum of the values in the array value x diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 4c7b4b80f..2f648648d 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,7 +2,8 @@ TODO ==== -- optimize assigning array and struct variables (multi-element assings -> memcopy) +- optimize assigning array and struct variables (multi-element assigns -> memcopy) + - hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine) - optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2)) - optimize several inner loops in gfx2 diff --git a/examples/test.p8 b/examples/test.p8 index 2c82b2c2b..35778b597 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,103 +4,62 @@ main { sub start() { - ubyte ubb = $f4 - byte bb = -123 - uword uww = $f4a1 - word ww = -12345 + ubyte[] arr1 = [1,2,3] + ubyte[] arr2 = [9,9,9] - conv.str_ub0($0f) - txt.print(conv.string_out) + arr1[2]=42 + txt.print_ub(arr2[0]) + txt.chrout(',') + txt.print_ub(arr2[1]) + txt.chrout(',') + txt.print_ub(arr2[2]) txt.nl() - txt.print_ub0($0f) + arr2=[99,88,77] + txt.print_ub(arr2[0]) + txt.chrout(',') + txt.print_ub(arr2[1]) + txt.chrout(',') + txt.print_ub(arr2[2]) + txt.nl() + arr2=arr1 + txt.print_ub(arr2[0]) + txt.chrout(',') + txt.print_ub(arr2[1]) + txt.chrout(',') + txt.print_ub(arr2[2]) txt.nl() txt.nl() - conv.str_ub(ubb) - txt.print(conv.string_out) - txt.nl() - txt.print_ub(ubb) - txt.nl() - txt.nl() - conv.str_ub(8) - txt.print(conv.string_out) - txt.nl() - txt.print_ub(8) - txt.nl() - txt.nl() - conv.str_b(bb) - txt.print(conv.string_out) - txt.nl() - txt.print_b(bb) - txt.nl() - txt.nl() - conv.str_b(-8) - txt.print(conv.string_out) - txt.nl() - txt.print_b(-8) - txt.nl() - txt.nl() - conv.str_ubhex(ubb) - txt.print(conv.string_out) - txt.nl() - txt.print_ubhex(ubb,false) - txt.nl() - txt.nl() + struct MyType { + uword v1 + uword w1 + uword w2 + } - conv.str_ubbin(ubb) - txt.print(conv.string_out) - txt.nl() - txt.print_ubbin(ubb,false) - txt.nl() - txt.nl() + MyType m1 = [1, 888, 999] + MyType m2 = [22, 222, 222] - conv.str_uwbin(uww) - txt.print(conv.string_out) + txt.print_uw(m2.v1) + txt.chrout(',') + txt.print_uw(m2.w1) + txt.chrout(',') + txt.print_uw(m2.w2) txt.nl() - txt.print_uwbin(uww, false) + m2 = [111,222,333] + txt.print_uw(m2.v1) + txt.chrout(',') + txt.print_uw(m2.w1) + txt.chrout(',') + txt.print_uw(m2.w2) txt.nl() - txt.nl() - - conv.str_uwhex(uww) - txt.print(conv.string_out) - txt.nl() - txt.print_uwhex(uww, false) - txt.nl() - txt.nl() - - conv.str_uw0(987) - txt.print(conv.string_out) - txt.nl() - txt.print_uw0(987) - txt.nl() - txt.nl() - - conv.str_uw(uww) - txt.print(conv.string_out) - txt.nl() - txt.print_uw(uww) - txt.nl() - txt.nl() - conv.str_uw(7) - txt.print(conv.string_out) - txt.nl() - txt.print_uw(7) - txt.nl() - txt.nl() - - conv.str_w(ww) - txt.print(conv.string_out) - txt.nl() - txt.print_w(ww) - txt.nl() - txt.nl() - - conv.str_w(99) - txt.print(conv.string_out) - txt.nl() - txt.print_w(99) + m2 = m1 + txt.print_uw(m2.v1) + txt.chrout(',') + txt.print_uw(m2.w1) + txt.chrout(',') + txt.print_uw(m2.w2) txt.nl() } }