c65gm/internal/optimizer/pass_transfer.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, ""
}
}