Skip to content

Commit d107262

Browse files
committed
EscapeAnalysis: make the use-point analysis more precise
In canEscapeToUsePoint only check the content node if it's a reference (see comment why this is needed). For all other node types, especially addresses, handle defer edges by propagating use-point infomation backward in the graph. This makes escape analysis more precise with address types, e.g. don't consider an inout address to escape to an apply if just the loaded value is passed to an apply argument.
1 parent e4504af commit d107262

File tree

4 files changed

+72
-43
lines changed

4 files changed

+72
-43
lines changed

include/swift/SILOptimizer/Analysis/EscapeAnalysis.h

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,16 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
411411
return false;
412412
return true;
413413
}
414+
415+
/// Returns true if the node has any predecessors with link to this node
416+
/// with a defer-edge.
417+
bool hasDeferPredecessors() const {
418+
for (Predecessor Pred : Preds) {
419+
if (Pred.getInt() == EdgeType::Defer)
420+
return true;
421+
}
422+
return false;
423+
}
414424

415425
friend class CGNodeMap;
416426
friend class ConnectionGraph;
@@ -631,19 +641,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
631641
}
632642

633643
/// Adds an argument/instruction in which the node's value is used.
634-
int addUsePoint(CGNode *Node, SILNode *User) {
635-
if (Node->getEscapeState() >= EscapeState::Global)
636-
return -1;
637-
638-
User = User->getRepresentativeSILNodeInObject();
639-
int Idx = (int)UsePoints.size();
640-
assert(UsePoints.count(User) == 0 && "value is already a use-point");
641-
UsePoints[User] = Idx;
642-
UsePointTable.push_back(User);
643-
assert(UsePoints.size() == UsePointTable.size());
644-
Node->setUsePointBit(Idx);
645-
return Idx;
646-
}
644+
int addUsePoint(CGNode *Node, SILNode *User);
647645

648646
/// Specifies that the node's value escapes to global or unidentified
649647
/// memory.

lib/SILOptimizer/Analysis/EscapeAnalysis.cpp

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,27 @@ updatePointsTo(CGNode *InitialNode, CGNode *pointsTo) {
376376
clearWorkListFlags(WorkList);
377377
}
378378

379+
int EscapeAnalysis::ConnectionGraph::addUsePoint(CGNode *Node, SILNode *User) {
380+
// For escaping nodes we have to make worst case assumptions anyway. So no
381+
// need to store use point information.
382+
if (Node->getEscapeState() >= EscapeState::Global &&
383+
// ... except it has defer predecessor edges, which are (potentially) not
384+
// escaping: use-points are propagated in reverse direction along defer
385+
// edges!
386+
!Node->hasDeferPredecessors()) {
387+
return -1;
388+
}
389+
390+
User = User->getRepresentativeSILNodeInObject();
391+
int Idx = (int)UsePoints.size();
392+
assert(UsePoints.count(User) == 0 && "value is already a use-point");
393+
UsePoints[User] = Idx;
394+
UsePointTable.push_back(User);
395+
assert(UsePoints.size() == UsePointTable.size());
396+
Node->setUsePointBit(Idx);
397+
return Idx;
398+
}
399+
379400
void EscapeAnalysis::ConnectionGraph::propagateEscapeStates() {
380401
bool Changed = false;
381402
do {
@@ -449,6 +470,10 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() {
449470
for (CGNode *Def : Node->defersTo) {
450471
Changed |= Def->mergeUsePoints(Node);
451472
}
473+
for (Predecessor Pred : Node->Preds) {
474+
if (Pred.getInt() == EdgeType::Defer)
475+
Changed |= Pred.getPointer()->mergeUsePoints(Node);
476+
}
452477
}
453478
} while (Changed);
454479
}
@@ -1858,23 +1883,28 @@ bool EscapeAnalysis::canEscapeToUsePoint(SILValue V, SILNode *UsePoint,
18581883
if (ConGraph->isUsePoint(UsePoint, Node))
18591884
return true;
18601885

1861-
assert(isPointer(V) && "should not have a node for a non-pointer");
1862-
1863-
// Check if the object "content" can escape to the called function.
1864-
// This will catch cases where V is a reference and a pointer to a stored
1865-
// property escapes.
1866-
// It's also important in case of a pointer assignment, e.g.
1867-
// V = V1
1868-
// apply(V1)
1869-
// In this case the apply is only a use-point for V1 and V1's content node.
1870-
// As V1's content node is the same as V's content node, we also make the
1871-
// check for the content node.
1872-
CGNode *ContentNode = ConGraph->getContentNode(Node);
1873-
if (ContentNode->escapesInsideFunction(false))
1874-
return true;
1886+
if (V->getType().hasReferenceSemantics()) {
1887+
// In case V is the result of getUnderlyingObject, we also have to check
1888+
// the content node. Example:
1889+
//
1890+
// %a = ref_element %r
1891+
// apply %f(%a)
1892+
//
1893+
// The reference %r does not actually escape to the function call. But in
1894+
// case this API is called like
1895+
//
1896+
// canEscapeToUsePoint(getUnderlyingObject(applyArgument))
1897+
//
1898+
// it would yield false, although the projected object address is passed to
1899+
// the function.
1900+
1901+
CGNode *ContentNode = ConGraph->getContentNode(Node);
1902+
if (ContentNode->escapesInsideFunction(false))
1903+
return true;
18751904

1876-
if (ConGraph->isUsePoint(UsePoint, ContentNode))
1877-
return true;
1905+
if (ConGraph->isUsePoint(UsePoint, ContentNode))
1906+
return true;
1907+
}
18781908

18791909
return false;
18801910
}

lib/SILOptimizer/Transforms/StackPromotion.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,9 @@ bool StackPromotion::tryPromoteAlloc(AllocRefInst *ARI, EscapeAnalysis *EA,
127127
if (SILInstruction *I = dyn_cast<SILInstruction>(UsePoint)) {
128128
UsePoints.push_back(I);
129129
} else {
130-
// Also block arguments can be use points.
131-
SILBasicBlock *UseBB = cast<SILPhiArgument>(UsePoint)->getParent();
130+
// Also block arguments can be use points (even function arguments, as
131+
// use-points are propagated backwards along defer edges).
132+
SILBasicBlock *UseBB = cast<SILArgument>(UsePoint)->getParent();
132133
// For simplicity we just add the first instruction of the block as use
133134
// point.
134135
UsePoints.push_back(&UseBB->front());

test/SILOptimizer/escape_analysis.sil

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ bb0(%0 : $*Y, %1 : $X):
119119
// CHECK-LABEL: CG of deferringEdge
120120
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%3.1)
121121
// CHECK-NEXT: Arg %1 Esc: A, Succ:
122-
// CHECK-NEXT: Val %3 Esc: %3, Succ: (%3.1), %0
122+
// CHECK-NEXT: Val %3 Esc: %0,%3, Succ: (%3.1), %0
123123
// CHECK-NEXT: Con %3.1 Esc: A, Succ: (%3.2)
124124
// CHECK-NEXT: Con %3.2 Esc: A, Succ: %1
125125
// CHECK-NEXT: Ret Esc: R, Succ: %0
@@ -153,8 +153,8 @@ bb0:
153153
// CHECK-NEXT: Val %1 Esc: A, Succ: (%1.1)
154154
// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%11.1)
155155
// CHECK-NEXT: Val %4 Esc: A, Succ: (%1.1)
156-
// CHECK-NEXT: Val %7 Esc: %11, Succ: (%1.1)
157-
// CHECK-NEXT: Val %11 Esc: %11, Succ: (%1.1), %7, %11.1
156+
// CHECK-NEXT: Val %7 Esc: %0,%11, Succ: (%1.1)
157+
// CHECK-NEXT: Val %11 Esc: %0,%11, Succ: (%1.1), %7, %11.1
158158
// CHECK-NEXT: Con %11.1 Esc: A, Succ: (%1.1), %0, %1, %4
159159
// CHECK-NEXT: Ret Esc: R, Succ: %11.1
160160
// CHECK-NEXT: End
@@ -212,7 +212,7 @@ bb0(%0 : $LinkedNode):
212212

213213
// CHECK-LABEL: CG of loadNext
214214
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%2.1)
215-
// CHECK-NEXT: Val %2 Esc: %2, Succ: (%2.1), %0, %2.2
215+
// CHECK-NEXT: Val %2 Esc: %0,%2, Succ: (%2.1), %0, %2.2
216216
// CHECK-NEXT: Con %2.1 Esc: A, Succ: (%2.2)
217217
// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%2.1)
218218
// CHECK-NEXT: Ret Esc: R, Succ: %2.2
@@ -406,12 +406,12 @@ sil @call_copy_addr_content : $@convention(thin) () -> () {
406406
// CHECK-LABEL: CG of test_partial_apply
407407
// CHECK-NEXT: Arg %1 Esc: G, Succ:
408408
// CHECK-NEXT: Arg %2 Esc: A, Succ: (%6.3)
409-
// CHECK-NEXT: Val %3 Esc: %14,%15,%17, Succ: (%6.1)
410-
// CHECK-NEXT: Val %6 Esc: %14,%15,%16, Succ: (%6.1)
409+
// CHECK-NEXT: Val %3 Esc: %14,%15,%16,%17, Succ: (%6.1)
410+
// CHECK-NEXT: Val %6 Esc: %14,%15,%16,%17, Succ: (%6.1)
411411
// CHECK-NEXT: Con %6.1 Esc: %14,%15,%16,%17, Succ: (%6.2)
412-
// CHECK-NEXT: Con %6.2 Esc: %14,%15,%16,%17, Succ: %2
412+
// CHECK-NEXT: Con %6.2 Esc: %2,%14,%15,%16,%17, Succ: %2
413413
// CHECK-NEXT: Con %6.3 Esc: G, Succ:
414-
// CHECK-NEXT: Val %12 Esc: %14,%15, Succ: %3, %6
414+
// CHECK-NEXT: Val %12 Esc: %14,%15,%16,%17, Succ: %3, %6
415415
// CHECK-NEXT: End
416416
sil @test_partial_apply : $@convention(thin) (Int64, @owned X, @owned Y) -> Int64 {
417417
bb0(%0 : $Int64, %1 : $X, %2 : $Y):
@@ -443,7 +443,7 @@ bb0(%0 : $Int64, %1 : $X, %2 : $Y):
443443
// CHECK-NEXT: Con %2.1 Esc: A, Succ: (%2.2)
444444
// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%2.3)
445445
// CHECK-NEXT: Con %2.3 Esc: G, Succ:
446-
// CHECK-NEXT: Val %7 Esc: %8, Succ: %2
446+
// CHECK-NEXT: Val %7 Esc: %2,%8, Succ: %2
447447
// CHECK-NEXT: End
448448
sil @closure1 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } <Int64>, @owned <τ_0_0> { var τ_0_0 } <Y>) -> Int64 {
449449
bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } <Int64>, %2 : $<τ_0_0> { var τ_0_0 } <Y>):
@@ -761,7 +761,7 @@ sil @unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error
761761
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%1.3)
762762
// CHECK-NEXT: Val %1 Esc: %6, Succ: (%1.1)
763763
// CHECK-NEXT: Con %1.1 Esc: %6, Succ: (%1.2)
764-
// CHECK-NEXT: Con %1.2 Esc: %6, Succ: %0
764+
// CHECK-NEXT: Con %1.2 Esc: %0,%6, Succ: %0
765765
// CHECK-NEXT: Con %1.3 Esc: G, Succ:
766766
// CHECK-NEXT: Val %5 Esc: %6, Succ: %1
767767
// CHECK-NEXT: End
@@ -969,7 +969,7 @@ bb0(%0 : $Builtin.Int64, %1 : $X, %2 : $X, %3 : $X):
969969
// CHECK-NEXT: Arg %0 Esc: A, Succ:
970970
// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1)
971971
// CHECK-NEXT: Con %1.1 Esc: , Succ: (%1.2)
972-
// CHECK-NEXT: Con %1.2 Esc: , Succ: %0
972+
// CHECK-NEXT: Con %1.2 Esc: %0, Succ: %0
973973
// CHECK-NEXT: End
974974
sil @test_existential_addr : $@convention(thin) (@owned Pointer) -> () {
975975
bb0(%0 : $Pointer):
@@ -1181,7 +1181,7 @@ bb0(%0 : $*Array<X>):
11811181
// CHECK-LABEL: CG of arraysemantics_get_element_address
11821182
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
11831183
// CHECK-NEXT: Con %0.1 Esc: A, Succ:
1184-
// CHECK-NEXT: Val %4 Esc: , Succ: %0.1
1184+
// CHECK-NEXT: Val %4 Esc: %0,%4, Succ: %0.1
11851185
// CHECK-NEXT: End
11861186
sil @arraysemantics_get_element_address : $@convention(thin) (Array<X>) -> () {
11871187
bb0(%0 : $Array<X>):

0 commit comments

Comments
 (0)