From 9f87e0a8c653e66a432b64240900803cf86fae62 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 10 Jan 2025 17:10:44 -0800 Subject: [PATCH 1/7] [SIL] Print AST types with print-module-on-error. --- lib/SIL/Utils/PrettyStackTrace.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SIL/Utils/PrettyStackTrace.cpp b/lib/SIL/Utils/PrettyStackTrace.cpp index e851fd4fa6483..46bffa6c17bab 100644 --- a/lib/SIL/Utils/PrettyStackTrace.cpp +++ b/lib/SIL/Utils/PrettyStackTrace.cpp @@ -96,7 +96,7 @@ void PrettyStackTraceSILFunction::printFunctionInfo(llvm::raw_ostream &out) cons if (SILPrintOnError) func->print(out); if (SILPrintModuleOnError) - func->getModule().print(out); + func->getModule().print(out, func->getModule().getSwiftModule()); } void PrettyStackTraceSILNode::print(llvm::raw_ostream &out) const { From afe95135a006211fdc7e1b8454bac4ee99ead674 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 10 Jan 2025 17:11:15 -0800 Subject: [PATCH 2/7] [NFC] SILCombine: Ennamespace canonicalize class. --- lib/SILOptimizer/SILCombiner/SILCombine.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/SILOptimizer/SILCombiner/SILCombine.cpp b/lib/SILOptimizer/SILCombiner/SILCombine.cpp index 4411f69026703..0c6aaf24e021e 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombine.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombine.cpp @@ -120,6 +120,8 @@ void SILCombiner::addReachableCodeToWorklist(SILBasicBlock *BB) { // Implementation //===----------------------------------------------------------------------===// +namespace swift { + // Define a CanonicalizeInstruction subclass for use in SILCombine. class SILCombineCanonicalize final : CanonicalizeInstruction { SmallSILInstructionWorklist<256> &Worklist; @@ -162,6 +164,8 @@ class SILCombineCanonicalize final : CanonicalizeInstruction { } }; +} // end namespace swift + SILCombiner::SILCombiner(SILFunctionTransform *trans, bool removeCondFails, bool enableCopyPropagation) : parentTransform(trans), From 5ce6c6cf141dadb8a9e304351304d91fb26b732d Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 10 Jan 2025 17:12:27 -0800 Subject: [PATCH 3/7] [NFC] SILCombine: Extract processing function. The pass is structured to drain an instruction worklist and perform a sequence of operations on each popped instruction. Extract that sequence of operations into a new processInstruction function. Enables testing the sequence on a single instruction. --- lib/SILOptimizer/SILCombiner/SILCombine.cpp | 144 ++++++++++---------- lib/SILOptimizer/SILCombiner/SILCombiner.h | 3 + 2 files changed, 78 insertions(+), 69 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombine.cpp b/lib/SILOptimizer/SILCombiner/SILCombine.cpp index 0c6aaf24e021e..d82f21ff26276 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombine.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombine.cpp @@ -418,90 +418,96 @@ bool SILCombiner::doOneIteration(SILFunction &F, unsigned Iteration) { if (!parentTransform->continueWithNextSubpassRun(I)) return false; - // Check to see if we can DCE the instruction. - if (isInstructionTriviallyDead(I)) { - LLVM_DEBUG(llvm::dbgs() << "SC: DCE: " << *I << '\n'); - eraseInstFromFunction(*I); - ++NumDeadInst; - MadeChange = true; - continue; - } + processInstruction(I, scCanonicalize, MadeChange); + } + + Worklist.resetChecked(); + return MadeChange; +} + +void SILCombiner::processInstruction(SILInstruction *I, + SILCombineCanonicalize &scCanonicalize, + bool &MadeChange) { + // Check to see if we can DCE the instruction. + if (isInstructionTriviallyDead(I)) { + LLVM_DEBUG(llvm::dbgs() << "SC: DCE: " << *I << '\n'); + eraseInstFromFunction(*I); + ++NumDeadInst; + MadeChange = true; + return; + } #ifndef NDEBUG - std::string OrigIStr; + std::string OrigIStr; #endif - LLVM_DEBUG(llvm::raw_string_ostream SS(OrigIStr); I->print(SS); - OrigIStr = SS.str();); - LLVM_DEBUG(llvm::dbgs() << "SC: Visiting: " << OrigIStr << '\n'); + LLVM_DEBUG(llvm::raw_string_ostream SS(OrigIStr); I->print(SS); + OrigIStr = SS.str();); + LLVM_DEBUG(llvm::dbgs() << "SC: Visiting: " << OrigIStr << '\n'); - // Canonicalize the instruction. - if (scCanonicalize.tryCanonicalize(I)) { - MadeChange = true; - continue; - } + // Canonicalize the instruction. + if (scCanonicalize.tryCanonicalize(I)) { + MadeChange = true; + return; + } - // If we have reached this point, all attempts to do simple simplifications - // have failed. First if we have an owned forwarding value, we try to - // sink. Otherwise, we perform the actual SILCombine operation. - if (EnableSinkingOwnedForwardingInstToUses) { - // If we have an ownership forwarding single value inst that forwards - // through its first argument and it is trivially duplicatable, see if it - // only has consuming uses. If so, we can duplicate the instruction into - // the consuming use blocks and destroy any destroy_value uses of it that - // we see. This makes it easier for SILCombine to fold instructions with - // owned parameters since chains of these values will be in the same - // block. - if (auto *svi = dyn_cast(I)) { - if (auto fwdOp = ForwardingOperation(svi)) { - if (fwdOp.getSingleForwardingOperand() && - SILValue(svi)->getOwnershipKind() == OwnershipKind::Owned) { - // Try to sink the value. If we sank the value and deleted it, - // continue. If we didn't optimize or sank but we are still able to - // optimize further, we fall through to SILCombine below. - if (trySinkOwnedForwardingInst(svi)) { - continue; - } + // If we have reached this point, all attempts to do simple simplifications + // have failed. First if we have an owned forwarding value, we try to + // sink. Otherwise, we perform the actual SILCombine operation. + if (EnableSinkingOwnedForwardingInstToUses) { + // If we have an ownership forwarding single value inst that forwards + // through its first argument and it is trivially duplicatable, see if it + // only has consuming uses. If so, we can duplicate the instruction into + // the consuming use blocks and destroy any destroy_value uses of it that + // we see. This makes it easier for SILCombine to fold instructions with + // owned parameters since chains of these values will be in the same + // block. + if (auto *svi = dyn_cast(I)) { + if (auto fwdOp = ForwardingOperation(svi)) { + if (fwdOp.getSingleForwardingOperand() && + SILValue(svi)->getOwnershipKind() == OwnershipKind::Owned) { + // Try to sink the value. If we sank the value and deleted it, + // return. If we didn't optimize or sank but we are still able to + // optimize further, we fall through to SILCombine below. + if (trySinkOwnedForwardingInst(svi)) { + return; } } } } + } - // Then begin... SILCombine. - Builder.setInsertionPoint(I); + // Then begin... SILCombine. + Builder.setInsertionPoint(I); - SILInstruction *currentInst = I; - if (SILInstruction *Result = visit(I)) { - ++NumCombined; - // Should we replace the old instruction with a new one? - Worklist.replaceInstructionWithInstruction(I, Result + SILInstruction *currentInst = I; + if (SILInstruction *Result = visit(I)) { + ++NumCombined; + // Should we replace the old instruction with a new one? + Worklist.replaceInstructionWithInstruction(I, Result #ifndef NDEBUG - , - OrigIStr + , + OrigIStr #endif - ); - currentInst = Result; - MadeChange = true; - } + ); + currentInst = Result; + MadeChange = true; + } - // Eliminate copies created that this SILCombine iteration may have - // introduced during OSSA-RAUW. - canonicalizeOSSALifetimes(currentInst->isDeleted() ? nullptr : currentInst); - - // Builder's tracking list has been accumulating instructions created by the - // during this SILCombine iteration. To finish this iteration, go through - // the tracking list and add its contents to the worklist and then clear - // said list in preparation for the next iteration. - for (SILInstruction *I : *Builder.getTrackingList()) { - if (!I->isDeleted()) { - LLVM_DEBUG(llvm::dbgs() - << "SC: add " << *I << " from tracking list to worklist\n"); - Worklist.add(I); - } + // Eliminate copies created that this SILCombine iteration may have + // introduced during OSSA-RAUW. + canonicalizeOSSALifetimes(currentInst->isDeleted() ? nullptr : currentInst); + + // Builder's tracking list has been accumulating instructions created by the + // during this SILCombine iteration. To finish this iteration, go through + // the tracking list and add its contents to the worklist and then clear + // said list in preparation for the next iteration. + for (SILInstruction *I : *Builder.getTrackingList()) { + if (!I->isDeleted()) { + LLVM_DEBUG(llvm::dbgs() + << "SC: add " << *I << " from tracking list to worklist\n"); + Worklist.add(I); } - Builder.getTrackingList()->clear(); } - - Worklist.resetChecked(); - return MadeChange; + Builder.getTrackingList()->clear(); } namespace swift::test { diff --git a/lib/SILOptimizer/SILCombiner/SILCombiner.h b/lib/SILOptimizer/SILCombiner/SILCombiner.h index 0f8eaed69619e..7b84a08a7bfa2 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombiner.h +++ b/lib/SILOptimizer/SILCombiner/SILCombiner.h @@ -412,6 +412,9 @@ class SILCombiner : /// Perform one SILCombine iteration. bool doOneIteration(SILFunction &F, unsigned Iteration); + void processInstruction(SILInstruction *instruction, + SILCombineCanonicalize &scCanonicalize, + bool &MadeChange); /// Add reachable code to the worklist. Meant to be used when starting to /// process a new function. void addReachableCodeToWorklist(SILBasicBlock *BB); From 6deba02792582f77437d053ceaba57dfc54e8f4d Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 10 Jan 2025 17:17:54 -0800 Subject: [PATCH 4/7] [Test] Renamed test case. --- lib/SILOptimizer/SILCombiner/SILCombine.cpp | 9 +++++---- test/SILOptimizer/sil_combine_apply_unit.sil | 10 +++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombine.cpp b/lib/SILOptimizer/SILCombiner/SILCombine.cpp index d82f21ff26276..575bfc92009c7 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombine.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombine.cpp @@ -512,11 +512,12 @@ void SILCombiner::processInstruction(SILInstruction *I, namespace swift::test { // Arguments: -// - instruction: the instruction to be canonicalized +// - instruction: the instruction to be visited // Dumps: -// - the function after the canonicalization is attempted -static FunctionTest SILCombineCanonicalizeInstruction( - "sil_combine_instruction", [](auto &function, auto &arguments, auto &test) { +// - the function after the visitation is attempted +static FunctionTest SILCombineVisitInstruction( + "sil_combine_visit_instruction", + [](auto &function, auto &arguments, auto &test) { SILCombiner combiner(test.getPass(), false, false); auto inst = arguments.takeInstruction(); combiner.Builder.setInsertionPoint(inst); diff --git a/test/SILOptimizer/sil_combine_apply_unit.sil b/test/SILOptimizer/sil_combine_apply_unit.sil index 721c204e4c362..594ec6d19a60c 100644 --- a/test/SILOptimizer/sil_combine_apply_unit.sil +++ b/test/SILOptimizer/sil_combine_apply_unit.sil @@ -40,7 +40,7 @@ entry(%input : $*Input): %nunca = alloc_stack $Nunca %callee = function_ref @rdar127452206_callee : $@convention(thin) @Sendable @substituted <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0) -> (@out τ_0_2, @error_indirect τ_0_1) for %convert = convert_function %callee : $@convention(thin) @Sendable @substituted <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0) -> (@out τ_0_2, @error_indirect τ_0_1) for to $@convention(thin) @substituted <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0) -> (@out τ_0_2, @error_indirect τ_0_1) for // user: %216 - specify_test "sil_combine_instruction @instruction[4]" + specify_test "sil_combine_visit_instruction @instruction[4]" apply [nothrow] %convert(%output, %nunca, %input) : $@convention(thin) @substituted <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0) -> (@out τ_0_2, @error_indirect τ_0_1) for dealloc_stack %nunca : $*Nunca dealloc_stack %output : $*Output @@ -62,7 +62,7 @@ entry(%c : @owned $C): %borrowMaybeC = function_ref @borrowMaybeC : $@convention(thin) (@guaranteed Optional) -> () %borrowC = convert_function %borrowMaybeC to $@convention(thin) (@guaranteed C) -> () %void = apply %borrowC(%c) : $@convention(thin) (@guaranteed C) -> () - specify_test "sil_combine_instruction %void" + specify_test "sil_combine_visit_instruction %void" destroy_value %c %retval = tuple () return %retval @@ -89,7 +89,7 @@ entry(%c : @owned $C, %c2 : @owned $C): %borrowMaybeC2 = function_ref @borrowMaybeC2 : $@convention(thin) (@guaranteed Optional, @guaranteed Optional) -> () %borrowC2 = convert_function %borrowMaybeC2 to $@convention(thin) (@guaranteed C, @guaranteed C) -> () %void = apply %borrowC2(%c, %c2) : $@convention(thin) (@guaranteed C, @guaranteed C) -> () - specify_test "sil_combine_instruction %void" + specify_test "sil_combine_visit_instruction %void" destroy_value %c destroy_value %c2 %retval = tuple () @@ -116,7 +116,7 @@ sil [ossa] @convert_function__to_optional__owned_as_guaranteed__3 : $@convention entry(%c : @owned $C): %borrowMaybeC = function_ref @borrowMaybeCThrowing : $@convention(thin) (@guaranteed Optional) -> (@error Error) %borrowC = convert_function %borrowMaybeC to $@convention(thin) (@guaranteed C) -> (@error Error) - specify_test "sil_combine_instruction @instruction" + specify_test "sil_combine_visit_instruction @instruction" try_apply %borrowC(%c) : $@convention(thin) (@guaranteed C) -> (@error Error), normal success, error failure @@ -160,7 +160,7 @@ sil [ossa] @convert_function__to_optional__owned_as_guaranteed__4 : $@convention entry(%c : @owned $C, %c2 : @owned $C): %borrowMaybeC2 = function_ref @borrowMaybeC2Throwing : $@convention(thin) (@guaranteed Optional, @guaranteed Optional) -> (@error Error) %borrowC2 = convert_function %borrowMaybeC2 to $@convention(thin) (@guaranteed C, @guaranteed C) -> (@error Error) - specify_test "sil_combine_instruction @instruction" + specify_test "sil_combine_visit_instruction @instruction" try_apply %borrowC2(%c, %c2) : $@convention(thin) (@guaranteed C, @guaranteed C) -> (@error Error), normal success, error failure From af5cd31f3c6087421880109321bac890acd697bf Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 13 Jan 2025 17:00:23 -0800 Subject: [PATCH 5/7] [Test] Added test for SILCombiner::processInst. --- lib/SILOptimizer/SILCombiner/SILCombine.cpp | 30 +++++++++++++++++++++ lib/SILOptimizer/SILCombiner/SILCombiner.h | 6 +++++ 2 files changed, 36 insertions(+) diff --git a/lib/SILOptimizer/SILCombiner/SILCombine.cpp b/lib/SILOptimizer/SILCombiner/SILCombine.cpp index 575bfc92009c7..b58ffa9aa22ef 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombine.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombine.cpp @@ -510,6 +510,36 @@ void SILCombiner::processInstruction(SILInstruction *I, Builder.getTrackingList()->clear(); } +namespace swift::test { +struct SILCombinerProcessInstruction { + void operator()(SILCombiner &combiner, SILInstruction *inst, + SILCombineCanonicalize &canonicalizer, bool &madeChange) { + combiner.processInstruction(inst, canonicalizer, madeChange); + } +}; +// Arguments: +// - instruction: the instruction to be processed +// - bool: remove cond_fails +// - bool: enable lifetime canonicalization +// Dumps: +// - the function after the processing is attempted +static FunctionTest SILCombineProcessInstruction( + "sil_combine_process_instruction", + [](auto &function, auto &arguments, auto &test) { + auto inst = arguments.takeInstruction(); + auto removeCondFails = arguments.takeBool(); + auto enableCopyPropagation = arguments.takeBool(); + SILCombiner combiner(test.getPass(), removeCondFails, + enableCopyPropagation); + SILCombineCanonicalize canonicalizer(combiner.Worklist, + *test.getDeadEndBlocks()); + bool madeChange = false; + SILCombinerProcessInstruction()(combiner, inst, canonicalizer, + madeChange); + function.dump(); + }); +} // end namespace swift::test + namespace swift::test { // Arguments: // - instruction: the instruction to be visited diff --git a/lib/SILOptimizer/SILCombiner/SILCombiner.h b/lib/SILOptimizer/SILCombiner/SILCombiner.h index 7b84a08a7bfa2..d1f2c9ba2d473 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombiner.h +++ b/lib/SILOptimizer/SILCombiner/SILCombiner.h @@ -47,6 +47,10 @@ namespace swift { class AliasAnalysis; +class SILCombineCanonicalize; +namespace test { +struct SILCombinerProcessInstruction; +} /// This is a class which maintains the state of the combiner and simplifies /// many operations such as removing/adding instructions and syncing them with @@ -415,6 +419,8 @@ class SILCombiner : void processInstruction(SILInstruction *instruction, SILCombineCanonicalize &scCanonicalize, bool &MadeChange); + friend test::SILCombinerProcessInstruction; + /// Add reachable code to the worklist. Meant to be used when starting to /// process a new function. void addReachableCodeToWorklist(SILBasicBlock *BB); From 4135540f6271e6a5c4481d6707cf23c792173418 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 13 Jan 2025 15:25:56 -0800 Subject: [PATCH 6/7] [NFC] PrunedLiveness: Permit postDomBlocks dupes. According to the documentation, ``` It's ok if postDomBlocks has duplicates or extraneous blocks, as long as they jointly post-dominate all live blocks that aren't on dead-end paths. ``` Previously, though, duplicates resulted in trapping: each element of `postDomBlocks` is pushed into a `BasicBlockWorklist`; when the second occurrence of an element in `postDomBlocks` was encountered, `BasicBlockWorklist::push` would trigger an assertion failure. Here, `pushIfNotVisited` is called instead. --- lib/SIL/Utils/PrunedLiveness.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SIL/Utils/PrunedLiveness.cpp b/lib/SIL/Utils/PrunedLiveness.cpp index 3e5e2e690a73d..5faa5c75b74b1 100644 --- a/lib/SIL/Utils/PrunedLiveness.cpp +++ b/lib/SIL/Utils/PrunedLiveness.cpp @@ -860,7 +860,7 @@ void PrunedLiveRange::computeBoundary( // Visit each post-dominating block as the starting point for a // backward CFG traversal. for (auto *block : postDomBlocks) { - blockWorklist.push(block); + blockWorklist.pushIfNotVisited(block); } while (auto *block = blockWorklist.pop()) { // Process each block that has not been visited and is not LiveOut. From ebf602803e4610233ba7d4a8786798e323f1126e Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 13 Jan 2025 16:38:53 -0800 Subject: [PATCH 7/7] [OSSACanOwned] Fix liveness passed to completion. To determine where a lifetime ends within dead-end blocks, OSSACanonicalizeOwned uses OSSACompleteLifetime's visitAvailabilityBoundary. This API takes a liveness which it uses to walk forward to the availability boundary. Specifically, the liveness passed from OSSACanonicalizeOwned to OSSACompleteLifetime is a variation of OSSACanonicalizeOwned's own liveness (it has destroys added). There is a mismatch in the characteristics of livenesses created by OSSACanonicalizeOwned and OSSACompleteLifetime: The former deals with not only direct uses of a value but also uses of its copies; that introduces the possibility for consuming uses in the middle of liveness. The latter on the other hand deals only with uses of a single value (nestedly, but at each level of nesting only a single value). Passing a liveness from the former to the latter without handling this mismatch is incorrect: OSSACompleteLifetime understands consuming uses to always end a lifetime, even when they are in the middle of a copy-extended liveness. The result is that OSSACompleteLifetime produces non-sensical results when provided with such a liveness. To address this, fixup the liveness passed from OSSACanonicalizeOwned to OSSACompleteLifetime by demoting consuming uses that appear within (that's to say _not_ on the boundary) of liveness to non-consuming uses. rdar://142846936 --- .../Utils/CanonicalizeOSSALifetime.cpp | 37 +++++++++++++++++++ .../canonicalize_ossa_lifetime_unit.sil | 27 ++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp b/lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp index 7b2c35a9d722a..2ad7fc09da167 100644 --- a/lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp +++ b/lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp @@ -285,6 +285,43 @@ void CanonicalizeOSSALifetime::extendLivenessToDeadEnds() { completeLiveness.updateForUse(destroy, /*lifetimeEnding*/ true); } + // Demote consuming uses within complete liveness to non-consuming uses. + // + // OSSALifetimeCompletion considers the lifetime of a single value. Such + // lifetimes never continue beyond consumes. + std::optional> lastUsers; + auto isConsumeOnBoundary = [&](SILInstruction *instruction) -> bool { + if (!lastUsers) { + // Avoid computing lastUsers if possible. + auto *function = getCurrentDef()->getFunction(); + auto *deadEnds = deadEndBlocksAnalysis->get(function); + llvm::SmallVector completeConsumingBlocks( + consumingBlocks.getArrayRef()); + for (auto &block : *function) { + if (!deadEnds->isDeadEnd(&block)) + continue; + completeConsumingBlocks.push_back(&block); + } + PrunedLivenessBoundary boundary; + liveness->computeBoundary(boundary, completeConsumingBlocks); + + lastUsers.emplace(); + for (auto *lastUser : boundary.lastUsers) { + lastUsers->insert(lastUser); + } + } + return lastUsers->contains(instruction); + }; + for (auto pair : liveness->getAllUsers()) { + if (!pair.second.isEnding()) + continue; + auto *instruction = pair.first; + if (isConsumeOnBoundary(instruction)) + continue; + // Demote instruction's lifetime-ending-ness to non-lifetime-ending. + completeLiveness.updateForUse(pair.first, /*lifetimeEnding=*/false); + } + OSSALifetimeCompletion::visitAvailabilityBoundary( getCurrentDef(), completeLiveness, [&](auto *unreachable, auto end) { if (end == OSSALifetimeCompletion::LifetimeEnd::Boundary) { diff --git a/test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil b/test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil index 470b062eb7c04..55324facef221 100644 --- a/test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil +++ b/test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil @@ -857,3 +857,30 @@ exit: %retval = tuple () return %retval : $() } + +// CHECK-LABEL: begin running test {{.*}} on consume_copy_before_use_in_dead_end +// CHECK-LABEL: sil [ossa] @consume_copy_before_use_in_dead_end : {{.*}} { +// CHECK-NOT: destroy_value [dead_end] +// CHECK-LABEL: } // end sil function 'consume_copy_before_use_in_dead_end' +// CHECK-LABEL: end running test {{.*}} on consume_copy_before_use_in_dead_end +sil [ossa] @consume_copy_before_use_in_dead_end : $@convention(thin) (@owned C) -> () { +entry(%c : @owned $C): + cond_br undef, exit, die + +exit: + destroy_value %c + %retval = tuple () + return %retval + +die: + %move = move_value [lexical] %c + %copy = copy_value %move + specify_test "canonicalize_ossa_lifetime true false true %move" + apply undef(%move) : $@convention(thin) (@owned C) -> () + %addr = alloc_stack $C + %token = store_borrow %copy to %addr + apply undef() : $@convention(thin) () -> () + %reload = load_borrow %token + apply undef(%reload) : $@convention(thin) (@guaranteed C) -> () + unreachable +}