From 8bd5cc01b49febffd3eb0d010057f04190741dc8 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 15 Jan 2019 00:57:53 +0100 Subject: [PATCH] asm assignment opts --- .../src/prog8/compiler/target/c64/AsmGen.kt | 79 ++--------- .../prog8/compiler/target/c64/AsmOptimizer.kt | 127 ++++++++++++++++++ 2 files changed, 137 insertions(+), 69 deletions(-) create mode 100644 compiler/src/prog8/compiler/target/c64/AsmOptimizer.kt diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index 2a89f4fd8..b51f882ae 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -85,65 +85,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, return AssemblyProgram(program.name) } - private fun optimizeAssembly(lines: MutableList): Int { - // sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated. - val removeLines = mutableListOf() - 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) { if(splitlines) { 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 " } 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_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]) """ lda #<$number + ldy #>$number sta ${segment[1].callLabel} - lda #>$number - sta ${segment[1].callLabel}+1 + sty ${segment[1].callLabel}+1 """ }, // var = ubytevar @@ -1695,9 +1636,9 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.POP_MEM_WORD)) { segment -> """ lda #<${hexVal(segment[0])} + ldy #>${hexVal(segment[0])} sta ${hexVal(segment[1])} - lda #>${hexVal(segment[0])} - sta ${hexValPlusOne(segment[1])} + sty ${hexValPlusOne(segment[1])} """ }, // mem uword = ubyte var @@ -1720,11 +1661,11 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, }, // mem uword = uword var 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 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 AsmPattern( @@ -1742,9 +1683,9 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, val index = intVal(segment[0]) """ lda ${segment[1].callLabel}+$index + ldy #0 sta ${hexVal(segment[3])} - lda #0 - sta ${hexValPlusOne(segment[3])} + sty ${hexValPlusOne(segment[3])} """ }, // mem uword = bytearray[index] (sign extended) @@ -2248,7 +2189,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, // uwordarray[index] = address-of var AsmPattern(listOf(Opcode.PUSH_ADDR_HEAPVAR, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment -> 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 AsmPattern(listOf(Opcode.PUSH_MEM_UW, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment -> diff --git a/compiler/src/prog8/compiler/target/c64/AsmOptimizer.kt b/compiler/src/prog8/compiler/target/c64/AsmOptimizer.kt new file mode 100644 index 000000000..a76f190f1 --- /dev/null +++ b/compiler/src/prog8/compiler/target/c64/AsmOptimizer.kt @@ -0,0 +1,127 @@ +package prog8.compiler.target.c64 + +fun optimizeAssembly(lines: MutableList): 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 { + + // 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() + 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, 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 { + // sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated + val removeLines = mutableListOf() + 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 { + // sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated. + val removeLines = mutableListOf() + 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 +}