UNPKG

@specs-feup/clava

Version:

A C/C++ source-to-source compiler written in Typescript

190 lines 6.92 kB
import ControlFlowGraph from "../graphs/ControlFlowGraph.js"; import CfgNodeType from "../graphs/cfg/CfgNodeType.js"; import LivenessUtils from "./LivenessUtils.js"; export default class LivenessAnalyser { /** * A Cytoscape graph representing the CFG */ cfg; /** * Maps each CFG node ID to the corresponding def set */ defs; /** * Maps each CFG node ID to the corresponding use set */ uses; /** * Maps each CFG node ID to the corresponding LiveIn set */ liveIn; /** * Maps each CFG node ID to the corresponding LiveOut set */ liveOut; /** * Creates a new instance of the LivenessAnalyser class * @param cfg - The control flow graph. Can be either a Cytoscape graph or a ControlFlowGraph object. */ constructor(cfg) { this.cfg = this.validateCfg(cfg); this.defs = new Map(); this.uses = new Map(); this.liveIn = new Map(); this.liveOut = new Map(); } /** * Checks if the given control flow graph is a Cytoscape graph or a ControlFlowGraph object. * Additionally, verifies if each instruction list node contains only one statement. * @param cfg - The control flow graph to be validated */ validateCfg(cfg) { if (cfg instanceof ControlFlowGraph) cfg = cfg.graph; else if (!LivenessUtils.isCytoscapeGraph(cfg)) throw new Error("'cfg' must be a Cytoscape graph or a ControlFlowGraph object."); for (const node of cfg.nodes()) { const nodeData = node.data(); if (nodeData.type === CfgNodeType.INST_LIST && nodeData.stmts.length > 1) throw new Error("Each instruction list node of the control flow graph must contain only one statement."); } return cfg; } /** * Computes the def, use, live in and live out sets of each CFG node * @returns An array that contains the def, use, live in and live out of each CFG node. */ analyse() { this.computeDefs(); this.computeUses(); this.computeLiveInOut(); return [this.defs, this.uses, this.liveIn, this.liveOut]; } /** * * @param $jp - * @returns The def set for the given joinpoint */ computeDef($jp) { if ($jp === undefined) { throw new Error("Joinpoint is undefined"); } const declaredVars = LivenessUtils.getVarDeclsWithInit($jp); const assignedVars = LivenessUtils.getAssignedVars($jp); return LivenessUtils.unionSets(declaredVars, assignedVars); } /** * * @param $jp - * @returns The use set for the given joinpoint */ computeUse($jp) { if ($jp === undefined) { throw new Error("Joinpoint is undefined"); } return LivenessUtils.getVarRefs($jp); } /** * Computes the def set of each CFG node according to its type */ computeDefs() { for (const node of this.cfg.nodes()) { const nodeData = node.data(); const $nodeStmt = nodeData.nodeStmt; const nodeType = nodeData.type; let def; switch (nodeType) { case CfgNodeType.START: case CfgNodeType.END: case CfgNodeType.SCOPE: case CfgNodeType.THEN: case CfgNodeType.ELSE: case CfgNodeType.LOOP: case CfgNodeType.SWITCH: case CfgNodeType.CASE: def = new Set(); break; case CfgNodeType.IF: def = this.computeDef($nodeStmt?.cond); break; default: def = this.computeDef($nodeStmt); } this.defs.set(node.id(), def); } } /** * Computes the use set of each CFG node according to its type */ computeUses() { for (const node of this.cfg.nodes()) { const nodeData = node.data(); const $nodeStmt = nodeData.nodeStmt; const nodeType = nodeData.type; let use; switch (nodeType) { case CfgNodeType.START: case CfgNodeType.END: case CfgNodeType.SCOPE: case CfgNodeType.THEN: case CfgNodeType.ELSE: case CfgNodeType.LOOP: use = new Set(); break; case CfgNodeType.IF: use = this.computeUse($nodeStmt?.cond); break; case CfgNodeType.SWITCH: use = this.computeUse($nodeStmt?.condition); break; case CfgNodeType.CASE: { const $switchCondition = ($nodeStmt?.getAncestor("switch")).condition; use = $nodeStmt?.isDefault ? new Set() : this.computeUse($switchCondition); break; } default: use = this.computeUse($nodeStmt); } this.uses.set(node.id(), use); } } /** * Computes the LiveIn and LiveOut set of each CFG node */ computeLiveInOut() { for (const node of this.cfg.nodes()) { this.liveIn.set(node.id(), new Set()); this.liveOut.set(node.id(), new Set()); } let liveChanged; do { liveChanged = false; for (const node of this.cfg.nodes()) { const def = this.defs.get(node.id()); const use = this.uses.get(node.id()); // Save current liveIn and liveOut const oldLiveIn = this.liveIn.get(node.id()); const oldLiveOut = this.liveOut.get(node.id()); // Compute and save new liveIn const diff = LivenessUtils.differenceSets(oldLiveOut, def); const newLiveIn = LivenessUtils.unionSets(use, diff); this.liveIn.set(node.id(), newLiveIn); // Compute and save new liveOut let newLiveOut = new Set(); for (const child of LivenessUtils.getChildren(node)) { const childId = child.id(); const childLiveIn = this.liveIn.get(childId); newLiveOut = LivenessUtils.unionSets(newLiveOut, childLiveIn); } this.liveOut.set(node.id(), newLiveOut); // Update liveChanged if (!LivenessUtils.isSameSet(oldLiveIn, newLiveIn) || !LivenessUtils.isSameSet(oldLiveOut, newLiveOut)) liveChanged = true; } } while (liveChanged); } } //# sourceMappingURL=LivenessAnalyser.js.map