From 2950d26c8e6d5c756e237174098b5e4a3ea7cbea Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 6 Mar 2021 22:10:03 +0100 Subject: [PATCH] array and struct value assignments now via memcopy instead of assignment per element --- .../astprocessing/StatementReorderer.kt | 87 ++++++++++--------- .../compiler/functions/BuiltinFunctions.kt | 3 +- .../prog8/ast/expressions/AstExpressions.kt | 10 ++- .../src/prog8/ast/statements/AstStatements.kt | 3 + docs/source/todo.rst | 2 - examples/test.p8 | 1 - examples/textelite.p8 | 13 ++- 7 files changed, 67 insertions(+), 52 deletions(-) diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index 7cf4bb39b..88749c99e 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -270,24 +270,16 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport val targetType = assignment.target.inferType(program) if(targetType.istype(DataType.STRUCT) && (valueType.istype(DataType.STRUCT) || valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes )) { - val assignments = if (assignment.value is ArrayLiteralValue) { - throw FatalAstException("array literal should have been translated to a variable at "+assignment.position.toString()) + if (assignment.value is ArrayLiteralValue) { + errors.err("cannot assign non-const array value, use separate assignment per field", assignment.position) } else { - 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 + return copyStructValue(assignment) } } if(targetType.typeOrElse(DataType.STRUCT) in ArrayDatatypes && valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes ) { if (assignment.value is ArrayLiteralValue) { - throw FatalAstException("array literal should have been translated to a variable at "+assignment.position.toString()) + errors.err("cannot assign non-const array value, use separate assignment per element", assignment.position) } else { return copyArrayValue(assignment) } @@ -378,52 +370,61 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport return listOf(IAstModification.ReplaceNode(assign, memcopy, assign.parent)) } - private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment): List { - // TODO use memcopy beyond a certain number of elements + private fun copyStructValue(structAssignment: Assignment): List { val identifier = structAssignment.target.identifier!! - val identifierName = identifier.nameInSource.single() val targetVar = identifier.targetVarDecl(program)!! val struct = targetVar.struct!! when (structAssignment.value) { is IdentifierReference -> { val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program)!! + val memsize = struct.memsize(program.memsizer) when { sourceVar.struct!=null -> { // struct memberwise copy val sourceStruct = sourceVar.struct!! if(sourceStruct!==targetVar.struct) { - // structs are not the same in assignment - return listOf() // error will be printed elsewhere + errors.err("struct type mismatch", structAssignment.position) + return listOf() } - if(struct.statements.size!=sourceStruct.statements.size) - return listOf() // error will be printed elsewhere - return struct.statements.zip(sourceStruct.statements).map { member -> - val targetDecl = member.first as VarDecl - val sourceDecl = member.second as VarDecl - if(targetDecl.name != sourceDecl.name) - throw FatalAstException("struct member mismatch") - val mangled = mangledStructMemberName(identifierName, targetDecl.name) - val idref = IdentifierReference(listOf(mangled), structAssignment.position) - val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name) - val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position) - val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position), sourceIdref, member.second.position) - assign.linkParents(structAssignment) - assign + if(struct.statements.size!=sourceStruct.statements.size) { + errors.err("struct element count mismatch", structAssignment.position) + return listOf() } + if(memsize!=sourceStruct.memsize(program.memsizer)) { + errors.err("memory size mismatch", structAssignment.position) + return listOf() + } + val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), structAssignment.position), + mutableListOf( + AddressOf(structAssignment.value as IdentifierReference, structAssignment.position), + AddressOf(identifier, structAssignment.position), + NumericLiteralValue.optimalInteger(memsize, structAssignment.position) + ), + true, + structAssignment.position + ) + return listOf(IAstModification.ReplaceNode(structAssignment, memcopy, structAssignment.parent)) } sourceVar.isArray -> { - val array = (sourceVar.value as ArrayLiteralValue).value - if(struct.statements.size!=array.size) - return listOf() // error will be printed elsewhere - return struct.statements.zip(array).map { - val decl = it.first as VarDecl - val mangled = mangledStructMemberName(identifierName, decl.name) - val targetName = IdentifierReference(listOf(mangled), structAssignment.position) - val target = AssignTarget(targetName, null, null, structAssignment.position) - val assign = Assignment(target, it.second, structAssignment.position) - assign.linkParents(structAssignment) - assign + val array = sourceVar.value as ArrayLiteralValue + if(struct.statements.size!=array.value.size) { + errors.err("struct element count mismatch", structAssignment.position) + return listOf() } + if(memsize!=array.memsize(program.memsizer)) { + errors.err("memory size mismatch", structAssignment.position) + return listOf() + } + val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), structAssignment.position), + mutableListOf( + AddressOf(structAssignment.value as IdentifierReference, structAssignment.position), + AddressOf(identifier, structAssignment.position), + NumericLiteralValue.optimalInteger(memsize, structAssignment.position) + ), + true, + structAssignment.position + ) + return listOf(IAstModification.ReplaceNode(structAssignment, memcopy, structAssignment.parent)) } else -> { throw FatalAstException("can only assign arrays or structs to structs") @@ -431,7 +432,7 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport } } is ArrayLiteralValue -> { - throw IllegalArgumentException("not going to flatten a structLv assignment here") + throw IllegalArgumentException("not going to do a structLv assignment here") } else -> throw FatalAstException("strange struct value") } diff --git a/compiler/src/prog8/compiler/functions/BuiltinFunctions.kt b/compiler/src/prog8/compiler/functions/BuiltinFunctions.kt index 08e6571bb..797224d66 100644 --- a/compiler/src/prog8/compiler/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/compiler/functions/BuiltinFunctions.kt @@ -321,8 +321,7 @@ private fun builtinSizeof(args: List, position: Position, program: P val target = (args[0] as IdentifierReference).targetStatement(program) ?: throw CannotEvaluateException("sizeof", "no target") - fun structSize(target: StructDecl) = - NumericLiteralValue(DataType.UBYTE, target.statements.map { memsizer.memorySize((it as VarDecl).datatype) }.sum(), position) + fun structSize(target: StructDecl) = NumericLiteralValue(DataType.UBYTE, target.memsize(memsizer), position) return when { dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> { diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index fb3568209..7906e0cc3 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -6,7 +6,7 @@ import prog8.ast.base.* import prog8.ast.statements.* import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstVisitor -import java.util.* +import java.util.Objects import kotlin.math.abs @@ -542,6 +542,14 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be return type==other.type && value.contentEquals(other.value) } + fun memsize(memsizer: IMemSizer): Int { + if(type.isKnown) { + val eltType = ArrayElementTypes.getValue(type.typeOrElse(DataType.STRUCT)) + return memsizer.memorySize(eltType) * value.size + } + else throw IllegalArgumentException("array datatype is not yet known") + } + fun guessDatatype(program: Program): InferredTypes.InferredType { // Educated guess of the desired array literal's datatype. // If it's inside a for loop, assume the data type of the loop variable is what we want. diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index 567b0841e..3152acb22 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -1000,6 +1000,9 @@ class StructDecl(override val name: String, val numberOfElements: Int get() = this.statements.size + fun memsize(memsizer: IMemSizer) = + statements.map { memsizer.memorySize((it as VarDecl).datatype) }.sum() + override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 2f648648d..c11ff71a7 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,8 +2,6 @@ TODO ==== -- 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 35778b597..97303d376 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -31,7 +31,6 @@ main { txt.nl() - struct MyType { uword v1 uword w1 diff --git a/examples/textelite.p8 b/examples/textelite.p8 index f179086c6..27dfd1f90 100644 --- a/examples/textelite.p8 +++ b/examples/textelite.p8 @@ -414,14 +414,18 @@ galaxy { sub init(ubyte galaxynum) { number = 1 planet.number = 255 - seed = [base0, base1, base2] + seed[0] = base0 + seed[1] = base1 + seed[2] = base2 repeat galaxynum-1 { nextgalaxy() } } sub nextgalaxy() { - seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] + seed[0] = twist(seed[0]) + seed[1] = twist(seed[1]) + seed[2] = twist(seed[2]) number++ if number==9 number = 1 @@ -658,7 +662,10 @@ galaxy { planet.species_kind = (planet.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result } - planet.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] + planet.goatsoup_seed[0] = lsb(seed[1]) + planet.goatsoup_seed[1] = msb(seed[1]) + planet.goatsoup_seed[2] = lsb(seed[2]) + planet.goatsoup_seed[3] = seed2_msb } sub tweakseed() {