Skip to content

Commit fd6c26e

Browse files
committed
EscapeAnalysis: don't compute the connection graph for very large functions
For functions which results in > 10000 nodes, just bail and don't compute the connection graph. The node merging algorithm is quadratic and can result in significant compile times for very large functions. rdar://problem/56268570
1 parent f33c2ad commit fd6c26e

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

include/swift/SILOptimizer/Analysis/EscapeAnalysis.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,9 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
652652
/// True if this is a summary graph.
653653
bool isSummaryGraph;
654654

655+
/// True if the graph could be computed.
656+
bool valid = true;
657+
655658
/// Track the currently active intrusive worklist -- one at a time.
656659
CGNodeWorklist *activeWorklist = nullptr;
657660

@@ -859,6 +862,22 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
859862
bool forwardTraverseDefer(CGNode *startNode, CGNodeVisitor &&visitor);
860863

861864
public:
865+
866+
/// Returns true if the graph could be computed.
867+
///
868+
/// For very large functions (> 10000 nodes), graphs are not cumputed to
869+
/// avoid quadratic complexity of the node merging algorithm.
870+
bool isValid() const {
871+
assert((valid || isEmpty()) && "invalid graph must not contain nodes");
872+
return valid;
873+
}
874+
875+
/// Invalides the graph in case it's getting too large.
876+
void invalidate() {
877+
clear();
878+
valid = false;
879+
}
880+
862881
/// Get the content node pointed to by \p ptrVal.
863882
///
864883
/// If \p ptrVal cannot be mapped to a node, return nullptr.

lib/SILOptimizer/Analysis/EscapeAnalysis.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ void EscapeAnalysis::ConnectionGraph::clear() {
403403
UsePoints.clear();
404404
UsePointTable.clear();
405405
NodeAllocator.DestroyAll();
406+
valid = true;
406407
assert(ToMerge.empty());
407408
}
408409

@@ -416,6 +417,9 @@ void EscapeAnalysis::ConnectionGraph::clear() {
416417
// it's interior property.
417418
EscapeAnalysis::CGNode *
418419
EscapeAnalysis::ConnectionGraph::getNode(SILValue V) {
420+
if (!isValid())
421+
return nullptr;
422+
419423
// Early filter obvious non-pointer opcodes.
420424
if (isa<FunctionRefInst>(V) || isa<DynamicFunctionRefInst>(V) ||
421425
isa<PreviousDynamicFunctionRefInst>(V))
@@ -1028,6 +1032,9 @@ CGNode *EscapeAnalysis::ConnectionGraph::getReturnNode() {
10281032

10291033
bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph,
10301034
CGNodeMap &Mapping) {
1035+
assert(isValid());
1036+
assert(SourceGraph->isValid());
1037+
10311038
// The main point of the merging algorithm is to map each content node in the
10321039
// source graph to a content node in this (destination) graph. This may
10331040
// require creating new nodes or merging existing nodes in this graph.
@@ -1492,6 +1499,11 @@ void EscapeAnalysis::ConnectionGraph::print(llvm::raw_ostream &OS) const {
14921499
#ifndef NDEBUG
14931500
OS << "CG of " << F->getName() << '\n';
14941501

1502+
if (!isValid()) {
1503+
OS << " invalid\n";
1504+
return;
1505+
}
1506+
14951507
// Assign the same IDs to SILValues as the SILPrinter does.
14961508
llvm::DenseMap<const SILNode *, unsigned> InstToIDMap;
14971509
InstToIDMap[nullptr] = (unsigned)-1;
@@ -1595,6 +1607,7 @@ void EscapeAnalysis::ConnectionGraph::verify() const {
15951607
// Invalidating EscapeAnalysis clears the connection graph.
15961608
if (isEmpty())
15971609
return;
1610+
assert(isValid());
15981611

15991612
verifyStructure();
16001613

@@ -1728,10 +1741,21 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo,
17281741
// Create edges for the instructions.
17291742
for (auto &i : *bb) {
17301743
analyzeInstruction(&i, FInfo, BottomUpOrder, RecursionDepth);
1744+
1745+
// Bail if the graph gets too big. The node merging algorithm has
1746+
// quadratic complexity and we want to avoid this.
1747+
// TODO: fix the quadratic complexity (if possible) and remove this limit.
1748+
if (ConGraph->Nodes.size() > 10000) {
1749+
ConGraph->invalidate();
1750+
return false;
1751+
}
17311752
}
17321753
return true;
17331754
});
17341755

1756+
if (!ConGraph->isValid())
1757+
return;
1758+
17351759
// Second step: create defer-edges for block arguments.
17361760
for (SILBasicBlock &BB : *ConGraph->F) {
17371761
if (!reachable.isVisited(&BB))
@@ -2478,6 +2502,16 @@ void EscapeAnalysis::recompute(FunctionInfo *Initial) {
24782502
bool EscapeAnalysis::mergeCalleeGraph(SILInstruction *AS,
24792503
ConnectionGraph *CallerGraph,
24802504
ConnectionGraph *CalleeGraph) {
2505+
if (!CallerGraph->isValid())
2506+
return false;
2507+
2508+
if (!CalleeGraph->isValid()) {
2509+
setAllEscaping(AS, CallerGraph);
2510+
// Conservatively assume that setting that setAllEscaping(AS) did change the
2511+
// graph.
2512+
return true;
2513+
}
2514+
24812515
// This CGNodeMap uses an intrusive worklist to keep track of Mapped nodes
24822516
// from the CalleeGraph. Meanwhile, mergeFrom uses separate intrusive
24832517
// worklists to update nodes in the CallerGraph.
@@ -2536,6 +2570,11 @@ bool EscapeAnalysis::mergeCalleeGraph(SILInstruction *AS,
25362570

25372571
bool EscapeAnalysis::mergeSummaryGraph(ConnectionGraph *SummaryGraph,
25382572
ConnectionGraph *Graph) {
2573+
if (!Graph->isValid()) {
2574+
bool changed = SummaryGraph->isValid();
2575+
SummaryGraph->invalidate();
2576+
return changed;
2577+
}
25392578

25402579
// Make a 1-to-1 mapping of all arguments and the return value. This CGNodeMap
25412580
// node map uses an intrusive worklist to keep track of Mapped nodes from the

0 commit comments

Comments
 (0)