import { FnStmtKind } from "../ast.ts"; import { Reporter } from "../info.ts"; import { Block, Fn, LocalId, Mir, RValue, visitBlockDsts, visitBlockSrcs, } from "./mir.ts"; export function eliminateUnusedLocals( mir: Mir, reporter: Reporter, isPassOne: boolean, ) { for (const fn of mir.fns) { new EliminateUnusedLocalsFnPass(fn, reporter, isPassOne).pass(); } } class EliminateUnusedLocalsFnPass { private locals: LocalId[]; public constructor( private fn: Fn, private reporter: Reporter, private isPassOne: boolean, ) { this.locals = this.fn.locals .slice(1 + (fn.stmt.kind as FnStmtKind).params.length) .map((local) => local.id); } public pass() { for (const block of this.fn.blocks) { this.markLocalsInBlock(block); } for (const local of this.locals) { for (const block of this.fn.blocks) { this.eliminateLocalInBlock(block, local); } } for (const id of this.locals) { const local = this.fn.locals.find((local) => local.id === id)!; if (local.sym?.type === "let" && this.isPassOne) { this.reporter.reportWarning({ reporter: "analysis mf'er", msg: `unused let symbol '${local.sym.ident}'`, pos: local.sym.pos, }); } } this.fn.locals = this.fn.locals .filter((local) => !this.locals.includes(local.id)); } private eliminateLocalInBlock(block: Block, local: LocalId) { const elimIndices: number[] = []; visitBlockDsts(block, (dst, i) => { if (dst === local) { elimIndices.push(i); } }); for (const i of elimIndices.toReversed()) { block.ops.splice(i, 1); } } private markLocalsInBlock(block: Block) { visitBlockSrcs(block, (src) => this.markUsed(src)); } private markUsed(local: RValue) { if (local.type !== "local") { return; } this.locals = this.locals.filter((lid) => lid !== local.id); } }