Skip to content

Commit 45f3cf9

Browse files
committed
[OwnedLifetimeCan] Persist [dead_end].
When creating `destroy_value`s, create them with the `dead_end` flag if all subsequent destroys (those from which it is notionally being hoisted) have the flag.
1 parent 534ea14 commit 45f3cf9

File tree

2 files changed

+251
-7
lines changed

2 files changed

+251
-7
lines changed

lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -943,7 +943,7 @@ void CanonicalizeOSSALifetime::findExtendedBoundary(
943943
/// record it as a final consume.
944944
static void
945945
insertDestroyBeforeInstruction(SILInstruction *nextInstruction,
946-
SILValue currentDef,
946+
SILValue currentDef, IsDeadEnd_t isDeadEnd,
947947
CanonicalOSSAConsumeInfo &consumes,
948948
SmallVectorImpl<DestroyValueInst *> &destroys,
949949
InstModCallbacks &callbacks) {
@@ -974,13 +974,63 @@ insertDestroyBeforeInstruction(SILInstruction *nextInstruction,
974974
SILBuilderWithScope builder(nextInstruction);
975975
auto loc =
976976
RegularLocation::getAutoGeneratedLocation(nextInstruction->getLoc());
977-
auto *dvi = builder.createDestroyValue(loc, currentDef);
977+
auto *dvi =
978+
builder.createDestroyValue(loc, currentDef, DontPoisonRefs, isDeadEnd);
978979
callbacks.createdNewInst(dvi);
979980
consumes.recordFinalConsume(dvi);
980981
++NumDestroysGenerated;
981982
destroys.push_back(dvi);
982983
}
983984

985+
/// Whether a destroy created at \p inst should be marked [dead_end].
986+
///
987+
/// It should be if
988+
/// (1) \p inst is itself in a dead-end region
989+
/// (2) all destroys after \p inst are [dead_end]
990+
static IsDeadEnd_t
991+
isDeadEndDestroy(SILInstruction *inst,
992+
SmallPtrSetVector<SILInstruction *, 8> const &destroys,
993+
BasicBlockSet &semanticDestroysBlocks,
994+
DeadEndBlocks *deadEnds) {
995+
auto *parent = inst->getParent();
996+
if (!deadEnds->isDeadEnd(parent)) {
997+
// Only destroys in dead-ends can be non-meaningful (aka "dead end").
998+
return IsntDeadEnd;
999+
}
1000+
if (semanticDestroysBlocks.contains(parent)) {
1001+
// `parent` has a semantic destroy somewhere. Is it after `inst`?
1002+
for (auto *i = inst; i; i = i->getNextInstruction()) {
1003+
if (!destroys.contains(i)) {
1004+
continue;
1005+
}
1006+
auto *dvi = cast<DestroyValueInst>(i);
1007+
if (!dvi->isDeadEnd()) {
1008+
// Some subsequent destroy within `parent` was meaningful, so one
1009+
// created at `inst` must be too.
1010+
return IsntDeadEnd;
1011+
}
1012+
}
1013+
}
1014+
// Walk the portion of the dead-end region after `parent` to check that all
1015+
// destroys are non-meaningful.
1016+
BasicBlockWorklist worklist(inst->getFunction());
1017+
for (auto *successor : parent->getSuccessorBlocks()) {
1018+
worklist.push(successor);
1019+
}
1020+
while (auto *block = worklist.pop()) {
1021+
assert(deadEnds->isDeadEnd(block));
1022+
if (semanticDestroysBlocks.contains(block)) {
1023+
// Some subsequent destroy was meaningful, so one created at `inst`
1024+
// must be too.
1025+
return IsntDeadEnd;
1026+
}
1027+
for (auto *successor : block->getSuccessorBlocks()) {
1028+
worklist.pushIfNotVisited(successor);
1029+
}
1030+
}
1031+
return IsDeadEnd;
1032+
}
1033+
9841034
/// Inserts destroys along the boundary where needed and records all final
9851035
/// consuming uses.
9861036
///
@@ -992,6 +1042,18 @@ insertDestroyBeforeInstruction(SILInstruction *nextInstruction,
9921042
void CanonicalizeOSSALifetime::insertDestroysOnBoundary(
9931043
PrunedLivenessBoundary const &boundary,
9941044
SmallVectorImpl<DestroyValueInst *> &newDestroys) {
1045+
BasicBlockSet semanticDestroyBlocks(getCurrentDef()->getFunction());
1046+
for (auto *destroy : destroys) {
1047+
if (!cast<DestroyValueInst>(destroy)->isDeadEnd()) {
1048+
semanticDestroyBlocks.insert(destroy->getParent());
1049+
}
1050+
}
1051+
auto isDeadEnd = [&semanticDestroyBlocks,
1052+
this](SILInstruction *inst) -> IsDeadEnd_t {
1053+
return isDeadEndDestroy(
1054+
inst, destroys, semanticDestroyBlocks,
1055+
deadEndBlocksAnalysis->get(getCurrentDef()->getFunction()));
1056+
};
9951057
BasicBlockSet seenMergePoints(getCurrentDef()->getFunction());
9961058
for (auto *instruction : boundary.lastUsers) {
9971059
if (destroys.contains(instruction)) {
@@ -1013,7 +1075,8 @@ void CanonicalizeOSSALifetime::insertDestroysOnBoundary(
10131075
}
10141076
auto *insertionPoint = &*successor->begin();
10151077
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(),
1016-
consumes, newDestroys, getCallbacks());
1078+
isDeadEnd(insertionPoint), consumes,
1079+
newDestroys, getCallbacks());
10171080
LLVM_DEBUG(llvm::dbgs() << " Destroy after terminator "
10181081
<< *instruction << " at beginning of ";
10191082
successor->printID(llvm::dbgs(), false);
@@ -1022,7 +1085,8 @@ void CanonicalizeOSSALifetime::insertDestroysOnBoundary(
10221085
continue;
10231086
}
10241087
auto *insertionPoint = instruction->getNextInstruction();
1025-
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(), consumes,
1088+
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(),
1089+
isDeadEnd(insertionPoint), consumes,
10261090
newDestroys, getCallbacks());
10271091
LLVM_DEBUG(llvm::dbgs()
10281092
<< " Destroy at last use " << insertionPoint << "\n");
@@ -1031,22 +1095,25 @@ void CanonicalizeOSSALifetime::insertDestroysOnBoundary(
10311095
}
10321096
for (auto *edgeDestination : boundary.boundaryEdges) {
10331097
auto *insertionPoint = &*edgeDestination->begin();
1034-
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(), consumes,
1098+
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(),
1099+
isDeadEnd(insertionPoint), consumes,
10351100
newDestroys, getCallbacks());
10361101
LLVM_DEBUG(llvm::dbgs() << " Destroy on edge " << edgeDestination << "\n");
10371102
}
10381103
for (auto *def : boundary.deadDefs) {
10391104
if (auto *arg = dyn_cast<SILArgument>(def)) {
10401105
auto *insertionPoint = &*arg->getParent()->begin();
1041-
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(), consumes,
1106+
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(),
1107+
isDeadEnd(insertionPoint), consumes,
10421108
newDestroys, getCallbacks());
10431109
LLVM_DEBUG(llvm::dbgs()
10441110
<< " Destroy after dead def arg " << arg << "\n");
10451111
} else {
10461112
auto *instruction = cast<SILInstruction>(def);
10471113
auto *insertionPoint = instruction->getNextInstruction();
10481114
assert(insertionPoint && "def instruction was a terminator?!");
1049-
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(), consumes,
1115+
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(),
1116+
isDeadEnd(insertionPoint), consumes,
10501117
newDestroys, getCallbacks());
10511118
LLVM_DEBUG(llvm::dbgs()
10521119
<< " Destroy after dead def inst " << instruction << "\n");

test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,183 @@ destroy:
324324
return %retval : $()
325325
}
326326

327+
// CHECK-LABEL: begin running test {{.*}} on preserve_dead_end_1
328+
// CHECK-LABEL: sil [ossa] @preserve_dead_end_1 : {{.*}} {
329+
// CHECK: destroy_value [dead_end]
330+
// CHECK-LABEL: } // end sil function 'preserve_dead_end_1'
331+
// CHECK-LABEL: end running test {{.*}} on preserve_dead_end_1
332+
sil [ossa] @preserve_dead_end_1 : $@convention(thin) () -> () {
333+
entry:
334+
specify_test "canonicalize_ossa_lifetime true false true %c"
335+
336+
%get = function_ref @getOwned : $@convention(thin) () -> @owned C
337+
%barrier = function_ref @barrier : $@convention(thin) () -> ()
338+
339+
%c = apply %get() : $@convention(thin) () -> @owned C
340+
apply %barrier() : $@convention(thin) () -> ()
341+
destroy_value [dead_end] %c : $C
342+
unreachable
343+
}
344+
345+
// CHECK-LABEL: begin running test {{.*}} on preserve_dead_end_2
346+
// CHECK-LABEL: sil [ossa] @preserve_dead_end_2 : {{.*}} {
347+
// CHECK: destroy_value [dead_end]
348+
// CHECK-LABEL: } // end sil function 'preserve_dead_end_2'
349+
// CHECK-LABEL: end running test {{.*}} on preserve_dead_end_2
350+
sil [ossa] @preserve_dead_end_2 : $@convention(thin) () -> () {
351+
entry:
352+
specify_test "canonicalize_ossa_lifetime true false true %c"
353+
354+
%get = function_ref @getOwned : $@convention(thin) () -> @owned C
355+
%barrier = function_ref @barrier : $@convention(thin) () -> ()
356+
%borrow = function_ref @borrowC : $@convention(thin) (@guaranteed C) -> ()
357+
358+
%c = apply %get() : $@convention(thin) () -> @owned C
359+
%c2 = copy_value %c : $C
360+
destroy_value %c : $C
361+
apply %borrow(%c2) : $@convention(thin) (@guaranteed C) -> ()
362+
363+
apply %barrier() : $@convention(thin) () -> ()
364+
destroy_value [dead_end] %c2 : $C
365+
unreachable
366+
}
367+
368+
// CHECK-LABEL: begin running test {{.*}} on preserve_dead_end_3
369+
// CHECK-LABEL: sil [ossa] @preserve_dead_end_3 : {{.*}} {
370+
// CHECK: destroy_value [dead_end]
371+
// CHECK-LABEL: } // end sil function 'preserve_dead_end_3'
372+
// CHECK-LABEL: end running test {{.*}} on preserve_dead_end_3
373+
sil [ossa] @preserve_dead_end_3 : $@convention(thin) () -> () {
374+
entry:
375+
specify_test "canonicalize_ossa_lifetime true false true %c"
376+
377+
%get = function_ref @getOwned : $@convention(thin) () -> @owned C
378+
%barrier = function_ref @barrier : $@convention(thin) () -> ()
379+
%borrow = function_ref @borrowC : $@convention(thin) (@guaranteed C) -> ()
380+
381+
%c = apply %get() : $@convention(thin) () -> @owned C
382+
%c2 = copy_value %c : $C
383+
destroy_value %c : $C
384+
apply %borrow(%c2) : $@convention(thin) (@guaranteed C) -> ()
385+
br die
386+
387+
die:
388+
apply %barrier() : $@convention(thin) () -> ()
389+
destroy_value [dead_end] %c2 : $C
390+
unreachable
391+
}
392+
393+
// CHECK-LABEL: begin running test {{.*}} on preserve_dead_end_4
394+
// CHECK-LABEL: sil [ossa] @preserve_dead_end_4 : {{.*}} {
395+
// CHECK-NOT: destroy_value [dead_end]
396+
// CHECK-LABEL: } // end sil function 'preserve_dead_end_4'
397+
// CHECK-LABEL: end running test {{.*}} on preserve_dead_end_4
398+
sil [ossa] @preserve_dead_end_4 : $@convention(thin) () -> () {
399+
entry:
400+
specify_test "canonicalize_ossa_lifetime true false true %c"
401+
402+
%get = function_ref @getOwned : $@convention(thin) () -> @owned C
403+
%barrier = function_ref @barrier : $@convention(thin) () -> ()
404+
%borrow = function_ref @borrowC : $@convention(thin) (@guaranteed C) -> ()
405+
406+
%c = apply %get() : $@convention(thin) () -> @owned C
407+
%c2 = copy_value %c : $C
408+
destroy_value %c : $C
409+
apply %borrow(%c2) : $@convention(thin) (@guaranteed C) -> ()
410+
cond_br undef, exit, die
411+
412+
exit:
413+
destroy_value %c2 : $C
414+
%retval = tuple ()
415+
return %retval : $()
416+
417+
die:
418+
apply %barrier() : $@convention(thin) () -> ()
419+
destroy_value [dead_end] %c2 : $C
420+
unreachable
421+
}
422+
423+
// CHECK-LABEL: begin running test {{.*}} on preserve_dead_end_5
424+
// CHECK-LABEL: sil [ossa] @preserve_dead_end_5 : {{.*}} {
425+
// CHECK-NOT: destroy_value [dead_end]
426+
// CHECK-LABEL: } // end sil function 'preserve_dead_end_5'
427+
// CHECK-LABEL: end running test {{.*}} on preserve_dead_end_5
428+
sil [ossa] @preserve_dead_end_5 : $@convention(thin) () -> () {
429+
entry:
430+
specify_test "canonicalize_ossa_lifetime true false true %c"
431+
432+
%get = function_ref @getOwned : $@convention(thin) () -> @owned C
433+
%barrier = function_ref @barrier : $@convention(thin) () -> ()
434+
%borrow = function_ref @borrowC : $@convention(thin) (@guaranteed C) -> ()
435+
436+
%c = apply %get() : $@convention(thin) () -> @owned C
437+
%c5 = copy_value %c : $C
438+
apply %borrow(%c5) : $@convention(thin) (@guaranteed C) -> ()
439+
440+
apply %barrier() : $@convention(thin) () -> ()
441+
destroy_value %c : $C
442+
destroy_value [dead_end] %c5 : $C
443+
unreachable
444+
}
445+
446+
// CHECK-LABEL: begin running test {{.*}} on preserve_dead_end_6
447+
// CHECK-LABEL: sil [ossa] @preserve_dead_end_6 : {{.*}} {
448+
// CHECK: destroy_value [dead_end]
449+
// CHECK-LABEL: } // end sil function 'preserve_dead_end_6'
450+
// CHECK-LABEL: end running test {{.*}} on preserve_dead_end_6
451+
sil [ossa] @preserve_dead_end_6 : $@convention(thin) () -> () {
452+
entry:
453+
specify_test "canonicalize_ossa_lifetime true false true %c"
454+
455+
%get = function_ref @getOwned : $@convention(thin) () -> @owned C
456+
%barrier = function_ref @barrier : $@convention(thin) () -> ()
457+
%borrow = function_ref @borrowC : $@convention(thin) (@guaranteed C) -> ()
458+
459+
%c = apply %get() : $@convention(thin) () -> @owned C
460+
%c2 = copy_value %c : $C
461+
destroy_value %c : $C
462+
apply %borrow(%c2) : $@convention(thin) (@guaranteed C) -> ()
463+
cond_br undef, die1, die2
464+
465+
die1:
466+
destroy_value [dead_end] %c2 : $C
467+
unreachable
468+
469+
die2:
470+
apply %barrier() : $@convention(thin) () -> ()
471+
destroy_value [dead_end] %c2 : $C
472+
unreachable
473+
}
474+
475+
// CHECK-LABEL: begin running test {{.*}} on preserve_dead_end_7
476+
// CHECK-LABEL: sil [ossa] @preserve_dead_end_7 : {{.*}} {
477+
// CHECK-NOT: destroy_value [dead_end]
478+
// CHECK-LABEL: } // end sil function 'preserve_dead_end_7'
479+
// CHECK-LABEL: end running test {{.*}} on preserve_dead_end_7
480+
sil [ossa] @preserve_dead_end_7 : $@convention(thin) () -> () {
481+
entry:
482+
specify_test "canonicalize_ossa_lifetime true false true %c"
483+
484+
%get = function_ref @getOwned : $@convention(thin) () -> @owned C
485+
%barrier = function_ref @barrier : $@convention(thin) () -> ()
486+
%borrow = function_ref @borrowC : $@convention(thin) (@guaranteed C) -> ()
487+
488+
%c = apply %get() : $@convention(thin) () -> @owned C
489+
%c2 = copy_value %c : $C
490+
destroy_value %c : $C
491+
apply %borrow(%c2) : $@convention(thin) (@guaranteed C) -> ()
492+
cond_br undef, die1, die2
493+
494+
die1:
495+
destroy_value %c2 : $C
496+
unreachable
497+
498+
die2:
499+
apply %barrier() : $@convention(thin) () -> ()
500+
destroy_value [dead_end] %c2 : $C
501+
unreachable
502+
}
503+
327504
// (1) When no end is specified, the lifetime ends after the barrier.
328505
// CHECK-LABEL: begin running test {{.*}} on lexical_end_at_end_1: canonicalize_ossa_lifetime
329506
// CHECK-LABEL: sil [ossa] @lexical_end_at_end_1 : {{.*}} {

0 commit comments

Comments
 (0)