Skip to content

Commit 689db17

Browse files
committed
Resurrect call-graph dot viewing.
It's useful for debugging.
1 parent cdaff82 commit 689db17

File tree

2 files changed

+197
-0
lines changed

2 files changed

+197
-0
lines changed

include/swift/SILPasses/PassManager.h

+5
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ class SILPassManager {
160160
/// if the pass manager requested to stop the execution
161161
/// of the optimization cycle (this is a debug feature).
162162
bool runFunctionPasses(PassList FuncTransforms);
163+
164+
/// Displays the call graph in an external dot-viewer.
165+
/// This function is meant for use from the debugger.
166+
/// When asserts are disabled, this is a NoOp.
167+
void viewCallGraph();
163168
};
164169

165170
} // end namespace swift

lib/SILPasses/PassManager/PassManager.cpp

+192
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#define DEBUG_TYPE "sil-passmanager"
1414

15+
#include "swift/Basic/DemangleWrappers.h"
1516
#include "swift/SILPasses/PassManager.h"
1617
#include "swift/SIL/SILFunction.h"
1718
#include "swift/SIL/SILModule.h"
@@ -20,9 +21,11 @@
2021
#include "llvm/ADT/Statistic.h"
2122
#include "llvm/ADT/StringSwitch.h"
2223
#include "swift/SILAnalysis/FunctionOrder.h"
24+
#include "swift/SILAnalysis/BasicCalleeAnalysis.h"
2325
#include "llvm/Support/CommandLine.h"
2426
#include "llvm/Support/Debug.h"
2527
#include "llvm/Support/TimeValue.h"
28+
#include "llvm/Support/GraphWriter.h"
2629

2730
using namespace swift;
2831

@@ -445,4 +448,193 @@ void SILPassManager::addPassForName(StringRef Name) {
445448
addPass(P);
446449
}
447450

451+
//===----------------------------------------------------------------------===//
452+
// View Call-Graph Implementation
453+
//===----------------------------------------------------------------------===//
454+
455+
#ifndef NDEBUG
456+
457+
namespace {
458+
459+
/// An explicit graph data structure for the call graph.
460+
/// Used for viewing the callgraph as dot file with llvm::ViewGraph.
461+
struct CallGraph {
462+
463+
struct Node;
464+
465+
struct Edge {
466+
FullApplySite FAS;
467+
Node *Child;
468+
bool Incomplete;
469+
};
470+
471+
struct Node {
472+
SILFunction *F;
473+
CallGraph *CG;
474+
int NumCallSites = 0;
475+
SmallVector<Edge, 8> Children;
476+
};
477+
478+
struct child_iterator : public std::iterator<std::random_access_iterator_tag,
479+
Node *, ptrdiff_t> {
480+
SmallVectorImpl<Edge>::iterator baseIter;
481+
482+
child_iterator(SmallVectorImpl<Edge>::iterator baseIter) :
483+
baseIter(baseIter)
484+
{ }
485+
486+
child_iterator &operator++() { baseIter++; return *this; }
487+
child_iterator operator++(int) { auto tmp = *this; baseIter++; return tmp; }
488+
Node *operator*() const { return baseIter->Child; }
489+
bool operator==(const child_iterator &RHS) const {
490+
return baseIter == RHS.baseIter;
491+
}
492+
bool operator!=(const child_iterator &RHS) const {
493+
return baseIter != RHS.baseIter;
494+
}
495+
difference_type operator-(const child_iterator &RHS) const {
496+
return baseIter - RHS.baseIter;
497+
}
498+
};
499+
500+
CallGraph(SILModule *M, BasicCalleeAnalysis *BCA);
501+
502+
std::vector<Node> Nodes;
503+
504+
/// The SILValue IDs which are printed as edge source labels.
505+
llvm::DenseMap<const ValueBase *, unsigned> InstToIDMap;
506+
507+
typedef std::vector<Node>::iterator iterator;
508+
};
509+
510+
CallGraph::CallGraph(SILModule *M, BasicCalleeAnalysis *BCA) {
511+
Nodes.resize(M->getFunctionList().size());
512+
llvm::DenseMap<SILFunction *, Node *> NodeMap;
513+
int idx = 0;
514+
for (SILFunction &F : *M) {
515+
Node &Nd = Nodes[idx++];
516+
Nd.F = &F;
517+
Nd.CG = this;
518+
NodeMap[&F] = &Nd;
519+
520+
F.numberValues(InstToIDMap);
521+
}
522+
523+
for (Node &Nd : Nodes) {
524+
for (SILBasicBlock &BB : *Nd.F) {
525+
for (SILInstruction &I : BB) {
526+
if (FullApplySite FAS = FullApplySite::isa(&I)) {
527+
auto CList = BCA->getCalleeList(FAS);
528+
for (SILFunction *Callee : CList) {
529+
Node *CalleeNode = NodeMap[Callee];
530+
Nd.Children.push_back({FAS, CalleeNode,CList.isIncomplete()});
531+
}
532+
}
533+
}
534+
}
535+
}
536+
}
537+
538+
} // end swift namespace
539+
540+
namespace llvm {
541+
542+
/// Wraps a dot node label string to multiple lines. The \p NumEdgeLabels
543+
/// gives an estimate on the minimum width of the node shape.
544+
static void wrap(std::string &Str, int NumEdgeLabels) {
545+
unsigned ColNum = 0;
546+
unsigned LastSpace = 0;
547+
unsigned MaxColumns = std::max(60, NumEdgeLabels * 8);
548+
for (unsigned i = 0; i != Str.length(); ++i) {
549+
if (ColNum == MaxColumns) {
550+
if (!LastSpace)
551+
LastSpace = i;
552+
Str.insert(LastSpace + 1, "\\l");
553+
ColNum = i - LastSpace - 1;
554+
LastSpace = 0;
555+
} else
556+
++ColNum;
557+
if (Str[i] == ' ' || Str[i] == '.')
558+
LastSpace = i;
559+
}
560+
}
448561

562+
/// CallGraph GraphTraits specialization so the CallGraph can be
563+
/// iterable by generic graph iterators.
564+
template <> struct GraphTraits<CallGraph::Node *> {
565+
typedef CallGraph::Node NodeType;
566+
typedef CallGraph::child_iterator ChildIteratorType;
567+
568+
static NodeType *getEntryNode(NodeType *N) { return N; }
569+
static inline ChildIteratorType child_begin(NodeType *N) {
570+
return N->Children.begin();
571+
}
572+
static inline ChildIteratorType child_end(NodeType *N) {
573+
return N->Children.end();
574+
}
575+
};
576+
577+
template <> struct GraphTraits<CallGraph *>
578+
: public GraphTraits<CallGraph::Node *> {
579+
typedef CallGraph *GraphType;
580+
581+
static NodeType *getEntryNode(GraphType F) { return nullptr; }
582+
583+
typedef CallGraph::iterator nodes_iterator;
584+
static nodes_iterator nodes_begin(GraphType CG) {
585+
return CG->Nodes.begin();
586+
}
587+
static nodes_iterator nodes_end(GraphType CG) { return CG->Nodes.end(); }
588+
static unsigned size(GraphType CG) { return CG->Nodes.size(); }
589+
};
590+
591+
/// This is everything the llvm::GraphWriter needs to write the call graph in
592+
/// a dot file.
593+
template <>
594+
struct DOTGraphTraits<CallGraph *> : public DefaultDOTGraphTraits {
595+
596+
DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {}
597+
598+
std::string getNodeLabel(const CallGraph::Node *Node,
599+
const CallGraph *Graph) {
600+
std::string Label = Node->F->getName();
601+
wrap(Label, Node->NumCallSites);
602+
return Label;
603+
}
604+
605+
std::string getNodeDescription(const CallGraph::Node *Node,
606+
const CallGraph *Graph) {
607+
std::string Label = demangle_wrappers::
608+
demangleSymbolAsString(Node->F->getName());
609+
wrap(Label, Node->NumCallSites);
610+
return Label;
611+
}
612+
613+
static std::string getEdgeSourceLabel(const CallGraph::Node *Node,
614+
CallGraph::child_iterator I) {
615+
std::string Label;
616+
raw_string_ostream O(Label);
617+
SILInstruction *Inst = I.baseIter->FAS.getInstruction();
618+
O << '%' << Node->CG->InstToIDMap[Inst];
619+
return Label;
620+
}
621+
622+
static std::string getEdgeAttributes(const CallGraph::Node *Node,
623+
CallGraph::child_iterator I,
624+
const CallGraph *Graph) {
625+
CallGraph::Edge *Edge = I.baseIter;
626+
if (Edge->Incomplete)
627+
return "color=\"red\"";
628+
return "";
629+
}
630+
};
631+
} // end llvm namespace
632+
#endif
633+
634+
void SILPassManager::viewCallGraph() {
635+
/// When asserts are disabled, this should be a NoOp.
636+
#ifndef NDEBUG
637+
CallGraph OCG(getModule(), getAnalysis<BasicCalleeAnalysis>());
638+
llvm::ViewGraph(&OCG, "callgraph");
639+
#endif
640+
}

0 commit comments

Comments
 (0)