From 83fbf86b1ceb1471d309207ded7603d3cc347f23 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 27 Nov 2020 23:46:01 +0100 Subject: [PATCH] no longer generate double assignment to the indexer var for in-place modifying array variable --- compiler/src/prog8/ast/AstToplevel.kt | 8 ++ .../ast/processing/StatementReorderer.kt | 34 +++----- .../src/prog8/ast/statements/AstStatements.kt | 13 ++- examples/test.p8 | 85 +++++++++++++------ 4 files changed, 93 insertions(+), 47 deletions(-) diff --git a/compiler/src/prog8/ast/AstToplevel.kt b/compiler/src/prog8/ast/AstToplevel.kt index 48a39346f..d215b9f99 100644 --- a/compiler/src/prog8/ast/AstToplevel.kt +++ b/compiler/src/prog8/ast/AstToplevel.kt @@ -216,6 +216,14 @@ interface INameScope { null } + fun previousSibling(stmt: Statement): Statement? { + val previousIdx = statements.indexOfFirst { it===stmt } - 1 + return if(previousIdx>=0) + statements[previousIdx] + else + null + } + fun indexOfChild(stmt: Statement): Int { val idx = statements.indexOfFirst { it===stmt } if(idx>=0) diff --git a/compiler/src/prog8/ast/processing/StatementReorderer.kt b/compiler/src/prog8/ast/processing/StatementReorderer.kt index 70af20439..947c8d2db 100644 --- a/compiler/src/prog8/ast/processing/StatementReorderer.kt +++ b/compiler/src/prog8/ast/processing/StatementReorderer.kt @@ -96,36 +96,30 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte val subroutine = expr.definingSubroutine()!! val statement = expr.containingStatement() val indexerVarPrefix = "prog8_autovar_index_" - val indexerVarName: String val repo = subroutine.asmGenInfo.usedAutoArrayIndexerForStatements - val freeVar = repo.filter { statement !in it.value }.map { it.key }.firstOrNull() - if(freeVar==null) { + // TODO make this even smarter so that an indexerVar can be reused for a different following statement... requires updating the partOfStatement? + var indexerVar = repo.firstOrNull { it.replaces isSameAs expr.indexer } + if(indexerVar==null) { // add another loop index var to be used for this expression - val statementId = expr.hashCode() - indexerVarName = "$indexerVarPrefix$statementId" + val indexerVarName = "$indexerVarPrefix${expr.indexer.hashCode()}" + indexerVar = AsmGenInfo.ArrayIndexerInfo(indexerVarName, expr.indexer, statement) + repo.add(indexerVar) // create the indexer var at block level scope val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE, null, indexerVarName, null, null, isArray = false, autogeneratedDontRemove = true, position = expr.position) modifications.add(IAstModification.InsertFirst(vardecl, subroutine)) - var statements = repo[indexerVarName] - if(statements==null) { - statements = mutableSetOf() - repo[indexerVarName] = statements - } - statements.add(statement) - } else { - // reuse an already created indexer autovar - repo.getValue(freeVar).add(statement) - indexerVarName = freeVar } + indexerVar.used++ // keep track of how many times it it used, to avoid assigning it multiple times - // assign the indexing expression to the helper variable, and replace the indexer with just the variable + // replace the indexer with just the variable + // assign the indexing expression to the helper variable, but only if that hasn't been done already val indexerExpression = expr.indexer.origExpression!! - val target = AssignTarget(IdentifierReference(listOf(indexerVarName), indexerExpression.position), null, null, indexerExpression.position) - val assign = Assignment(target, indexerExpression, indexerExpression.position) - val beforeWhat = expr.containingStatement() - modifications.add(IAstModification.InsertBefore(beforeWhat, assign, beforeWhat.definingScope())) + val target = AssignTarget(IdentifierReference(listOf(indexerVar.name), indexerExpression.position), null, null, indexerExpression.position) + if(indexerVar.used==1) { + val assign = Assignment(target, indexerExpression, indexerExpression.position) + modifications.add(IAstModification.InsertBefore(statement, assign, statement.definingScope())) + } modifications.add(IAstModification.SetExpression( { expr.indexer.indexVar = it as IdentifierReference expr.indexer.indexNum = null diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index f5040b4d8..446f53cac 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -313,6 +313,7 @@ class ArrayIndex(var origExpression: Expression?, // will be replaced } is IdentifierReference -> { indexVar = replacement + indexNum = null } else -> { throw FatalAstException("invalid replace") @@ -347,7 +348,12 @@ class ArrayIndex(var origExpression: Expression?, // will be replaced fun constIndex() = indexNum?.number?.toInt() - infix fun isSameAs(other: ArrayIndex) = indexNum==other.indexNum && indexVar == other.indexVar + infix fun isSameAs(other: ArrayIndex): Boolean { + return if(indexNum!=null || indexVar!=null) + indexNum==other.indexNum && indexVar == other.indexVar + else + other.origExpression!=null && origExpression!! isSameAs other.origExpression!! + } } open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() { @@ -679,16 +685,19 @@ class NopStatement(override val position: Position): Statement() { override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) } + class AsmGenInfo { // This class contains various attributes that influence the assembly code generator. // Conceptually it should be part of any INameScope. // But because the resulting code only creates "real" scopes on a subroutine level, // it's more consistent to only define these attributes on a Subroutine node. - var usedAutoArrayIndexerForStatements = mutableMapOf>() + var usedAutoArrayIndexerForStatements = mutableListOf() var usedRegsaveA = false var usedRegsaveX = false var usedRegsaveY = false var usedFloatEvalResultVar = false + + class ArrayIndexerInfo(val name: String, val replaces: ArrayIndex, val partOfStatement: Statement, var used: Int=0) } // the subroutine class covers both the normal user-defined subroutines, diff --git a/examples/test.p8 b/examples/test.p8 index 48d8c2b56..8ab03fa92 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,33 +7,68 @@ main { sub start() { - float fl - word ww - uword uw - byte bb - ubyte ub - str string1 = "irmen" - uword[] array = [1111,2222,3333] + ubyte[] ubarray = [100,200] - uw = string1 as uword - txt.print_uwhex(uw,1) - txt.chrout('\n') - uw = array as uword - txt.print_uwhex(uw,1) - txt.chrout('\n') - uw = name() as uword - txt.print_uwhex(uw,1) - txt.chrout('\n') - uw = name() - txt.print_uwhex(uw,1) - txt.chrout('\n') - - - test_stack.test() + ubyte index = 0 + ubarray[index+1] += 13 + ubarray[index+1] += 13 + ubarray[index+1] += 13 + ubarray[index+2] += 13 + txt.print_ub(ubarray[1]) + txt.chrout('\n') } - sub name() -> str { - return "irmen" - } +; sub start222() { +; +; ubyte[] ubarray = [100,200] +; uword[] uwarray = [1000,2000] +; float[] flarray = [100.1, 200.2] +; +; ubyte index = 1 +; +; ubarray[1] += 3 +; txt.print_ub(ubarray[1]) +; txt.chrout('\n') +; ubarray[index] += 3 +; txt.print_ub(ubarray[1]) +; txt.chrout('\n') +; index = 0 +; ubarray[index*99+1] += 3 +; txt.print_ub(ubarray[1]) +; txt.chrout('\n') +; txt.chrout('\n') +; +; index = 1 +; uwarray[1] += 3 +; txt.print_uw(uwarray[1]) +; txt.chrout('\n') +; uwarray[index] += 3 +; txt.print_uw(uwarray[1]) +; txt.chrout('\n') +; index = 0 +; uwarray[index*99+1] += 3 +; txt.print_uw(uwarray[1]) +; txt.chrout('\n') +; txt.chrout('\n') +; +; index=1 +; flarray[1] += 3.0 +; floats.print_f(flarray[1]) +; txt.chrout('\n') +; flarray[index] += 3.0 +; floats.print_f(flarray[1]) +; txt.chrout('\n') +; index = 0 +; flarray[index*99+1] += 3.0 +; floats.print_f(flarray[1]) +; txt.chrout('\n') +; +; test_stack.test() +; +; } +; +; sub name() -> str { +; return "irmen" +; } }