slige/compiler/middle/elim_transient_locals.ts
2025-01-17 11:50:14 +01:00

112 lines
2.9 KiB
TypeScript

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 });
}
}