mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 19:36:32 +00:00
112 lines
2.9 KiB
TypeScript
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 });
|
|
}
|
|
}
|