def MaybeAddDataFlowElements( self, g: nx.MultiDiGraph, tag_hook: typing.Optional[llvm_util.TagHook] ) -> None: if self.dataflow == "none": return prefix = lambda s: f"{g.name}_{s}" unprefix = lambda s: s[len(f"{g.name}_") :] # Collect the edges to add so that we don't modify the graph while # iterating. edges_to_add: typing.List[typing.Tuple[str, str, str, int]] = [] for statement, data in nx_utils.StatementNodeIterator(g): # TODO(github.com/ChrisCummins/ProGraML/issues/9): Separate !IDENTIFIER # and !IMMEDIATE uses. def_, uses = GetLlvmStatementDefAndUses( data["text"], store_destination_is_def=self.store_destination_is_def ) if def_: # Data flow out edge. def_name = f"{prefix(def_)}_operand" edges_to_add.append( (statement, def_name, def_name, 0, def_, data, "def") ) for position, identifier in enumerate(uses): # Data flow in edge. identifier_name = f"{prefix(identifier)}_operand" edges_to_add.append( ( identifier_name, statement, identifier_name, position, identifier, data, "use", ) ) for ( src, dst, identifier, position, name, original_node, dtype, ) in edges_to_add: g.add_edge(src, dst, flow="data", position=position) node = g.nodes[identifier] # TODO(github.com/ChrisCummins/ProGraML/issues/9): Separate !IDENTIFIER # and !IMMEDIATE nodes. node["type"] = "identifier" node["name"] = name node["text"] = name node["x"] = self.dictionary["!IDENTIFIER"] if tag_hook is not None: other_attrs = tag_hook.OnIdentifier(original_node, node, dtype) or {} for attrname, attrval in other_attrs.items(): node[attrname] = attrval
def ToControlFlowGraph(g: nx.MultiDiGraph): """Create a new graph with only the statements and control flow edges.""" # CFGs cannot have parallel edges, so we use only a DiGraph rather than # MultiDiGraph. cfg = nx.DiGraph() for node, data in nx_utils.StatementNodeIterator(g): cfg.add_node(node, **data) for src, dst, data in nx_utils.ControlFlowEdgeIterator(g): cfg.add_edge(src, dst, **data)
def FindCallSites(graph, source_function, destination_function): """Find the statements in function that call another function.""" call_sites = [] for node, data in nx_utils.StatementNodeIterator(graph): if data["function"] != source_function: continue called_function = GetCalledFunctionName(data["text"]) if not called_function: continue if called_function == destination_function: call_sites.append(node) return call_sites
def test_every_statement_has_a_predecessor(simple_bytecode: str): """Test that every statement (except entry blocks) have control preds.""" builder = graph_builder.ProGraMLGraphBuilder() graph = builder.Build(simple_bytecode) entry_blocks = set([node for node, _ in nx_utils.EntryBlockIterator(graph)]) for node, _ in nx_utils.StatementNodeIterator(graph): if not node or node in entry_blocks: continue for edge in graph.in_edges(node): if graph.edges[edge[0], edge[1], 0]["flow"] == programl_pb2.Edge.CONTROL: break else: assert False, f"{node} has no control flow predecessor."
def test_StatementNodeIterator(graph): assert len(list(nx_utils.StatementNodeIterator(graph))) == 5