20#include <mlir/Analysis/CallGraph.h>
21#include <mlir/IR/Operation.h>
22#include <mlir/IR/SymbolTable.h>
23#include <mlir/Interfaces/CallInterfaces.h>
25#include <llvm/ADT/DepthFirstIterator.h>
26#include <llvm/ADT/SmallVector.h>
27#include <llvm/Support/ErrorHandling.h>
43 assert(!
isExternal() &&
"the external node has no callable region");
44 return callableRegion;
54 assert(
isExternal() &&
"abstract edges are only valid on external nodes");
55 addEdge(node, Edge::Kind::Abstract);
66 return llvm::any_of(edges, [](
const Edge &edge) {
return edge.
isChild(); });
70void CallGraphNode::addEdge(
CallGraphNode *node, Edge::Kind kind) {
71 edges.insert({
this, node, kind});
80static void computeCallGraph(
81 mlir::Operation *op,
CallGraph &cg, mlir::SymbolTableCollection &symbolTable,
84 if (mlir::CallOpInterface call = mlir::dyn_cast<mlir::CallOpInterface>(op)) {
88 if (resolveCalls && parentNode) {
89 parentNode->addCallEdge(cg.resolveCallable(call, symbolTable));
95 if (mlir::CallableOpInterface callable = mlir::dyn_cast<mlir::CallableOpInterface>(op)) {
96 if (
auto *callableRegion = callable.getCallableRegion()) {
97 parentNode = cg.getOrAddNode(callableRegion, parentNode);
103 for (mlir::Region ®ion : op->getRegions()) {
104 for (mlir::Operation &nested : region.getOps()) {
105 computeCallGraph(&nested, cg, symbolTable, parentNode, resolveCalls);
111 : externalCallerNode(nullptr),
112 unknownCalleeNode(nullptr) {
116 mlir::SymbolTableCollection symbolTable;
118 op, *
this, symbolTable,
nullptr,
122 op, *
this, symbolTable,
nullptr,
130 region && mlir::isa<mlir::CallableOpInterface>(region->getParentOp()) &&
131 "expected parent operation to be callable"
133 std::unique_ptr<CallGraphNode> &node = nodes[region];
147 externalCallerNode.addAbstractEdge(node.get());
156 const auto *it = nodes.find(region);
157 return it == nodes.
end() ? nullptr : it->second.get();
164 mlir::CallOpInterface call, mlir::SymbolTableCollection &symbolTable
167 if (mlir::succeeded(res)) {
168 if (
auto *node =
lookupNode(res->get().getCallableRegion())) {
181 if (edge.isChild()) {
187 for (
auto &it : nodes) {
201 os <<
"// ---- CallGraph ----\n";
206 os <<
"<External-Caller-Node>";
210 os <<
"<Unknown-Callee-Node>";
215 auto *parentOp = callableRegion->getParentOp();
216 os <<
"'" << callableRegion->getParentOp()->getName() <<
"' - Region #"
217 << callableRegion->getRegionNumber();
218 auto attrs = parentOp->getAttrDictionary();
220 os <<
" : " << attrs;
224 for (
auto &nodeIt : nodes) {
228 os <<
"// - Node : ";
233 for (
auto &edge : *node) {
237 }
else if (edge.isChild()) {
242 emitNodeName(edge.getTarget());
248 os <<
"// -- SCCs --\n";
250 for (
auto &scc : make_range(llvm::scc_begin(
this), llvm::scc_end(
this))) {
251 os <<
"// - SCC : \n";
252 for (
auto &node : scc) {
253 os <<
"// -- Node :";
260 os <<
"// -------------------\n";
This class represents a directed edge between two nodes in the callgraph.
bool isChild() const
Returns true if this edge represents a Child edge.
CallGraphNode * getTarget() const
Returns the target node for this edge.
This is a simple port of the mlir::CallGraphNode with llzk::CallGraph as a friend class,...
bool isExternal() const
Returns true if this node is an external node.
mlir::Region * getCallableRegion() const
Returns the callable region this node represents.
void addChildEdge(CallGraphNode *child)
Adds a reference edge to the given child node.
bool hasChildren() const
Returns true if this node has any child edges.
void addCallEdge(CallGraphNode *node)
Add an outgoing call edge from this node.
void addAbstractEdge(CallGraphNode *node)
Adds an abstract reference edge to the given node.
llzk::function::FuncDefOp getCalledFunction() const
Returns the called function that the callable region represents.
This is a port of mlir::CallGraph that has been adapted to use the custom symbol lookup helpers (see ...
CallGraph(mlir::Operation *op)
CallGraphNode * getExternalCallerNode() const
Return the callgraph node representing an external caller.
void dump() const
Dump the graph in a human readable format.
void print(llvm::raw_ostream &os) const
void eraseNode(CallGraphNode *node)
Erase the given node from the callgraph.
CallGraphNode * resolveCallable(mlir::CallOpInterface call, mlir::SymbolTableCollection &symbolTable) const
Resolve the callable for given callee to a node in the callgraph, or the external node if a valid nod...
CallGraphNode * lookupNode(mlir::Region *region) const
Lookup a call graph node for the given region, or nullptr if none is registered.
CallGraphNode * getUnknownCalleeNode() const
Return the callgraph node representing an indirect callee.
CallGraphNode * getOrAddNode(mlir::Region *region, CallGraphNode *parentNode)
Get or add a call graph node for the given region.
bool isNullOrEmpty(mlir::ArrayAttr a)
mlir::FailureOr< SymbolLookupResult< T > > resolveCallable(mlir::SymbolTableCollection &symbolTable, mlir::CallOpInterface call)
Based on mlir::CallOpInterface::resolveCallable, but using LLZK lookup helpers.