c65gm/internal/optimizer/pass_imm.go

77 lines
1.5 KiB
Go

package optimizer
import "strings"
// passImmElimination removes redundant immediate loads.
// Pattern: lda #N; ... (no A modification) ...; lda #N → remove second
// Applies to A, X, Y.
func passImmElimination(lines []asmLine) []asmLine {
rs := newRegState()
var result []asmLine
for _, line := range lines {
if !line.isCode {
if line.isLabel {
rs.reset()
}
result = append(result, line)
continue
}
if isChainBreaker(line.opcode) {
rs.reset()
result = append(result, line)
continue
}
if isRedundantImm(line, rs) {
continue // remove line
}
updateRegState(line, rs)
result = append(result, line)
}
return result
}
func isRedundantImm(line asmLine, rs *regState) bool {
if !isImmediate(line.operand) {
return false
}
val := parseValueAfterHash(line.operand)
if val < 0 {
return false // unparseable (label ref, <label, >label)
}
switch line.opcode {
case "lda":
if rs.a.val == val {
return true
}
rs.loadImm("a", val)
case "ldx":
if rs.x.val == val {
return true
}
rs.loadImm("x", val)
case "ldy":
if rs.y.val == val {
return true
}
rs.loadImm("y", val)
}
return false
}
// parseValueAfterHash extracts the numeric value after '#'. Returns -1 for unparseable.
func parseValueAfterHash(operand string) int {
if len(operand) < 2 {
return -1
}
s := operand[1:]
// ACME high/low byte operators are not evaluable at optimize time
if strings.HasPrefix(s, "<") || strings.HasPrefix(s, ">") {
return -1
}
return parseHexOrDec(s)
}