Skip to content

Commit 7fb4e21

Browse files
committed
EscapeAnalysis: Make EscapeState and UsePoints a property of the content node only.
For alias analysis query to be generally correct, we need to effectively merge the escape state and use points for everything in a defer web. It was unclear from the current design whether the "escaping" property applied to the pointer value or its content. The implementation is inconsistent in how it was treated. It appears that some bugs have been worked around by propagating forward through defer edges, some have been worked around by querying the content instead of the pointer, and others have been worked around be creating fake use points at block arguments. If we always simply query the content for escape state and use points, then we never need to propagate along defer edges. The current code that propagates escape state along defer edges in one direction is simply incorrect from the perspective of alias analysis. One very attractive solution is to merge nodes eagerly without creating any defer edges, but that would be a much more radical change even than what I've done here. It would also pose some new issues: how to resolve the current "node types" when merging and how to deal with missing content nodes. This solution of applying escape state to content nodes solves all these problems without too radical of a change at the expense of eagerly creating content nodes. (The potential graph memory usage is not really an issue because it's possible to drastically shrink the size of the graph anyway in a future commit--I've been able to fit a node within one cache line). This solution nicely preserves graph structure which makes it easy to debug and relate to the IR. Eagerly creating content nodes also solves the missing content node problem. For example, when querying canEscapeTo, we need to know whether to look at the escape state for just the pointer value itself, or also for its content. It may be possible the its content node is actually part of the same object at the IR level. If the content node is missing, then we don't know if the object's interior address is not recognizable/representable or whether we simply never saw an access to the interior address. We can't simply look at whether the current IR value happens to be a reference, because that doesn't tell us whether the graph node may have been merged with a non-reference node or even with it's own content node. To be correct in general, this query would need to be extremely conservative. However, if content nodes are always created for references, then we only need to query the escape state of a pointer's content node. The content node's flag tells us if it's an interior node, in which case it will always point to another content node which also needs to be queried.
1 parent 8b926af commit 7fb4e21

File tree

5 files changed

+93
-78
lines changed

5 files changed

+93
-78
lines changed

include/swift/SILOptimizer/Analysis/EscapeAnalysis.h

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -641,10 +641,10 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
641641
CGNode *ReturnNode = nullptr;
642642

643643
/// The list of use points.
644-
llvm::SmallVector<SILNode *, 16> UsePointTable;
644+
llvm::SmallVector<SILInstruction *, 16> UsePointTable;
645645

646646
/// Mapping of use points to bit indices in CGNode::UsePoints.
647-
llvm::DenseMap<SILNode *, int> UsePoints;
647+
llvm::DenseMap<SILInstruction *, int> UsePoints;
648648

649649
/// The allocator for nodes.
650650
llvm::SpecificBumpPtrAllocator<CGNode> NodeAllocator;
@@ -798,19 +798,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
798798
Node->mappedValue = V;
799799
}
800800

801-
/// Adds an argument/instruction in which the node's value is used.
802-
int addUsePoint(CGNode *Node, SILNode *User) {
803-
if (Node->getEscapeState() >= EscapeState::Global)
804-
return -1;
805-
806-
User = User->getRepresentativeSILNodeInObject();
807-
int Idx = (int)UsePoints.size();
808-
assert(UsePoints.count(User) == 0 && "value is already a use-point");
809-
UsePoints[User] = Idx;
810-
UsePointTable.push_back(User);
811-
assert(UsePoints.size() == UsePointTable.size());
812-
Node->setUsePointBit(Idx);
813-
return Idx;
814801
/// If \p pointer is a pointer, set it's content to global escaping.
815802
///
816803
/// Only mark the content node as escaping. Marking a pointer node as
@@ -828,13 +815,8 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
828815
content->markEscaping();
829816
}
830817

831-
void escapeContentsOf(CGNode *Node) {
832-
CGNode *escapedContent = Node->getContentNodeOrNull();
833-
if (!escapedContent) {
834-
escapedContent = createContentNode(Node, /*hasRC=*/false);
835-
}
836-
escapedContent->markEscaping();
837-
}
818+
/// Adds an argument/instruction in which the node's value is used.
819+
int addUsePoint(CGNode *Node, SILInstruction *User);
838820

839821
/// Creates a defer-edge between \p From and \p To.
840822
/// This may trigger node merges to keep the graph invariance 4).
@@ -920,11 +902,11 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
920902
/// (indirectly) somehow refer to the Node's value.
921903
/// Use-points are only values which are relevant for lifeness computation,
922904
/// e.g. release or apply instructions.
923-
bool isUsePoint(SILNode *UsePoint, CGNode *Node);
905+
bool isUsePoint(SILInstruction *UsePoint, CGNode *Node);
924906

925907
/// Returns all use points of \p Node in \p UsePoints.
926908
void getUsePoints(CGNode *Node,
927-
llvm::SmallVectorImpl<SILNode *> &UsePoints);
909+
llvm::SmallVectorImpl<SILInstruction *> &UsePoints);
928910

929911
/// Computes the use point information.
930912
void computeUsePoints();
@@ -1108,10 +1090,11 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
11081090
bool mergeSummaryGraph(ConnectionGraph *SummaryGraph,
11091091
ConnectionGraph *Graph);
11101092

1111-
/// Returns true if the value \p V can escape to the \p UsePoint, where
1112-
/// \p UsePoint is either a release-instruction or a function call.
1113-
bool canEscapeToUsePoint(SILValue V, SILNode *UsePoint,
1114-
ConnectionGraph *ConGraph);
1093+
/// Returns true if the value \p value or any address within that value can
1094+
/// escape to the \p usePoint, where \p usePoint is either a
1095+
/// release-instruction or a function call.
1096+
bool canEscapeToUsePoint(SILValue value, SILInstruction *usePoint,
1097+
ConnectionGraph *conGraph);
11151098

11161099
friend struct ::CGForDotView;
11171100

lib/SILOptimizer/Analysis/AliasAnalysis.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -737,8 +737,8 @@ bool AliasAnalysis::mayValueReleaseInterfereWithInstruction(SILInstruction *User
737737
// The most important check: does the object escape the current function?
738738
auto LO = getUnderlyingObject(V);
739739
auto *ConGraph = EA->getConnectionGraph(User->getFunction());
740-
auto *Node = ConGraph->getNodeOrNull(LO);
741-
if (Node && !Node->escapes())
740+
auto *Content = ConGraph->getValueContent(LO);
741+
if (Content && !Content->escapes())
742742
return false;
743743

744744
// This is either a non-local allocation or a scoped allocation that escapes.

lib/SILOptimizer/Analysis/EscapeAnalysis.cpp

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -804,10 +804,14 @@ void EscapeAnalysis::ConnectionGraph::propagateEscapeStates() {
804804
Changed = false;
805805

806806
for (CGNode *Node : Nodes) {
807-
// Propagate the state to all successor nodes.
807+
// Propagate the state to all pointsTo nodes. It would be sufficient to
808+
// only follow proper pointsTo edges, since this loop also follows defer
809+
// edges, but this may converge faster.
808810
if (Node->pointsTo) {
809811
Changed |= Node->pointsTo->mergeEscapeState(Node->State);
810812
}
813+
// Note: Propagating along defer edges may be interesting from an SSA
814+
// standpoint, but it is entirely irrelevant alias analysis.
811815
for (CGNode *Def : Node->defersTo) {
812816
Changed |= Def->mergeEscapeState(Node->State);
813817
}
@@ -845,14 +849,13 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() {
845849
/// liferange. And that must be a releasing instruction.
846850
int ValueIdx = -1;
847851
for (const Operand &Op : I.getAllOperands()) {
848-
ValueBase *OpV = Op.get();
849-
if (CGNode *OpNd = lookupNode(EA->getPointerRoot(OpV))) {
850-
if (ValueIdx < 0) {
851-
ValueIdx = addUsePoint(OpNd, &I);
852-
} else {
853-
OpNd->setUsePointBit(ValueIdx);
854-
}
855-
}
852+
CGNode *content = getValueContent(Op.get());
853+
if (!content)
854+
continue;
855+
if (ValueIdx < 0)
856+
ValueIdx = addUsePoint(content, &I);
857+
else
858+
content->setUsePointBit(ValueIdx);
856859
}
857860
break;
858861
}
@@ -867,11 +870,10 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() {
867870
do {
868871
Changed = false;
869872
for (CGNode *Node : Nodes) {
870-
// Propagate the bits to all successor nodes.
871-
Node->visitSuccessors([&Changed, Node](CGNode *succ) {
872-
Changed |= succ->mergeUsePoints(Node);
873-
return true;
874-
});
873+
// Propagate the bits to pointsTo. A release of a node may also release
874+
// any content pointed to be the node.
875+
if (Node->pointsTo)
876+
Changed |= Node->pointsTo->mergeUsePoints(Node);
875877
}
876878
} while (Changed);
877879
}
@@ -1097,11 +1099,10 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph,
10971099
/// somehow refer to the Node's value.
10981100
/// Use-points are only values which are relevant for lifeness computation,
10991101
/// e.g. release or apply instructions.
1100-
bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILNode *UsePoint,
1102+
bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILInstruction *UsePoint,
11011103
CGNode *Node) {
11021104
assert(Node->getEscapeState() < EscapeState::Global &&
11031105
"Use points are only valid for non-escaping nodes");
1104-
UsePoint = UsePoint->getRepresentativeSILNodeInObject();
11051106
auto Iter = UsePoints.find(UsePoint);
11061107
if (Iter == UsePoints.end())
11071108
return false;
@@ -1111,8 +1112,8 @@ bool EscapeAnalysis::ConnectionGraph::isUsePoint(SILNode *UsePoint,
11111112
return Node->UsePoints.test(Idx);
11121113
}
11131114

1114-
void EscapeAnalysis::ConnectionGraph::
1115-
getUsePoints(CGNode *Node, llvm::SmallVectorImpl<SILNode *> &UsePoints) {
1115+
void EscapeAnalysis::ConnectionGraph::getUsePoints(
1116+
CGNode *Node, llvm::SmallVectorImpl<SILInstruction *> &UsePoints) {
11161117
assert(Node->getEscapeState() < EscapeState::Global &&
11171118
"Use points are only valid for non-escaping nodes");
11181119
for (int Idx = Node->UsePoints.find_first(); Idx >= 0;
@@ -2571,13 +2572,13 @@ bool EscapeAnalysis::canEscapeToValue(SILValue V, SILValue To) {
25712572
return true;
25722573
auto *ConGraph = getConnectionGraph(F);
25732574

2574-
CGNode *Node = ConGraph->getNodeOrNull(V);
2575-
if (!Node)
2575+
CGNode *valueContent = ConGraph->getValueContent(V);
2576+
if (!valueContent)
25762577
return true;
2577-
CGNode *ToNode = ConGraph->getNodeOrNull(To);
2578-
if (!ToNode)
2578+
CGNode *userContent = ConGraph->getValueContent(To);
2579+
if (!userContent)
25792580
return true;
2580-
return ConGraph->mayReach(ToNode, Node);
2581+
return ConGraph->mayReach(userContent, valueContent);
25812582
}
25822583

25832584
bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) {
@@ -2592,27 +2593,26 @@ bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) {
25922593
return true;
25932594
auto *ConGraph = getConnectionGraph(F);
25942595

2595-
CGNode *Node1 = ConGraph->getNodeOrNull(V1);
2596-
if (!Node1)
2596+
CGNode *Content1 = ConGraph->getValueContent(V1);
2597+
if (!Content1)
25972598
return true;
2598-
CGNode *Node2 = ConGraph->getNodeOrNull(V2);
2599-
if (!Node2)
2599+
2600+
CGNode *Content2 = ConGraph->getValueContent(V2);
2601+
if (!Content2)
26002602
return true;
26012603

26022604
// Finish the check for one value being a non-escaping local object.
2603-
if (isUniq1 && Node1->valueEscapesInsideFunction(V1))
2605+
if (isUniq1 && Content1->valueEscapesInsideFunction(V1))
26042606
isUniq1 = false;
26052607

2606-
if (isUniq2 && Node2->valueEscapesInsideFunction(V2))
2608+
if (isUniq2 && Content2->valueEscapesInsideFunction(V2))
26072609
isUniq2 = false;
26082610

26092611
if (!isUniq1 && !isUniq2)
26102612
return true;
26112613

26122614
// Check if both nodes may point to the same content.
2613-
CGNode *Content1 = getValueContent(ConGraph, V1);
2614-
CGNode *Content2 = getValueContent(ConGraph, V2);
2615-
2615+
// FIXME!!!: This will be rewritten to use node flags in the next commit.
26162616
SILType T1 = V1->getType();
26172617
SILType T2 = V2->getType();
26182618
if (T1.isAddress() && T2.isAddress()) {

lib/SILOptimizer/Transforms/StackPromotion.cpp

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -108,32 +108,20 @@ bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA,
108108
return false;
109109

110110
auto *ConGraph = EA->getConnectionGraph(ARI->getFunction());
111-
auto *Node = ConGraph->getNodeOrNull(ARI);
112-
if (!Node)
111+
auto *contentNode = ConGraph->getValueContent(ARI);
112+
if (!contentNode)
113113
return false;
114114

115115
// The most important check: does the object escape the current function?
116-
if (Node->escapes())
116+
if (contentNode->escapes())
117117
return false;
118118

119119
LLVM_DEBUG(llvm::dbgs() << "Promote " << *ARI);
120120

121121
// Collect all use-points of the allocation. These are refcount instructions
122122
// and apply instructions.
123-
llvm::SmallVector<SILNode *, 8> BaseUsePoints;
124123
llvm::SmallVector<SILInstruction *, 8> UsePoints;
125-
ConGraph->getUsePoints(Node, BaseUsePoints);
126-
for (SILNode *UsePoint : BaseUsePoints) {
127-
if (SILInstruction *I = dyn_cast<SILInstruction>(UsePoint)) {
128-
UsePoints.push_back(I);
129-
} else {
130-
// Also block arguments can be use points.
131-
SILBasicBlock *UseBB = cast<SILPhiArgument>(UsePoint)->getParent();
132-
// For simplicity we just add the first instruction of the block as use
133-
// point.
134-
UsePoints.push_back(&UseBB->front());
135-
}
136-
}
124+
ConGraph->getUsePoints(contentNode, UsePoints);
137125

138126
ValueLifetimeAnalysis VLA(ARI, UsePoints);
139127
// Check if there is a use point before the allocation (this can happen e.g.

lib/SILOptimizer/UtilityPasses/EscapeAnalysisDumper.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@ llvm::cl::opt<bool> EnableGraphWriter(
2525

2626
namespace {
2727

28+
static bool gatherValues(EscapeAnalysis *EA, SILFunction &Fn,
29+
std::vector<SILValue> &Values) {
30+
for (auto &BB : Fn) {
31+
for (auto *Arg : BB.getArguments()) {
32+
if (EA->isPointer(Arg))
33+
Values.push_back(SILValue(Arg));
34+
}
35+
for (auto &II : BB) {
36+
for (auto result : II.getResults()) {
37+
if (EA->isPointer(result))
38+
Values.push_back(result);
39+
}
40+
}
41+
}
42+
return Values.size() > 1;
43+
}
44+
2845
/// Dumps the escape information of all functions in the module.
2946
/// Only dumps if the compiler is built with assertions.
3047
/// For details see EscapeAnalysis.
@@ -43,6 +60,33 @@ class EscapeAnalysisDumper : public SILModuleTransform {
4360
ConnectionGraph->print(llvm::outs());
4461
if (EnableGraphWriter)
4562
ConnectionGraph->dumpCG();
63+
64+
// Gather up all Values in Fn.
65+
std::vector<SILValue> Values;
66+
if (!gatherValues(EA, F, Values))
67+
continue;
68+
69+
// Emit the N^2 escape analysis evaluation of the values.
70+
for (auto &bb : F) {
71+
for (auto &ii : bb) {
72+
if (auto fas = FullApplySite::isa(&ii)) {
73+
for (unsigned i = 0, e = Values.size(); i != e; ++i) {
74+
SILValue val = Values[i];
75+
bool escape = EA->canEscapeTo(val, fas);
76+
llvm::outs() << (escape ? "May" : "No") << "Escape: " << val
77+
<< " to " << ii;
78+
}
79+
}
80+
if (RefCountingInst *rci = dyn_cast<RefCountingInst>(&ii)) {
81+
for (unsigned i = 0, e = Values.size(); i != e; ++i) {
82+
SILValue val = Values[i];
83+
bool escape = EA->canEscapeTo(val, rci);
84+
llvm::outs() << (escape ? "May" : "No") << "Escape: " << val
85+
<< " to " << ii;
86+
}
87+
}
88+
}
89+
}
4690
}
4791
}
4892
#endif

0 commit comments

Comments
 (0)