asm assignment opts

This commit is contained in:
Irmen de Jong 2019-01-15 00:57:53 +01:00
parent 5f2bf2b375
commit 8bd5cc01b4
2 changed files with 137 additions and 69 deletions

View File

@ -85,65 +85,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
return AssemblyProgram(program.name) return AssemblyProgram(program.name)
} }
private fun optimizeAssembly(lines: MutableList<String>): Int {
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
val removeLines = mutableListOf<Int>()
var numberOfOptimizations = 0
for(pair in lines.withIndex().windowed(2)) {
val first = pair[0].value
val second = pair[1].value
if(first.trimStart().startsWith(';') || second.trimStart().startsWith(';'))
continue // skip over asm comments
if((" iny" in first || "\tiny" in first) && (" dey" in second || "\tdey" in second)
|| (" inx" in first || "\tinx" in first) && (" dex" in second || "\tdex" in second)
|| (" dey" in first || "\tdey" in first) && (" iny" in second || "\tiny" in second)
|| (" dex" in first || "\tdex" in first) && (" inx" in second || "\tinx" in second))
{
removeLines.add(pair[0].index)
removeLines.add(pair[1].index)
numberOfOptimizations++
}
}
for(i in removeLines.reversed())
lines.removeAt(i)
removeLines.clear()
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated
for(pair in lines.withIndex().windowed(2)) {
val first = pair[0].value.trim()
val second = pair[1].value.trim()
if(first.startsWith(';') || second.startsWith(';'))
continue // skip over asm comments
if((first.startsWith("sta ") && second.startsWith("lda ")) ||
(first.startsWith("stx ") && second.startsWith("ldx ")) ||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
(first.startsWith("lda ") && second.startsWith("lda ")) ||
(first.startsWith("ldy ") && second.startsWith("ldy ")) ||
(first.startsWith("ldx ") && second.startsWith("ldx ")) ||
(first.startsWith("sta ") && second.startsWith("lda ")) ||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
(first.startsWith("stx ") && second.startsWith("ldx "))
) {
val firstLoc = first.substring(4)
val secondLoc = second.substring(4)
if(firstLoc==secondLoc) {
removeLines.add(pair[1].index)
numberOfOptimizations++
}
}
}
for(i in removeLines.reversed())
lines.removeAt(i)
removeLines.clear()
return numberOfOptimizations
}
private fun out(str: String, splitlines: Boolean=true) { private fun out(str: String, splitlines: Boolean=true) {
if(splitlines) { if(splitlines) {
for (line in str.split('\n')) { for (line in str.split('\n')) {
@ -576,7 +517,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
" sta ${ESTACK_LO.toHex()},x | tya | sta ${ESTACK_HI.toHex()},x | dex " " sta ${ESTACK_LO.toHex()},x | tya | sta ${ESTACK_HI.toHex()},x | dex "
} }
Opcode.PUSH_ADDR_HEAPVAR -> { Opcode.PUSH_ADDR_HEAPVAR -> {
" lda #<${ins.callLabel} | sta ${ESTACK_LO.toHex()},x | lda #>${ins.callLabel} | sta ${ESTACK_HI.toHex()},x | dex" " lda #<${ins.callLabel} | sta ${ESTACK_LO.toHex()},x | lda #>${ins.callLabel} | sta ${ESTACK_HI.toHex()},x | dex"
} }
Opcode.POP_REGAX_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself") Opcode.POP_REGAX_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself")
Opcode.POP_REGXY_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself") Opcode.POP_REGXY_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself")
@ -1595,9 +1536,9 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
val number = hexVal(segment[0]) val number = hexVal(segment[0])
""" """
lda #<$number lda #<$number
ldy #>$number
sta ${segment[1].callLabel} sta ${segment[1].callLabel}
lda #>$number sty ${segment[1].callLabel}+1
sta ${segment[1].callLabel}+1
""" """
}, },
// var = ubytevar // var = ubytevar
@ -1695,9 +1636,9 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.POP_MEM_WORD)) { segment -> AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.POP_MEM_WORD)) { segment ->
""" """
lda #<${hexVal(segment[0])} lda #<${hexVal(segment[0])}
ldy #>${hexVal(segment[0])}
sta ${hexVal(segment[1])} sta ${hexVal(segment[1])}
lda #>${hexVal(segment[0])} sty ${hexValPlusOne(segment[1])}
sta ${hexValPlusOne(segment[1])}
""" """
}, },
// mem uword = ubyte var // mem uword = ubyte var
@ -1720,11 +1661,11 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
}, },
// mem uword = uword var // mem uword = uword var
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.POP_MEM_WORD)) { segment -> AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.POP_MEM_WORD)) { segment ->
" lda ${segment[0].callLabel} || sta ${hexVal(segment[1])} | lda ${segment[0].callLabel}+1 | sta ${hexValPlusOne(segment[1])}" " lda ${segment[0].callLabel} | ldy ${segment[0].callLabel}+1 | sta ${hexVal(segment[1])} | sty ${hexValPlusOne(segment[1])}"
}, },
// mem uword = address-of var // mem uword = address-of var
AsmPattern(listOf(Opcode.PUSH_ADDR_HEAPVAR, Opcode.POP_MEM_WORD)) { segment -> AsmPattern(listOf(Opcode.PUSH_ADDR_HEAPVAR, Opcode.POP_MEM_WORD)) { segment ->
" lda #<${segment[0].callLabel} || sta ${hexVal(segment[1])} | lda #>${segment[0].callLabel} | sta ${hexValPlusOne(segment[1])}" " lda #<${segment[0].callLabel} | ldy #>{segment[0].callLabel} | sta ${hexVal(segment[1])} | sty ${hexValPlusOne(segment[1])}"
}, },
// mem (u)word = mem (u)word // mem (u)word = mem (u)word
AsmPattern( AsmPattern(
@ -1742,9 +1683,9 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
val index = intVal(segment[0]) val index = intVal(segment[0])
""" """
lda ${segment[1].callLabel}+$index lda ${segment[1].callLabel}+$index
ldy #0
sta ${hexVal(segment[3])} sta ${hexVal(segment[3])}
lda #0 sty ${hexValPlusOne(segment[3])}
sta ${hexValPlusOne(segment[3])}
""" """
}, },
// mem uword = bytearray[index] (sign extended) // mem uword = bytearray[index] (sign extended)
@ -2248,7 +2189,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
// uwordarray[index] = address-of var // uwordarray[index] = address-of var
AsmPattern(listOf(Opcode.PUSH_ADDR_HEAPVAR, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment -> AsmPattern(listOf(Opcode.PUSH_ADDR_HEAPVAR, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment ->
val index = intVal(segment[1])*2 val index = intVal(segment[1])*2
" lda #<${segment[0].callLabel} | sta ${segment[2].callLabel}+$index | lda #>${segment[0].callLabel} | sta ${segment[2].callLabel}+${index+1}" " lda #<${segment[0].callLabel} | ldy #>${segment[0].callLabel} | sta ${segment[2].callLabel}+$index | sty ${segment[2].callLabel}+${index+1}"
}, },
// uwordarray[index] = mem uword // uwordarray[index] = mem uword
AsmPattern(listOf(Opcode.PUSH_MEM_UW, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment -> AsmPattern(listOf(Opcode.PUSH_MEM_UW, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment ->

View File

@ -0,0 +1,127 @@
package prog8.compiler.target.c64
fun optimizeAssembly(lines: MutableList<String>): Int {
var numberOfOptimizations = 0
var linesByTwo = getLinesBy(lines, 2)
var removeLines = optimizeIncDec(linesByTwo)
if(removeLines.isNotEmpty()) {
for (i in removeLines.reversed())
lines.removeAt(i)
linesByTwo = getLinesBy(lines, 2)
numberOfOptimizations++
}
removeLines = optimizeStoreLoadSame(linesByTwo)
if(removeLines.isNotEmpty()) {
for (i in removeLines.reversed())
lines.removeAt(i)
numberOfOptimizations++
}
var linesByFourteen = getLinesBy(lines, 14)
removeLines = optimizeSameAssignments(linesByFourteen)
if(removeLines.isNotEmpty()) {
for (i in removeLines.reversed())
lines.removeAt(i)
numberOfOptimizations++
}
println("ASM OPTIMIZATIONS: $numberOfOptimizations") // TODO weg
return numberOfOptimizations
}
fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>): List<Int> {
// optimize sequential assignments of the same value to various targets (bytes, words, floats)
// the float one is the one that requires 2*7=14 lines of code to check...
val removeLines = mutableListOf<Int>()
for (pair in linesByFourteen) {
val first = pair[0].value.trimStart()
val second = pair[1].value.trimStart()
val third = pair[2].value.trimStart()
val fourth = pair[3].value.trimStart()
val fifth = pair[4].value.trimStart()
val sixth = pair[5].value.trimStart()
val seventh = pair[6].value.trimStart()
val eighth = pair[7].value.trimStart()
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
fifth.startsWith("lda") && sixth.startsWith("ldy") && seventh.startsWith("sta") && eighth.startsWith("sty")) {
val firstvalue = first.substring(4)
val secondvalue = second.substring(4)
val thirdvalue = fifth.substring(4)
val fourthvalue = sixth.substring(4)
if(firstvalue==thirdvalue && secondvalue==fourthvalue) {
// lda/ldy sta/sty twice the same word --> remove second lda/ldy pair (fifth and sixth lines)
removeLines.add(pair[4].index)
removeLines.add(pair[5].index)
}
}
if(first.startsWith("lda") && second.startsWith("sta") && third.startsWith("lda") && fourth.startsWith("sta")) {
val firstvalue = first.substring(4)
val secondvalue = third.substring(4)
if(firstvalue==secondvalue) {
// lda value / sta ? / lda same-value / sta ? -> remove second lda (third line)
removeLines.add(pair[2].index)
}
}
// @todo check float initializations.
}
return removeLines
}
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
// all lines (that aren't empty or comments) in sliding pairs of 2
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
private fun optimizeStoreLoadSame(linesByTwo: List<List<IndexedValue<String>>>): List<Int> {
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated
val removeLines = mutableListOf<Int>()
for (pair in linesByTwo) {
val first = pair[0].value.trimStart()
val second = pair[1].value.trimStart()
if ((first.startsWith("sta ") && second.startsWith("lda ")) ||
(first.startsWith("stx ") && second.startsWith("ldx ")) ||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
(first.startsWith("lda ") && second.startsWith("lda ")) ||
(first.startsWith("ldy ") && second.startsWith("ldy ")) ||
(first.startsWith("ldx ") && second.startsWith("ldx ")) ||
(first.startsWith("sta ") && second.startsWith("lda ")) ||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
(first.startsWith("stx ") && second.startsWith("ldx "))
) {
val firstLoc = first.substring(4)
val secondLoc = second.substring(4)
if (firstLoc == secondLoc) {
removeLines.add(pair[1].index)
}
}
}
return removeLines
}
private fun optimizeIncDec(linesByTwo: List<List<IndexedValue<String>>>): List<Int> {
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
val removeLines = mutableListOf<Int>()
for (pair in linesByTwo) {
val first = pair[0].value
val second = pair[1].value
if ((" iny" in first || "\tiny" in first) && (" dey" in second || "\tdey" in second)
|| (" inx" in first || "\tinx" in first) && (" dex" in second || "\tdex" in second)
|| (" dey" in first || "\tdey" in first) && (" iny" in second || "\tiny" in second)
|| (" dex" in first || "\tdex" in first) && (" inx" in second || "\tinx" in second)) {
removeLines.add(pair[0].index)
removeLines.add(pair[1].index)
}
}
return removeLines
}