Skip to content

Commit 747f3ff

Browse files
authored
Merge pull request swiftlang#28381 from eeckstein/fix-destoy-hoisting
2 parents ff80326 + bb38711 commit 747f3ff

File tree

4 files changed

+78
-10
lines changed

4 files changed

+78
-10
lines changed

include/swift/SIL/MemoryLifetime.h

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,21 @@ class MemoryLocations {
287287
/// for memory locations. Consider renaming it.
288288
class MemoryDataflow {
289289

290+
/// What kind of terminators can be reached from a block.
291+
enum class ExitReachability : uint8_t {
292+
/// Worst case: the block is part of a cycle which neither reaches a
293+
/// function-exit nor an unreachable-instruction.
294+
InInfiniteLoop,
295+
296+
/// An unreachable-instruction can be reached from the block, but not a
297+
/// function-exit (like "return" or "throw").
298+
ReachesUnreachable,
299+
300+
/// A function-exit can be reached from the block.
301+
/// This is the case for most basic blocks.
302+
ReachesExit
303+
};
304+
290305
public:
291306
using Bits = MemoryLocations::Bits;
292307

@@ -313,11 +328,10 @@ class MemoryDataflow {
313328
/// This flag is only computed if entryReachabilityAnalysis is called.
314329
bool reachableFromEntry = false;
315330

316-
/// True, if any function-exit block can be reached from this block, i.e. is
317-
/// not a block which eventually ends in an unreachable instruction.
331+
/// What kind of terminators can be reached from this block.
318332
///
319-
/// This flag is only computed if exitReachableAnalysis is called.
320-
bool exitReachable = false;
333+
/// This is only computed if exitReachableAnalysis is called.
334+
ExitReachability exitReachability = ExitReachability::InInfiniteLoop;
321335

322336
BlockState(SILBasicBlock *block = nullptr) : block(block) { }
323337

@@ -336,6 +350,14 @@ class MemoryDataflow {
336350
killSet |= loc->subLocations;
337351
}
338352
}
353+
354+
bool exitReachable() const {
355+
return exitReachability == ExitReachability::ReachesExit;
356+
}
357+
358+
bool isInInfiniteLoop() const {
359+
return exitReachability == ExitReachability::InInfiniteLoop;
360+
}
339361
};
340362

341363
private:

lib/SIL/MemoryLifetime.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -435,16 +435,21 @@ void MemoryDataflow::exitReachableAnalysis() {
435435
llvm::SmallVector<BlockState *, 16> workList;
436436
for (BlockState &state : blockStates) {
437437
if (state.block->getTerminator()->isFunctionExiting()) {
438-
state.exitReachable = true;
438+
state.exitReachability = ExitReachability::ReachesExit;
439+
workList.push_back(&state);
440+
} else if (isa<UnreachableInst>(state.block->getTerminator())) {
441+
state.exitReachability = ExitReachability::ReachesUnreachable;
439442
workList.push_back(&state);
440443
}
441444
}
442445
while (!workList.empty()) {
443446
BlockState *state = workList.pop_back_val();
444447
for (SILBasicBlock *pred : state->block->getPredecessorBlocks()) {
445448
BlockState *predState = block2State[pred];
446-
if (!predState->exitReachable) {
447-
predState->exitReachable = true;
449+
if (predState->exitReachability < state->exitReachability) {
450+
// As there are 3 states, each block can be put into the workList 2
451+
// times maximum.
452+
predState->exitReachability = state->exitReachability;
448453
workList.push_back(predState);
449454
}
450455
}
@@ -806,7 +811,7 @@ void MemoryLifetimeVerifier::checkFunction(MemoryDataflow &dataFlow) {
806811
const Bits &nonTrivialLocations = locations.getNonTrivialLocations();
807812
Bits bits(locations.getNumLocations());
808813
for (BlockState &st : dataFlow) {
809-
if (!st.reachableFromEntry || !st.exitReachable)
814+
if (!st.reachableFromEntry || !st.exitReachable())
810815
continue;
811816

812817
// Check all instructions in the block.

lib/SILOptimizer/Transforms/DestroyHoisting.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,16 @@ void DestroyHoisting::expandStores(MemoryDataflow &dataFlow) {
208208
// Initialize the dataflow for moving destroys up the control flow.
209209
void DestroyHoisting::initDataflow(MemoryDataflow &dataFlow) {
210210
for (BlockState &st : dataFlow) {
211-
st.entrySet.set();
212211
st.genSet.reset();
213212
st.killSet.reset();
213+
if (st.isInInfiniteLoop()) {
214+
// Ignore blocks which are in an infinite loop and prevent any destroy
215+
// hoisting across such block borders.
216+
st.entrySet.reset();
217+
st.exitSet.reset();
218+
continue;
219+
}
220+
st.entrySet.set();
214221
if (isa<UnreachableInst>(st.block->getTerminator())) {
215222
if (canIgnoreUnreachableBlock(st.block, dataFlow)) {
216223
st.exitSet.set();
@@ -284,7 +291,7 @@ bool DestroyHoisting::canIgnoreUnreachableBlock(SILBasicBlock *block,
284291
SILBasicBlock *singlePred = block->getSinglePredecessorBlock();
285292
if (!singlePred)
286293
return false;
287-
if (!dataFlow.getState(singlePred)->exitReachable)
294+
if (!dataFlow.getState(singlePred)->exitReachable())
288295
return false;
289296

290297
// Check if none of the locations are touched in the unreachable-block.
@@ -374,6 +381,10 @@ void DestroyHoisting::moveDestroys(MemoryDataflow &dataFlow) {
374381
if (isa<UnreachableInst>(block->getTerminator()) && state.exitSet.any())
375382
continue;
376383

384+
// Ignore blocks which are in an infinite loop.
385+
if (state.isInInfiniteLoop())
386+
continue;
387+
377388
// Do the inner-block processing.
378389
activeDestroys = state.exitSet;
379390
moveDestroysInBlock(block, activeDestroys, toRemove);

test/SILOptimizer/destroy_hoisting.sil

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,33 @@ bb1:
248248
%r = tuple ()
249249
return %r : $()
250250
}
251+
252+
// CHECK-LABEL: sil [ossa] @test_simple_infinite_loop
253+
// CHECK-NOT: destroy_addr
254+
// CHECK: } // end sil function 'test_simple_infinite_loop'
255+
sil [ossa] @test_simple_infinite_loop : $@convention(thin) (@in_guaranteed S) -> () {
256+
bb0(%0 : $*S):
257+
br bb1
258+
bb1:
259+
br bb1
260+
}
261+
262+
// CHECK-LABEL: sil [ossa] @test_infinite_loop
263+
// CHECK-NOT: destroy_addr
264+
// CHECK: bb3:
265+
// CHECK-NEXT: destroy_addr %1
266+
// CHECK-NOT: destroy_addr
267+
// CHECK: } // end sil function 'test_infinite_loop'
268+
sil [ossa] @test_infinite_loop : $@convention(thin) (@in_guaranteed S, @in S) -> () {
269+
bb0(%0 : $*S, %1 : $*S):
270+
cond_br undef, bb1, bb2
271+
bb1:
272+
br bb1
273+
bb2:
274+
br bb3
275+
bb3:
276+
destroy_addr %1 : $*S
277+
%r = tuple ()
278+
return %r : $()
279+
}
280+

0 commit comments

Comments
 (0)