Skip to content

Commit e4af124

Browse files
authored
Merge pull request swiftlang#27138 from eeckstein/fix-stacknesting
StackNesting: fix a corner case crash related to unreachable blocks
2 parents 7882a97 + 8cdeb45 commit e4af124

File tree

3 files changed

+88
-39
lines changed

3 files changed

+88
-39
lines changed

include/swift/SILOptimizer/Utils/StackNesting.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,13 @@ class StackNesting {
155155
return bitNumberForAlloc(AllocInst);
156156
}
157157

158+
/// Insert deallocations at block boundaries.
159+
Changes insertDeallocsAtBlockBoundaries();
160+
158161
/// Modifies the SIL to end up with a correct stack nesting.
159162
///
160163
/// Returns the status of what changes were made.
161-
Changes adaptDeallocs();
164+
bool adaptDeallocs();
162165
};
163166

164167
} // end namespace swift

lib/SILOptimizer/Utils/StackNesting.cpp

Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ void StackNesting::setup(SILFunction *F) {
2424

2525
// We use pointers to BlockInfo structs. Therefore it's important that the
2626
// BlockInfos vector is never re-allocated.
27+
BlockInfos.clear();
2728
BlockInfos.reserve(F->size());
2829

2930
// Start with the function entry block and add blocks while walking down along
@@ -231,36 +232,26 @@ bool StackNesting::insertDeallocs(const BitVector &AliveBefore,
231232
return changesMade;
232233
}
233234

234-
StackNesting::Changes StackNesting::adaptDeallocs() {
235-
236-
bool InstChanged = false;
237-
bool CFGChanged = false;
238-
BitVector Bits(StackLocs.size());
239-
240-
// Visit all blocks. Actually the order doesn't matter, but let's to it in
241-
// the same order as in solve().
235+
// Insert deallocations at block boundaries.
236+
// This can be necessary for unreachable blocks. Example:
237+
//
238+
// %1 = alloc_stack
239+
// %2 = alloc_stack
240+
// cond_br %c, bb2, bb3
241+
// bb2: <--- need to insert a dealloc_stack %2 at the begin of bb2
242+
// dealloc_stack %1
243+
// unreachable
244+
// bb3:
245+
// dealloc_stack %2
246+
// dealloc_stack %1
247+
StackNesting::Changes StackNesting::insertDeallocsAtBlockBoundaries() {
248+
Changes changes = Changes::None;
242249
for (const BlockInfo &BI : reversed(BlockInfos)) {
243-
// Collect the alive-bits (at the block exit) from the successor blocks.
244-
Bits = BI.AliveStackLocsAtExit;
245-
246-
// Insert deallocations at block boundaries.
247-
// This can be necessary for unreachable blocks. Example:
248-
//
249-
// %1 = alloc_stack
250-
// %2 = alloc_stack
251-
// cond_br %c, bb2, bb3
252-
// bb2: <--- need to insert a dealloc_stack %2 at the begin of bb2
253-
// dealloc_stack %1
254-
// unreachable
255-
// bb3:
256-
// dealloc_stack %2
257-
// dealloc_stack %1
258-
//
259250
for (unsigned SuccIdx = 0, NumSuccs = BI.Successors.size();
260-
SuccIdx < NumSuccs; ++ SuccIdx) {
261-
BlockInfo *SuccBI = BI.Successors[SuccIdx];
251+
SuccIdx < NumSuccs; ++SuccIdx) {
262252

263-
if (SuccBI->AliveStackLocsAtEntry == Bits)
253+
BlockInfo *SuccBI = BI.Successors[SuccIdx];
254+
if (SuccBI->AliveStackLocsAtEntry == BI.AliveStackLocsAtExit)
264255
continue;
265256

266257
// Insert deallocations for all locations which are alive at the end of
@@ -271,11 +262,26 @@ StackNesting::Changes StackNesting::adaptDeallocs() {
271262
// block, we have to insert a new block where we can add the
272263
// deallocations.
273264
InsertionBlock = splitEdge(BI.Block->getTerminator(), SuccIdx);
274-
CFGChanged = true;
265+
changes = Changes::CFG;
266+
}
267+
if (insertDeallocs(BI.AliveStackLocsAtExit, SuccBI->AliveStackLocsAtEntry,
268+
&InsertionBlock->front(), None)) {
269+
if (changes == Changes::None)
270+
changes = Changes::Instructions;
275271
}
276-
InstChanged |= insertDeallocs(Bits, SuccBI->AliveStackLocsAtEntry,
277-
&InsertionBlock->front(), None);
278272
}
273+
}
274+
return changes;
275+
}
276+
277+
bool StackNesting::adaptDeallocs() {
278+
bool InstChanged = false;
279+
BitVector Bits(StackLocs.size());
280+
281+
// Visit all blocks. Actually the order doesn't matter, but let's to it in
282+
// the same order as in solve().
283+
for (const BlockInfo &BI : reversed(BlockInfos)) {
284+
Bits = BI.AliveStackLocsAtExit;
279285

280286
// Insert/remove deallocations inside blocks.
281287
for (SILInstruction *StackInst : reversed(BI.StackInsts)) {
@@ -310,19 +316,29 @@ StackNesting::Changes StackNesting::adaptDeallocs() {
310316
}
311317
assert(Bits == BI.AliveStackLocsAtEntry && "dataflow didn't converge");
312318
}
313-
if (CFGChanged)
314-
return Changes::CFG;
315-
if (InstChanged)
316-
return Changes::Instructions;
317-
return Changes::None;
319+
return InstChanged;
318320
}
319321

320322
StackNesting::Changes StackNesting::correctStackNesting(SILFunction *F) {
321323
setup(F);
322-
if (solve()) {
323-
return adaptDeallocs();
324+
if (!solve())
325+
return Changes::None;
326+
327+
// Insert deallocs at block boundaries. This might be necessary in CFG sub
328+
// graphs which don't reach a function exit, but only an unreachable.
329+
Changes changes = insertDeallocsAtBlockBoundaries();
330+
if (changes != Changes::None) {
331+
// Those inserted deallocs make it necessary to re-compute the analysis.
332+
setup(F);
333+
solve();
334+
}
335+
// Do the real work: extend lifetimes by moving deallocs.
336+
if (adaptDeallocs()) {
337+
if (changes == Changes::None)
338+
changes = Changes::Instructions;
324339
}
325-
return Changes::None;
340+
341+
return changes;
326342
}
327343

328344
void StackNesting::dump() const {

test/SILOptimizer/allocbox_to_stack.sil

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,9 +1086,39 @@ bb2:
10861086
bb3:
10871087
strong_release %1 : ${ var Int }
10881088
unreachable
1089+
}
1090+
1091+
1092+
// Verify that we don't crash on this one:
1093+
//
1094+
// CHECK-LABEL: sil @unreachable_with_two_predecessors
1095+
sil @unreachable_with_two_predecessors : $@convention(thin) (Int) -> () {
1096+
bb0(%0 : $Int):
1097+
cond_br undef, bb1, bb4
10891098

1099+
bb1:
1100+
%4 = alloc_stack $Bool
1101+
%1 = alloc_box ${ var Int }
1102+
%1a = project_box %1 : ${ var Int }, 0
1103+
store %0 to %1a : $*Int
1104+
cond_br undef, bb3, bb2
1105+
1106+
bb2:
1107+
dealloc_stack %4 : $*Bool
1108+
br bb4
1109+
1110+
bb3:
1111+
%9 = load %1a : $*Int
1112+
strong_release %1 : ${ var Int }
1113+
dealloc_stack %4 : $*Bool
1114+
%13 = tuple ()
1115+
return %13 : $()
1116+
1117+
bb4:
1118+
unreachable
10901119
}
10911120

1121+
10921122
// Verify the case in which a box used by a partial apply which is then used by a try_apply.
10931123
//
10941124
// <rdar://problem/42815224> Swift CI: 2. Swift Source Compatibility Suite (master): Kitura project fails with compiler crash

0 commit comments

Comments
 (0)