2022-09-19 17:41:43 +00:00
|
|
|
package prog8.codegen.intermediate
|
2022-08-21 15:21:29 +00:00
|
|
|
|
2023-05-01 21:00:51 +00:00
|
|
|
import prog8.code.core.IErrorReporter
|
2022-09-19 17:41:43 +00:00
|
|
|
import prog8.intermediate.*
|
|
|
|
|
2023-05-01 21:00:51 +00:00
|
|
|
class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
|
|
|
fun optimize(optimizationsEnabled: Boolean, errors: IErrorReporter) {
|
|
|
|
if(!optimizationsEnabled)
|
|
|
|
return optimizeOnlyJoinChunks()
|
|
|
|
|
|
|
|
peepholeOptimize()
|
|
|
|
val remover = IRUnusedCodeRemover(irprog, errors)
|
|
|
|
var totalRemovals = 0
|
|
|
|
do {
|
|
|
|
val numRemoved = remover.optimize()
|
|
|
|
totalRemovals += numRemoved
|
|
|
|
} while(numRemoved>0 && errors.noErrors())
|
|
|
|
errors.report()
|
|
|
|
|
|
|
|
if(totalRemovals>0) {
|
|
|
|
irprog.linkChunks() // re-link again.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun optimizeOnlyJoinChunks() {
|
2023-05-18 09:32:20 +00:00
|
|
|
irprog.foreachSub { sub ->
|
2023-05-03 20:31:04 +00:00
|
|
|
joinChunks(sub)
|
2023-04-07 20:03:13 +00:00
|
|
|
removeEmptyChunks(sub)
|
|
|
|
joinChunks(sub)
|
|
|
|
}
|
|
|
|
irprog.linkChunks() // re-link
|
|
|
|
}
|
|
|
|
|
2023-05-01 21:00:51 +00:00
|
|
|
private fun peepholeOptimize() {
|
2023-05-18 09:32:20 +00:00
|
|
|
irprog.foreachSub { sub ->
|
2023-05-03 20:31:04 +00:00
|
|
|
joinChunks(sub)
|
2022-10-12 22:56:44 +00:00
|
|
|
removeEmptyChunks(sub)
|
2022-09-30 00:47:33 +00:00
|
|
|
joinChunks(sub)
|
2023-05-18 11:51:13 +00:00
|
|
|
|
2022-10-16 16:30:14 +00:00
|
|
|
sub.chunks.withIndex().forEach { (index, chunk1) ->
|
2022-09-08 20:59:13 +00:00
|
|
|
// we don't optimize Inline Asm chunks here.
|
2022-10-16 16:30:14 +00:00
|
|
|
val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null
|
|
|
|
if(chunk1 is IRCodeChunk) {
|
2022-09-08 20:59:13 +00:00
|
|
|
do {
|
2022-10-16 16:30:14 +00:00
|
|
|
val indexedInstructions = chunk1.instructions.withIndex()
|
|
|
|
.map { IndexedValue(it.index, it.value) }
|
|
|
|
val changed = removeNops(chunk1, indexedInstructions)
|
2023-10-28 03:44:47 +00:00
|
|
|
|| replaceConcatZeroMsbWithExt(chunk1, indexedInstructions)
|
2023-12-31 16:04:28 +00:00
|
|
|
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions)
|
2022-10-16 16:30:14 +00:00
|
|
|
|| removeUselessArithmetic(chunk1, indexedInstructions)
|
2023-07-14 21:17:29 +00:00
|
|
|
|| removeNeedlessCompares(chunk1, indexedInstructions)
|
2022-10-16 16:30:14 +00:00
|
|
|
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|
|
|
|
|| removeDoubleSecClc(chunk1, indexedInstructions)
|
|
|
|
|| cleanupPushPop(chunk1, indexedInstructions)
|
2023-07-03 19:57:32 +00:00
|
|
|
// TODO other optimizations
|
2022-09-08 20:59:13 +00:00
|
|
|
} while (changed)
|
|
|
|
}
|
2022-08-25 19:02:18 +00:00
|
|
|
}
|
2022-10-16 16:30:14 +00:00
|
|
|
removeEmptyChunks(sub)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
2022-10-31 23:15:39 +00:00
|
|
|
|
2023-07-03 19:57:32 +00:00
|
|
|
// TODO also do register optimization step here at the end?
|
|
|
|
|
2023-05-01 21:00:51 +00:00
|
|
|
irprog.linkChunks() // re-link
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
|
2023-10-28 03:44:47 +00:00
|
|
|
private fun replaceConcatZeroMsbWithExt(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
|
|
|
var changed = false
|
|
|
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
|
|
|
if (ins.opcode == Opcode.CONCAT) {
|
|
|
|
// if the previous instruction loads a zero in the msb, this can be turned into EXT.B instead
|
|
|
|
val prev = indexedInstructions[idx-1].value
|
|
|
|
if(prev.opcode==Opcode.LOAD && prev.immediate==0 && prev.reg1==ins.reg2) {
|
|
|
|
chunk.instructions[idx] = IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = ins.reg1, reg2 = ins.reg3)
|
|
|
|
chunk.instructions.removeAt(idx-1)
|
|
|
|
changed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return changed
|
|
|
|
}
|
|
|
|
|
2022-10-12 22:56:44 +00:00
|
|
|
private fun removeEmptyChunks(sub: IRSubroutine) {
|
|
|
|
if(sub.chunks.isEmpty())
|
|
|
|
return
|
|
|
|
|
|
|
|
/*
|
|
|
|
Empty Code chunk with label ->
|
|
|
|
If next chunk has no label -> move label to next chunk, remove original
|
2023-11-25 00:08:26 +00:00
|
|
|
If next chunk has label -> label name should be the same, remove original. Otherwise merge both labels into 1.
|
2022-10-16 16:30:14 +00:00
|
|
|
If is last chunk -> keep chunk in place because of the label.
|
2022-10-12 22:56:44 +00:00
|
|
|
Empty Code chunk without label ->
|
|
|
|
should not have been generated! ERROR.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
val relabelChunks = mutableListOf<Pair<Int, String>>()
|
|
|
|
val removeChunks = mutableListOf<Int>()
|
2023-11-25 00:08:26 +00:00
|
|
|
val replaceLabels = mutableMapOf<String, String>()
|
2022-10-12 22:56:44 +00:00
|
|
|
|
|
|
|
sub.chunks.withIndex().forEach { (index, chunk) ->
|
2022-10-16 16:30:14 +00:00
|
|
|
if(chunk is IRCodeChunk && chunk.instructions.isEmpty()) {
|
|
|
|
if(chunk.label==null) {
|
2022-10-12 22:56:44 +00:00
|
|
|
removeChunks += index
|
2022-10-16 16:30:14 +00:00
|
|
|
} else {
|
|
|
|
if (index < sub.chunks.size - 1) {
|
|
|
|
val nextchunk = sub.chunks[index + 1]
|
|
|
|
if (nextchunk.label == null) {
|
|
|
|
// can transplant label to next chunk and remove this empty one.
|
|
|
|
relabelChunks += Pair(index + 1, chunk.label!!)
|
|
|
|
removeChunks += index
|
|
|
|
} else {
|
2023-11-25 00:08:26 +00:00
|
|
|
// merge both labels into 1 except if this is the label chunk at the start of the subroutine
|
|
|
|
if(index>0) {
|
|
|
|
if (chunk.label == nextchunk.label)
|
|
|
|
removeChunks += index
|
|
|
|
else {
|
|
|
|
removeChunks += index
|
|
|
|
replaceLabels[chunk.label!!] = nextchunk.label!!
|
|
|
|
replaceLabels.entries.forEach { (key, value) ->
|
|
|
|
if (value == chunk.label)
|
|
|
|
replaceLabels[key] = nextchunk.label!!
|
|
|
|
}
|
|
|
|
}
|
2022-10-16 16:30:14 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-12 22:56:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
relabelChunks.forEach { (index, label) ->
|
2022-11-02 21:12:42 +00:00
|
|
|
val chunk = IRCodeChunk(label, null)
|
2023-08-02 19:26:40 +00:00
|
|
|
val subChunk = sub.chunks[index]
|
|
|
|
chunk.instructions += subChunk.instructions
|
|
|
|
if(subChunk is IRCodeChunk)
|
|
|
|
chunk.appendSrcPositions(subChunk.sourceLinesPositions)
|
2022-10-12 22:56:44 +00:00
|
|
|
sub.chunks[index] = chunk
|
|
|
|
}
|
|
|
|
removeChunks.reversed().forEach { index -> sub.chunks.removeAt(index) }
|
2023-11-25 00:08:26 +00:00
|
|
|
|
|
|
|
sub.chunks.forEach { chunk ->
|
|
|
|
chunk.instructions.withIndex().forEach { (idx, instr) ->
|
|
|
|
instr.labelSymbol?.let {
|
|
|
|
if(instr.opcode in OpcodesThatBranch) {
|
|
|
|
replaceLabels.forEach { (from, to) ->
|
|
|
|
if (it == from) {
|
|
|
|
chunk.instructions[idx] = instr.copy(labelSymbol = to)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
val actualPrefix = "$from."
|
|
|
|
if (it.startsWith(actualPrefix))
|
|
|
|
chunk.instructions[idx] = instr.copy(labelSymbol = "$to.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-10-12 22:56:44 +00:00
|
|
|
}
|
|
|
|
|
2022-09-30 00:47:33 +00:00
|
|
|
private fun joinChunks(sub: IRSubroutine) {
|
2022-10-25 20:57:04 +00:00
|
|
|
// Subroutine contains a list of chunks. Some can be joined into one.
|
2022-09-30 00:47:33 +00:00
|
|
|
|
2022-10-12 22:56:44 +00:00
|
|
|
if(sub.chunks.isEmpty())
|
2022-09-30 00:47:33 +00:00
|
|
|
return
|
|
|
|
|
2023-05-03 20:31:04 +00:00
|
|
|
fun mayJoinCodeChunks(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
|
2022-10-25 20:57:04 +00:00
|
|
|
if(chunk.label!=null)
|
|
|
|
return false
|
2022-09-30 00:47:33 +00:00
|
|
|
if(previous is IRCodeChunk && chunk is IRCodeChunk) {
|
2022-10-25 20:57:04 +00:00
|
|
|
// if the previous chunk doesn't end in a jump or a return, flow continues into the next chunk
|
|
|
|
val lastInstruction = previous.instructions.lastOrNull()
|
|
|
|
if(lastInstruction!=null)
|
|
|
|
return lastInstruction.opcode !in OpcodesThatJump
|
2022-09-30 00:47:33 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
val chunks = mutableListOf<IRCodeChunkBase>()
|
|
|
|
chunks += sub.chunks[0]
|
|
|
|
for(ix in 1 until sub.chunks.size) {
|
2022-10-30 22:42:41 +00:00
|
|
|
val lastChunk = chunks.last()
|
2023-05-03 20:31:04 +00:00
|
|
|
val candidate = sub.chunks[ix]
|
|
|
|
when(candidate) {
|
|
|
|
is IRCodeChunk -> {
|
|
|
|
if(mayJoinCodeChunks(lastChunk, candidate)) {
|
|
|
|
lastChunk.instructions += candidate.instructions
|
|
|
|
lastChunk.next = candidate.next
|
2023-08-02 19:26:40 +00:00
|
|
|
if(lastChunk is IRCodeChunk)
|
|
|
|
lastChunk.appendSrcPositions(candidate.sourceLinesPositions)
|
2023-05-03 20:31:04 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
chunks += candidate
|
|
|
|
}
|
|
|
|
is IRInlineAsmChunk -> {
|
|
|
|
if(candidate.label!=null)
|
|
|
|
chunks += candidate
|
|
|
|
else if(lastChunk.isEmpty()) {
|
|
|
|
val label = lastChunk.label
|
|
|
|
if(label!=null)
|
|
|
|
chunks += IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
|
|
|
|
else
|
|
|
|
chunks += candidate
|
|
|
|
}
|
|
|
|
}
|
|
|
|
is IRInlineBinaryChunk -> {
|
|
|
|
if(candidate.label!=null)
|
|
|
|
chunks += candidate
|
|
|
|
else if(lastChunk.isEmpty()) {
|
|
|
|
val label = lastChunk.label
|
|
|
|
if(label!=null)
|
|
|
|
chunks += IRInlineBinaryChunk(label, candidate.data, candidate.next)
|
|
|
|
else
|
|
|
|
chunks += candidate
|
|
|
|
}
|
|
|
|
}
|
2022-10-30 22:42:41 +00:00
|
|
|
}
|
2022-09-30 00:47:33 +00:00
|
|
|
}
|
|
|
|
sub.chunks.clear()
|
2022-10-25 20:57:04 +00:00
|
|
|
sub.chunks += chunks
|
2022-09-30 00:47:33 +00:00
|
|
|
}
|
|
|
|
|
2022-09-26 17:46:44 +00:00
|
|
|
private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
2022-08-21 15:21:29 +00:00
|
|
|
// push followed by pop to same target, or different target->replace with load
|
|
|
|
var changed = false
|
|
|
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
2022-09-19 17:41:43 +00:00
|
|
|
if(ins.opcode== Opcode.PUSH) {
|
2022-10-04 20:54:14 +00:00
|
|
|
if(idx < chunk.instructions.size-1) {
|
2023-03-12 21:16:20 +00:00
|
|
|
val insAfter = chunk.instructions[idx+1]
|
|
|
|
if(insAfter.opcode == Opcode.POP) {
|
2022-09-26 17:46:44 +00:00
|
|
|
if(ins.reg1==insAfter.reg1) {
|
2022-10-04 20:54:14 +00:00
|
|
|
chunk.instructions.removeAt(idx)
|
|
|
|
chunk.instructions.removeAt(idx)
|
2022-08-21 15:21:29 +00:00
|
|
|
} else {
|
2022-10-04 20:54:14 +00:00
|
|
|
chunk.instructions[idx] = IRInstruction(Opcode.LOADR, ins.type, reg1=insAfter.reg1, reg2=ins.reg1)
|
|
|
|
chunk.instructions.removeAt(idx+1)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
changed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return changed
|
|
|
|
}
|
|
|
|
|
2022-09-26 17:46:44 +00:00
|
|
|
private fun removeDoubleSecClc(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
2022-08-21 15:21:29 +00:00
|
|
|
// double sec, clc
|
|
|
|
// sec+clc or clc+sec
|
|
|
|
var changed = false
|
|
|
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
2022-09-19 17:41:43 +00:00
|
|
|
if(ins.opcode== Opcode.SEC || ins.opcode== Opcode.CLC) {
|
2022-10-04 20:54:14 +00:00
|
|
|
if(idx < chunk.instructions.size-1) {
|
2023-03-12 21:16:20 +00:00
|
|
|
val insAfter = chunk.instructions[idx+1]
|
|
|
|
if(insAfter.opcode == ins.opcode) {
|
2022-10-04 20:54:14 +00:00
|
|
|
chunk.instructions.removeAt(idx)
|
2022-08-21 15:21:29 +00:00
|
|
|
changed = true
|
|
|
|
}
|
2023-03-12 21:16:20 +00:00
|
|
|
else if(ins.opcode== Opcode.SEC && insAfter.opcode== Opcode.CLC) {
|
2022-10-04 20:54:14 +00:00
|
|
|
chunk.instructions.removeAt(idx)
|
2022-08-21 15:21:29 +00:00
|
|
|
changed = true
|
|
|
|
}
|
2023-03-12 21:16:20 +00:00
|
|
|
else if(ins.opcode== Opcode.CLC && insAfter.opcode== Opcode.SEC) {
|
2022-10-04 20:54:14 +00:00
|
|
|
chunk.instructions.removeAt(idx)
|
2022-08-21 15:21:29 +00:00
|
|
|
changed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return changed
|
|
|
|
}
|
|
|
|
|
2022-10-16 16:30:14 +00:00
|
|
|
private fun removeWeirdBranches(chunk: IRCodeChunk, nextChunk: IRCodeChunkBase?, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
2022-08-21 15:21:29 +00:00
|
|
|
var changed = false
|
|
|
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
2022-09-19 17:41:43 +00:00
|
|
|
val labelSymbol = ins.labelSymbol
|
2022-10-06 22:34:56 +00:00
|
|
|
|
2022-10-16 16:30:14 +00:00
|
|
|
// remove jump/branch to label immediately below (= next chunk if it has that label)
|
2022-09-19 17:41:43 +00:00
|
|
|
if(ins.opcode== Opcode.JUMP && labelSymbol!=null) {
|
2022-10-16 16:30:14 +00:00
|
|
|
if(idx==chunk.instructions.size-1 && ins.branchTarget===nextChunk) {
|
|
|
|
chunk.instructions.removeAt(idx)
|
|
|
|
changed = true
|
|
|
|
}
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
2023-03-12 21:16:20 +00:00
|
|
|
|
2022-09-30 13:27:03 +00:00
|
|
|
// remove useless RETURN
|
2023-04-11 20:28:19 +00:00
|
|
|
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR)) {
|
2023-03-12 21:16:20 +00:00
|
|
|
val previous = chunk.instructions[idx-1]
|
|
|
|
if(previous.opcode in OpcodesThatJump) {
|
|
|
|
chunk.instructions.removeAt(idx)
|
|
|
|
changed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// replace subsequent opcodes that jump by just the first
|
|
|
|
if(idx>0 && (ins.opcode in OpcodesThatJump)) {
|
|
|
|
val previous = chunk.instructions[idx-1]
|
|
|
|
if(previous.opcode in OpcodesThatJump) {
|
|
|
|
chunk.instructions.removeAt(idx)
|
|
|
|
changed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// replace call + return --> jump
|
2023-05-12 21:26:36 +00:00
|
|
|
// This can no longer be done here on the IR level, with the current CALL opcode that encodes the full subroutine call setup.
|
|
|
|
// If machine code is ever generated from this IR, *that* should possibly optimize the JSR + RTS into a JMP.
|
|
|
|
// if(idx>0 && ins.opcode==Opcode.RETURN) {
|
|
|
|
// val previous = chunk.instructions[idx-1]
|
|
|
|
// if(previous.opcode==Opcode.CALL) {
|
|
|
|
// chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
|
|
|
|
// chunk.instructions.removeAt(idx)
|
|
|
|
// changed = true
|
|
|
|
// }
|
|
|
|
// }
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
return changed
|
|
|
|
}
|
|
|
|
|
2023-07-14 21:17:29 +00:00
|
|
|
private fun removeNeedlessCompares(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
|
|
|
// a CMPI with 0, after an instruction like LOAD that already sets the status bits, can be removed.
|
|
|
|
// but only if the instruction after it is not using the Carry bit because that won't be set by a LOAD instruction etc.
|
|
|
|
var changed = false
|
|
|
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
|
|
|
if(idx>0 && idx<(indexedInstructions.size-1) && ins.opcode==Opcode.CMPI && ins.immediate==0) {
|
|
|
|
val previous = indexedInstructions[idx-1].value
|
2023-12-31 16:04:28 +00:00
|
|
|
if(previous.reg1==ins.reg1) {
|
|
|
|
if (previous.opcode in OpcodesThatSetStatusbitsIncludingCarry) {
|
2023-07-14 21:17:29 +00:00
|
|
|
chunk.instructions.removeAt(idx)
|
|
|
|
changed = true
|
2023-12-31 16:04:28 +00:00
|
|
|
} else if (previous.opcode in OpcodesThatSetStatusbitsButNotCarry) {
|
|
|
|
val next = indexedInstructions[idx + 1].value
|
|
|
|
if (next.opcode !in arrayOf(Opcode.BSTCC, Opcode.BSTCS, Opcode.BSTPOS, Opcode.BSTNEG)) {
|
|
|
|
chunk.instructions.removeAt(idx)
|
|
|
|
changed = true
|
|
|
|
}
|
2023-07-14 21:17:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return changed
|
|
|
|
}
|
|
|
|
|
2022-09-26 17:46:44 +00:00
|
|
|
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
2022-08-21 15:21:29 +00:00
|
|
|
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
|
|
|
|
var changed = false
|
|
|
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
|
|
|
when (ins.opcode) {
|
|
|
|
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> {
|
2023-04-09 13:06:40 +00:00
|
|
|
if (ins.immediate == 1) {
|
2022-10-04 20:54:14 +00:00
|
|
|
chunk.instructions.removeAt(idx)
|
2022-08-21 15:21:29 +00:00
|
|
|
changed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Opcode.ADD, Opcode.SUB -> {
|
2023-04-09 13:06:40 +00:00
|
|
|
if (ins.immediate == 1) {
|
2022-10-04 20:54:14 +00:00
|
|
|
chunk.instructions[idx] = IRInstruction(
|
2022-08-21 15:21:29 +00:00
|
|
|
if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC,
|
|
|
|
ins.type,
|
|
|
|
ins.reg1
|
|
|
|
)
|
|
|
|
changed = true
|
2023-04-09 13:06:40 +00:00
|
|
|
} else if (ins.immediate == 0) {
|
2022-10-04 20:54:14 +00:00
|
|
|
chunk.instructions.removeAt(idx)
|
2022-08-21 15:21:29 +00:00
|
|
|
changed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Opcode.AND -> {
|
2023-04-09 13:06:40 +00:00
|
|
|
if (ins.immediate == 0) {
|
|
|
|
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
|
2022-08-21 15:21:29 +00:00
|
|
|
changed = true
|
2023-04-09 13:06:40 +00:00
|
|
|
} else if (ins.immediate == 255 && ins.type == IRDataType.BYTE) {
|
2022-10-04 20:54:14 +00:00
|
|
|
chunk.instructions.removeAt(idx)
|
2022-08-21 15:21:29 +00:00
|
|
|
changed = true
|
2023-04-09 13:06:40 +00:00
|
|
|
} else if (ins.immediate == 65535 && ins.type == IRDataType.WORD) {
|
2022-10-04 20:54:14 +00:00
|
|
|
chunk.instructions.removeAt(idx)
|
2022-08-21 15:21:29 +00:00
|
|
|
changed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Opcode.OR -> {
|
2023-04-09 13:06:40 +00:00
|
|
|
if (ins.immediate == 0) {
|
2022-10-04 20:54:14 +00:00
|
|
|
chunk.instructions.removeAt(idx)
|
2022-08-21 15:21:29 +00:00
|
|
|
changed = true
|
2023-04-09 13:06:40 +00:00
|
|
|
} else if ((ins.immediate == 255 && ins.type == IRDataType.BYTE) || (ins.immediate == 65535 && ins.type == IRDataType.WORD)) {
|
|
|
|
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = ins.immediate)
|
2022-08-21 15:21:29 +00:00
|
|
|
changed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Opcode.XOR -> {
|
2023-04-09 13:06:40 +00:00
|
|
|
if (ins.immediate == 0) {
|
2022-10-04 20:54:14 +00:00
|
|
|
chunk.instructions.removeAt(idx)
|
2022-08-21 15:21:29 +00:00
|
|
|
changed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else -> {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return changed
|
|
|
|
}
|
|
|
|
|
2022-09-26 17:46:44 +00:00
|
|
|
private fun removeNops(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
2022-08-21 15:21:29 +00:00
|
|
|
var changed = false
|
|
|
|
indexedInstructions.reversed().forEach { (idx, ins) ->
|
|
|
|
if (ins.opcode == Opcode.NOP) {
|
|
|
|
changed = true
|
2022-10-04 20:54:14 +00:00
|
|
|
chunk.instructions.removeAt(idx)
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return changed
|
|
|
|
}
|
|
|
|
|
2022-09-26 17:46:44 +00:00
|
|
|
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
2023-12-06 21:17:08 +00:00
|
|
|
return false
|
|
|
|
|
|
|
|
/*
|
2022-08-21 15:21:29 +00:00
|
|
|
var changed = false
|
|
|
|
indexedInstructions.forEach { (idx, ins) ->
|
|
|
|
|
|
|
|
// TODO: detect multiple loads to the same target registers, only keep first (if source is not I/O memory)
|
|
|
|
// TODO: detect multiple stores to the same target, only keep first (if target is not I/O memory)
|
|
|
|
// TODO: detect multiple float ffrom/fto to the same target, only keep first
|
|
|
|
// TODO: detect multiple sequential rnd with same reg1, only keep one
|
|
|
|
// TODO: detect subsequent same xors/nots/negs, remove the pairs completely as they cancel out
|
|
|
|
// TODO: detect multiple same ands, ors; only keep first
|
|
|
|
// TODO: (hard) detect multiple registers being assigned the same value (and not changed) - use only 1 of them
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
return changed
|
2023-12-06 21:17:08 +00:00
|
|
|
*/
|
2022-08-21 15:21:29 +00:00
|
|
|
}
|
|
|
|
}
|