package optimizer import "strings" // passLoadElimination removes redundant memory-to-register loads. // Pattern: lda X; ... (X not modified) ...; lda X → remove second // Applies to A, X, Y registers identically. func passLoadElimination(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 } // Check for redundant memory load if line.opcode == "lda" && !isImmediate(line.operand) { if isIndexedOperand(line.operand) { // Indexed/indirect addressing: address depends on X/Y, can't track by operand text alone updateRegState(line, rs) } else if rs.a.src == line.operand { continue // redundant, remove } else { rs.a.src = line.operand } } else if line.opcode == "ldx" && !isImmediate(line.operand) { if isIndexedOperand(line.operand) { updateRegState(line, rs) } else if rs.x.src == line.operand { continue } else { rs.x.src = line.operand } } else if line.opcode == "ldy" && !isImmediate(line.operand) { if isIndexedOperand(line.operand) { updateRegState(line, rs) } else if rs.y.src == line.operand { continue } else { rs.y.src = line.operand } } else { updateRegState(line, rs) } result = append(result, line) } return result } // isIndexedOperand returns true for addressing modes where the effective address // depends on X or Y registers (e.g. (zp),y, $xxxx,x, zp,x). // Such loads can't be tracked by operand text alone since the register may change. func isIndexedOperand(operand string) bool { return strings.Contains(operand, "(") || strings.Contains(operand, ",") } func isImmediate(operand string) bool { return len(operand) > 0 && operand[0] == '#' }