package tfm.graphs.pdg; import tfm.arcs.Arc; import tfm.graphs.cfg.CFG; import tfm.graphs.pdg.PDG; import tfm.graphs.augmented.PPDG; import tfm.nodes.GraphNode; import tfm.nodes.NodeFactory; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** * A simple but slow finder of control dependencies. *
* It has a polynomial complexity (between cubed and n**4) with respect to the number of nodes in the CFG. * It uses the following definition of control dependence: *
* A node b is control dependent on another node a if and only if b postdominates * one but not all of the successors of a. *
* A node b postdominates another node a if and only if b appears in every path * from a to the "Exit" node. *
* There exist better, cheaper approaches that have linear complexity w.r.t. the number of edges in the CFG. * Usage: pass an empty {@link PDG} and a filled {@link CFG} and then run {@link #analyze()}. * This builder should only be used once, and then discarded. */ public class ControlDependencyBuilder { private final PDG pdg; private final CFG cfg; public ControlDependencyBuilder(PDG pdg, CFG cfg) { this.pdg = pdg; this.cfg = cfg; } public void analyze() { Map, GraphNode> nodeMap = new HashMap<>(); assert cfg.getRootNode().isPresent(); assert pdg.getRootNode().isPresent(); nodeMap.put(cfg.getRootNode().get(), pdg.getRootNode().get()); Set> roots = new HashSet<>(cfg.vertexSet()); roots.remove(cfg.getRootNode().get()); Set> cfgNodes = new HashSet<>(cfg.vertexSet()); cfgNodes.removeIf(node -> node.getInstruction().equals("Exit")); for (GraphNode node : cfgNodes) registerNode(node, nodeMap); for (GraphNode src : cfgNodes) { for (GraphNode dest : cfgNodes) { if (src == dest) continue; if (hasControlDependence(src, dest)) { pdg.addControlDependencyArc(nodeMap.get(src), nodeMap.get(dest)); roots.remove(dest); } } } // In the original definition, nodes were dependent by default on the Enter/Start node for (GraphNode node : roots) if (!node.getInstruction().equals("Exit")) pdg.addControlDependencyArc(pdg.getRootNode().get(), nodeMap.get(node)); } public void registerNode(GraphNode node, Map, GraphNode> nodeMap) { if (nodeMap.containsKey(node) || node.getInstruction().equals("Exit")) return; GraphNode clone = NodeFactory.graphNode(node.getId(), node.getInstruction(), node.getAstNode()); nodeMap.put(node, clone); pdg.addVertex(clone); } public boolean hasControlDependence(GraphNode a, GraphNode b) { int yes = 0; Set list = cfg.outgoingEdgesOf(a); // Nodes with less than 1 outgoing arc cannot control another node. if (cfg.outDegreeOf(a) < 2) return false; for (Arc arc : cfg.outgoingEdgesOf(a)) { GraphNode successor = cfg.getEdgeTarget(arc); if (postdominates(successor, b)) yes++; } int no = list.size() - yes; return yes > 0 && no > 0; } public boolean postdominates(GraphNode a, GraphNode b) { return postdominates(a, b, new HashSet<>()); } private boolean postdominates(GraphNode a, GraphNode b, Set> visited) { // Stop w/ success if a == b or a has already been visited if (a.equals(b) || visited.contains(a)) return true; Set outgoing = cfg.outgoingEdgesOf(a); // Limit the traversal if it is a PPDG if (pdg instanceof PPDG) outgoing = outgoing.stream().filter(Arc::isExecutableControlFlowArc).collect(Collectors.toSet()); // Stop w/ failure if there are no edges to traverse from a if (outgoing.isEmpty()) return false; // Find all possible paths starting from a, if ALL find b, then true, else false visited.add(a); for (Arc out : outgoing) { if (!postdominates(cfg.getEdgeTarget(out), b, visited)) return false; } return true; } }