import { FnStmtKind } from "../ast.ts"; import { Block, Fn, Local, LocalId, Mir, replaceBlockSrcs, RValue, } from "./mir.ts"; export function eliminateTransientLocals(mir: Mir) { for (const fn of mir.fns) { const otherLocals = fn.locals .slice(1 + (fn.stmt.kind as FnStmtKind).params.length) .map((local) => local.id); for (const block of fn.blocks) { new EliminateTransientLocalsBlockPass(block, otherLocals) .pass(); } } } type Candidate = { dst: LocalId; src: LocalId; }; class EliminateTransientLocalsBlockPass { private candidates: Candidate[] = []; public constructor( private block: Block, private readonly locals: LocalId[], ) { } public pass() { this.findCandidatesInBlock(this.block); this.candidates = this.candidates .filter((cand) => this.locals.includes(cand.dst)); this.eliminateCandidatesInBlock(this.block); } private eliminateCandidatesInBlock(block: Block) { replaceBlockSrcs(block, (src) => this.replaceMaybe(src)); } private replaceMaybe(src: RValue): RValue { if (src.type !== "local") { return src; } const candidate = this.candidates .find((cand) => cand.dst === src.id)?.src; return candidate !== undefined ? { type: "local", id: candidate } : src; } private findCandidatesInBlock(block: Block) { for (const op of block.ops) { const ok = op.kind; switch (ok.type) { case "error": break; case "assign": this.markDst(ok.dst, ok.src); break; case "assign_error": case "assign_null": case "assign_bool": case "assign_int": case "assign_string": case "assign_fn": case "field": case "assign_field": case "index": case "assign_index": case "call_val": case "binary": break; default: throw new Error(); } } const tk = block.ter.kind; switch (tk.type) { case "error": break; case "return": break; case "jump": break; case "if": break; } } private markDst(dst: LocalId, src: RValue) { if (src.type !== "local") { return; } this.candidates = this.candidates .filter((cand) => cand.dst !== dst); this.candidates = this.candidates .filter((cand) => cand.src !== dst); this.candidates.push({ dst, src: src.id }); } }