sort assignments to enable same-value optimization

This commit is contained in:
Irmen de Jong 2019-01-23 21:50:43 +01:00
parent d37c9d1680
commit 39a5e341af
5 changed files with 210 additions and 43 deletions

View File

@ -781,17 +781,17 @@ data class AssignTarget(val register: Register?,
return null return null
} }
fun shortString(): String { fun shortString(withTypePrefix: Boolean=false): String {
if(register!=null) if(register!=null)
return register.toString() return (if(withTypePrefix) "0register::" else "") + register.toString()
if(identifier!=null) if(identifier!=null)
return identifier.nameInSource.last() return (if(withTypePrefix) "3identifier::" else "") + identifier.nameInSource.last()
if(arrayindexed!=null) if(arrayindexed!=null)
return arrayindexed.identifier.nameInSource.last() return (if(withTypePrefix) "2arrayidx::" else "") + arrayindexed.identifier.nameInSource.last()
val address = memoryAddress?.addressExpression val address = memoryAddress?.addressExpression
if(address is LiteralValue) if(address is LiteralValue)
return address.asIntegerValue.toString() return (if(withTypePrefix) "1address::" else "") +address.asIntegerValue.toString()
return "???" return if(withTypePrefix) "???::???" else "???"
} }
} }

View File

@ -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. // - 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 directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
private val vardeclsToAdd = mutableMapOf<INameScope, MutableList<VarDecl>>() private val vardeclsToAdd = mutableMapOf<INameScope, MutableList<VarDecl>>()
override fun process(module: Module) { override fun process(module: Module) {
super.process(module) super.process(module)
module.statements = sortConstantAssignments(module.statements)
val (blocks, other) = module.statements.partition { it is Block } val (blocks, other) = module.statements.partition { it is Block }
module.statements = other.asSequence().plus(blocks.sortedBy { (it as Block).address ?: Int.MAX_VALUE }).toMutableList() 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 { override fun process(block: Block): IStatement {
block.statements = sortConstantAssignments(block.statements)
val subroutines = block.statements.filterIsInstance<Subroutine>() val subroutines = block.statements.filterIsInstance<Subroutine>()
var numSubroutinesAtEnd = 0 var numSubroutinesAtEnd = 0
// move all subroutines to the end of the block // 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 { override fun process(subroutine: Subroutine): IStatement {
super.process(subroutine) super.process(subroutine)
subroutine.statements = sortConstantAssignments(subroutine.statements)
val varDecls = subroutine.statements.filterIsInstance<VarDecl>() val varDecls = subroutine.statements.filterIsInstance<VarDecl>()
subroutine.statements.removeAll(varDecls) subroutine.statements.removeAll(varDecls)
subroutine.statements.addAll(0, varDecls) subroutine.statements.addAll(0, varDecls)
@ -120,6 +123,55 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He
return subroutine return subroutine
} }
override fun process(scope: AnonymousScope): AnonymousScope {
scope.statements = sortConstantAssignments(scope.statements)
return super.process(scope)
}
private fun sortConstantAssignments(statements: MutableList<IStatement>): MutableList<IStatement> {
// 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<IStatement>()
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<IStatement>): Pair<List<Assignment>, IStatement?> {
val sequence= mutableListOf<Assignment>(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 { override fun process(decl: VarDecl): IStatement {
super.process(decl) super.process(decl)
if(decl.type!=VarDeclType.VAR || decl.value==null) if(decl.type!=VarDeclType.VAR || decl.value==null)

View File

@ -54,27 +54,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
} }
} }
val linesToRemove = mutableListOf<Int>() val linesToRemove = deduplicateAssignments(subroutine.statements)
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
}
if(linesToRemove.isNotEmpty()) { if(linesToRemove.isNotEmpty()) {
linesToRemove.reversed().forEach{subroutine.statements.removeAt(it)} linesToRemove.reversed().forEach{subroutine.statements.removeAt(it)}
} }
@ -82,6 +62,31 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
return subroutine return subroutine
} }
private fun deduplicateAssignments(statements: List<IStatement>): MutableList<Int> {
// removes 'duplicate' assignments that assign the same target
val linesToRemove = mutableListOf<Int>()
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<RegisterOrStatusflag> { private fun returnregisters(subroutine: Subroutine): List<RegisterOrStatusflag> {
return when { return when {
subroutine.returntypes.isEmpty() -> listOf() subroutine.returntypes.isEmpty() -> listOf()
@ -439,6 +444,14 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
return super.process(assignment) 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 { private fun same(target: AssignTarget, value: IExpression): Boolean {
return when { return when {
target.memoryAddress!=null -> false target.memoryAddress!=null -> false

View File

@ -1,11 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4"> <module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true"> <component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/build" />
</content> </content>
<orderEntry type="jdk" jdkName="Python 3.7" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
</module> </module>

View File

@ -5,14 +5,6 @@
sub start() { 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 ub1
ubyte ub2 ubyte ub2
byte b1 byte b1
@ -37,7 +29,118 @@
word[3] wa2 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
} }