Skip to content

Commit 9495af6

Browse files
committed
Fix -escapes-internal-verify for summary graph merging.
Don't merge node properties until after node merging begins, so internal verification can run right before each merge. Rework ConnectionGraph::mergeFrom. Remove an extra loop. Defer mergeAllScheduledNodes until all the source graph's mapped nodes are added so that the graph is always structurally valid before a merge. This is also necessary to avoid EscapeAnalysis assert: (!To->mergeTo), in setPointsToEdge. Enable -escapes-internal-verify to all tests in escape_analysis.sil. Add hand-reduced unit tests in escape_analysis_reduced.sil.
1 parent e50ff0f commit 9495af6

File tree

6 files changed

+400
-51
lines changed

6 files changed

+400
-51
lines changed

include/swift/SILOptimizer/Analysis/EscapeAnalysis.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,6 @@ class EscapeAnalysis : public BottomUpIPAnalysis {
688688
CGNode *FromMergeTarget = From->getMergeTarget();
689689
CGNode *ToMergeTarget = To->getMergeTarget();
690690
if (FromMergeTarget != ToMergeTarget) {
691-
ToMergeTarget->mergeProperties(FromMergeTarget);
692691
FromMergeTarget->mergeTo = ToMergeTarget;
693692
ToMerge.push_back(FromMergeTarget);
694693
}

lib/SILOptimizer/Analysis/EscapeAnalysis.cpp

Lines changed: 47 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,8 @@ void EscapeAnalysis::ConnectionGraph::initializePointsTo(CGNode *initialNode,
558558

559559
CGNode *predNode = pred.getPredNode();
560560
if (predNode->pointsTo) {
561-
assert(predNode->pointsTo->getMergeTarget() == newPointsTo);
561+
assert(predNode->pointsTo->getMergeTarget()
562+
== newPointsTo->getMergeTarget());
562563
return Traversal::Backtrack;
563564
}
564565
predNode->pointsTo = newPointsTo;
@@ -761,6 +762,11 @@ void EscapeAnalysis::ConnectionGraph::mergePointsTo(CGNode *initialNode,
761762
CGNode *newPointsTo) {
762763
CGNode *oldPointsTo = initialNode->pointsTo;
763764
assert(oldPointsTo && "merging content should not initialize any pointsTo");
765+
766+
// newPointsTo may already be scheduled for a merge. Only create new edges to
767+
// unmerged nodes. This may create a temporary pointsTo mismatch in the defer
768+
// web, but Graph verification takes merged nodes into consideration.
769+
newPointsTo = newPointsTo->getMergeTarget();
764770
if (oldPointsTo == newPointsTo)
765771
return;
766772

@@ -870,8 +876,6 @@ CGNode *EscapeAnalysis::ConnectionGraph::createContentNode(
870876
CGNode *newContent =
871877
allocNode(nullptr, NodeType::Content, isInterior, hasReferenceOnly);
872878
initializePointsToEdge(addrNode, newContent);
873-
assert(ToMerge.empty()
874-
&& "Initially setting pointsTo should not require any node merges");
875879
return newContent;
876880
}
877881

@@ -1002,57 +1006,58 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph,
10021006
CGNodeMap &Mapping) {
10031007
// The main point of the merging algorithm is to map each content node in the
10041008
// source graph to a content node in this (destination) graph. This may
1005-
// require to create new nodes or to merge existing nodes in this graph.
1009+
// require creating new nodes or merging existing nodes in this graph.
10061010

10071011
// First step: replicate the points-to edges and the content nodes of the
10081012
// source graph in this graph.
10091013
bool Changed = false;
1010-
bool NodesMerged;
1011-
do {
1012-
NodesMerged = false;
1013-
for (unsigned Idx = 0; Idx < Mapping.getMappedNodes().size(); ++Idx) {
1014-
CGNode *SourceNd = Mapping.getMappedNodes()[Idx];
1015-
CGNode *DestNd = Mapping.get(SourceNd);
1016-
assert(DestNd);
1017-
1018-
if (SourceNd->getEscapeState() >= EscapeState::Global) {
1019-
// We don't need to merge the source subgraph of nodes which have the
1020-
// global escaping state set.
1021-
// Just set global escaping in the caller node and that's it.
1022-
Changed |= DestNd->mergeEscapeState(EscapeState::Global);
1023-
// If DestNd is an interior node, its content still needs to be created.
1024-
if (!DestNd->isInterior())
1025-
continue;
1026-
}
1027-
1028-
CGNode *SourcePT = SourceNd->pointsTo;
1029-
if (!SourcePT)
1014+
for (unsigned Idx = 0; Idx < Mapping.getMappedNodes().size(); ++Idx) {
1015+
CGNode *SourceNd = Mapping.getMappedNodes()[Idx];
1016+
CGNode *DestNd = Mapping.get(SourceNd);
1017+
assert(DestNd);
1018+
1019+
if (SourceNd->getEscapeState() >= EscapeState::Global) {
1020+
// We don't need to merge the source subgraph of nodes which have the
1021+
// global escaping state set.
1022+
// Just set global escaping in the caller node and that's it.
1023+
Changed |= DestNd->mergeEscapeState(EscapeState::Global);
1024+
// If DestNd is an interior node, its content still needs to be created.
1025+
if (!DestNd->isInterior())
10301026
continue;
1027+
}
1028+
1029+
CGNode *SourcePT = SourceNd->pointsTo;
1030+
if (!SourcePT)
1031+
continue;
10311032

1032-
CGNode *DestPT = DestNd->pointsTo;
1033+
CGNode *MappedDestPT = Mapping.get(SourcePT);
1034+
CGNode *DestPT = DestNd->pointsTo;
1035+
if (!MappedDestPT) {
10331036
if (!DestPT) {
10341037
DestPT = createMergedContent(DestNd, SourcePT);
10351038
Changed = true;
10361039
}
1037-
CGNode *MappedDestPT = Mapping.get(SourcePT);
1038-
if (!MappedDestPT) {
1039-
// This is the first time the dest node is seen; just add the mapping.
1040-
Mapping.add(SourcePT, DestPT);
1041-
continue;
1042-
}
1043-
// We already found the destination node through another path.
1044-
assert(Mapping.getMappedNodes().contains(SourcePT));
1045-
if (DestPT == MappedDestPT)
1046-
continue;
1040+
// This is the first time the dest node is seen; just add the mapping.
1041+
Mapping.add(SourcePT, DestPT);
1042+
continue;
1043+
}
1044+
if (DestPT == MappedDestPT)
1045+
continue;
10471046

1048-
// There are two content nodes in this graph which map to the same
1049-
// content node in the source graph -> we have to merge them.
1050-
scheduleToMerge(DestPT, MappedDestPT);
1051-
mergeAllScheduledNodes();
1052-
Changed = true;
1053-
NodesMerged = true;
1047+
// We already found the destination node through another path.
1048+
assert(Mapping.getMappedNodes().contains(SourcePT));
1049+
Changed = true;
1050+
if (!DestPT) {
1051+
initializePointsToEdge(DestNd, MappedDestPT);
1052+
continue;
10541053
}
1055-
} while (NodesMerged);
1054+
// There are two content nodes in this graph which map to the same
1055+
// content node in the source graph -> we have to merge them.
1056+
// Defer merging the nodes until all mapped nodes are created so that the
1057+
// graph is structurally valid before merging.
1058+
scheduleToMerge(DestPT, MappedDestPT);
1059+
}
1060+
mergeAllScheduledNodes();
10561061
Mapping.getMappedNodes().reset(); // Make way for a different worklist.
10571062

10581063
// Second step: add the source graph's defer edges to this graph.

test/SILOptimizer/escape_analysis.sil

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt %s -escapes-dump -o /dev/null | %FileCheck %s
1+
// RUN: %target-sil-opt %s -escapes-dump -escapes-internal-verify -o /dev/null | %FileCheck %s
22

33
// REQUIRES: asserts
44

@@ -594,7 +594,7 @@ sil_global @global_ln : $LinkedNode
594594
// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%1)
595595
// CHECK-NEXT: Con [int] %1 Esc: A, Succ: (%2)
596596
// CHECK-NEXT: Con [ref] %2 Esc: G, Succ:
597-
// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%4.1), %2
597+
// CHECK-NEXT: Val [ref] %4 Esc: G, Succ: (%4.1), %2
598598
// CHECK-NEXT: Con [int] %4.1 Esc: G, Succ: (%4.2)
599599
// CHECK-NEXT: Con [ref] %4.2 Esc: G, Succ:
600600
// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4
@@ -614,7 +614,7 @@ bb0(%0 : $LinkedNode):
614614
// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ:
615615
// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1)
616616
// CHECK-NEXT: Con [ref] %1.1 Esc: G, Succ: %0
617-
// CHECK-NEXT: Val [ref] %4 Esc: , Succ: (%0.1), %0
617+
// CHECK-NEXT: Val [ref] %4 Esc: G, Succ: (%0.1), %0
618618
// CHECK-NEXT: Ret [ref] return Esc: , Succ: %4
619619
// CHECK-NEXT: End
620620
sil @let_escape : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode {
@@ -628,7 +628,7 @@ bb0(%0 : $LinkedNode):
628628

629629
// CHECK-LABEL: CG of return_same
630630
// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%5.1)
631-
// CHECK-NEXT: Val [ref] %3 Esc: , Succ: (%5.1), %5.2
631+
// CHECK-NEXT: Val [ref] %3 Esc: G, Succ: (%5.1), %5.2
632632
// CHECK-NEXT: Val [ref] %5 Esc: , Succ: (%5.1), %0, %3
633633
// CHECK-NEXT: Con [int] %5.1 Esc: G, Succ: (%5.2)
634634
// CHECK-NEXT: Con [ref] %5.2 Esc: G, Succ: (%5.1)

test/SILOptimizer/escape_analysis_dead_store.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt %s -dead-store-elim -enable-sil-verify-all | %FileCheck %s
1+
// RUN: %target-sil-opt %s -dead-store-elim -enable-sil-verify-all -escapes-internal-verify | %FileCheck %s
22

33
sil_stage canonical
44

test/SILOptimizer/escape_analysis_invalidate.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt %s -temp-rvalue-opt -enable-sil-verify-all | %FileCheck %s
1+
// RUN: %target-sil-opt %s -temp-rvalue-opt -enable-sil-verify-all -escapes-internal-verify | %FileCheck %s
22
//
33
// TempRValue iteratively uses EscapeAnalysis and deletes
44
// instructions. Make sure that the connection graph remains valid

0 commit comments

Comments
 (0)