Skip to content

Commit 191f9db

Browse files
committed
EscapeAnalysis: eliminate dangling pointers.
Avoid dangling pointers in the connection graph when SILValues are deleted. This didn't show up in normal compilation because the values were only used for printing the graph. But now we have more assertions that check for well-formed graph. These could hit the dangling pointers. To guarantee this can't happen, establish a strict invariant that the mappedValue is only used for nodes that exist in the value-to-node map. Then clear out mappedValue whenever a value is deleted. Add verification to ensure that each node's mappedValue is valid and catch dangling pointers quickly. This completely changes the way that a node values are set and used which in turn makes it *much* easier to debug the graph and associate the nodes with the SIL function. For example, it's normal to view or dump the graph at a key point. When the nodes are printed, the associated SILValues are now more precise and consistent. However, it's also normal to call CGNode::dump() from tracing code or within a debugger. Previously, there was no way to assocate the output of CGNode::dump() with the printed or displayed connection graph. Now both forms of output have identical node IDs! While we're at it, make sure the hasRC flag is always merged conservatively in a couple more places. Fixed <rdar://57290845> While running SILFunctionTransform "TempRValueOpt"
1 parent ff80326 commit 191f9db

File tree

4 files changed

+539
-402
lines changed

4 files changed

+539
-402
lines changed

include/swift/SILOptimizer/Analysis/EscapeAnalysis.h

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -252,16 +252,18 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
252252
/// pointer points to (see NodeType).
253253
class CGNode {
254254

255-
/// The associated value in the function. This is always valid for Argument
256-
/// and Value nodes, and always nullptr for Return nodes. For Content nodes,
257-
/// i is only used for debug printing. A Content's value 'V' is unreliable
258-
/// because it can change as a result of the order the the graph is
259-
/// constructed and summary graphs are merged. Sometimes it is derived from
260-
/// the instructions that access the content, other times, it's simply
261-
/// inherited from its parent. There may be multiple nodes associated to the
262-
/// same value, e.g. a Content node has the same V as its points-to
263-
/// predecessor.
264-
ValueBase *V;
255+
/// The associated value in the function. This is only valid for nodes that
256+
/// are mapped to a value in the graph's Values2Nodes map. Multiple values
257+
/// may be mapped to the same node, but only one value is associated with
258+
/// the node via 'mappedValue'. Setting 'mappedValue' to a valid SILValue
259+
/// for an unmapped node would result in a dangling pointer when the
260+
/// SILValue is deleted.
261+
///
262+
/// Argument and Value nodes are always initially mapped, but may become
263+
/// unmapped when their SILValue is deleted. Content nodes are conditionally
264+
/// mapped, only if they are associated with an explicit dereference in the
265+
/// code (which has not been deleted). Return nodes are never mapped.
266+
ValueBase *mappedValue;
265267

266268
/// The outgoing points-to edge (if any) to a Content node. See also:
267269
/// pointsToIsEdge.
@@ -317,7 +319,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
317319

318320
/// The constructor.
319321
CGNode(ValueBase *V, NodeType Type, bool hasRC)
320-
: V(V), UsePoints(0), hasRC(hasRC), Type(Type) {
322+
: mappedValue(V), UsePoints(0), hasRC(hasRC), Type(Type) {
321323
switch (Type) {
322324
case NodeType::Argument:
323325
case NodeType::Value:
@@ -333,6 +335,11 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
333335
}
334336
}
335337

338+
/// Get the representative node that maps to a SILValue and depth in the
339+
/// pointsTo graph.
340+
std::pair<const CGNode *, unsigned>
341+
getRepNode(SmallPtrSetImpl<const CGNode *> &visited) const;
342+
336343
/// Merges the use points from another node and returns true if there are
337344
/// any changes.
338345
bool mergeUsePoints(CGNode *RHS) {
@@ -417,24 +424,28 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
417424
friend struct CGNodeWorklist;
418425

419426
public:
420-
SILValue getValue() const {
421-
assert(Type == NodeType::Argument || Type == NodeType::Value);
422-
return V;
423-
}
424-
SILValue getValueOrNull() const { return V; }
425-
426-
void updateValue(SILValue newValue) {
427-
assert(isContent());
428-
V = newValue;
429-
}
427+
struct RepValue {
428+
// May only be an invalid SILValue for Return nodes or deleted values.
429+
llvm::PointerIntPair<SILValue, 1, bool> valueAndIsReturn;
430+
unsigned depth;
431+
432+
SILValue getValue() const { return valueAndIsReturn.getPointer(); }
433+
bool isReturn() const { return valueAndIsReturn.getInt(); }
434+
void
435+
print(llvm::raw_ostream &stream,
436+
const llvm::DenseMap<const SILNode *, unsigned> &instToIDMap) const;
437+
};
438+
// Get the representative SILValue for this node its depth relative to the
439+
// node that is mapped to this value.
440+
RepValue getRepValue() const;
430441

431442
/// Return true if this node represents content.
432443
bool isContent() const { return Type == NodeType::Content; }
433444

434445
/// Return true if this node represents an entire reference counted object.
435446
bool hasRefCount() const { return hasRC; }
436447

437-
void setRefCount() { hasRC = true; }
448+
void setRefCount(bool rc) { hasRC = rc; }
438449

439450
/// Returns the escape state.
440451
EscapeState getEscapeState() const { return State; }
@@ -465,9 +476,8 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
465476
/// Note that in the false-case the node's value can still escape via
466477
/// the return instruction.
467478
///
468-
/// \p nodeValue is often the same as 'this->V', but is sometimes a more
469-
/// refined value. For content nodes, 'this->V' is only a placeholder that
470-
/// does not necessarilly represent the node's memory.
479+
/// \p nodeValue is often the same as 'this->getRepValue().getValue()', but
480+
/// is sometimes a more refined value specific to a content nodes.
471481
bool escapesInsideFunction(SILValue nodeValue) const {
472482
switch (getEscapeState()) {
473483
case EscapeState::None:
@@ -478,7 +488,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
478488
case EscapeState::Global:
479489
return true;
480490
}
481-
482491
llvm_unreachable("Unhandled EscapeState in switch.");
483492
}
484493

@@ -644,10 +653,9 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
644653
/// Returns null, if V is not a "pointer".
645654
CGNode *getNode(ValueBase *V, bool createIfNeeded = true);
646655

647-
/// Helper to create a content node and update the pointsTo graph. \p
648-
/// addrNode will point to the new content node. The new content node is
649-
/// directly initialized with the remaining function arguments.
650-
CGNode *createContentNode(CGNode *addrNode, SILValue addrVal, bool hasRC);
656+
/// Helper to create and return a content node with the given \p hasRC
657+
/// flag. \p addrNode will gain a points-to edge to the new content node.
658+
CGNode *createContentNode(CGNode *addrNode, bool hasRC);
651659

652660
/// Create a new content node based on an existing content node to support
653661
/// graph merging.
@@ -687,6 +695,8 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
687695
void setNode(ValueBase *V, CGNode *Node) {
688696
assert(Values2Nodes.find(V) == Values2Nodes.end());
689697
Values2Nodes[V] = Node;
698+
if (!Node->mappedValue)
699+
Node->mappedValue = V;
690700
}
691701

692702
/// Adds an argument/instruction in which the node's value is used.
@@ -707,7 +717,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
707717
void escapeContentsOf(CGNode *Node) {
708718
CGNode *escapedContent = Node->getContentNodeOrNull();
709719
if (!escapedContent) {
710-
escapedContent = createContentNode(Node, Node->V, /*hasRC=*/false);
720+
escapedContent = createContentNode(Node, /*hasRC=*/false);
711721
}
712722
escapedContent->markEscaping();
713723
}
@@ -736,10 +746,10 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
736746
/// Propagates the escape states through the graph.
737747
void propagateEscapeStates();
738748

739-
/// Removes a value from the graph.
740-
/// It does not delete its node but makes sure that the value cannot be
741-
/// lookup-up with getNode() anymore.
742-
void removeFromGraph(ValueBase *V) { Values2Nodes.erase(V); }
749+
/// Remove a value from the graph. Do not delete the mapped node, but reset
750+
/// mappedValue if it is set to this value, and make sure that the node
751+
/// cannot be looked up with getNode().
752+
void removeFromGraph(ValueBase *V);
743753

744754
enum class Traversal { Follow, Backtrack, Halt };
745755

@@ -821,7 +831,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
821831

822832
/// Just verifies the graph structure. This function can also be called
823833
/// during the graph is modified, e.g. in mergeAllScheduledNodes().
824-
void verifyStructure() const;
834+
void verifyStructure(bool allowMerge = false) const;
825835

826836
friend struct ::CGForDotView;
827837
friend class EscapeAnalysis;

0 commit comments

Comments
 (0)