From a091814fbaae3d50142e869440e4fde082369073 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 11 Feb 2022 08:12:41 -0800 Subject: [PATCH 1/2] [Gardening] Removed extraneous import. --- lib/SILOptimizer/Utils/LexicalDestroyFolding.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/SILOptimizer/Utils/LexicalDestroyFolding.cpp b/lib/SILOptimizer/Utils/LexicalDestroyFolding.cpp index 690fbd497521b..1c623ab6b31aa 100644 --- a/lib/SILOptimizer/Utils/LexicalDestroyFolding.cpp +++ b/lib/SILOptimizer/Utils/LexicalDestroyFolding.cpp @@ -77,7 +77,6 @@ /// TODO: Handle partial_apply, try_apply, and begin_apply. //===----------------------------------------------------------------------===// -#include "swift/Basic/GraphNodeWorklist.h" #include "swift/SIL/BasicBlockDatastructures.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/OwnershipUtils.h" From 4e2667b4e7190020c8fd5d9ac7e1eddd05fb0707 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 11 Feb 2022 13:29:59 -0800 Subject: [PATCH 2/2] [CopyForwarding] Removed destroy hoisting. It has been replaced by the destroy hoisting in SSADestroyHoisting. --- .../Transforms/CopyForwarding.cpp | 227 +----------- test/SILOptimizer/copyforward.sil | 2 +- test/SILOptimizer/copyforward_ossa.sil | 346 +----------------- test/SILOptimizer/spec_conf1.swift | 2 +- test/SILOptimizer/spec_conf2.swift | 2 +- 5 files changed, 16 insertions(+), 563 deletions(-) diff --git a/lib/SILOptimizer/Transforms/CopyForwarding.cpp b/lib/SILOptimizer/Transforms/CopyForwarding.cpp index 84a99f2d1ac04..c3fd9a781e7b5 100644 --- a/lib/SILOptimizer/Transforms/CopyForwarding.cpp +++ b/lib/SILOptimizer/Transforms/CopyForwarding.cpp @@ -86,8 +86,6 @@ using namespace swift; // Temporary debugging flag until this pass is better tested. static llvm::cl::opt EnableCopyForwarding("enable-copyforwarding", llvm::cl::init(true)); -static llvm::cl::opt EnableDestroyHoisting("enable-destroyhoisting", - llvm::cl::init(true)); /// \return true if the given copy source value can only be accessed via the /// given def (this def uniquely identifies the object). @@ -513,7 +511,6 @@ class CopyForwarding { PostOrderAnalysis *PostOrder; DominanceAnalysis *DomAnalysis; RCIdentityAnalysis *RCIAnalysis; - bool DoGlobalHoisting; bool HasChanged; bool HasChangedCFG; @@ -535,7 +532,6 @@ class CopyForwarding { SmallPtrSet SrcUserInsts; SmallPtrSet SrcDebugValueInsts; SmallVector TakePoints; - SmallPtrSet StoredValueUserInsts; SmallVector DestroyPoints; SmallPtrSet DeadInBlocks; @@ -579,7 +575,7 @@ class CopyForwarding { CopyForwarding(PostOrderAnalysis *PO, DominanceAnalysis *DA, RCIdentityAnalysis *RCIAnalysis) : PostOrder(PO), DomAnalysis(DA), RCIAnalysis(RCIAnalysis), - DoGlobalHoisting(false), HasChanged(false), HasChangedCFG(false), + HasChanged(false), HasChangedCFG(false), IsSrcLoadedFrom(false), HasUnknownStoredValue(false), HasForwardedToCopy(false), CurrentCopy(nullptr) {} @@ -590,7 +586,6 @@ class CopyForwarding { // some alloc_stack cases after global destroy hoisting. CopyForwarding will // be reapplied after the transparent function is inlined at which point // global hoisting will be done. - DoGlobalHoisting = !F->isTransparent(); if (HasChangedCFG) { // We are only invalidating the analysis that we use internally. // We'll invalidate the analysis that are used by other passes at the end. @@ -605,7 +600,6 @@ class CopyForwarding { SrcUserInsts.clear(); SrcDebugValueInsts.clear(); TakePoints.clear(); - StoredValueUserInsts.clear(); DestroyPoints.clear(); DeadInBlocks.clear(); CurrentCopy = nullptr; @@ -621,15 +615,13 @@ class CopyForwarding { void forwardCopiesOf(SILValue Def, SILFunction *F); protected: - bool propagateCopy(CopyAddrInst *CopyInst, bool hoistingDestroy); + bool propagateCopy(CopyAddrInst *CopyInst); CopyAddrInst *findCopyIntoDeadTemp( CopyAddrInst *destCopy, SmallVectorImpl &debugInstsToDelete); bool forwardDeadTempCopy(CopyAddrInst *destCopy); bool forwardPropagateCopy(); bool backwardPropagateCopy(); - bool hoistDestroy(SILInstruction *DestroyPoint, SILLocation DestroyLoc, - SmallVectorImpl &debugInstsToDelete); bool isSourceDeadAtCopy(); @@ -671,7 +663,7 @@ class CopyDestUserVisitor : public AddressUserVisitor { /// If the forwarded copy is not an [init], then insert a destroy of the copy's /// dest. bool CopyForwarding:: -propagateCopy(CopyAddrInst *CopyInst, bool hoistingDestroy) { +propagateCopy(CopyAddrInst *CopyInst) { if (!EnableCopyForwarding) return false; @@ -693,7 +685,7 @@ propagateCopy(CopyAddrInst *CopyInst, bool hoistingDestroy) { // Handle copy-of-copy without analyzing uses. // Assumes that CurrentCopy->getSrc() is dead after CurrentCopy. - assert(CurrentCopy->isTakeOfSrc() || hoistingDestroy); + assert(CurrentCopy->isTakeOfSrc()); if (forwardDeadTempCopy(CurrentCopy)) { HasChanged = true; ++NumDeadTemp; @@ -703,8 +695,7 @@ propagateCopy(CopyAddrInst *CopyInst, bool hoistingDestroy) { if (forwardPropagateCopy()) { LLVM_DEBUG(llvm::dbgs() << " Forwarding Copy:" << *CurrentCopy); if (!CurrentCopy->isInitializationOfDest()) { - // Replace the original copy with a destroy. We may be able to hoist it - // more in another pass but don't currently iterate. + // Replace the original copy with a destroy. SILBuilderWithScope(CurrentCopy) .createDestroyAddr(CurrentCopy->getLoc(), CurrentCopy->getDest()); } @@ -832,16 +823,7 @@ forwardDeadTempCopy(CopyAddrInst *destCopy) { deadDebugUser->eraseFromParent(); } - // Either `destCopy` is a take, or the caller is hoisting a destroy: - // copy_addr %temp, %dest - // ... - // destroy %temp - // - // If the caller is hoisting a destroy, and we return `true` then it will - // erase the destroy for us. Either way, it's safe to simply rewrite destCopy. - // For now, don't bother finding the subsequent destroy, because this isn't - // the common case. - + // `destCopy` is a take. It's safe to simply rewrite destCopy. destCopy->setSrc(srcCopy->getSrc()); destCopy->setIsTakeOfSrc(srcCopy->isTakeOfSrc()); srcCopy->eraseFromParent(); @@ -878,8 +860,7 @@ bool CopyForwarding::doesCopyDominateDestUsers( return true; } -// Add all recognized users of storedValue to StoredValueUserInsts. Return true -// if all users were recgonized. +// Return true if all users were recgonized. // // To find all SSA users of storedValue, we first find the RC root, then search // past any instructions that may propagate the reference. @@ -900,19 +881,16 @@ bool CopyForwarding::markStoredValueUsers(SILValue storedValue) { // Recognize any uses that have no results as normal uses. They cannot // transitively propagate a reference. if (user->getResults().empty()) { - StoredValueUserInsts.insert(user); continue; } // Recognize full applies as normal uses. They may transitively retain, but // the caller cannot rely on that. if (FullApplySite::isa(user)) { - StoredValueUserInsts.insert(user); continue; } // A single-valued use is nontransitive if its result is trivial. if (auto *SVI = dyn_cast(user)) { if (SVI->getType().isTrivial(*F)) { - StoredValueUserInsts.insert(user); continue; } } @@ -957,8 +935,7 @@ static DeallocStackInst *getSingleDealloc(AllocStackInst *ASI) { /// If the last use (deinit) is a copy, replace it with a destroy+copy[init]. /// /// The caller has already guaranteed that the lifetime of the copy's source -/// ends at this copy. Either the copy is a [take] or a destroy can be hoisted -/// to the copy. +/// ends at this copy. The copy is a [take]. bool CopyForwarding::forwardPropagateCopy() { SILValue CopyDest = CurrentCopy->getDest(); @@ -1221,98 +1198,6 @@ bool CopyForwarding::backwardPropagateCopy() { return true; } -/// Attempt to hoist a destroy point up to the last use. If the last use is a -/// copy, eliminate both the copy and the destroy. -/// -/// The copy will be eliminated if the original is not accessed between the -/// point of copy and the original's destruction. -/// -/// CurrentDef = // no aliases -/// ... -/// Copy = copy_addr [init] Def -/// ... // no access to CurrentDef -/// destroy_addr Def -/// -/// Return true if a destroy was inserted, forwarded from a copy, or the -/// block was marked dead-in. -/// \p debugInstsToDelete will contain the debug_value users of copy src -/// that need to be deleted if the hoist is successful -bool CopyForwarding::hoistDestroy( - SILInstruction *DestroyPoint, SILLocation DestroyLoc, - SmallVectorImpl &debugInstsToDelete) { - if (!EnableDestroyHoisting) - return false; - - assert(!SrcUserInsts.count(DestroyPoint) && "caller should check terminator"); - SILBasicBlock *BB = DestroyPoint->getParent(); - - // If DestroyPoint is a block terminator, we must hoist. - bool MustHoist = (DestroyPoint == BB->getTerminator()); - // If we haven't seen anything significant, avoid useless hoisting. - bool ShouldHoist = MustHoist; - - auto tryToInsertHoistedDestroyAfter = [&](SILInstruction *afterInst) { - if (!ShouldHoist) - return false; - LLVM_DEBUG(llvm::dbgs() << " Hoisting to Use:" << *afterInst); - SILBuilderWithScope(std::next(afterInst->getIterator()), afterInst) - .createDestroyAddr(DestroyLoc, CurrentDef); - HasChanged = true; - return true; - }; - - auto SI = DestroyPoint->getIterator(), SE = BB->begin(); - while (SI != SE) { - --SI; - SILInstruction *Inst = &*SI; - if (!SrcUserInsts.count(Inst)) { - if (StoredValueUserInsts.count(Inst)) { - // The current definition may take ownership of a value stored into its - // address. Its lifetime cannot end before the last use of that stored - // value. - // CurrentDef = ... - // Copy = copy_addr CurrentDef to ... - // store StoredValue to CurrentDef - // ... // no access to CurrentDef - // retain StoredValue - // destroy_addr CurrentDef - LLVM_DEBUG(llvm::dbgs() << " Cannot hoist above stored value use:" - << *Inst); - return tryToInsertHoistedDestroyAfter(Inst); - } - if (isa(Inst)) { - // Collect debug_value uses of copy src. If the hoist is - // successful, these instructions will have consumed operand and need to - // be erased. - if (SrcDebugValueInsts.contains(Inst)) { - debugInstsToDelete.push_back(Inst); - } - } - if (!ShouldHoist && isa(Inst)) - ShouldHoist = true; - continue; - } - if (auto *CopyInst = dyn_cast(Inst)) { - if (!CopyInst->isTakeOfSrc() && CopyInst->getSrc() == CurrentDef) { - // This use is a copy of CurrentDef. Attempt to forward CurrentDef to - // all uses of the copy's value. - if (propagateCopy(CopyInst, /*hoistingDestroy=*/true)) - return true; - } - } - return tryToInsertHoistedDestroyAfter(Inst); - } - if (!DoGlobalHoisting) { - // If DoGlobalHoisting is set, then we should never mark a DeadInBlock, so - // MustHoist should be false. - assert(!MustHoist && - "Cannot hoist above a terminator with global hoisting disabled."); - return false; - } - DeadInBlocks.insert(BB); - return true; -} - /// Perform CopyForwarding on the current Def. void CopyForwarding::forwardCopiesOf(SILValue Def, SILFunction *F) { reset(F); @@ -1322,97 +1207,9 @@ void CopyForwarding::forwardCopiesOf(SILValue Def, SILFunction *F) { if (!visitAddressUsers(Def, nullptr, visitor)) return; - // First forward any copies that implicitly destroy CurrentDef. There is no - // need to hoist Destroy for these. + // Forward any copies that implicitly destroy CurrentDef. for (auto *CopyInst : TakePoints) { - propagateCopy(CopyInst, /*hoistingDestroy=*/false); - } - // If the copied address is also loaded from, then destroy hoisting is unsafe. - // - // TODO: Record all loads during collectUsers. Implement findRetainPoints to - // peek though projections of the load, like unchecked_enum_data to find the - // true extent of the lifetime including transitively referenced objects. - if (IsSrcLoadedFrom || HasUnknownStoredValue) - return; - - bool HoistedDestroyFound = false; - SILLocation HoistedDestroyLoc = F->getLocation(); - const SILDebugScope *HoistedDebugScope = nullptr; - SmallVector debugInstsToDelete; - - for (auto *Destroy : DestroyPoints) { - // If hoistDestroy returns false, it was not worth hoisting. - if (hoistDestroy(Destroy, Destroy->getLoc(), debugInstsToDelete)) { - // Propagate DestroyLoc for any destroy hoisted above a block. - if (DeadInBlocks.count(Destroy->getParent())) { - HoistedDestroyLoc = Destroy->getLoc(); - HoistedDebugScope = Destroy->getDebugScope(); - HoistedDestroyFound = true; - } - // We either just created a new destroy, forwarded a copy, or will - // continue propagating from this dead-in block. In any case, erase the - // original Destroy. - Destroy->eraseFromParent(); - assert(HasChanged || !DeadInBlocks.empty() && "HasChanged should be set"); - // Since the hoist was successful, delete all dead debug_value - for (auto *deadDebugUser : debugInstsToDelete) { - deadDebugUser->eraseFromParent(); - } - } - debugInstsToDelete.clear(); - } - // Any blocks containing a DestroyPoints where hoistDestroy did not find a use - // are now marked in DeadInBlocks. - if (DeadInBlocks.empty()) - return; - - assert(HoistedDestroyFound && "Hoisted destroy should have been found"); - - DestroyPoints.clear(); - - // Propagate dead-in blocks upward via PostOrder traversal. - // TODO: We could easily handle hoisting above loops if LoopInfo is available. - // - for (auto *BB : PostOrder->get(F)->getPostOrder()) { - SmallVector DeadInSuccs; - ArrayRef Succs = BB->getSuccessors(); - if (Succs.empty()) - continue; - - for (unsigned EdgeIdx = 0, End = Succs.size(); EdgeIdx != End; ++EdgeIdx) { - if (DeadInBlocks.count(Succs[EdgeIdx].getBB())) - DeadInSuccs.push_back(EdgeIdx); - } - if (DeadInSuccs.size() == Succs.size() && - !SrcUserInsts.count(BB->getTerminator())) { - // All successors are dead, so continue hoisting. - bool WasHoisted = hoistDestroy(BB->getTerminator(), HoistedDestroyLoc, - debugInstsToDelete); - (void)WasHoisted; - assert(WasHoisted && "should always hoist above a terminator"); - for (auto *deadDebugUser : debugInstsToDelete) { - deadDebugUser->eraseFromParent(); - } - debugInstsToDelete.clear(); - continue; - } - // Emit a destroy on each CFG edge leading to a dead-in block. This requires - // splitting critical edges and will naturally handle redundant branch - // targets. - for (unsigned EdgeIdx : DeadInSuccs) { - SILBasicBlock *SuccBB = splitCriticalEdge(BB->getTerminator(), EdgeIdx); - if (SuccBB) - HasChangedCFG = true; - else - SuccBB = BB->getSuccessors()[EdgeIdx]; - - // We make no attempt to use the best DebugLoc, because in all known - // cases, we only have one. - SILBuilder B(SuccBB->begin()); - B.setCurrentDebugScope(HoistedDebugScope); - B.createDestroyAddr(HoistedDestroyLoc, CurrentDef); - HasChanged = true; - } + propagateCopy(CopyInst); } } @@ -1539,7 +1336,7 @@ static llvm::cl::opt ForwardStop("copy-forward-stop", class CopyForwardingPass : public SILFunctionTransform { void run() override { - if (!EnableCopyForwarding && !EnableDestroyHoisting) + if (!EnableCopyForwarding) return; // This pass assumes that the ownership lifetime of a value in a memory @@ -1570,7 +1367,7 @@ class CopyForwardingPass : public SILFunctionTransform for (auto &BB : *getFunction()) for (auto II = BB.begin(), IE = BB.end(); II != IE; ++II) { if (auto *CopyInst = dyn_cast(&*II)) { - if (EnableDestroyHoisting && canNRVO(CopyInst)) { + if (canNRVO(CopyInst)) { NRVOCopies.push_back(CopyInst); continue; } diff --git a/test/SILOptimizer/copyforward.sil b/test/SILOptimizer/copyforward.sil index 85dac2798645e..1074b55513825 100644 --- a/test/SILOptimizer/copyforward.sil +++ b/test/SILOptimizer/copyforward.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enforce-exclusivity=none -enable-sil-verify-all %s -copy-forwarding -enable-copyforwarding -enable-destroyhoisting | %FileCheck %s +// RUN: %target-sil-opt -enforce-exclusivity=none -enable-sil-verify-all %s -copy-forwarding -enable-copyforwarding | %FileCheck %s // CopyForwarding currently only runs on OSSA. This file only contains // tests that will break is CopyForwarding were to run on non-OSSA SIL. diff --git a/test/SILOptimizer/copyforward_ossa.sil b/test/SILOptimizer/copyforward_ossa.sil index 889543dfa2e02..0275dc264381f 100644 --- a/test/SILOptimizer/copyforward_ossa.sil +++ b/test/SILOptimizer/copyforward_ossa.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -copy-forwarding -enable-copyforwarding -enable-destroyhoisting -allow-critical-edges=false | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all %s -copy-forwarding -enable-copyforwarding -allow-critical-edges=false | %FileCheck %s // This is the ossa version of CopyForwarding tests sil_stage raw @@ -58,45 +58,6 @@ bb3: // Preds: bb1 bb2 return %18 : $() // id: %20 } -// CHECK-LABEL: sil hidden [ossa] @forward_init : -// CHECK-NOT: copy_addr -// CHECK-NOT: destroy_addr -// CHECK-LABEL: } // end sil function 'forward_init' -sil hidden [ossa] @forward_init : $@convention(thin) (@in T) -> () { -bb0(%0 : $*T): - debug_value %0 : $*T, expr op_deref - %l1 = alloc_stack $T - copy_addr %0 to [initialization] %l1 : $*T - %f1 = function_ref @f_in : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () - %c1 = apply %f1(%l1) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () - dealloc_stack %l1 : $*T - debug_value %0 : $*T, expr op_deref - destroy_addr %0 : $*T - %r1 = tuple () - return %r1 : $() -} - -// CHECK-LABEL: sil hidden [ossa] @forward_noinit : -// CHECK-NOT: copy_addr -// CHECK: destroy_addr -// CHECK-LABEL: } // end sil function 'forward_noinit' -sil hidden [ossa] @forward_noinit : $@convention(thin) (@in T) -> () { -bb0(%0 : $*T): - debug_value %0 : $*T, expr op_deref - %l1 = alloc_stack $T - %f1 = function_ref @f_out : $@convention(thin) <τ_0_0> () -> @out τ_0_0 - %c1 = apply %f1(%l1) : $@convention(thin) <τ_0_0> () -> @out τ_0_0 - copy_addr %0 to %l1 : $*T - debug_value %l1 : $*T, expr op_deref - debug_value %0 : $*T, expr op_deref - %f2 = function_ref @f_in : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () - %c2 = apply %f2(%l1) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () - dealloc_stack %l1 : $*T - destroy_addr %0 : $*T - %r1 = tuple () - return %r1 : $() -} - // CHECK-LABEL: sil hidden [ossa] @forward_takeinit : // CHECK-NOT: copy_addr // CHECK-NOT: destroy_addr @@ -131,25 +92,6 @@ bb0(%0 : $*T): return %r1 : $() } -// CHECK-LABEL: sil hidden [ossa] @backward_init : -// CHECK-NOT: copy_addr -// CHECK-NOT: destroy_addr -// CHECK-LABEL: } // end sil function 'backward_init' -sil hidden [ossa] @backward_init : $@convention(thin) () -> @out T { -bb0(%0 : $*T): - %l1 = alloc_stack $T - %f1 = function_ref @f_out : $@convention(thin) <τ_0_0> () -> @out τ_0_0 - %c1 = apply %f1(%l1) : $@convention(thin) <τ_0_0> () -> @out τ_0_0 - debug_value %l1 : $*T, expr op_deref - copy_addr %l1 to [initialization] %0 : $*T - debug_value %0 : $*T, expr op_deref - debug_value %l1 : $*T, expr op_deref - destroy_addr %l1 : $*T - dealloc_stack %l1 : $*T - %t = tuple () - return %t : $() -} - // CHECK-LABEL: sil hidden [ossa] @backward_noinit : // CHECK: copy_addr // CHECK: destroy_addr @@ -201,61 +143,6 @@ bb0(%0 : $*T): return %t : $() } -// CHECK-LABEL: sil hidden [ossa] @branch : -// CHECK-NOT: copy_addr -// CHECK-LABEL: } // end sil function 'branch' -sil hidden [ossa] @branch : $@convention(thin) (@in T, Bool) -> () { -bb0(%0 : $*T, %1 : $Bool): - %2 = struct_extract %1 : $Bool, #Bool._value // user: %3 - cond_br %2, bb1, bb2 // id: %3 - -bb1: // Preds: bb0 - %4 = function_ref @f_in : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () // user: %7 - %5 = alloc_stack $T // users: %6, %7, %8 - copy_addr %0 to [initialization] %5 : $*T // id: %6 - %7 = apply %4(%5) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> () - dealloc_stack %5 : $*T // id: %8 - br bb3 // id: %9 - -bb2: - br bb3 - -bb3: // Preds: bb0 bb1 - destroy_addr %0 : $*T // id: %10 - %11 = tuple () // user: %12 - return %11 : $() // id: %12 -} - -enum A { - case Val(T) - init(_ val: T) -} - -sil [transparent] @_TFO8enuminit1A3ValU__fMGS0_Q__FQ_GS0_Q__ : $@convention(thin) (@in T, @thin A.Type) -> @out A - -// CHECK-LABEL: sil [ossa] @enuminit : -// CHECK-NOT: copy_addr -// CHECK-LABEL: } // end sil function 'enuminit' -sil [ossa] @enuminit : $@convention(thin) (@in T, @thin A.Type) -> @out A { -bb0(%0 : $*A, %1 : $*T, %2 : $@thin A.Type): - %3 = alloc_stack $A, var, name "sf" // users: %10, %14, %16 - // function_ref enuminit.A.Val (enuminit.A.Type)(A) -> enuminit.A - %4 = function_ref @_TFO8enuminit1A3ValU__fMGS0_Q__FQ_GS0_Q__ : $@convention(thin) <τ_0_0> (@in τ_0_0, @thin A<τ_0_0>.Type) -> @out A<τ_0_0> // user: %9 - %5 = metatype $@thin A.Type // user: %9 - %6 = alloc_stack $T // users: %7, %9, %12 - copy_addr %1 to [initialization] %6 : $*T // id: %7 - %8 = alloc_stack $A // users: %9, %10, %11 - %9 = apply %4(%8, %6, %5) : $@convention(thin) <τ_0_0> (@in τ_0_0, @thin A<τ_0_0>.Type) -> @out A<τ_0_0> - copy_addr [take] %8 to [initialization] %3 : $*A // id: %10 - dealloc_stack %8 : $*A // id: %11 - dealloc_stack %6 : $*T // id: %12 - destroy_addr %1 : $*T // id: %13 - copy_addr [take] %3 to [initialization] %0 : $*A // id: %14 - %15 = tuple () // user: %17 - dealloc_stack %3 : $*A // id: %16 - return %15 : $() // id: %17 -} - // CHECK-LABEL: sil hidden [ossa] @make_addronly : // CHECK-NOT: copy_addr // CHECK-LABEL: } // end sil function 'make_addronly' @@ -377,37 +264,6 @@ bb0: return %34 : $() } -// CHECK-LABEL: sil [ossa] @nil_comparison : -// CHECK: alloc_stack -// CHECK-NOT: copy_addr -// CHECK-NOT: destroy_addr -// CHECK: switch_enum_addr %0 -// CHECK: [[D:%.*]] = unchecked_take_enum_data_addr %0 -// CHECK: destroy_addr [[D]] -// CHECK-LABEL: } // end sil function 'nil_comparison' -sil [ossa] @nil_comparison : $@convention(thin) (@in Optional) -> Bool { -bb0(%0 : $*Optional): - %2 = alloc_stack $Optional - copy_addr %0 to [initialization] %2 : $*Optional - destroy_addr %0 : $*Optional - switch_enum_addr %2 : $*Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 - -bb1: - %6 = unchecked_take_enum_data_addr %2 : $*Optional, #Optional.some!enumelt - destroy_addr %6 : $*T - %8 = integer_literal $Builtin.Int1, -1 - br bb3(%8 : $Builtin.Int1) - -bb2: - %10 = integer_literal $Builtin.Int1, 0 - br bb3(%10 : $Builtin.Int1) - -bb3(%12 : $Builtin.Int1): - %13 = struct $Bool (%12 : $Builtin.Int1) - dealloc_stack %2 : $*Optional - return %13 : $Bool -} - sil @use: $@convention(thin) (@inout T) -> () // We currently don't handle reasoning about multiple copy_addr instructions at @@ -456,24 +312,6 @@ bb3: return %13 : $() } -// CHECK-LABEL: sil hidden [ossa] @test_in_guaranteed : -// CHECK: copy_addr %1 to [initialization] -// CHECK-NOT: copy_addr -// CHECK-LABEL: } // end sil function 'test_in_guaranteed' -sil hidden [ossa] @test_in_guaranteed : $@convention(thin) (@in T) -> @out T { -bb0(%0 : $*T, %1 : $*T): - %l1 = alloc_stack $T - copy_addr %1 to [initialization] %l1 : $*T - %f1 = function_ref @f_in_guaranteed : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () - %c2 = apply %f1(%l1) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () - copy_addr %l1 to [initialization] %0 : $*T - destroy_addr %l1 : $*T - dealloc_stack %l1 : $*T - destroy_addr %1 : $*T - %t = tuple () - return %t : $() -} - // CHECK-LABEL: sil hidden [ossa] @forward_unchecked_ref_cast_addr : // CHECK: unchecked_ref_cast_addr // CHECK-NOT: copy_addr @@ -492,48 +330,6 @@ bb0(%0 : $*AClass, %1 : $*AnyObject): return %11 : $() // id: %12 } -sil [ossa] @element_use : $@convention(thin) (@inout P) -> () - -// CHECK-LABEL: sil [ossa] @backward_propagate_enum_init : -// CHECK-NOT: copy_addr -// CHECK: %[[TMP:.*]] = init_enum_data_addr %0 : $*Optional

-// CHECK: copy_addr %1 to [initialization] %[[TMP]] -// CHECK-NOT: copy_addr -// CHECK-LABEL: } // end sil function 'backward_propagate_enum_init' -sil [ossa] @backward_propagate_enum_init : $@convention(thin) (@inout P) -> @out Optional

{ -bb0(%0 : $*Optional

, %1 : $*P): - %2 = alloc_stack $P - copy_addr %1 to [initialization] %2 : $*P - %3 = function_ref @element_use : $@convention(thin) (@inout P) -> () - %4 = apply %3(%1) : $@convention(thin) (@inout P) -> () - %5 = init_enum_data_addr %0 : $*Optional

, #Optional.some!enumelt - copy_addr %2 to [initialization] %5 : $*P - inject_enum_addr %0 : $*Optional

, #Optional.some!enumelt - destroy_addr %2 : $*P - dealloc_stack %2 : $*P - %27 = tuple () - return %27 : $() -} - -// CHECK-LABEL: sil [ossa] @backward_propagate_exi_init : -// CHECK-NOT: copy_addr -// CHECK: %[[TMP:.*]] = init_existential_addr %0 : $*P, $T -// CHECK: copy_addr %1 to [initialization] %[[TMP]] : $*T -// CHECK-NOT: copy_addr -// CHECK-LABEL: } // end sil function 'backward_propagate_exi_init' -sil [ossa] @backward_propagate_exi_init : $@convention(thin) (@inout T) -> @out P { -bb0(%0 : $*P, %1 : $*T): - %2 = alloc_stack $T - copy_addr %1 to [initialization] %2 : $*T - %3 = witness_method $T, #P.poke : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@inout τ_0_0) -> () - %4 = apply %3(%1) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@inout τ_0_0) -> () - %5 = init_existential_addr %0 : $*P, $T - copy_addr [take] %2 to [initialization] %5 : $*T - dealloc_stack %2 : $*T - %27 = tuple () - return %27 : $() -} - public struct S { @_hasStorage var f: T { get set } @_hasStorage var g: T { get set } @@ -596,27 +392,6 @@ bb0(%0 : $*T, %1 : $*T): return %7 : $() } -// Test a dead copy that initializes a stack local and destroy's it later. -// CHECK-LABEL: sil [ossa] @deadtemp_destroy : -// CHECK: %[[G:.*]] = struct_element_addr %0 : $*S, #S.g -// CHECK-NOT: copy_addr -// CHECK: %[[F:.*]] = struct_element_addr %0 : $*S, #S.f -// CHECK: copy_addr %[[G]] to %[[F]] : $*T -// CHECK-NOT: destroy_addr -// CHECK-LABEL: } // end sil function 'deadtemp_destroy' -sil [ossa] @deadtemp_destroy : $@convention(thin) (@inout S) -> () { -bb0(%0 : $*S): - %1 = struct_element_addr %0 : $*S, #S.g - %2 = alloc_stack $T - copy_addr %1 to [initialization] %2 : $*T - %4 = struct_element_addr %0 : $*S, #S.f - copy_addr %2 to %4 : $*T - destroy_addr %2 : $*T - dealloc_stack %2 : $*T - %7 = tuple () - return %7 : $() -} - struct ObjWrapper { var obj: AnyObject } @@ -707,85 +482,6 @@ bb0(%0 : $*T, %1 : $*T): return %r1 : $() } -// CHECK-LABEL: sil [ossa] @foo : $@convention(thin) (@guaranteed Builtin.NativeObject) -> @out Builtin.NativeObject { -// CHECK: bb0(%0 : $*Builtin.NativeObject, %1 : @guaranteed $Builtin.NativeObject): -// CHECK: [[COPY:%.*]] = copy_value %1 : $Builtin.NativeObject -// CHECK: store [[COPY]] to [init] %0 : $*Builtin.NativeObject -// CHECK-LABEL: } // end sil function 'foo' -sil [ossa] @foo : $@convention(thin) (@guaranteed Builtin.NativeObject) -> @out Builtin.NativeObject { -bb0(%0 : $*Builtin.NativeObject, %1 : @guaranteed $Builtin.NativeObject): - %2 = alloc_stack $Builtin.NativeObject - %copy = copy_value %1 : $Builtin.NativeObject - store %copy to [init] %2 : $*Builtin.NativeObject - copy_addr %2 to [initialization] %0 : $*Builtin.NativeObject - destroy_addr %2 : $*Builtin.NativeObject - dealloc_stack %2 : $*Builtin.NativeObject - %6 = tuple () - return %6 : $() -} - -// CHECK-LABEL: sil [ossa] @testKnownStoredValueUser : $@convention(thin) (@guaranteed AClass) -> @out AClass { -// CHECK: [[ALLOC:%.*]] = alloc_stack $AClass -// CHECK: [[COPY:%.*]] = copy_value %1 : $AClass -// CHECK: store [[COPY]] to [init] %0 : $*AClass -// CHECK-LABEL: } // end sil function 'testKnownStoredValueUser' -sil [ossa] @testKnownStoredValueUser : $@convention(thin) (@guaranteed AClass) -> (@out AClass) { -bb0(%0 : $*AClass, %1 : @guaranteed $AClass): - %2 = alloc_stack $AClass - %copy = copy_value %1 : $AClass - store %copy to [init] %2 : $*AClass - copy_addr %2 to [initialization] %0 : $*AClass - destroy_addr %2 : $*AClass - dealloc_stack %2 : $*AClass - %999 = tuple () - return %999 : $() -} - -// CHECK-LABEL: sil [ossa] @testExtractedStoredValueUser : $@convention(thin) (@guaranteed ObjWrapper) -> @out AnyObject { -// CHECK: bb0(%0 : $*AnyObject, %1 : @guaranteed $ObjWrapper): -// CHECK: [[ALLOC:%.*]] = alloc_stack $AnyObject -// CHECK: [[EXTRACT:%.*]] = struct_extract %1 : $ObjWrapper, #ObjWrapper.obj -// CHECK: [[COPY:%.*]] = copy_value [[EXTRACT]] : $AnyObject -// CHECK: store [[COPY]] to [init] %0 : $*AnyObject -// CHECK-LABEL: } // end sil function 'testExtractedStoredValueUser' -sil [ossa] @testExtractedStoredValueUser : $@convention(thin) (@guaranteed ObjWrapper) -> (@out AnyObject) { -bb0(%0 : $*AnyObject, %1 : @guaranteed $ObjWrapper): - %2 = alloc_stack $AnyObject - %3 = struct_extract %1 : $ObjWrapper, #ObjWrapper.obj - %copy = copy_value %3 : $AnyObject - store %copy to [init] %2 : $*AnyObject - copy_addr %2 to [initialization] %0 : $*AnyObject - destroy_addr %2 : $*AnyObject - dealloc_stack %2 : $*AnyObject - %999 = tuple () - return %999 : $() -} - -struct AClassWrapper { - var a: AClass - var b: AClass -} - -// CHECK-LABEL: sil [ossa] @testUnknownStoredValueUser : $@convention(thin) (@guaranteed AClass) -> @out AClass { -// CHECK: bb0(%0 : $*AClass, %1 : @guaranteed $AClass): -// CHECK: [[ALLOC:%.*]] = alloc_stack $AClass -// CHECK: [[COPY:%.*]] = copy_value %1 : $AClass -// CHECK: store [[COPY]] to [init] %0 : $*AClass -// CHECK: [[STRUCT:%.*]] = struct $AClassWrapper (%1 : $AClass, %1 : $AClass) -// CHECK-LABEL: } // end sil function 'testUnknownStoredValueUser' -sil [ossa] @testUnknownStoredValueUser : $@convention(thin) (@guaranteed AClass) -> (@out AClass) { -bb0(%0 : $*AClass, %1 : @guaranteed $AClass): - %2 = alloc_stack $AClass - %copy = copy_value %1 : $AClass - store %copy to [init] %2 : $*AClass - %3 = struct $AClassWrapper (%1 : $AClass, %1 : $AClass) - copy_addr %2 to [initialization] %0 : $*AClass - destroy_addr %2 : $*AClass - dealloc_stack %2 : $*AClass - %999 = tuple () - return %999 : $() -} - // [SR-8526]: Memory leak after switch in release configuration // CHECK-LABEL: sil [ossa] @testGlobalHoistToStoredValue : $@convention(thin) (@owned AClass, @inout AClass) -> () { // CHECK: bb0(%0 : @owned $AClass, %1 : $*AClass): @@ -816,46 +512,6 @@ bb1: return %v : $() } -// Test an "illegal" reinitialization of a stack location. This -// happens because lowering .int_fma_FPIEEE32 knows that the type is -// trivial, so avoids deinitialization. rdar://64671864. -// -// Note: we have given up on enforcing known SIL-patterns in -// CopyForwarding. Instead, we just remove restructions whenever we see -// unexpected patterns. Eventually, we will replace it with an OSSA -// pass and enforce all assumptions about SIL patterns in the verifier. - -// CHECK-LABEL: sil [ossa] @testCPF : $@convention(thin) (Float) -> @out Float { -// CHECK: bb0(%0 : $*Float, %1 : $Float): -// CHECK: [[TMP1:%.*]] = alloc_stack $Float -// CHECK: store %1 to [trivial] [[TMP1]] : $*Float -// CHECK: [[TMP2:%.*]] = alloc_stack $Float -// CHECK-NOT: copy_addr -// CHECK: [[FMA:%.*]] = builtin "int_fma_FPIEEE32" -// CHECK: [[RESULT:%.*]] = struct $Float ([[FMA]] : $Builtin.FPIEEE32) -// CHECK: store [[RESULT]] to [trivial] %0 : $*Float -// CHECK-NOT: copy_addr -// CHECK-NOT: destroy_addr -// CHECK-LABEL: } // end sil function 'testCPF' -sil [ossa] @testCPF : $@convention(thin) (Float) -> @out Float { -bb0(%0 : $*Float, %1 : $Float): - %2 = alloc_stack $Float - store %1 to [trivial] %2 : $*Float - %4 = alloc_stack $Float - copy_addr %2 to [initialization] %4 : $*Float - %6 = struct_extract %1 : $Float, #Float._value - %7 = builtin "int_fma_FPIEEE32"(%6 : $Builtin.FPIEEE32, %6 : $Builtin.FPIEEE32, %6 : $Builtin.FPIEEE32) : $Builtin.FPIEEE32 - %8 = struct $Float (%7 : $Builtin.FPIEEE32) - store %8 to [trivial] %4 : $*Float - copy_addr %4 to [initialization] %0 : $*Float - destroy_addr %4 : $*Float - dealloc_stack %4 : $*Float - destroy_addr %2 : $*Float - dealloc_stack %2 : $*Float - %15 = tuple () - return %15 : $() -} - // CHECK-LABEL: sil [ossa] @test_dynamic_lifetime : // CHECK: [[STK:%.*]] = alloc_stack [dynamic_lifetime] $NonTrivialStruct // CHECK: copy_addr [take] {{.*}} to [initialization] [[STK]] : $*NonTrivialStruct diff --git a/test/SILOptimizer/spec_conf1.swift b/test/SILOptimizer/spec_conf1.swift index 7e4b1a6e525ae..ab18f49dba6a8 100644 --- a/test/SILOptimizer/spec_conf1.swift +++ b/test/SILOptimizer/spec_conf1.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -O -Xllvm -sil-disable-pass=FunctionSignatureOpts -disable-arc-opts -emit-sil -Xllvm -enable-destroyhoisting=false %s | %FileCheck %s +// RUN: %target-swift-frontend -O -Xllvm -sil-disable-pass=FunctionSignatureOpts -disable-arc-opts -emit-sil %s | %FileCheck %s // We can't deserialize apply_inst with subst lists. When radar://14443304 // is fixed then we should convert this test to a SIL test. diff --git a/test/SILOptimizer/spec_conf2.swift b/test/SILOptimizer/spec_conf2.swift index 8c5ae90cc6ed5..c146992ae5bc9 100644 --- a/test/SILOptimizer/spec_conf2.swift +++ b/test/SILOptimizer/spec_conf2.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -O -Xllvm -sil-disable-pass=FunctionSignatureOpts -disable-arc-opts -emit-sil -Xllvm -enable-destroyhoisting=false %s | %FileCheck %s +// RUN: %target-swift-frontend -O -Xllvm -sil-disable-pass=FunctionSignatureOpts -disable-arc-opts -emit-sil %s | %FileCheck %s // We can't deserialize apply_inst with subst lists. When radar://14443304 // is fixed then we should convert this test to a SIL test.