@@ -110,18 +110,10 @@ EscapeAnalysis::findCachedPointerKind(SILType Ty, const SILFunction &F) const {
110
110
return pointerKind;
111
111
}
112
112
113
- static bool isExtractOfArrayUninitializedPointer (TupleExtractInst *TEI) {
114
- if (auto apply = dyn_cast<ApplyInst>(TEI->getOperand ()))
115
- if (ArraySemanticsCall (apply, " array.uninitialized" , false ))
116
- return true ;
117
-
118
- return false ;
119
- }
120
-
121
113
// If EscapeAnalysis should consider the given value to be a derived address or
122
114
// pointer based on one of its address or pointer operands, then return that
123
115
// operand value. Otherwise, return an invalid value.
124
- SILValue EscapeAnalysis::getPointerBase (SILValue value) const {
116
+ SILValue EscapeAnalysis::getPointerBase (SILValue value) {
125
117
switch (value->getKind ()) {
126
118
case ValueKind::IndexAddrInst:
127
119
case ValueKind::IndexRawPointerInst:
@@ -156,10 +148,8 @@ SILValue EscapeAnalysis::getPointerBase(SILValue value) const {
156
148
case ValueKind::TupleExtractInst: {
157
149
auto *TEI = cast<TupleExtractInst>(value);
158
150
// Special handling for extracting the pointer-result from an
159
- // array construction. We handle this like a ref_element_addr
160
- // rather than a projection. See the handling of tuple_extract
161
- // in analyzeInstruction().
162
- if (isExtractOfArrayUninitializedPointer (TEI))
151
+ // array construction. See createArrayUninitializedSubgraph.
152
+ if (canOptimizeArrayUninitializedResult (TEI))
163
153
return SILValue ();
164
154
return TEI->getOperand ();
165
155
}
@@ -188,7 +178,7 @@ SILValue EscapeAnalysis::getPointerBase(SILValue value) const {
188
178
// Recursively find the given value's pointer base. If the value cannot be
189
179
// represented in EscapeAnalysis as one of its operands, then return the same
190
180
// value.
191
- SILValue EscapeAnalysis::getPointerRoot (SILValue value) const {
181
+ SILValue EscapeAnalysis::getPointerRoot (SILValue value) {
192
182
while (true ) {
193
183
if (SILValue v2 = getPointerBase (value))
194
184
value = v2;
@@ -1721,28 +1711,6 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo,
1721
1711
<< FInfo->Graph .F ->getName () << ' \n ' );
1722
1712
}
1723
1713
1724
- // / Returns the tuple extract for the first two fields if all uses of \p I are
1725
- // / tuple_extract instructions.
1726
- static std::pair<TupleExtractInst *, TupleExtractInst *>
1727
- onlyUsedInTupleExtract (SILValue V) {
1728
- TupleExtractInst *field0 = nullptr ;
1729
- TupleExtractInst *field1 = nullptr ;
1730
- for (Operand *Use : getNonDebugUses (V)) {
1731
- if (auto *TEI = dyn_cast<TupleExtractInst>(Use->getUser ())) {
1732
- if (TEI->getFieldNo () == 0 ) {
1733
- field0 = TEI;
1734
- continue ;
1735
- }
1736
- if (TEI->getFieldNo () == 1 ) {
1737
- field1 = TEI;
1738
- continue ;
1739
- }
1740
- }
1741
- return std::make_pair (nullptr , nullptr );
1742
- }
1743
- return std::make_pair (field0, field1);
1744
- }
1745
-
1746
1714
bool EscapeAnalysis::buildConnectionGraphForCallees (
1747
1715
SILInstruction *Caller, CalleeList Callees, FunctionInfo *FInfo,
1748
1716
FunctionOrder &BottomUpOrder, int RecursionDepth) {
@@ -1799,38 +1767,79 @@ bool EscapeAnalysis::buildConnectionGraphForDestructor(
1799
1767
RecursionDepth);
1800
1768
}
1801
1769
1802
- // Handle array.uninitialized
1803
- bool EscapeAnalysis::createArrayUninitializedSubgraph (
1804
- FullApplySite apply, ConnectionGraph *conGraph) {
1770
+ EscapeAnalysis::ArrayUninitCall
1771
+ EscapeAnalysis::canOptimizeArrayUninitializedCall (ApplyInst *ai,
1772
+ ConnectionGraph *conGraph) {
1773
+ ArrayUninitCall call;
1774
+ // This must be an exact match so we don't accidentally optimize
1775
+ // "array.uninitialized_intrinsic".
1776
+ if (!ArraySemanticsCall (ai, " array.uninitialized" , false ))
1777
+ return call;
1805
1778
1806
1779
// Check if the result is used in the usual way: extracting the
1807
1780
// array and the element pointer with tuple_extract.
1808
- TupleExtractInst *arrayStruct;
1809
- TupleExtractInst *arrayElementPtr;
1810
- std::tie (arrayStruct, arrayElementPtr) =
1811
- onlyUsedInTupleExtract (cast<ApplyInst>(apply.getInstruction ()));
1812
- if (!arrayStruct || !arrayElementPtr)
1781
+ for (Operand *use : getNonDebugUses (ai)) {
1782
+ if (auto *tei = dyn_cast<TupleExtractInst>(use->getUser ())) {
1783
+ if (tei->getFieldNo () == 0 ) {
1784
+ call.arrayStruct = tei;
1785
+ continue ;
1786
+ }
1787
+ if (tei->getFieldNo () == 1 ) {
1788
+ call.arrayElementPtr = tei;
1789
+ continue ;
1790
+ }
1791
+ }
1792
+ // If there are any other uses, such as a release_value, erase the previous
1793
+ // call info and bail out.
1794
+ call.arrayStruct = nullptr ;
1795
+ call.arrayElementPtr = nullptr ;
1796
+ break ;
1797
+ }
1798
+ // An "array.uninitialized" call may have a first argument which is the
1799
+ // allocated array buffer. Make sure the call's argument is recognized by
1800
+ // EscapeAnalysis as a pointer, otherwise createArrayUninitializedSubgraph
1801
+ // won't be able to map the result nodes onto it. There is a variant of
1802
+ // @_semantics("array.uninitialized") that does not take the storage as input,
1803
+ // so it will effectively bail out here.
1804
+ if (isPointer (ai->getArgument (0 )))
1805
+ call.arrayStorageRef = ai->getArgument (0 );
1806
+ return call;
1807
+ }
1808
+
1809
+ bool EscapeAnalysis::canOptimizeArrayUninitializedResult (
1810
+ TupleExtractInst *tei) {
1811
+ ApplyInst *ai = dyn_cast<ApplyInst>(tei->getOperand ());
1812
+ if (!ai)
1813
1813
return false ;
1814
1814
1815
- // array.uninitialized may have a first argument which is the
1816
- // allocated array buffer. The call is like a struct(buffer)
1817
- // instruction.
1818
- CGNode *arrayRefNode = conGraph->getNode (apply.getArgument (0 ));
1819
- if (!arrayRefNode)
1820
- return false ;
1815
+ auto *conGraph = getConnectionGraph (ai->getFunction ());
1816
+ return canOptimizeArrayUninitializedCall (ai, conGraph).isValid ();
1817
+ }
1821
1818
1822
- CGNode *arrayStructNode = conGraph->getNode (arrayStruct);
1819
+ // Handle @_semantics("array.uninitialized")
1820
+ //
1821
+ // This call is analagous to a 'struct(storageRef)' instruction--we want a defer
1822
+ // edge from the returned Array struct to the storage Reference that it
1823
+ // contains.
1824
+ //
1825
+ // The returned unsafe pointer is handled simply by mapping the pointer value
1826
+ // onto the object node that the storage argument points to.
1827
+ void EscapeAnalysis::createArrayUninitializedSubgraph (
1828
+ ArrayUninitCall call, ConnectionGraph *conGraph) {
1829
+ CGNode *arrayStructNode = conGraph->getNode (call.arrayStruct );
1823
1830
assert (arrayStructNode && " Array struct must have a node" );
1824
1831
1825
- CGNode *arrayObjNode = conGraph->getValueContent (apply.getArgument (0 ));
1832
+ CGNode *arrayRefNode = conGraph->getNode (call.arrayStorageRef );
1833
+ assert (arrayRefNode && " canOptimizeArrayUninitializedCall checks isPointer" );
1834
+ // If the arrayRefNode != null then arrayObjNode must be valid.
1835
+ CGNode *arrayObjNode = conGraph->getValueContent (call.arrayStorageRef );
1826
1836
1827
1837
// The reference argument is effectively stored inside the returned
1828
- // array struct.
1838
+ // array struct. This is like struct(arrayRefNode).
1829
1839
conGraph->defer (arrayStructNode, arrayRefNode);
1830
1840
1831
1841
// Map the returned element pointer to the array object's field pointer.
1832
- conGraph->setNode (arrayElementPtr, arrayObjNode);
1833
- return true ;
1842
+ conGraph->setNode (call.arrayElementPtr , arrayObjNode);
1834
1843
}
1835
1844
1836
1845
void EscapeAnalysis::analyzeInstruction (SILInstruction *I,
@@ -1852,10 +1861,15 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
1852
1861
case ArrayCallKind::kMakeMutable :
1853
1862
// These array semantics calls do not capture anything.
1854
1863
return ;
1855
- case ArrayCallKind::kArrayUninitialized :
1856
- if (createArrayUninitializedSubgraph (FAS, ConGraph))
1864
+ case ArrayCallKind::kArrayUninitialized : {
1865
+ ArrayUninitCall call = canOptimizeArrayUninitializedCall (
1866
+ cast<ApplyInst>(FAS.getInstruction ()), ConGraph);
1867
+ if (call.isValid ()) {
1868
+ createArrayUninitializedSubgraph (call, ConGraph);
1857
1869
return ;
1870
+ }
1858
1871
break ;
1872
+ }
1859
1873
case ArrayCallKind::kGetElement :
1860
1874
if (CGNode *ArrayObjNode = ConGraph->getValueContent (ASC.getSelf ())) {
1861
1875
CGNode *LoadedElement = nullptr ;
@@ -2204,13 +2218,9 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
2204
2218
case SILInstructionKind::TupleExtractInst: {
2205
2219
// This is a tuple_extract which extracts the second result of an
2206
2220
// array.uninitialized call (otherwise getPointerBase should have already
2207
- // looked through it). The first result is the array itself. The second
2208
- // result (which is a pointer to the array elements) must be the content
2209
- // node of the first result. It's just like a ref_element_addr
2210
- // instruction. It is mapped to a node when processing
2211
- // array.uninitialized.
2221
+ // looked through it).
2212
2222
auto *TEI = cast<TupleExtractInst>(I);
2213
- assert (isExtractOfArrayUninitializedPointer (TEI)
2223
+ assert (canOptimizeArrayUninitializedResult (TEI)
2214
2224
&& " tuple_extract should be handled as projection" );
2215
2225
return ;
2216
2226
}
0 commit comments