67 lines
1.7 KiB
Go
67 lines
1.7 KiB
Go
package optimizer
|
|
|
|
import "strings"
|
|
|
|
// passStoreTransfer converts a store immediately followed by a reload of the
|
|
// same value into a different register, into a register-transfer instruction.
|
|
//
|
|
// sta M; ldy M → sta M; tay
|
|
// sta M; ldx M → sta M; tax
|
|
//
|
|
// A already holds M after the store, so the reload from memory is redundant.
|
|
// Only comments and @@OPT markers may separate the two instructions; a label
|
|
// or any other code line blocks the transform.
|
|
func passStoreTransfer(lines []asmLine, cfg *Config) []asmLine {
|
|
var result []asmLine
|
|
|
|
for i := 0; i < len(lines); i++ {
|
|
line := lines[i]
|
|
|
|
if line.isCode && line.opcode == "sta" && isSafeStldOperand(line.operand, cfg) {
|
|
if j, transfer := findTransferTarget(lines, i+1, line.operand); transfer != "" {
|
|
result = append(result, line)
|
|
for k := i + 1; k < j; k++ {
|
|
result = append(result, lines[k])
|
|
}
|
|
result = append(result, asmLine{
|
|
text: "\t" + transfer,
|
|
isCode: true,
|
|
opcode: transfer,
|
|
})
|
|
i = j
|
|
continue
|
|
}
|
|
}
|
|
|
|
result = append(result, line)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// findTransferTarget scans forward from start, skipping comments and @@OPT
|
|
// markers, for an ldy/ldx of the given operand. Returns the index of that line
|
|
// and the transfer opcode to use, or "" if no safe match is found.
|
|
func findTransferTarget(lines []asmLine, start int, operand string) (int, string) {
|
|
i := start
|
|
for i < len(lines) && (lines[i].isComment || lines[i].optMarker) {
|
|
i++
|
|
}
|
|
if i >= len(lines) || !lines[i].isCode {
|
|
return 0, ""
|
|
}
|
|
|
|
next := lines[i]
|
|
if next.operand != operand || len(strings.Fields(next.text)) != 2 {
|
|
return 0, ""
|
|
}
|
|
|
|
switch next.opcode {
|
|
case "ldy":
|
|
return i, "tay"
|
|
case "ldx":
|
|
return i, "tax"
|
|
default:
|
|
return 0, ""
|
|
}
|
|
}
|