mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Cleanup EscapeAnalysis::ConnectionGraph::initializePointsTo.
Remove cruft that I added in the previous commit. This eliminates unnecessary cleverness so initializePointsTo is now very simple. Add hand-coded SIL test cases to somewhat verify that the new algorithm works.
This commit is contained in:
@@ -456,24 +456,18 @@ void EscapeAnalysis::ConnectionGraph::initializePointsTo(CGNode *initialNode,
|
|||||||
CGNode *newPointsTo,
|
CGNode *newPointsTo,
|
||||||
bool createEdge) {
|
bool createEdge) {
|
||||||
// Track nodes that require pointsTo edges.
|
// Track nodes that require pointsTo edges.
|
||||||
llvm::SmallVector<CGNode *, 4> pointsToEdges;
|
llvm::SmallVector<CGNode *, 4> pointsToEdgeNodes;
|
||||||
|
if (createEdge)
|
||||||
|
pointsToEdgeNodes.push_back(initialNode);
|
||||||
|
|
||||||
// Step 1: Visit each node that reaches or is reachable via defer edges until
|
// Step 1: Visit each node that reaches or is reachable via defer edges until
|
||||||
// reaching a node with the newPointsTo or with a proper pointsTo edge.
|
// reaching a node with the newPointsTo or with a proper pointsTo edge.
|
||||||
|
|
||||||
// A worklist to gather updated nodes in the defer web.
|
// A worklist to gather updated nodes in the defer web.
|
||||||
CGNodeWorklist updatedNodes(this);
|
CGNodeWorklist updatedNodes(this);
|
||||||
updatedNodes.push(initialNode);
|
unsigned updateCount = 0;
|
||||||
if (createEdge)
|
|
||||||
pointsToEdges.push_back(initialNode);
|
|
||||||
unsigned updateCount = 1;
|
|
||||||
assert(updateCount == updatedNodes.size());
|
|
||||||
// Augment the worlist with the nodes that were reached via backward
|
|
||||||
// traversal. It's not as precise as DFS, but helps avoid redundant pointsTo
|
|
||||||
// edges in most cases.
|
|
||||||
llvm::SmallPtrSet<CGNode *, 8> backwardReachable;
|
|
||||||
|
|
||||||
auto visitDeferTarget = [&](CGNode *node, bool isSuccessor) {
|
auto visitDeferTarget = [&](CGNode *node, bool /*isSuccessor*/) {
|
||||||
if (updatedNodes.contains(node))
|
if (updatedNodes.contains(node))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -484,7 +478,7 @@ void EscapeAnalysis::ConnectionGraph::initializePointsTo(CGNode *initialNode,
|
|||||||
// nodes are initialized one at a time, each time a new defer edge is
|
// nodes are initialized one at a time, each time a new defer edge is
|
||||||
// created. If this were not complete, then the backward traversal below
|
// created. If this were not complete, then the backward traversal below
|
||||||
// in Step 2 could reach uninitialized nodes not seen here in Step 1.
|
// in Step 2 could reach uninitialized nodes not seen here in Step 1.
|
||||||
pointsToEdges.push_back(node);
|
pointsToEdgeNodes.push_back(node);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
++updateCount;
|
++updateCount;
|
||||||
@@ -493,31 +487,29 @@ void EscapeAnalysis::ConnectionGraph::initializePointsTo(CGNode *initialNode,
|
|||||||
// edge. Create a "fake" pointsTo edge to maintain the graph invariant
|
// edge. Create a "fake" pointsTo edge to maintain the graph invariant
|
||||||
// (this changes the structure of the graph but adding this edge has no
|
// (this changes the structure of the graph but adding this edge has no
|
||||||
// effect on the process of merging nodes or creating new defer edges).
|
// effect on the process of merging nodes or creating new defer edges).
|
||||||
pointsToEdges.push_back(node);
|
pointsToEdgeNodes.push_back(node);
|
||||||
}
|
}
|
||||||
updatedNodes.push(node);
|
updatedNodes.push(node);
|
||||||
if (!isSuccessor)
|
|
||||||
backwardReachable.insert(node);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
// Seed updatedNodes with initialNode.
|
||||||
|
visitDeferTarget(initialNode, true);
|
||||||
// updatedNodes may grow during this loop.
|
// updatedNodes may grow during this loop.
|
||||||
unsigned nextUpdatedNodeIdx = 0;
|
for (unsigned idx = 0; idx < updatedNodes.size(); ++idx)
|
||||||
for (; nextUpdatedNodeIdx < updatedNodes.size(); ++nextUpdatedNodeIdx)
|
updatedNodes[idx]->visitDefers(visitDeferTarget);
|
||||||
updatedNodes[nextUpdatedNodeIdx]->visitDefers(visitDeferTarget);
|
|
||||||
// Reset this worklist so others can be used, but updateNode.nodeVector still
|
// Reset this worklist so others can be used, but updateNode.nodeVector still
|
||||||
// holds all the nodes found by step 1.
|
// holds all the nodes found by step 1.
|
||||||
updatedNodes.reset();
|
updatedNodes.reset();
|
||||||
|
|
||||||
// Step 2: Update pointsTo fields by propagating backward from nodes that
|
// Step 2: Update pointsTo fields by propagating backward from nodes that
|
||||||
// already have a pointsTo edge.
|
// already have a pointsTo edge.
|
||||||
assert(nextUpdatedNodeIdx == updatedNodes.size());
|
|
||||||
--nextUpdatedNodeIdx;
|
|
||||||
bool processBackwardReachable = false;
|
|
||||||
do {
|
do {
|
||||||
while (!pointsToEdges.empty()) {
|
while (!pointsToEdgeNodes.empty()) {
|
||||||
CGNode *edgeNode = pointsToEdges.pop_back_val();
|
CGNode *edgeNode = pointsToEdgeNodes.pop_back_val();
|
||||||
if (!edgeNode->pointsTo) {
|
if (!edgeNode->pointsTo) {
|
||||||
|
// This node is either (1) a leaf node in the defer web (identified in
|
||||||
|
// step 1) or (2) an arbitrary node in a defer-cycle (identified in a
|
||||||
|
// previous iteration of the outer loop).
|
||||||
edgeNode->setPointsToEdge(newPointsTo);
|
edgeNode->setPointsToEdge(newPointsTo);
|
||||||
newPointsTo->mergeUsePoints(edgeNode);
|
newPointsTo->mergeUsePoints(edgeNode);
|
||||||
assert(updateCount--);
|
assert(updateCount--);
|
||||||
@@ -542,35 +534,19 @@ void EscapeAnalysis::ConnectionGraph::initializePointsTo(CGNode *initialNode,
|
|||||||
return Traversal::Follow;
|
return Traversal::Follow;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// For all nodes visited in step 1, if any node was not backward-reachable
|
// For all nodes visited in step 1, pick a single node that was not
|
||||||
// from a pointsTo edge, create an edge for it and restart traversal.
|
// backward-reachable from a pointsTo edge, create an edge for it and
|
||||||
//
|
// restart traversal. This only happens when step 1 fails to find leaves in
|
||||||
// First process all forward-reachable nodes in backward order, then process
|
// the defer web because of defer edge cycles.
|
||||||
// all backwardReachable nodes in forward order.
|
while (!updatedNodes.empty()) {
|
||||||
while (nextUpdatedNodeIdx != updatedNodes.size()) {
|
CGNode *node = updatedNodes.nodeVector.pop_back_val();
|
||||||
CGNode *node = updatedNodes[nextUpdatedNodeIdx];
|
if (!node->pointsTo) {
|
||||||
// When processBackwardReachable == true, the backwardReachable set is
|
pointsToEdgeNodes.push_back(node);
|
||||||
// empty and all forward reachable nodes already have a pointsTo edge.
|
break;
|
||||||
if (!backwardReachable.count(node)) {
|
|
||||||
if (!node->pointsTo) {
|
|
||||||
pointsToEdges.push_back(node);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (processBackwardReachable) {
|
|
||||||
++nextUpdatedNodeIdx;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (nextUpdatedNodeIdx > 0) {
|
|
||||||
--nextUpdatedNodeIdx;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// reverse direction
|
|
||||||
backwardReachable.clear();
|
|
||||||
processBackwardReachable = true;
|
|
||||||
}
|
}
|
||||||
// This outer loop is exceedingly unlikely to execute more than twice.
|
// This outer loop is exceedingly unlikely to execute more than twice.
|
||||||
} while (!pointsToEdges.empty());
|
} while (!pointsToEdgeNodes.empty());
|
||||||
assert(updateCount == 0);
|
assert(updateCount == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1535,3 +1535,123 @@ bb0:
|
|||||||
return %7 : $()
|
return %7 : $()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test the absence of redundant pointsTo edges
|
||||||
|
// CHECK-LABEL: CG of testInitializePointsToLeaf
|
||||||
|
// CHECK: Arg %0 Esc: A, Succ: (%0.1)
|
||||||
|
// CHECK: Con %0.1 Esc: A, Succ: (%0.2) [rc]
|
||||||
|
// CHECK: Con %0.2 Esc: A, Succ: (%12.1)
|
||||||
|
// CHECK: Val %2 Esc: %4, Succ: %0.2
|
||||||
|
// CHECK: Val %4 Esc: %4, Succ: %2
|
||||||
|
// CHECK: Val %7 Esc: %12, Succ: (%12.1), %0.2
|
||||||
|
// CHECK: Val %12 Esc: %12, Succ: (%12.1), %7
|
||||||
|
// CHECK: Con %12.1 Esc: A, Succ: (%13) [rc]
|
||||||
|
// CHECK: Con %13 Esc: A, Succ:
|
||||||
|
// CHECK-LABEL: End
|
||||||
|
class C {
|
||||||
|
var c: C
|
||||||
|
}
|
||||||
|
|
||||||
|
sil @testInitializePointsToWrapOptional : $@convention(method) (@guaranteed LinkedNode) -> Optional<LinkedNode> {
|
||||||
|
bb0(%0: $LinkedNode):
|
||||||
|
%adr = ref_element_addr %0 : $LinkedNode, #LinkedNode.next
|
||||||
|
%val = load %adr : $*LinkedNode
|
||||||
|
%optional = enum $Optional<LinkedNode>, #Optional.some!enumelt.1, %val : $LinkedNode
|
||||||
|
return %optional : $Optional<LinkedNode>
|
||||||
|
}
|
||||||
|
|
||||||
|
sil @testInitializePointsToLeaf : $@convention(method) (@guaranteed LinkedNode) -> () {
|
||||||
|
bb0(%0 : $LinkedNode):
|
||||||
|
%f1 = function_ref @testInitializePointsToWrapOptional : $@convention(method) (@guaranteed LinkedNode) -> Optional<LinkedNode>
|
||||||
|
%call1 = apply %f1(%0) : $@convention(method) (@guaranteed LinkedNode) -> Optional<LinkedNode>
|
||||||
|
switch_enum %call1 : $Optional<LinkedNode>, case #Optional.some!enumelt.1: bb2, case #Optional.none!enumelt: bb3
|
||||||
|
|
||||||
|
bb2(%arg1 : $LinkedNode):
|
||||||
|
br bb4
|
||||||
|
|
||||||
|
bb3:
|
||||||
|
br bb4
|
||||||
|
|
||||||
|
bb4:
|
||||||
|
%call2 = apply %f1(%0) : $@convention(method) (@guaranteed LinkedNode) -> Optional<LinkedNode>
|
||||||
|
switch_enum %call2 : $Optional<LinkedNode>, case #Optional.some!enumelt.1: bb10, case #Optional.none!enumelt: bb9
|
||||||
|
|
||||||
|
bb9:
|
||||||
|
%37 = integer_literal $Builtin.Int1, -1
|
||||||
|
cond_fail %37 : $Builtin.Int1, "Unexpectedly found nil while unwrapping an Optional value"
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
// %40
|
||||||
|
bb10(%arg2 : $LinkedNode):
|
||||||
|
%adr = ref_element_addr %arg2 : $LinkedNode, #LinkedNode.next
|
||||||
|
%val = load %adr : $*LinkedNode
|
||||||
|
%66 = tuple ()
|
||||||
|
return %66 : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Another test for redundant pointsTo edges. In the original
|
||||||
|
// implementation, redundant points edges were created whenever adding
|
||||||
|
// a defer edge from a node with uninitialized pointsTo to a node with
|
||||||
|
// already-initialized pointsTo.
|
||||||
|
// CHECK-LABEL: CG of testInitializePointsToRedundant
|
||||||
|
// CHECK: Arg %0 Esc: A, Succ: (%0.1)
|
||||||
|
// CHECK: Con %0.1 Esc: A, Succ: (%2) [rc]
|
||||||
|
// CHECK: Arg %1 Esc: A, Succ: (%0.1)
|
||||||
|
// CHECK: Con %2 Esc: A, Succ:
|
||||||
|
// CHECK: Val %7 Esc: %7,%18, Succ: %0
|
||||||
|
// CHECK: Val %12 Esc: %12,%14,%18, Succ: %1
|
||||||
|
// CHECK: Val %14 Esc: %18, Succ: (%0.1), %1, %12
|
||||||
|
// CHECK: Val %18 Esc: %18, Succ: %7, %14
|
||||||
|
// CHECK-LABEL: End
|
||||||
|
sil @testInitializePointsToMerge : $@convention(method) (@guaranteed C, @guaranteed C) -> C {
|
||||||
|
bb0(%0: $C, %1 : $C):
|
||||||
|
cond_br undef, bb1, bb2
|
||||||
|
|
||||||
|
bb1:
|
||||||
|
br bb3(%0 : $C)
|
||||||
|
|
||||||
|
bb2:
|
||||||
|
br bb3(%1 : $C)
|
||||||
|
|
||||||
|
bb3(%arg : $C):
|
||||||
|
return %arg : $C
|
||||||
|
}
|
||||||
|
|
||||||
|
sil @testInitializePointsToRedundant : $@convention(method) (@guaranteed C, @guaranteed C) -> () {
|
||||||
|
bb0(%0 : $C, %1 : $C):
|
||||||
|
%adr0 = ref_element_addr %0 : $C, #C.c
|
||||||
|
%val0 = load %adr0 : $*C
|
||||||
|
cond_br undef, bb1, bb2
|
||||||
|
|
||||||
|
bb1:
|
||||||
|
br bb3(%0 : $C)
|
||||||
|
|
||||||
|
bb2:
|
||||||
|
br bb3(%0 : $C)
|
||||||
|
|
||||||
|
bb3(%arg1 : $C):
|
||||||
|
br bb4
|
||||||
|
|
||||||
|
bb4:
|
||||||
|
cond_br undef, bb5, bb6
|
||||||
|
|
||||||
|
bb5:
|
||||||
|
br bb7(%1 : $C)
|
||||||
|
|
||||||
|
bb6:
|
||||||
|
br bb7(%1 : $C)
|
||||||
|
|
||||||
|
bb7(%arg2 : $C):
|
||||||
|
%f1 = function_ref @testInitializePointsToMerge : $@convention(method) (@guaranteed C, @guaranteed C) -> C
|
||||||
|
%call1 = apply %f1(%arg2, %1) : $@convention(method) (@guaranteed C, @guaranteed C) -> C
|
||||||
|
cond_br undef, bb8, bb9
|
||||||
|
|
||||||
|
bb8:
|
||||||
|
br bb10(%call1 : $C)
|
||||||
|
|
||||||
|
bb9:
|
||||||
|
br bb10(%arg1 : $C)
|
||||||
|
|
||||||
|
bb10(%arg3 : $C):
|
||||||
|
%66 = tuple ()
|
||||||
|
return %66 : $()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user