From 39a5e341afce243c8669863598bbaf005112445f Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 23 Jan 2019 21:50:43 +0100 Subject: [PATCH] sort assignments to enable same-value optimization --- compiler/src/prog8/ast/AST.kt | 12 +- compiler/src/prog8/ast/StmtReorderer.kt | 62 ++++++++- .../prog8/optimizing/StatementOptimizer.kt | 55 +++++--- docs/docs.iml | 3 +- examples/test.p8 | 121 ++++++++++++++++-- 5 files changed, 210 insertions(+), 43 deletions(-) diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index e939cd0ec..d7dd90832 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -781,17 +781,17 @@ data class AssignTarget(val register: Register?, return null } - fun shortString(): String { + fun shortString(withTypePrefix: Boolean=false): String { if(register!=null) - return register.toString() + return (if(withTypePrefix) "0register::" else "") + register.toString() if(identifier!=null) - return identifier.nameInSource.last() + return (if(withTypePrefix) "3identifier::" else "") + identifier.nameInSource.last() if(arrayindexed!=null) - return arrayindexed.identifier.nameInSource.last() + return (if(withTypePrefix) "2arrayidx::" else "") + arrayindexed.identifier.nameInSource.last() val address = memoryAddress?.addressExpression if(address is LiteralValue) - return address.asIntegerValue.toString() - return "???" + return (if(withTypePrefix) "1address::" else "") +address.asIntegerValue.toString() + return if(withTypePrefix) "???::???" else "???" } } diff --git a/compiler/src/prog8/ast/StmtReorderer.kt b/compiler/src/prog8/ast/StmtReorderer.kt index 65830ab0c..6ee834230 100644 --- a/compiler/src/prog8/ast/StmtReorderer.kt +++ b/compiler/src/prog8/ast/StmtReorderer.kt @@ -15,17 +15,14 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He // - all other subroutines will be moved to the end of their block. - // @todo sort the VariableInitializations and normal assignments: as long as the values are constants and they follow eachother without other stmts inbetween. something like this: - // // sort by datatype and value, so multiple initializations with the same value can be optimized (to load the value just once) - // val sortedInits = varinits.sortedWith(compareBy({it.value.resultingDatatype(namespace, heap)}, {it.value.constValue(namespace, heap)?.asNumericValue?.toDouble()})) - - private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option") private val vardeclsToAdd = mutableMapOf>() override fun process(module: Module) { super.process(module) + module.statements = sortConstantAssignments(module.statements) + val (blocks, other) = module.statements.partition { it is Block } module.statements = other.asSequence().plus(blocks.sortedBy { (it as Block).address ?: Int.MAX_VALUE }).toMutableList() @@ -51,6 +48,9 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He } override fun process(block: Block): IStatement { + + block.statements = sortConstantAssignments(block.statements) + val subroutines = block.statements.filterIsInstance() var numSubroutinesAtEnd = 0 // move all subroutines to the end of the block @@ -98,6 +98,9 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He override fun process(subroutine: Subroutine): IStatement { super.process(subroutine) + + subroutine.statements = sortConstantAssignments(subroutine.statements) + val varDecls = subroutine.statements.filterIsInstance() subroutine.statements.removeAll(varDecls) subroutine.statements.addAll(0, varDecls) @@ -120,6 +123,55 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He return subroutine } + override fun process(scope: AnonymousScope): AnonymousScope { + scope.statements = sortConstantAssignments(scope.statements) + return super.process(scope) + } + + private fun sortConstantAssignments(statements: MutableList): MutableList { + // sort assignments by datatype and value, so multiple initializations with the same value can be optimized (to load the value just once) + val result = mutableListOf() + val stmtIter = statements.iterator() + for(stmt in stmtIter) { + if(stmt is Assignment) { + val constval = stmt.value.constValue(namespace, heap) + if(constval!=null) { + val (sorted, trailing) = sortConstantAssignmentSequence(stmt, stmtIter) + result.addAll(sorted) + if(trailing!=null) + result.add(trailing) + } + else + result.add(stmt) + } + else + result.add(stmt) + } + return result + } + + private fun sortConstantAssignmentSequence(first: Assignment, stmtIter: MutableIterator): Pair, IStatement?> { + val sequence= mutableListOf(first) + var trailing: IStatement? = null + while(stmtIter.hasNext()) { + val next = stmtIter.next() + if(next is Assignment) { + val constValue = next.value.constValue(namespace, heap) + if(constValue==null) { + trailing = next + break + } + sequence.add(next) + } + else { + trailing=next + break + } + } + val sorted = sequence.sortedWith(compareBy({it.value.resultingDatatype(namespace, heap)}, {it.singleTarget?.shortString(true)})) + return Pair(sorted, trailing) + } + override fun process(decl: VarDecl): IStatement { super.process(decl) if(decl.type!=VarDeclType.VAR || decl.value==null) diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt index c213e90ae..502a5c61a 100644 --- a/compiler/src/prog8/optimizing/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt @@ -54,27 +54,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He } } - val linesToRemove = mutableListOf() - var previousAssignmentLine: Int? = null - for(i in 0 until subroutine.statements.size) { - val stmt = subroutine.statements[i] as? Assignment - if(stmt!=null) { - if(previousAssignmentLine==null) { - previousAssignmentLine = i - continue - } else { - val prev = subroutine.statements[previousAssignmentLine] as Assignment - if(prev.targets.size==1 && stmt.targets.size==1 && same(prev.targets[0], stmt.targets[0])) { - // get rid of the previous assignment, if the target is not MEMORY - if(isNotMemory(prev.targets[0])) - linesToRemove.add(previousAssignmentLine) - } - previousAssignmentLine = i - } - } else - previousAssignmentLine=null - } - + val linesToRemove = deduplicateAssignments(subroutine.statements) if(linesToRemove.isNotEmpty()) { linesToRemove.reversed().forEach{subroutine.statements.removeAt(it)} } @@ -82,6 +62,31 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He return subroutine } + private fun deduplicateAssignments(statements: List): MutableList { + // removes 'duplicate' assignments that assign the same target + val linesToRemove = mutableListOf() + var previousAssignmentLine: Int? = null + for (i in 0 until statements.size) { + val stmt = statements[i] as? Assignment + if (stmt != null) { + if (previousAssignmentLine == null) { + previousAssignmentLine = i + continue + } else { + val prev = statements[previousAssignmentLine] as Assignment + if (prev.targets.size == 1 && stmt.targets.size == 1 && same(prev.targets[0], stmt.targets[0])) { + // get rid of the previous assignment, if the target is not MEMORY + if (isNotMemory(prev.targets[0])) + linesToRemove.add(previousAssignmentLine) + } + previousAssignmentLine = i + } + } else + previousAssignmentLine = null + } + return linesToRemove + } + private fun returnregisters(subroutine: Subroutine): List { return when { subroutine.returntypes.isEmpty() -> listOf() @@ -439,6 +444,14 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He return super.process(assignment) } + override fun process(scope: AnonymousScope): AnonymousScope { + val linesToRemove = deduplicateAssignments(scope.statements) + if(linesToRemove.isNotEmpty()) { + linesToRemove.reversed().forEach{scope.statements.removeAt(it)} + } + return super.process(scope) + } + private fun same(target: AssignTarget, value: IExpression): Boolean { return when { target.memoryAddress!=null -> false diff --git a/docs/docs.iml b/docs/docs.iml index 28bcee67e..78aaf41eb 100644 --- a/docs/docs.iml +++ b/docs/docs.iml @@ -1,11 +1,10 @@ - + - \ No newline at end of file diff --git a/examples/test.p8 b/examples/test.p8 index 574102050..458dde1e7 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,14 +5,6 @@ sub start() { - memset($0400+40*3, 40*8, 81) - memsetw($0400+40*12, 8*40/2, $80a0) - memset($0400, 20, 33) - memcopy($0400, $0400+121, 20) - - - return - ubyte ub1 ubyte ub2 byte b1 @@ -37,7 +29,118 @@ word[3] wa2 - ub1=ub2*ub1 + A=$34 + Y=$34 + ub1=$33 + ub1=$34 + ub2=1 + ub2=2 + ub2=3 + ub2=4 + ub2=$34 + uw1=0 + uw1=1 + uw1=$0034 + w1=1 + w1=2 + w1=3 + w1=$0034 + + + if A>5 { + A=$34 + Y=$34 + ub1=$33 + ub1=$34 + ub2=1 + ub2=2 + ub2=3 + ub2=4 + ub2=$34 + uw1=0 + uw1=1 + uw1=$0034 + w1=1 + w1=2 + w1=3 + w1=$0034 + + } else { + A=$34 + Y=$34 + ub1=$33 + ub1=$34 + ub2=1 + ub2=2 + ub2=3 + ub2=4 + ub2=$34 + uw1=0 + uw1=1 + uw1=$0034 + w1=1 + w1=2 + w1=3 + w1=$0034 + + } + + while(true) { + A=$34 + Y=$34 + ub1=$33 + ub1=$34 + ub2=1 + ub2=2 + ub2=3 + ub2=4 + ub2=$34 + uw1=0 + uw1=1 + uw1=$0034 + w1=1 + w1=2 + w1=3 + w1=$0034 + + } + + repeat { + A=$34 + Y=$34 + ub1=$33 + ub1=$34 + ub2=1 + ub2=2 + ub2=3 + ub2=4 + ub2=$34 + uw1=0 + uw1=1 + uw1=$0034 + w1=1 + w1=2 + w1=3 + w1=$0034 + + } until(true) + + A=$34 + Y=$34 + ub1=$33 + ub1=$34 + ub2=1 + ub2=2 + ub2=3 + ub2=4 + ub2=$34 + uw1=0 + uw1=1 + uw1=$0034 + w1=1 + w1=2 + w1=3 + w1=$0034 }