import { Block, BlockId, Fn } from "./mir.ts"; export function createCfg(fn: Fn): Cfg { return new CfgBuilder(fn).build(); } export class Cfg { public constructor( private graph: Map, private entry_: BlockId, private exit: BlockId, ) {} public entry(): Block { return this.graph.get(this.entry_)!.block; } public parents(block: Block): Block[] { return this.graph .get(block.id)!.parents .map((id) => this.graph.get(id)!.block); } public children(block: Block): Block[] { return this.graph .get(block.id)!.children .map((id) => this.graph.get(id)!.block); } public index(block: Block): number { return this.graph.get(block.id)!.index; } public print() { for (const [id, node] of this.graph.entries()) { const l = (v: T[]) => v.map((v) => `${v}`).join(", "); console.log(`graph[${id}] = {`); console.log(` id: ${node.block.id},`); console.log(` index: ${node.index},`); console.log(` parents: [${l(node.parents)}],`); console.log(` children: [${l(node.children)}],`); console.log(`}`); } } } type CfgNode = { block: Block; index: number; parents: BlockId[]; children: BlockId[]; }; class CfgBuilder { private nodes: [Block, number][] = []; private edges: [BlockId, BlockId][] = []; public constructor(private fn: Fn) {} public build(): Cfg { for ( const [block, index] of this.fn.blocks .map((v, i) => [v, i] as const) ) { this.addNode(block, index); const tk = block.ter.kind; switch (tk.type) { case "error": break; case "return": break; case "jump": this.addEdge(block.id, tk.target); break; case "if": this.addEdge(block.id, tk.truthy); this.addEdge(block.id, tk.falsy); break; } } const graph = new Map(); for (const [block, index] of this.nodes) { const parents = this.edges .filter(([_from, to]) => to === block.id) .map(([from, _to]) => from); const children = this.edges .filter(([from, _to]) => from === block.id) .map(([_from, to]) => to); graph.set(block.id, { block, index, parents, children }); } return new Cfg(graph, this.fn.entry, this.fn.exit); } private addNode(block: Block, index: number) { this.nodes.push([block, index]); } private addEdge(from: BlockId, to: BlockId) { this.edges.push([from, to]); } }