77 lines
1.5 KiB
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)
|
|
}
|