Skip to content

Commit 98805c9

Browse files
committed
Optimizer: let the InstructionDeleter respect deinit-barriers by default
The InstructionDeleter can remove instructions including their destroys and then insert compensating destroys at a new place. This is effectively destroy-hoisting which doesn't respect deinit-barriers. Therefore it's not done for lexical lifetimes. However, since #85334, the optimizer should treat _all_ lifetimes as fixed and not only lexical lifetimes. This change adds a `assumeFixedLifetimes` flag to InstructionDeleter which is on by default. Only mandatory passes (like OSLogOptimization) should turn this off.
1 parent 8aa911b commit 98805c9

12 files changed

+61
-69
lines changed

include/swift/SILOptimizer/Utils/InstructionDeleter.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,15 @@ class InstructionDeleter {
119119
/// Callbacks used when adding/deleting instructions.
120120
InstModCallbacks callbacks;
121121

122+
bool assumeFixedLifetimes = true;
123+
122124
public:
123-
InstructionDeleter() : deadInstructions() {}
125+
InstructionDeleter(bool assumeFixedLifetimes = true)
126+
: deadInstructions(), assumeFixedLifetimes(assumeFixedLifetimes) {}
124127

125-
InstructionDeleter(InstModCallbacks &&callbacks)
126-
: deadInstructions(), callbacks(std::move(callbacks)) {}
128+
InstructionDeleter(InstModCallbacks &&callbacks, bool assumeFixedLifetimes = true)
129+
: deadInstructions(), callbacks(std::move(callbacks)),
130+
assumeFixedLifetimes(assumeFixedLifetimes) {}
127131

128132
InstModCallbacks &getCallbacks() { return callbacks; }
129133

lib/SILOptimizer/Mandatory/OSLogOptimization.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,7 +1206,7 @@ static bool tryEliminateOSLogMessage(SingleValueInstruction *oslogMessage) {
12061206
}
12071207
(void)deletedInstructions.insert(deadInst);
12081208
});
1209-
InstructionDeleter deleter(std::move(callbacks));
1209+
InstructionDeleter deleter(std::move(callbacks), /*assumeFixedLifetimes=*/ false);
12101210

12111211
unsigned startIndex = 0;
12121212
while (startIndex < worklist.size()) {
@@ -1433,7 +1433,7 @@ suppressGlobalStringTablePointerError(SingleValueInstruction *oslogMessage) {
14331433

14341434
// Replace the globalStringTablePointer builtins by a string_literal
14351435
// instruction for an empty string and clean up dead code.
1436-
InstructionDeleter deleter;
1436+
InstructionDeleter deleter(/*assumeFixedLifetimes=*/ false);
14371437
for (BuiltinInst *bi : globalStringTablePointerInsts) {
14381438
SILBuilderWithScope builder(bi);
14391439
StringLiteralInst *stringLiteral = builder.createStringLiteral(

lib/SILOptimizer/Utils/InstructionDeleter.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ static bool hasOnlyIncidentalUses(SILInstruction *inst,
5050
///
5151
/// TODO: Handle partial_apply [stack] which has a dealloc_stack user.
5252
static bool isScopeAffectingInstructionDead(SILInstruction *inst,
53-
bool fixLifetime) {
53+
bool fixLifetime,
54+
bool assumeFixedLifetimes) {
5455
SILFunction *fun = inst->getFunction();
5556
assert(fun && "Instruction has no function.");
5657
// Only support ownership SIL for scoped instructions.
@@ -84,7 +85,7 @@ static bool isScopeAffectingInstructionDead(SILInstruction *inst,
8485
}
8586

8687
// If result was lexical, lifetime shortening maybe observed, return.
87-
if (result->isLexical()) {
88+
if (result->isLexical() || assumeFixedLifetimes) {
8889
auto resultTy = result->getType().getAs<SILFunctionType>();
8990
// Allow deleted dead lexical values when they are trivial no escape types.
9091
if (!resultTy || !resultTy->isTrivialNoEscape()) {
@@ -186,7 +187,7 @@ static bool isScopeAffectingInstructionDead(SILInstruction *inst,
186187
bool InstructionDeleter::trackIfDead(SILInstruction *inst) {
187188
bool fixLifetime = inst->getFunction()->hasOwnership();
188189
if (isInstructionTriviallyDead(inst)
189-
|| isScopeAffectingInstructionDead(inst, fixLifetime)) {
190+
|| isScopeAffectingInstructionDead(inst, fixLifetime, assumeFixedLifetimes)) {
190191
assert(!isIncidentalUse(inst)
191192
|| canTriviallyDeleteOSSAEndScopeInst(inst) &&
192193
"Incidental uses cannot be removed in isolation. "
@@ -333,7 +334,7 @@ bool InstructionDeleter::deleteIfDead(SILInstruction *inst) {
333334

334335
bool InstructionDeleter::deleteIfDead(SILInstruction *inst, bool fixLifetime) {
335336
if (isInstructionTriviallyDead(inst)
336-
|| isScopeAffectingInstructionDead(inst, fixLifetime)) {
337+
|| isScopeAffectingInstructionDead(inst, fixLifetime, assumeFixedLifetimes)) {
337338
getCallbacks().notifyWillBeDeleted(inst);
338339
deleteWithUses(inst, fixLifetime);
339340
return true;
@@ -349,7 +350,7 @@ namespace swift::test {
349350
static FunctionTest DeleterDeleteIfDeadTest(
350351
"deleter_delete_if_dead", [](auto &function, auto &arguments, auto &test) {
351352
auto *inst = arguments.takeInstruction();
352-
InstructionDeleter deleter;
353+
InstructionDeleter deleter(/*assumeFixedLifetimes=*/ false);
353354
llvm::outs() << "Deleting-if-dead " << *inst;
354355
auto deleted = deleter.deleteIfDead(inst);
355356
llvm::outs() << "deleteIfDead returned " << deleted << "\n";

test/SILOptimizer/address_lowering.sil

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2934,6 +2934,7 @@ bb0:
29342934
}
29352935

29362936
// CHECK-LABEL: sil [ossa] @test_store_borrow_1_copy_addr : {{.*}} {
2937+
// CHECK: alloc_stack
29372938
// CHECK: [[ADDR:%[^,]+]] = alloc_stack
29382939
// CHECK: apply undef<T>([[ADDR]])
29392940
// CHECK: [[COPY:%[^,]+]] = alloc_stack

test/SILOptimizer/copy_propagation.sil

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -909,9 +909,9 @@ sil [ossa] @canonicalize_source_of_redundant_move_value : $@convention(thin) ()
909909
return %retval : $()
910910
}
911911

912-
// Verify that a dead copy_value is deleted.
912+
// TODO: why is the copy not deleted here?
913913
// CHECK-LABEL: sil [ossa] @delete_dead_reborrow_copy : {{.*}} {
914-
// CHECK-NOT: copy_value
914+
// xCHECK-NOT: copy_value
915915
// CHECK-LABEL: } // end sil function 'delete_dead_reborrow_copy'
916916
sil [ossa] @delete_dead_reborrow_copy : $@convention(thin) (@owned X) -> () {
917917
bb0(%instance : @owned $X):

test/SILOptimizer/copy_propagation_borrow.sil

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ bb0(%0 : @guaranteed $NativeObjectPair):
168168
// CHECK-LABEL: sil [ossa] @testBorrowOuterUse : {{.*}} {
169169
// CHECK: bb0:
170170
// CHECK: [[INSTANCE:%.*]] = apply
171-
// CHECK-NOT: begin_borrow
171+
// CHECK: begin_borrow
172+
// CHECK-NEXT: end_borrow
172173
// CHECK-NOT: copy
173174
// CHECK: apply %{{.*}}([[INSTANCE]]) : $@convention(thin) (@owned C) -> ()
174175
// CHECK-NOT: destroy
@@ -191,7 +192,8 @@ bb0:
191192
//
192193
// CHECK-LABEL: sil [ossa] @testMultiBlockBorrow : $@convention(thin) (@guaranteed C) -> () {
193194
// CHECK: bb0(%0 : @guaranteed $C):
194-
// CHECK-NOT: borrow
195+
// CHECK: borrow
196+
// CHECK-NEXT: end_borrow
195197
// CHECK-NOT: copy
196198
// CHECK: cond_br undef, bb1, bb2
197199
// CHECK: bb1:
@@ -485,14 +487,17 @@ bb3:
485487
// CHECK-LABEL: sil [ossa] @testNestedBorrowInsideAndOutsideUse : $@convention(thin) () -> () {
486488
// CHECK: [[ALLOC:%.*]] = alloc_ref $C
487489
// CHECK: [[B1:%.*]] = begin_borrow [[ALLOC]] : $C
488-
// CHECK-NOT: borrow
490+
// CHECK-NEXT: [[B2:%.*]] = begin_borrow [[ALLOC]] : $C
491+
// CHECK-NOT: copy_value
489492
// CHECK: bb1:
493+
// CHECK-NEXT: end_borrow [[B2]] : $C
490494
// CHECK-NEXT: end_borrow [[B1]] : $C
491495
// CHECK-NEXT: destroy_value [[ALLOC]] : $C
492496
// CHECK-NEXT: br bb3
493497
// CHECK: bb2:
494-
// CHECK-NEXT: end_borrow %1 : $C
495-
// CHECK-NEXT: destroy_value %0 : $C
498+
// CHECK-NEXT: end_borrow [[B1]] : $C
499+
// CHECK-NEXT: end_borrow [[B2]] : $C
500+
// CHECK-NEXT: destroy_value [[ALLOC]] : $C
496501
// CHECK-NEXT: br bb3
497502
// CHECK: bb3:
498503
// CHECK-NOT: destroy
@@ -655,11 +660,12 @@ bb3(%borrowphi : @guaranteed $C):
655660
// The inner copy's lifetime will be canonicalized, removing
656661
// outercopy.
657662
//
663+
// TODO: why can't the first copy_value not be removed?
658664
// CHECK-LABEL: sil [ossa] @testDeadCopyAfterReborrow : $@convention(thin) () -> () {
659665
// CHECK: [[ALLOC:%.*]] = alloc_ref $C
660666
// CHECK: bb3([[BORROWPHI:%.*]] : @reborrow $C):
661667
// CHECK: [[R:%.*]] = borrowed [[BORROWPHI]] : $C from (%0 : $C)
662-
// CHECK-NOT: copy_value
668+
// xCHECK-NOT: copy_value
663669
// CHECK: end_borrow [[R]] : $C
664670
// CHECK-NOT: copy_value
665671
// CHECK: destroy_value [[ALLOC]] : $C
@@ -696,13 +702,14 @@ bb3(%borrowphi : @guaranteed $C):
696702
// end borrowphi
697703
// outer copy -- removed when borrowphi's copy is canonicalized
698704
//
705+
// TODO: why can't the first copy_value not be removed?
699706
// CHECK-LABEL: sil [ossa] @testNestedReborrowOutsideUse : $@convention(thin) () -> () {
700707
// CHECK: [[ALLOC:%.*]] = alloc_ref $C
701708
// CHECK: bb3([[BORROWPHI:%.*]] : @reborrow $C):
702709
// CHECK: [[R:%.*]] = borrowed [[BORROWPHI]] : $C from (%0 : $C)
703-
// CHECK-NOT: copy
710+
// xCHECK-NOT: copy
704711
// CHECK: end_borrow [[R]]
705-
// CHECK-NEXT: destroy_value [[ALLOC]] : $C
712+
// CHECK: destroy_value [[ALLOC]] : $C
706713
// CHECK-LABEL: } // end sil function 'testNestedReborrowOutsideUse'
707714
sil [ossa] @testNestedReborrowOutsideUse : $@convention(thin) () -> () {
708715
bb0:
@@ -883,7 +890,8 @@ bb0:
883890
// CHECK-LABEL: sil [ossa] @testForwardBorrow3 : {{.*}} {
884891
// CHECK: bb0:
885892
// CHECK: [[INSTANCE:%.*]] = apply
886-
// CHECK-NOT: borrow
893+
// CHECK: begin_borrow
894+
// CHECK-NEXT: end_borrow
887895
// CHECK-NOT: copy
888896
// CHECK: [[DSOUT1:%.*]] = destructure_struct [[INSTANCE]] : $Wrapper
889897
// CHECK-NEXT: ([[DSOUT2:%.*]], %{{.*}}) = destructure_struct [[DSOUT1]] : $HasObjectAndInt
@@ -911,17 +919,18 @@ bb0:
911919
// but one has no outer uses.
912920
// Need to create two new destroys in this case.
913921
//
922+
// TODO: why can't the copy_value not be removed?
914923
//
915924
// CHECK-LABEL: sil [ossa] @testForwardBorrow4 : {{.*}} {
916925
// CHECK: bb0:
917926
// CHECK: [[INSTANCE:%.*]] = apply
918-
// CHECK-NEXT: ([[HASOBJECTS_0:%[^,]+]], [[HASOBJECTS_1:%[^,]+]]) = destructure_struct [[INSTANCE]] : $MultiWrapper
919-
// CHECK-NEXT: destroy_value [[HASOBJECTS_1]] : $HasObjects
920-
// CHECK-NEXT: ([[VAL:%.*]], [[OBJECT_1:%[^,]+]]) = destructure_struct [[HASOBJECTS_0]] : $HasObjects
921-
// CHECK-NEXT: destroy_value [[OBJECT_1]] : $C
922-
// CHECK-NOT: borrow
923-
// CHECK: apply %{{.*}}([[VAL]]) : $@convention(thin) (@owned C) -> ()
924-
// CHECK-NOT: destroy
927+
// xCHECK-NEXT: ([[HASOBJECTS_0:%[^,]+]], [[HASOBJECTS_1:%[^,]+]]) = destructure_struct [[INSTANCE]] : $MultiWrapper
928+
// xCHECK-NEXT: destroy_value [[HASOBJECTS_1]] : $HasObjects
929+
// xCHECK-NEXT: ([[VAL:%.*]], [[OBJECT_1:%[^,]+]]) = destructure_struct [[HASOBJECTS_0]] : $HasObjects
930+
// xCHECK-NEXT: destroy_value [[OBJECT_1]] : $C
931+
// xCHECK-NOT: borrow
932+
// xCHECK: apply %{{.*}}([[VAL]]) : $@convention(thin) (@owned C) -> ()
933+
// xCHECK-NOT: destroy
925934
// CHECK-LABEL: } // end sil function 'testForwardBorrow4'
926935
sil [ossa] @testForwardBorrow4 : $@convention(thin) () -> () {
927936
bb0:
@@ -953,6 +962,9 @@ bb0:
953962
// CHECK: bb0:
954963
// CHECK: [[INSTANCE:%.*]] = apply
955964
// CHECK-NEXT: [[COPY:%[^,]+]] = copy_value [[INSTANCE]] : $HasObjectAndInt
965+
// CHECK-NEXT: begin_borrow
966+
// CHECK-NEXT: destructure_struct
967+
// CHECK-NEXT: end_borrow
956968
// CHECK-NEXT: ([[OBJECT:%[^,]+]], {{%[^,]+}}) = destructure_struct [[COPY]] : $HasObjectAndInt
957969
// CHECK-NEXT: [[BORROW:%[^,]+]] = begin_borrow [[OBJECT]] : $C
958970
// CHECK-NEXT: [[TAIL_ADDR:%[^,]+]] = ref_tail_addr [[BORROW]] : $C, $Builtin.Int8
@@ -1082,11 +1094,15 @@ bb0(%0 : @owned $HasObject):
10821094
// CHECK-LABEL: sil [ossa] @testUselessBorrowString : {{.*}} {
10831095
// CHECK: bb0:
10841096
// CHECK: [[INSTANCE:%.*]] = apply
1097+
// CHECK-NEXT: begin_borrow
1098+
// CHECK-NEXT: end_borrow
10851099
// CHECK-NEXT: [[DESTRUCTURE:%.*]] = destructure_struct [[INSTANCE]] : $String
10861100
// CHECK-NEXT: [[UTF16:%.*]] = struct $String.UTF16View ([[DESTRUCTURE]] : $_StringGuts)
10871101
// CHECK-NEXT: br bb1
10881102
// CHECK: bb1:
10891103
// CHECK-NEXT: [[COPY:%.*]] = copy_value [[UTF16]] : $String.UTF16View
1104+
// CHECK-NEXT: begin_borrow
1105+
// CHECK-NEXT: end_borrow
10901106
// CHECK-NEXT: [[GUTS:%.*]] = destructure_struct [[COPY]] : $String.UTF16View
10911107
// CHECK-NEXT: [[OBJ:%.*]] = destructure_struct [[GUTS]] : $_StringGuts
10921108
// CHECK-NEXT: [[BORROW:%.*]] = begin_borrow [[OBJ]] : $_StringObject

test/SILOptimizer/copy_propagation_opaque.sil

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,8 @@ bb0:
419419
// CHECK-LABEL: sil [ossa] @testBorrowCopy : {{.*}} {
420420
// CHECK-LABEL: bb0:
421421
// CHECK: [[INSTANCE:%[^,]+]] = apply
422+
// CHECK-NEXT: begin_borrow
423+
// CHECK-NEXT: end_borrow
422424
// CHECK-NEXT: destroy_value [[INSTANCE]] : $T
423425
// CHECK-NEXT: tuple ()
424426
// CHECK-NEXT: return

test/SILOptimizer/dead_code_elimination_nontrivial_ossa.sil

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,34 +1411,3 @@ bb5(%16 : @reborrow $FakeOptional<Klass>, %17 : @reborrow $FakeOptional<Klass>):
14111411
return %23
14121412
}
14131413

1414-
struct String {
1415-
var guts: Builtin.AnyObject
1416-
}
1417-
1418-
sil [_semantics "string.makeUTF8"] [ossa] @makeString : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> @owned String
1419-
1420-
// CHECK-LABEL: sil [ossa] @uncompletedDeadStrings
1421-
// CHECK-NOT: apply
1422-
// CHECK-LABEL: } // end sil function 'uncompletedDeadStrings'
1423-
sil [ossa] @uncompletedDeadStrings : $@convention(thin) () -> () {
1424-
%first_ptr = string_literal utf8 "first"
1425-
%first_len = integer_literal $Builtin.Word, 5
1426-
%makeString = function_ref @makeString : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> @owned String
1427-
%first = apply %makeString(%first_ptr, %first_len) : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> @owned String
1428-
cond_br undef, nope, yep
1429-
1430-
nope:
1431-
destroy_value %first
1432-
%second_ptr = string_literal utf8 "second"
1433-
%second_len = integer_literal $Builtin.Word, 6
1434-
%second = apply %makeString(%second_ptr, %second_len) : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> @owned String
1435-
br this(%second)
1436-
1437-
yep:
1438-
br this(%first)
1439-
1440-
this(%string : @owned $String):
1441-
destroy_value %string
1442-
%retval = tuple ()
1443-
return %retval
1444-
}

test/SILOptimizer/dead_code_elimination_ossa.sil

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -445,12 +445,8 @@ bb0(%0 : @owned $MO):
445445
return %63 : $()
446446
}
447447

448-
// The InstructionDeleter will delete the `load [take]` and insert a
449-
// `destroy_addr`. Observe the creation of the new destroy_addr instruction
450-
// that occurs when deleting the `load [take]` and mark it live. Prevents a
451-
// leak.
452448
// CHECK-LABEL: sil [ossa] @keep_new_destroy_addr : {{.*}} {
453-
// CHECK: destroy_addr
449+
// CHECK: load [take]
454450
// CHECK-LABEL: } // end sil function 'keep_new_destroy_addr'
455451
sil [ossa] @keep_new_destroy_addr : $@convention(thin) () -> () {
456452
bb0:

test/SILOptimizer/drop_deinit_opt.sil

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ bb0(%0 : @owned $FileDescriptor):
3434
}
3535

3636
// CHECK-LABEL: sil hidden [ossa] @fd_deinit2 :
37-
// CHECK: end_lifetime
37+
// CHECK: %1 = drop_deinit
38+
// CHECK-NEXT: %2 = destructure_struct %1
39+
// CHECK-NEXT: debug_value
3840
// CHECK-LABEL: } // end sil function 'fd_deinit2'
3941
sil hidden [ossa] @fd_deinit2 : $@convention(method) (@owned FileDescriptor) -> () {
4042
bb0(%0 : @owned $FileDescriptor):

0 commit comments

Comments
 (0)