diff --git a/include/swift/SILOptimizer/Utils/InstructionDeleter.h b/include/swift/SILOptimizer/Utils/InstructionDeleter.h index d758c012741..1a793322bc5 100644 --- a/include/swift/SILOptimizer/Utils/InstructionDeleter.h +++ b/include/swift/SILOptimizer/Utils/InstructionDeleter.h @@ -119,11 +119,15 @@ class InstructionDeleter { /// Callbacks used when adding/deleting instructions. InstModCallbacks callbacks; -public: - InstructionDeleter() : deadInstructions() {} + bool assumeFixedLifetimes = true; - InstructionDeleter(InstModCallbacks &&callbacks) - : deadInstructions(), callbacks(std::move(callbacks)) {} +public: + InstructionDeleter(bool assumeFixedLifetimes = true) + : deadInstructions(), assumeFixedLifetimes(assumeFixedLifetimes) {} + + InstructionDeleter(InstModCallbacks &&callbacks, bool assumeFixedLifetimes = true) + : deadInstructions(), callbacks(std::move(callbacks)), + assumeFixedLifetimes(assumeFixedLifetimes) {} InstModCallbacks &getCallbacks() { return callbacks; } diff --git a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp index 2d209163648..4979d2c948f 100644 --- a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp +++ b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp @@ -1206,7 +1206,7 @@ static bool tryEliminateOSLogMessage(SingleValueInstruction *oslogMessage) { } (void)deletedInstructions.insert(deadInst); }); - InstructionDeleter deleter(std::move(callbacks)); + InstructionDeleter deleter(std::move(callbacks), /*assumeFixedLifetimes=*/ false); unsigned startIndex = 0; while (startIndex < worklist.size()) { @@ -1433,7 +1433,7 @@ suppressGlobalStringTablePointerError(SingleValueInstruction *oslogMessage) { // Replace the globalStringTablePointer builtins by a string_literal // instruction for an empty string and clean up dead code. - InstructionDeleter deleter; + InstructionDeleter deleter(/*assumeFixedLifetimes=*/ false); for (BuiltinInst *bi : globalStringTablePointerInsts) { SILBuilderWithScope builder(bi); StringLiteralInst *stringLiteral = builder.createStringLiteral( diff --git a/lib/SILOptimizer/Utils/InstructionDeleter.cpp b/lib/SILOptimizer/Utils/InstructionDeleter.cpp index ffefbc874ac..e3ee6b954a5 100644 --- a/lib/SILOptimizer/Utils/InstructionDeleter.cpp +++ b/lib/SILOptimizer/Utils/InstructionDeleter.cpp @@ -50,7 +50,8 @@ static bool hasOnlyIncidentalUses(SILInstruction *inst, /// /// TODO: Handle partial_apply [stack] which has a dealloc_stack user. static bool isScopeAffectingInstructionDead(SILInstruction *inst, - bool fixLifetime) { + bool fixLifetime, + bool assumeFixedLifetimes) { SILFunction *fun = inst->getFunction(); assert(fun && "Instruction has no function."); // 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->isLexical()) { + if (result->isLexical() || assumeFixedLifetimes) { auto resultTy = result->getType().getAs(); // Allow deleted dead lexical values when they are trivial no escape types. if (!resultTy || !resultTy->isTrivialNoEscape()) { @@ -186,7 +187,7 @@ static bool isScopeAffectingInstructionDead(SILInstruction *inst, bool InstructionDeleter::trackIfDead(SILInstruction *inst) { bool fixLifetime = inst->getFunction()->hasOwnership(); if (isInstructionTriviallyDead(inst) - || isScopeAffectingInstructionDead(inst, fixLifetime)) { + || isScopeAffectingInstructionDead(inst, fixLifetime, assumeFixedLifetimes)) { assert(!isIncidentalUse(inst) || canTriviallyDeleteOSSAEndScopeInst(inst) && "Incidental uses cannot be removed in isolation. " @@ -333,7 +334,7 @@ bool InstructionDeleter::deleteIfDead(SILInstruction *inst) { bool InstructionDeleter::deleteIfDead(SILInstruction *inst, bool fixLifetime) { if (isInstructionTriviallyDead(inst) - || isScopeAffectingInstructionDead(inst, fixLifetime)) { + || isScopeAffectingInstructionDead(inst, fixLifetime, assumeFixedLifetimes)) { getCallbacks().notifyWillBeDeleted(inst); deleteWithUses(inst, fixLifetime); return true; @@ -349,7 +350,7 @@ namespace swift::test { static FunctionTest DeleterDeleteIfDeadTest( "deleter_delete_if_dead", [](auto &function, auto &arguments, auto &test) { auto *inst = arguments.takeInstruction(); - InstructionDeleter deleter; + InstructionDeleter deleter(/*assumeFixedLifetimes=*/ false); llvm::outs() << "Deleting-if-dead " << *inst; auto deleted = deleter.deleteIfDead(inst); llvm::outs() << "deleteIfDead returned " << deleted << "\n"; diff --git a/test/SILOptimizer/address_lowering.sil b/test/SILOptimizer/address_lowering.sil index 6ce6bc53a62..bcf1037e7f5 100644 --- a/test/SILOptimizer/address_lowering.sil +++ b/test/SILOptimizer/address_lowering.sil @@ -2934,6 +2934,7 @@ bb0: } // CHECK-LABEL: sil [ossa] @test_store_borrow_1_copy_addr : {{.*}} { +// CHECK: alloc_stack // CHECK: [[ADDR:%[^,]+]] = alloc_stack // CHECK: apply undef([[ADDR]]) // CHECK: [[COPY:%[^,]+]] = alloc_stack diff --git a/test/SILOptimizer/copy_propagation.sil b/test/SILOptimizer/copy_propagation.sil index d58c4c78db5..99c16b48ee6 100644 --- a/test/SILOptimizer/copy_propagation.sil +++ b/test/SILOptimizer/copy_propagation.sil @@ -909,9 +909,9 @@ sil [ossa] @canonicalize_source_of_redundant_move_value : $@convention(thin) () 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-NOT: copy_value +// xCHECK-NOT: copy_value // CHECK-LABEL: } // end sil function 'delete_dead_reborrow_copy' sil [ossa] @delete_dead_reborrow_copy : $@convention(thin) (@owned X) -> () { bb0(%instance : @owned $X): diff --git a/test/SILOptimizer/copy_propagation_borrow.sil b/test/SILOptimizer/copy_propagation_borrow.sil index ae313bf7a68..15b7149ba9e 100644 --- a/test/SILOptimizer/copy_propagation_borrow.sil +++ b/test/SILOptimizer/copy_propagation_borrow.sil @@ -168,7 +168,8 @@ bb0(%0 : @guaranteed $NativeObjectPair): // CHECK-LABEL: sil [ossa] @testBorrowOuterUse : {{.*}} { // CHECK: bb0: // CHECK: [[INSTANCE:%.*]] = apply -// CHECK-NOT: begin_borrow +// CHECK: begin_borrow +// CHECK-NEXT: end_borrow // CHECK-NOT: copy // CHECK: apply %{{.*}}([[INSTANCE]]) : $@convention(thin) (@owned C) -> () // CHECK-NOT: destroy @@ -191,7 +192,8 @@ bb0: // // CHECK-LABEL: sil [ossa] @testMultiBlockBorrow : $@convention(thin) (@guaranteed C) -> () { // CHECK: bb0(%0 : @guaranteed $C): -// CHECK-NOT: borrow +// CHECK: borrow +// CHECK-NEXT: end_borrow // CHECK-NOT: copy // CHECK: cond_br undef, bb1, bb2 // CHECK: bb1: @@ -485,14 +487,17 @@ bb3: // CHECK-LABEL: sil [ossa] @testNestedBorrowInsideAndOutsideUse : $@convention(thin) () -> () { // CHECK: [[ALLOC:%.*]] = alloc_ref $C // CHECK: [[B1:%.*]] = begin_borrow [[ALLOC]] : $C -// CHECK-NOT: borrow +// CHECK-NEXT: [[B2:%.*]] = begin_borrow [[ALLOC]] : $C +// CHECK-NOT: copy_value // CHECK: bb1: +// CHECK-NEXT: end_borrow [[B2]] : $C // CHECK-NEXT: end_borrow [[B1]] : $C // CHECK-NEXT: destroy_value [[ALLOC]] : $C // CHECK-NEXT: br bb3 // CHECK: bb2: -// CHECK-NEXT: end_borrow %1 : $C -// CHECK-NEXT: destroy_value %0 : $C +// CHECK-NEXT: end_borrow [[B1]] : $C +// CHECK-NEXT: end_borrow [[B2]] : $C +// CHECK-NEXT: destroy_value [[ALLOC]] : $C // CHECK-NEXT: br bb3 // CHECK: bb3: // CHECK-NOT: destroy @@ -655,11 +660,12 @@ bb3(%borrowphi : @guaranteed $C): // The inner copy's lifetime will be canonicalized, removing // outercopy. // +// TODO: why can't the first copy_value not be removed? // CHECK-LABEL: sil [ossa] @testDeadCopyAfterReborrow : $@convention(thin) () -> () { // CHECK: [[ALLOC:%.*]] = alloc_ref $C // CHECK: bb3([[BORROWPHI:%.*]] : @reborrow $C): // CHECK: [[R:%.*]] = borrowed [[BORROWPHI]] : $C from (%0 : $C) -// CHECK-NOT: copy_value +// xCHECK-NOT: copy_value // CHECK: end_borrow [[R]] : $C // CHECK-NOT: copy_value // CHECK: destroy_value [[ALLOC]] : $C @@ -696,13 +702,14 @@ bb3(%borrowphi : @guaranteed $C): // end borrowphi // 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: [[ALLOC:%.*]] = alloc_ref $C // CHECK: bb3([[BORROWPHI:%.*]] : @reborrow $C): // CHECK: [[R:%.*]] = borrowed [[BORROWPHI]] : $C from (%0 : $C) -// CHECK-NOT: copy +// xCHECK-NOT: copy // CHECK: end_borrow [[R]] -// CHECK-NEXT: destroy_value [[ALLOC]] : $C +// CHECK: destroy_value [[ALLOC]] : $C // CHECK-LABEL: } // end sil function 'testNestedReborrowOutsideUse' sil [ossa] @testNestedReborrowOutsideUse : $@convention(thin) () -> () { bb0: @@ -883,7 +890,8 @@ bb0: // CHECK-LABEL: sil [ossa] @testForwardBorrow3 : {{.*}} { // CHECK: bb0: // CHECK: [[INSTANCE:%.*]] = apply -// CHECK-NOT: borrow +// CHECK: begin_borrow +// CHECK-NEXT: end_borrow // CHECK-NOT: copy // CHECK: [[DSOUT1:%.*]] = destructure_struct [[INSTANCE]] : $Wrapper // CHECK-NEXT: ([[DSOUT2:%.*]], %{{.*}}) = destructure_struct [[DSOUT1]] : $HasObjectAndInt @@ -911,17 +919,18 @@ bb0: // but one has no outer uses. // 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: bb0: // CHECK: [[INSTANCE:%.*]] = apply -// CHECK-NEXT: ([[HASOBJECTS_0:%[^,]+]], [[HASOBJECTS_1:%[^,]+]]) = destructure_struct [[INSTANCE]] : $MultiWrapper -// CHECK-NEXT: destroy_value [[HASOBJECTS_1]] : $HasObjects -// CHECK-NEXT: ([[VAL:%.*]], [[OBJECT_1:%[^,]+]]) = destructure_struct [[HASOBJECTS_0]] : $HasObjects -// CHECK-NEXT: destroy_value [[OBJECT_1]] : $C -// CHECK-NOT: borrow -// CHECK: apply %{{.*}}([[VAL]]) : $@convention(thin) (@owned C) -> () -// CHECK-NOT: destroy +// xCHECK-NEXT: ([[HASOBJECTS_0:%[^,]+]], [[HASOBJECTS_1:%[^,]+]]) = destructure_struct [[INSTANCE]] : $MultiWrapper +// xCHECK-NEXT: destroy_value [[HASOBJECTS_1]] : $HasObjects +// xCHECK-NEXT: ([[VAL:%.*]], [[OBJECT_1:%[^,]+]]) = destructure_struct [[HASOBJECTS_0]] : $HasObjects +// xCHECK-NEXT: destroy_value [[OBJECT_1]] : $C +// xCHECK-NOT: borrow +// xCHECK: apply %{{.*}}([[VAL]]) : $@convention(thin) (@owned C) -> () +// xCHECK-NOT: destroy // CHECK-LABEL: } // end sil function 'testForwardBorrow4' sil [ossa] @testForwardBorrow4 : $@convention(thin) () -> () { bb0: @@ -953,6 +962,9 @@ bb0: // CHECK: bb0: // CHECK: [[INSTANCE:%.*]] = apply // 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: [[BORROW:%[^,]+]] = begin_borrow [[OBJECT]] : $C // 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: bb0: // CHECK: [[INSTANCE:%.*]] = apply +// CHECK-NEXT: begin_borrow +// CHECK-NEXT: end_borrow // CHECK-NEXT: [[DESTRUCTURE:%.*]] = destructure_struct [[INSTANCE]] : $String // CHECK-NEXT: [[UTF16:%.*]] = struct $String.UTF16View ([[DESTRUCTURE]] : $_StringGuts) // CHECK-NEXT: br bb1 // CHECK: bb1: // 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: [[OBJ:%.*]] = destructure_struct [[GUTS]] : $_StringGuts // CHECK-NEXT: [[BORROW:%.*]] = begin_borrow [[OBJ]] : $_StringObject diff --git a/test/SILOptimizer/copy_propagation_opaque.sil b/test/SILOptimizer/copy_propagation_opaque.sil index 36d646ed090..0a155514661 100644 --- a/test/SILOptimizer/copy_propagation_opaque.sil +++ b/test/SILOptimizer/copy_propagation_opaque.sil @@ -419,6 +419,8 @@ bb0: // CHECK-LABEL: sil [ossa] @testBorrowCopy : {{.*}} { // CHECK-LABEL: bb0: // CHECK: [[INSTANCE:%[^,]+]] = apply +// CHECK-NEXT: begin_borrow +// CHECK-NEXT: end_borrow // CHECK-NEXT: destroy_value [[INSTANCE]] : $T // CHECK-NEXT: tuple () // CHECK-NEXT: return diff --git a/test/SILOptimizer/dead_code_elimination_nontrivial_ossa.sil b/test/SILOptimizer/dead_code_elimination_nontrivial_ossa.sil index 7089ac1fbb6..acae05195e4 100644 --- a/test/SILOptimizer/dead_code_elimination_nontrivial_ossa.sil +++ b/test/SILOptimizer/dead_code_elimination_nontrivial_ossa.sil @@ -1411,34 +1411,3 @@ bb5(%16 : @reborrow $FakeOptional, %17 : @reborrow $FakeOptional): 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 -} diff --git a/test/SILOptimizer/dead_code_elimination_ossa.sil b/test/SILOptimizer/dead_code_elimination_ossa.sil index 3f43f7029f4..6ea606affec 100644 --- a/test/SILOptimizer/dead_code_elimination_ossa.sil +++ b/test/SILOptimizer/dead_code_elimination_ossa.sil @@ -445,12 +445,8 @@ bb0(%0 : @owned $MO): 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: destroy_addr +// CHECK: load [take] // CHECK-LABEL: } // end sil function 'keep_new_destroy_addr' sil [ossa] @keep_new_destroy_addr : $@convention(thin) () -> () { bb0: diff --git a/test/SILOptimizer/drop_deinit_opt.sil b/test/SILOptimizer/drop_deinit_opt.sil index 1fe18325c32..d1e39cedc96 100644 --- a/test/SILOptimizer/drop_deinit_opt.sil +++ b/test/SILOptimizer/drop_deinit_opt.sil @@ -34,7 +34,9 @@ bb0(%0 : @owned $FileDescriptor): } // 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' sil hidden [ossa] @fd_deinit2 : $@convention(method) (@owned FileDescriptor) -> () { bb0(%0 : @owned $FileDescriptor): diff --git a/test/SILOptimizer/predictable_deadalloc_elim_ownership.sil b/test/SILOptimizer/predictable_deadalloc_elim_ownership.sil index 0ea7230d614..28ca19186c2 100644 --- a/test/SILOptimizer/predictable_deadalloc_elim_ownership.sil +++ b/test/SILOptimizer/predictable_deadalloc_elim_ownership.sil @@ -165,7 +165,8 @@ bb0(%0 : @owned $Builtin.NativeObject, %1 : $*Builtin.NativeObject): // CHECK: bb0([[ARG0:%.*]] : @owned $Builtin.NativeObject, [[ARG1:%.*]] : $*Builtin.NativeObject): // CHECK-NOT: alloc_stack // CHECK: destroy_value [[ARG0]] -// CHECK: destroy_addr [[ARG1]] +// CHECK: [[L:%.*]] = load [take] [[ARG1]] +// CHECK: destroy_value [[L]] // CHECK: } // end sil function 'simple_init_take' sil [ossa] @simple_init_take : $@convention(thin) (@owned Builtin.NativeObject, @in Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject, %1 : $*Builtin.NativeObject): diff --git a/test/SILOptimizer/silgen_cleanup.sil b/test/SILOptimizer/silgen_cleanup.sil index 3f928878be8..55bda0a14b5 100644 --- a/test/SILOptimizer/silgen_cleanup.sil +++ b/test/SILOptimizer/silgen_cleanup.sil @@ -283,9 +283,9 @@ bb0(%0 : $*X3, %1 : @guaranteed $Builtin.NativeObject): // // CHECK-LABEL: sil [ossa] @testDestructureTupleNoCrash : $@convention(thin) (@owned (Builtin.NativeObject, Builtin.NativeObject)) -> () { // CHECKOPT: bb0( -// CHECKOPT-NEXT: destroy_value -// CHECKOPT-NEXT: tuple -// CHECKOPT-NEXT: return +// CHECKOPT-NEXT: (%1, %2) = destructure_tuple +// CHECKOPT: destroy_value %2 +// CHECKOPT-NEXT: destroy_value %1 // CHECK: } // end sil function 'testDestructureTupleNoCrash' sil [ossa] @testDestructureTupleNoCrash : $@convention(thin) (@owned (Builtin.NativeObject, Builtin.NativeObject)) -> () { bb0(%0 : @owned $(Builtin.NativeObject, Builtin.NativeObject)):