COP 5021 Lecture -*- Outline -*- * Control Flow Graphs (2.1) Q: What kind of information is in a CFG? Where control may flow after each elementary block, and where control may come from ** use of (Control) flow graphs ------------------------------------------ USE OF (CONTROL) FLOW GRAPHS Mathematically (in text): flows: Stmt -> Powerset(Label x Label) CFGs are used to: summarize control flow in a program, direct information in a dataflow analysis Are an inherited attribute depends on information above an AST node ------------------------------------------ Q: Why is the CFG an inherited attribute? it depends on what happens around the AST node, e.g., whether the node is in a sequence (CompoundS) ** overview in XText ------------------------------------------ CFGs IN XTEXT Not a built-in feature of XText's framework Stored as a static field in a map of type FlowGraph Flowgraph API: package edu.ucf.cs.whilelang.utility; import java.util.AbstractMap; /*...*/ /** A Map from statements (S) to sets of pairs of labels * (Set>) that can be used as a flow graph. */ public class FlowGraph extends AbstractMap>> // implements Map>> { /** The representation of this flowgraph. */ private Map>> map = new HashMap>>(); /** Initialize this object to be an empty flowgraph. */ public FlowGraph() { } /** Initialize this object to be a singleton flowgraph. */ public FlowGraph(S s, Set> flws) { map.put(s, flws); } public void clear(); public Set>>> entrySet(); public Set> get(S stmt); public Set> put(S stmt, Set> fls); public Set> putUnion(S stmt, Set> fls); /* ... */ @Override public String toString(); } ------------------------------------------ Take a look at the code in FlowGraph.java It's important that putUnion doesn't commit argument exposure i.e., that it creates a new set and doesn't just use the argument fls it is passed; otherwise the aliasing causes problems *** construction of the cfgMap in WhileLangCFGValidator ------------------------------------------ // file WhileLangCFGValidator.xtend package edu.ucf.cs.whilelang.validation import edu.ucf.cs.whilelang.utility.CFG /*...*/ /** * This class constructs a control flow graph (CFG) for the program. * See Section 2.1 of "Principles of Program Analysis" * by Nielsen, Nielsen, and Hankin (Springer-Verlag, 1999 and 2005). */ class WhileLangCFGValidator extends AbstractWhileLangValidator { /** What is the ElementaryBlock with the given label? */ val Map itsBlockMap = CFG.itsBlockMap /** What is the set of flows within each statement? */ val FlowGraph cfgMap = CFG.cfgMap @Check def constructCFG(Program p) { p.body.constructIBM p.body.constructFlows } /* ... */ def dispatch void constructFlows(AssignS a) { cfgMap.putUnion(a, new SetRepUtility>()) } def dispatch void constructFlows(SkipS s) { cfgMap.putUnion(s, new SetRepUtility>()) } def dispatch void constructFlows(CompoundS c) { // recursively treat each sub-statement for (i : 0..c.stmts.size()-1) { c.stmts.get(i).constructFlows } // the flows of each sub-statement are in the flows of c for (i : 0..c.stmts.size()-1) { cfgMap.putUnion(c, cfgMap.get(c.stmts.get(i))) } // the flows of c also contain a flow from each statement to the next for (i : 0..>( new Pair(j, CFG.init(c.stmts.get(i+1))) )) } } } def dispatch void constructFlows(WhileS c) { // construct the flows of the body c.block.constructFlows cfgMap.putUnion(c, cfgMap.get(c.block)) cfgMap.putUnion(c, new SetRepUtility(new Pair(c.bexp.label, CFG.init(c.block)))) for (j : CFG.finals(c.block)) { cfgMap.putUnion(c, new SetRepUtility>( new Pair(j, c.bexp.label) )) } } def dispatch void constructFlows(IfS ifs) { // construct the flows of the two sub-statements ifs.s1.constructFlows ifs.s2.constructFlows // add in all those flows cfgMap.putUnion(ifs, cfgMap.get(ifs.s1)) cfgMap.putUnion(ifs, cfgMap.get(ifs.s2)) // add flows from the test to the init of each sub-statement cfgMap.putUnion(ifs, new SetRepUtility>( new Pair(ifs.bexp.label, CFG.init(ifs.s1)) )) cfgMap.putUnion(ifs, new SetRepUtility>( new Pair(ifs.bexp.label, CFG.init(ifs.s2)) )) } def resetMaps() { itsBlockMap.clear() cfgMap.clear() } } ------------------------------------------ Look at the file in more detail... The ..< operator in Xtend gives a range that is open on the right; we don't want to use c.stmts.size()-2, as that may be -1 when c.stmts.size() == 1, and then XTend counts down instead of up! ------------------------------------------ INIT ATTRIBUTE SYNTHESIZED // in CFG.xtend def dispatch static int init(AssignS s) { s.label } def dispatch static int init(SkipS s) { s.label } def dispatch static int init(IfS s) { s.bexp.label } def dispatch static int init(WhileS s) { s.bexp.label } def dispatch static int init(CompoundS s) { s.stmts.get(0).init } ------------------------------------------ Q: Why do IfS and WhileS use the bexp field? because it's their condition that has the label ------------------------------------------ FINALS ATTRIBUTE Result is a set of labels: Set // in CFG.xtend def dispatch static Set finals(AssignS s) { new SetRepUtility(s.label) } def dispatch static Set finals(SkipS s) { new SetRepUtility(s.label) } def dispatch static Set finals(IfS s) { } def dispatch static Set finals(WhileS s) { } def dispatch static Set finals(CompoundS s) { s.stmts.get(s.stmts.size()-1).finals } ------------------------------------------ ... val ret = s.s1.finals; // for IfS ret.addAll(s.s2.finals); return ret; ... new SetRepUtility(s.bexp.label) // for WhileS ------------------------------------------ BLOCKS ATTRIBUTE set of the elementary blocks in a statement // file CFG.xtend def dispatch static Set blocks(AssignS s) { new SetRepUtility(s as ElementaryBlock) } /** Returns the set of elementary blocks in a statement. */ def dispatch static Set blocks(SkipS s) { new SetRepUtility(s as ElementaryBlock) } /** Returns the set of elementary blocks in a statement. */ def dispatch static Set blocks(IfS s) { } /** Returns the set of elementary blocks in a statement. */ def dispatch static Set blocks(WhileS s) { val Set ret = s.block.blocks ret.add(s.bexp as ElementaryBlock) return ret } /** Returns the set of elementary blocks in a statement. */ def dispatch static Set blocks(CompoundS s) { val ret = new SetRepUtility(); for (c : s.stmts) { ret.addAll(c.blocks); } return ret } ------------------------------------------ Q: Why are only assignment, skip, and labeled expressions blocks? Because those are the elementary blocks that take atomic actions ... // for IfS val ret = s.s1.blocks ret.addAll(s.s2.blocks) ret.add(s.bexp as ElementaryBlock) return ret ... // for WhileS val Set ret = s.block.blocks ret.add(s.bexp as ElementaryBlock) return ret ------------------------------------------ LABELS ATTRIBUTE def dispatch static Set labels(AssignS s) { } def dispatch static Set labels(SkipS s) { } def dispatch static Set labels(IfS s) { } /** Returns the set of the labels of all labels in a statement. */ def dispatch static Set labels(WhileS s) { } /** Returns the set of the labels of all elementary blocks in a statement. */ def dispatch static Set labels(CompoundS s) { } ------------------------------------------ ... new SetRepUtility(s.label) // for AssignS and SkipS ... // for IfS val ret = new SetRepUtility(s.bexp.label) ret.addAll(s.s1.labels) ret.addAll(s.s2.labels) return ret ... // for WhileS val ret = new SetRepUtility(s.bexp.label) ret.addAll(s.block.labels) return ret ... // for CompoundS val ret = new SetRepUtility(); for (c : s.stmts) { ret.addAll(c.labels) } return ret