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, "" } }