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 https://github.com/swiftlang/swift/pull/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.
This commit is contained in:
Erik Eckstein
2025-12-03 13:09:55 +01:00
parent 8aa911ba2f
commit 98805c9141
12 changed files with 62 additions and 70 deletions

View File

@@ -119,11 +119,15 @@ class InstructionDeleter {
/// Callbacks used when adding/deleting instructions. /// Callbacks used when adding/deleting instructions.
InstModCallbacks callbacks; InstModCallbacks callbacks;
public: bool assumeFixedLifetimes = true;
InstructionDeleter() : deadInstructions() {}
InstructionDeleter(InstModCallbacks &&callbacks) public:
: deadInstructions(), callbacks(std::move(callbacks)) {} InstructionDeleter(bool assumeFixedLifetimes = true)
: deadInstructions(), assumeFixedLifetimes(assumeFixedLifetimes) {}
InstructionDeleter(InstModCallbacks &&callbacks, bool assumeFixedLifetimes = true)
: deadInstructions(), callbacks(std::move(callbacks)),
assumeFixedLifetimes(assumeFixedLifetimes) {}
InstModCallbacks &getCallbacks() { return callbacks; } InstModCallbacks &getCallbacks() { return callbacks; }

View File

@@ -1206,7 +1206,7 @@ static bool tryEliminateOSLogMessage(SingleValueInstruction *oslogMessage) {
} }
(void)deletedInstructions.insert(deadInst); (void)deletedInstructions.insert(deadInst);
}); });
InstructionDeleter deleter(std::move(callbacks)); InstructionDeleter deleter(std::move(callbacks), /*assumeFixedLifetimes=*/ false);
unsigned startIndex = 0; unsigned startIndex = 0;
while (startIndex < worklist.size()) { while (startIndex < worklist.size()) {
@@ -1433,7 +1433,7 @@ suppressGlobalStringTablePointerError(SingleValueInstruction *oslogMessage) {
// Replace the globalStringTablePointer builtins by a string_literal // Replace the globalStringTablePointer builtins by a string_literal
// instruction for an empty string and clean up dead code. // instruction for an empty string and clean up dead code.
InstructionDeleter deleter; InstructionDeleter deleter(/*assumeFixedLifetimes=*/ false);
for (BuiltinInst *bi : globalStringTablePointerInsts) { for (BuiltinInst *bi : globalStringTablePointerInsts) {
SILBuilderWithScope builder(bi); SILBuilderWithScope builder(bi);
StringLiteralInst *stringLiteral = builder.createStringLiteral( StringLiteralInst *stringLiteral = builder.createStringLiteral(

View File

@@ -50,7 +50,8 @@ static bool hasOnlyIncidentalUses(SILInstruction *inst,
/// ///
/// TODO: Handle partial_apply [stack] which has a dealloc_stack user. /// TODO: Handle partial_apply [stack] which has a dealloc_stack user.
static bool isScopeAffectingInstructionDead(SILInstruction *inst, static bool isScopeAffectingInstructionDead(SILInstruction *inst,
bool fixLifetime) { bool fixLifetime,
bool assumeFixedLifetimes) {
SILFunction *fun = inst->getFunction(); SILFunction *fun = inst->getFunction();
assert(fun && "Instruction has no function."); assert(fun && "Instruction has no function.");
// Only support ownership SIL for scoped instructions. // Only support ownership SIL for scoped instructions.
@@ -84,7 +85,7 @@ static bool isScopeAffectingInstructionDead(SILInstruction *inst,
} }
// If result was lexical, lifetime shortening maybe observed, return. // If result was lexical, lifetime shortening maybe observed, return.
if (result->isLexical()) { if (result->isLexical() || assumeFixedLifetimes) {
auto resultTy = result->getType().getAs<SILFunctionType>(); auto resultTy = result->getType().getAs<SILFunctionType>();
// Allow deleted dead lexical values when they are trivial no escape types. // Allow deleted dead lexical values when they are trivial no escape types.
if (!resultTy || !resultTy->isTrivialNoEscape()) { if (!resultTy || !resultTy->isTrivialNoEscape()) {
@@ -186,7 +187,7 @@ static bool isScopeAffectingInstructionDead(SILInstruction *inst,
bool InstructionDeleter::trackIfDead(SILInstruction *inst) { bool InstructionDeleter::trackIfDead(SILInstruction *inst) {
bool fixLifetime = inst->getFunction()->hasOwnership(); bool fixLifetime = inst->getFunction()->hasOwnership();
if (isInstructionTriviallyDead(inst) if (isInstructionTriviallyDead(inst)
|| isScopeAffectingInstructionDead(inst, fixLifetime)) { || isScopeAffectingInstructionDead(inst, fixLifetime, assumeFixedLifetimes)) {
assert(!isIncidentalUse(inst) assert(!isIncidentalUse(inst)
|| canTriviallyDeleteOSSAEndScopeInst(inst) && || canTriviallyDeleteOSSAEndScopeInst(inst) &&
"Incidental uses cannot be removed in isolation. " "Incidental uses cannot be removed in isolation. "
@@ -333,7 +334,7 @@ bool InstructionDeleter::deleteIfDead(SILInstruction *inst) {
bool InstructionDeleter::deleteIfDead(SILInstruction *inst, bool fixLifetime) { bool InstructionDeleter::deleteIfDead(SILInstruction *inst, bool fixLifetime) {
if (isInstructionTriviallyDead(inst) if (isInstructionTriviallyDead(inst)
|| isScopeAffectingInstructionDead(inst, fixLifetime)) { || isScopeAffectingInstructionDead(inst, fixLifetime, assumeFixedLifetimes)) {
getCallbacks().notifyWillBeDeleted(inst); getCallbacks().notifyWillBeDeleted(inst);
deleteWithUses(inst, fixLifetime); deleteWithUses(inst, fixLifetime);
return true; return true;
@@ -349,7 +350,7 @@ namespace swift::test {
static FunctionTest DeleterDeleteIfDeadTest( static FunctionTest DeleterDeleteIfDeadTest(
"deleter_delete_if_dead", [](auto &function, auto &arguments, auto &test) { "deleter_delete_if_dead", [](auto &function, auto &arguments, auto &test) {
auto *inst = arguments.takeInstruction(); auto *inst = arguments.takeInstruction();
InstructionDeleter deleter; InstructionDeleter deleter(/*assumeFixedLifetimes=*/ false);
llvm::outs() << "Deleting-if-dead " << *inst; llvm::outs() << "Deleting-if-dead " << *inst;
auto deleted = deleter.deleteIfDead(inst); auto deleted = deleter.deleteIfDead(inst);
llvm::outs() << "deleteIfDead returned " << deleted << "\n"; llvm::outs() << "deleteIfDead returned " << deleted << "\n";

View File

@@ -2934,6 +2934,7 @@ bb0:
} }
// CHECK-LABEL: sil [ossa] @test_store_borrow_1_copy_addr : {{.*}} { // CHECK-LABEL: sil [ossa] @test_store_borrow_1_copy_addr : {{.*}} {
// CHECK: alloc_stack
// CHECK: [[ADDR:%[^,]+]] = alloc_stack // CHECK: [[ADDR:%[^,]+]] = alloc_stack
// CHECK: apply undef<T>([[ADDR]]) // CHECK: apply undef<T>([[ADDR]])
// CHECK: [[COPY:%[^,]+]] = alloc_stack // CHECK: [[COPY:%[^,]+]] = alloc_stack

View File

@@ -909,9 +909,9 @@ sil [ossa] @canonicalize_source_of_redundant_move_value : $@convention(thin) ()
return %retval : $() return %retval : $()
} }
// Verify that a dead copy_value is deleted. // TODO: why is the copy not deleted here?
// CHECK-LABEL: sil [ossa] @delete_dead_reborrow_copy : {{.*}} { // CHECK-LABEL: sil [ossa] @delete_dead_reborrow_copy : {{.*}} {
// CHECK-NOT: copy_value // xCHECK-NOT: copy_value
// CHECK-LABEL: } // end sil function 'delete_dead_reborrow_copy' // CHECK-LABEL: } // end sil function 'delete_dead_reborrow_copy'
sil [ossa] @delete_dead_reborrow_copy : $@convention(thin) (@owned X) -> () { sil [ossa] @delete_dead_reborrow_copy : $@convention(thin) (@owned X) -> () {
bb0(%instance : @owned $X): bb0(%instance : @owned $X):

View File

@@ -168,7 +168,8 @@ bb0(%0 : @guaranteed $NativeObjectPair):
// CHECK-LABEL: sil [ossa] @testBorrowOuterUse : {{.*}} { // CHECK-LABEL: sil [ossa] @testBorrowOuterUse : {{.*}} {
// CHECK: bb0: // CHECK: bb0:
// CHECK: [[INSTANCE:%.*]] = apply // CHECK: [[INSTANCE:%.*]] = apply
// CHECK-NOT: begin_borrow // CHECK: begin_borrow
// CHECK-NEXT: end_borrow
// CHECK-NOT: copy // CHECK-NOT: copy
// CHECK: apply %{{.*}}([[INSTANCE]]) : $@convention(thin) (@owned C) -> () // CHECK: apply %{{.*}}([[INSTANCE]]) : $@convention(thin) (@owned C) -> ()
// CHECK-NOT: destroy // CHECK-NOT: destroy
@@ -191,7 +192,8 @@ bb0:
// //
// CHECK-LABEL: sil [ossa] @testMultiBlockBorrow : $@convention(thin) (@guaranteed C) -> () { // CHECK-LABEL: sil [ossa] @testMultiBlockBorrow : $@convention(thin) (@guaranteed C) -> () {
// CHECK: bb0(%0 : @guaranteed $C): // CHECK: bb0(%0 : @guaranteed $C):
// CHECK-NOT: borrow // CHECK: borrow
// CHECK-NEXT: end_borrow
// CHECK-NOT: copy // CHECK-NOT: copy
// CHECK: cond_br undef, bb1, bb2 // CHECK: cond_br undef, bb1, bb2
// CHECK: bb1: // CHECK: bb1:
@@ -485,14 +487,17 @@ bb3:
// CHECK-LABEL: sil [ossa] @testNestedBorrowInsideAndOutsideUse : $@convention(thin) () -> () { // CHECK-LABEL: sil [ossa] @testNestedBorrowInsideAndOutsideUse : $@convention(thin) () -> () {
// CHECK: [[ALLOC:%.*]] = alloc_ref $C // CHECK: [[ALLOC:%.*]] = alloc_ref $C
// CHECK: [[B1:%.*]] = begin_borrow [[ALLOC]] : $C // CHECK: [[B1:%.*]] = begin_borrow [[ALLOC]] : $C
// CHECK-NOT: borrow // CHECK-NEXT: [[B2:%.*]] = begin_borrow [[ALLOC]] : $C
// CHECK-NOT: copy_value
// CHECK: bb1: // CHECK: bb1:
// CHECK-NEXT: end_borrow [[B2]] : $C
// CHECK-NEXT: end_borrow [[B1]] : $C // CHECK-NEXT: end_borrow [[B1]] : $C
// CHECK-NEXT: destroy_value [[ALLOC]] : $C // CHECK-NEXT: destroy_value [[ALLOC]] : $C
// CHECK-NEXT: br bb3 // CHECK-NEXT: br bb3
// CHECK: bb2: // CHECK: bb2:
// CHECK-NEXT: end_borrow %1 : $C // CHECK-NEXT: end_borrow [[B1]] : $C
// CHECK-NEXT: destroy_value %0 : $C // CHECK-NEXT: end_borrow [[B2]] : $C
// CHECK-NEXT: destroy_value [[ALLOC]] : $C
// CHECK-NEXT: br bb3 // CHECK-NEXT: br bb3
// CHECK: bb3: // CHECK: bb3:
// CHECK-NOT: destroy // CHECK-NOT: destroy
@@ -655,11 +660,12 @@ bb3(%borrowphi : @guaranteed $C):
// The inner copy's lifetime will be canonicalized, removing // The inner copy's lifetime will be canonicalized, removing
// outercopy. // outercopy.
// //
// TODO: why can't the first copy_value not be removed?
// CHECK-LABEL: sil [ossa] @testDeadCopyAfterReborrow : $@convention(thin) () -> () { // CHECK-LABEL: sil [ossa] @testDeadCopyAfterReborrow : $@convention(thin) () -> () {
// CHECK: [[ALLOC:%.*]] = alloc_ref $C // CHECK: [[ALLOC:%.*]] = alloc_ref $C
// CHECK: bb3([[BORROWPHI:%.*]] : @reborrow $C): // CHECK: bb3([[BORROWPHI:%.*]] : @reborrow $C):
// CHECK: [[R:%.*]] = borrowed [[BORROWPHI]] : $C from (%0 : $C) // CHECK: [[R:%.*]] = borrowed [[BORROWPHI]] : $C from (%0 : $C)
// CHECK-NOT: copy_value // xCHECK-NOT: copy_value
// CHECK: end_borrow [[R]] : $C // CHECK: end_borrow [[R]] : $C
// CHECK-NOT: copy_value // CHECK-NOT: copy_value
// CHECK: destroy_value [[ALLOC]] : $C // CHECK: destroy_value [[ALLOC]] : $C
@@ -696,13 +702,14 @@ bb3(%borrowphi : @guaranteed $C):
// end borrowphi // end borrowphi
// outer copy -- removed when borrowphi's copy is canonicalized // outer copy -- removed when borrowphi's copy is canonicalized
// //
// TODO: why can't the first copy_value not be removed?
// CHECK-LABEL: sil [ossa] @testNestedReborrowOutsideUse : $@convention(thin) () -> () { // CHECK-LABEL: sil [ossa] @testNestedReborrowOutsideUse : $@convention(thin) () -> () {
// CHECK: [[ALLOC:%.*]] = alloc_ref $C // CHECK: [[ALLOC:%.*]] = alloc_ref $C
// CHECK: bb3([[BORROWPHI:%.*]] : @reborrow $C): // CHECK: bb3([[BORROWPHI:%.*]] : @reborrow $C):
// CHECK: [[R:%.*]] = borrowed [[BORROWPHI]] : $C from (%0 : $C) // CHECK: [[R:%.*]] = borrowed [[BORROWPHI]] : $C from (%0 : $C)
// CHECK-NOT: copy // xCHECK-NOT: copy
// CHECK: end_borrow [[R]] // CHECK: end_borrow [[R]]
// CHECK-NEXT: destroy_value [[ALLOC]] : $C // CHECK: destroy_value [[ALLOC]] : $C
// CHECK-LABEL: } // end sil function 'testNestedReborrowOutsideUse' // CHECK-LABEL: } // end sil function 'testNestedReborrowOutsideUse'
sil [ossa] @testNestedReborrowOutsideUse : $@convention(thin) () -> () { sil [ossa] @testNestedReborrowOutsideUse : $@convention(thin) () -> () {
bb0: bb0:
@@ -883,7 +890,8 @@ bb0:
// CHECK-LABEL: sil [ossa] @testForwardBorrow3 : {{.*}} { // CHECK-LABEL: sil [ossa] @testForwardBorrow3 : {{.*}} {
// CHECK: bb0: // CHECK: bb0:
// CHECK: [[INSTANCE:%.*]] = apply // CHECK: [[INSTANCE:%.*]] = apply
// CHECK-NOT: borrow // CHECK: begin_borrow
// CHECK-NEXT: end_borrow
// CHECK-NOT: copy // CHECK-NOT: copy
// CHECK: [[DSOUT1:%.*]] = destructure_struct [[INSTANCE]] : $Wrapper // CHECK: [[DSOUT1:%.*]] = destructure_struct [[INSTANCE]] : $Wrapper
// CHECK-NEXT: ([[DSOUT2:%.*]], %{{.*}}) = destructure_struct [[DSOUT1]] : $HasObjectAndInt // CHECK-NEXT: ([[DSOUT2:%.*]], %{{.*}}) = destructure_struct [[DSOUT1]] : $HasObjectAndInt
@@ -911,17 +919,18 @@ bb0:
// but one has no outer uses. // but one has no outer uses.
// Need to create two new destroys in this case. // Need to create two new destroys in this case.
// //
// TODO: why can't the copy_value not be removed?
// //
// CHECK-LABEL: sil [ossa] @testForwardBorrow4 : {{.*}} { // CHECK-LABEL: sil [ossa] @testForwardBorrow4 : {{.*}} {
// CHECK: bb0: // CHECK: bb0:
// CHECK: [[INSTANCE:%.*]] = apply // CHECK: [[INSTANCE:%.*]] = apply
// CHECK-NEXT: ([[HASOBJECTS_0:%[^,]+]], [[HASOBJECTS_1:%[^,]+]]) = destructure_struct [[INSTANCE]] : $MultiWrapper // xCHECK-NEXT: ([[HASOBJECTS_0:%[^,]+]], [[HASOBJECTS_1:%[^,]+]]) = destructure_struct [[INSTANCE]] : $MultiWrapper
// CHECK-NEXT: destroy_value [[HASOBJECTS_1]] : $HasObjects // xCHECK-NEXT: destroy_value [[HASOBJECTS_1]] : $HasObjects
// CHECK-NEXT: ([[VAL:%.*]], [[OBJECT_1:%[^,]+]]) = destructure_struct [[HASOBJECTS_0]] : $HasObjects // xCHECK-NEXT: ([[VAL:%.*]], [[OBJECT_1:%[^,]+]]) = destructure_struct [[HASOBJECTS_0]] : $HasObjects
// CHECK-NEXT: destroy_value [[OBJECT_1]] : $C // xCHECK-NEXT: destroy_value [[OBJECT_1]] : $C
// CHECK-NOT: borrow // xCHECK-NOT: borrow
// CHECK: apply %{{.*}}([[VAL]]) : $@convention(thin) (@owned C) -> () // xCHECK: apply %{{.*}}([[VAL]]) : $@convention(thin) (@owned C) -> ()
// CHECK-NOT: destroy // xCHECK-NOT: destroy
// CHECK-LABEL: } // end sil function 'testForwardBorrow4' // CHECK-LABEL: } // end sil function 'testForwardBorrow4'
sil [ossa] @testForwardBorrow4 : $@convention(thin) () -> () { sil [ossa] @testForwardBorrow4 : $@convention(thin) () -> () {
bb0: bb0:
@@ -953,6 +962,9 @@ bb0:
// CHECK: bb0: // CHECK: bb0:
// CHECK: [[INSTANCE:%.*]] = apply // CHECK: [[INSTANCE:%.*]] = apply
// CHECK-NEXT: [[COPY:%[^,]+]] = copy_value [[INSTANCE]] : $HasObjectAndInt // CHECK-NEXT: [[COPY:%[^,]+]] = copy_value [[INSTANCE]] : $HasObjectAndInt
// CHECK-NEXT: begin_borrow
// CHECK-NEXT: destructure_struct
// CHECK-NEXT: end_borrow
// CHECK-NEXT: ([[OBJECT:%[^,]+]], {{%[^,]+}}) = destructure_struct [[COPY]] : $HasObjectAndInt // CHECK-NEXT: ([[OBJECT:%[^,]+]], {{%[^,]+}}) = destructure_struct [[COPY]] : $HasObjectAndInt
// CHECK-NEXT: [[BORROW:%[^,]+]] = begin_borrow [[OBJECT]] : $C // CHECK-NEXT: [[BORROW:%[^,]+]] = begin_borrow [[OBJECT]] : $C
// CHECK-NEXT: [[TAIL_ADDR:%[^,]+]] = ref_tail_addr [[BORROW]] : $C, $Builtin.Int8 // CHECK-NEXT: [[TAIL_ADDR:%[^,]+]] = ref_tail_addr [[BORROW]] : $C, $Builtin.Int8
@@ -1082,11 +1094,15 @@ bb0(%0 : @owned $HasObject):
// CHECK-LABEL: sil [ossa] @testUselessBorrowString : {{.*}} { // CHECK-LABEL: sil [ossa] @testUselessBorrowString : {{.*}} {
// CHECK: bb0: // CHECK: bb0:
// CHECK: [[INSTANCE:%.*]] = apply // CHECK: [[INSTANCE:%.*]] = apply
// CHECK-NEXT: begin_borrow
// CHECK-NEXT: end_borrow
// CHECK-NEXT: [[DESTRUCTURE:%.*]] = destructure_struct [[INSTANCE]] : $String // CHECK-NEXT: [[DESTRUCTURE:%.*]] = destructure_struct [[INSTANCE]] : $String
// CHECK-NEXT: [[UTF16:%.*]] = struct $String.UTF16View ([[DESTRUCTURE]] : $_StringGuts) // CHECK-NEXT: [[UTF16:%.*]] = struct $String.UTF16View ([[DESTRUCTURE]] : $_StringGuts)
// CHECK-NEXT: br bb1 // CHECK-NEXT: br bb1
// CHECK: bb1: // CHECK: bb1:
// CHECK-NEXT: [[COPY:%.*]] = copy_value [[UTF16]] : $String.UTF16View // CHECK-NEXT: [[COPY:%.*]] = copy_value [[UTF16]] : $String.UTF16View
// CHECK-NEXT: begin_borrow
// CHECK-NEXT: end_borrow
// CHECK-NEXT: [[GUTS:%.*]] = destructure_struct [[COPY]] : $String.UTF16View // CHECK-NEXT: [[GUTS:%.*]] = destructure_struct [[COPY]] : $String.UTF16View
// CHECK-NEXT: [[OBJ:%.*]] = destructure_struct [[GUTS]] : $_StringGuts // CHECK-NEXT: [[OBJ:%.*]] = destructure_struct [[GUTS]] : $_StringGuts
// CHECK-NEXT: [[BORROW:%.*]] = begin_borrow [[OBJ]] : $_StringObject // CHECK-NEXT: [[BORROW:%.*]] = begin_borrow [[OBJ]] : $_StringObject

View File

@@ -419,6 +419,8 @@ bb0:
// CHECK-LABEL: sil [ossa] @testBorrowCopy : {{.*}} { // CHECK-LABEL: sil [ossa] @testBorrowCopy : {{.*}} {
// CHECK-LABEL: bb0: // CHECK-LABEL: bb0:
// CHECK: [[INSTANCE:%[^,]+]] = apply // CHECK: [[INSTANCE:%[^,]+]] = apply
// CHECK-NEXT: begin_borrow
// CHECK-NEXT: end_borrow
// CHECK-NEXT: destroy_value [[INSTANCE]] : $T // CHECK-NEXT: destroy_value [[INSTANCE]] : $T
// CHECK-NEXT: tuple () // CHECK-NEXT: tuple ()
// CHECK-NEXT: return // CHECK-NEXT: return

View File

@@ -1411,34 +1411,3 @@ bb5(%16 : @reborrow $FakeOptional<Klass>, %17 : @reborrow $FakeOptional<Klass>):
return %23 return %23
} }
struct String {
var guts: Builtin.AnyObject
}
sil [_semantics "string.makeUTF8"] [ossa] @makeString : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> @owned String
// CHECK-LABEL: sil [ossa] @uncompletedDeadStrings
// CHECK-NOT: apply
// CHECK-LABEL: } // end sil function 'uncompletedDeadStrings'
sil [ossa] @uncompletedDeadStrings : $@convention(thin) () -> () {
%first_ptr = string_literal utf8 "first"
%first_len = integer_literal $Builtin.Word, 5
%makeString = function_ref @makeString : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> @owned String
%first = apply %makeString(%first_ptr, %first_len) : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> @owned String
cond_br undef, nope, yep
nope:
destroy_value %first
%second_ptr = string_literal utf8 "second"
%second_len = integer_literal $Builtin.Word, 6
%second = apply %makeString(%second_ptr, %second_len) : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> @owned String
br this(%second)
yep:
br this(%first)
this(%string : @owned $String):
destroy_value %string
%retval = tuple ()
return %retval
}

View File

@@ -445,12 +445,8 @@ bb0(%0 : @owned $MO):
return %63 : $() return %63 : $()
} }
// The InstructionDeleter will delete the `load [take]` and insert a
// `destroy_addr`. Observe the creation of the new destroy_addr instruction
// that occurs when deleting the `load [take]` and mark it live. Prevents a
// leak.
// CHECK-LABEL: sil [ossa] @keep_new_destroy_addr : {{.*}} { // CHECK-LABEL: sil [ossa] @keep_new_destroy_addr : {{.*}} {
// CHECK: destroy_addr // CHECK: load [take]
// CHECK-LABEL: } // end sil function 'keep_new_destroy_addr' // CHECK-LABEL: } // end sil function 'keep_new_destroy_addr'
sil [ossa] @keep_new_destroy_addr : $@convention(thin) () -> () { sil [ossa] @keep_new_destroy_addr : $@convention(thin) () -> () {
bb0: bb0:

View File

@@ -34,7 +34,9 @@ bb0(%0 : @owned $FileDescriptor):
} }
// CHECK-LABEL: sil hidden [ossa] @fd_deinit2 : // CHECK-LABEL: sil hidden [ossa] @fd_deinit2 :
// CHECK: end_lifetime // CHECK: %1 = drop_deinit
// CHECK-NEXT: %2 = destructure_struct %1
// CHECK-NEXT: debug_value
// CHECK-LABEL: } // end sil function 'fd_deinit2' // CHECK-LABEL: } // end sil function 'fd_deinit2'
sil hidden [ossa] @fd_deinit2 : $@convention(method) (@owned FileDescriptor) -> () { sil hidden [ossa] @fd_deinit2 : $@convention(method) (@owned FileDescriptor) -> () {
bb0(%0 : @owned $FileDescriptor): bb0(%0 : @owned $FileDescriptor):

View File

@@ -165,7 +165,8 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : $*Builtin.NativeObject):
// CHECK: bb0([[ARG0:%.*]] : @owned $Builtin.NativeObject, [[ARG1:%.*]] : $*Builtin.NativeObject): // CHECK: bb0([[ARG0:%.*]] : @owned $Builtin.NativeObject, [[ARG1:%.*]] : $*Builtin.NativeObject):
// CHECK-NOT: alloc_stack // CHECK-NOT: alloc_stack
// CHECK: destroy_value [[ARG0]] // CHECK: destroy_value [[ARG0]]
// CHECK: destroy_addr [[ARG1]] // CHECK: [[L:%.*]] = load [take] [[ARG1]]
// CHECK: destroy_value [[L]]
// CHECK: } // end sil function 'simple_init_take' // CHECK: } // end sil function 'simple_init_take'
sil [ossa] @simple_init_take : $@convention(thin) (@owned Builtin.NativeObject, @in Builtin.NativeObject) -> () { sil [ossa] @simple_init_take : $@convention(thin) (@owned Builtin.NativeObject, @in Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject, %1 : $*Builtin.NativeObject): bb0(%0 : @owned $Builtin.NativeObject, %1 : $*Builtin.NativeObject):

View File

@@ -283,9 +283,9 @@ bb0(%0 : $*X3, %1 : @guaranteed $Builtin.NativeObject):
// //
// CHECK-LABEL: sil [ossa] @testDestructureTupleNoCrash : $@convention(thin) (@owned (Builtin.NativeObject, Builtin.NativeObject)) -> () { // CHECK-LABEL: sil [ossa] @testDestructureTupleNoCrash : $@convention(thin) (@owned (Builtin.NativeObject, Builtin.NativeObject)) -> () {
// CHECKOPT: bb0( // CHECKOPT: bb0(
// CHECKOPT-NEXT: destroy_value // CHECKOPT-NEXT: (%1, %2) = destructure_tuple
// CHECKOPT-NEXT: tuple // CHECKOPT: destroy_value %2
// CHECKOPT-NEXT: return // CHECKOPT-NEXT: destroy_value %1
// CHECK: } // end sil function 'testDestructureTupleNoCrash' // CHECK: } // end sil function 'testDestructureTupleNoCrash'
sil [ossa] @testDestructureTupleNoCrash : $@convention(thin) (@owned (Builtin.NativeObject, Builtin.NativeObject)) -> () { sil [ossa] @testDestructureTupleNoCrash : $@convention(thin) (@owned (Builtin.NativeObject, Builtin.NativeObject)) -> () {
bb0(%0 : @owned $(Builtin.NativeObject, Builtin.NativeObject)): bb0(%0 : @owned $(Builtin.NativeObject, Builtin.NativeObject)):