diff --git a/include/swift/SILAnalysis/ARCAnalysis.h b/include/swift/SILAnalysis/ARCAnalysis.h index 0c20bed9e25..82a44130f7e 100644 --- a/include/swift/SILAnalysis/ARCAnalysis.h +++ b/include/swift/SILAnalysis/ARCAnalysis.h @@ -15,6 +15,7 @@ #include "swift/SIL/SILValue.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SetVector.h" namespace swift { @@ -39,10 +40,10 @@ bool canUseValue(SILInstruction *User, SILValue Ptr, AliasAnalysis *AA); /// insertion pts, and decrement insertion pts. struct ARCMatchingSet { SILValue Ptr; - llvm::SmallPtrSet Increments; - llvm::SmallPtrSet IncrementInsertPts; - llvm::SmallPtrSet Decrements; - llvm::SmallPtrSet DecrementInsertPts; + llvm::SetVector Increments; + llvm::SetVector IncrementInsertPts; + llvm::SetVector Decrements; + llvm::SetVector DecrementInsertPts; void clear() { Ptr = SILValue(); diff --git a/lib/SILAnalysis/GlobalARCSequenceDataflow.cpp b/lib/SILAnalysis/GlobalARCSequenceDataflow.cpp index 9a0bfa94bfc..6ca0c8743ef 100644 --- a/lib/SILAnalysis/GlobalARCSequenceDataflow.cpp +++ b/lib/SILAnalysis/GlobalARCSequenceDataflow.cpp @@ -39,9 +39,9 @@ static bool isAutoreleasePoolCall(SILInstruction &I) { return false; return llvm::StringSwitch(FRI->getReferencedFunction()->getName()) - .Case("objc_autoreleasePoolPush", true) - .Case("objc_autoreleasePoolPop", true) - .Default(false); + .Case("objc_autoreleasePoolPush", true) + .Case("objc_autoreleasePoolPop", true) + .Default(false); } namespace llvm { @@ -61,7 +61,7 @@ raw_ostream &operator<<(raw_ostream &OS, } llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - TopDownRefCountState::LatticeState S) { + TopDownRefCountState::LatticeState S) { using LatticeState = TopDownRefCountState::LatticeState; switch (S) { case LatticeState::None: @@ -76,41 +76,216 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, } } // end namespace llvm +//===----------------------------------------------------------------------===// +// Lattice State Merging +//===----------------------------------------------------------------------===// + +static inline BottomUpRefCountState::LatticeState +MergeBottomUpLatticeStates(BottomUpRefCountState::LatticeState L1, + BottomUpRefCountState::LatticeState L2) { + using LatticeState = BottomUpRefCountState::LatticeState; + // If both states equal, return the first. + if (L1 == L2) + return L1; + + // If either are none, return None. + if (L1 == LatticeState::None || L2 == LatticeState::None) + return LatticeState::None; + + // Canonicalize. + if (unsigned(L1) > unsigned(L2)) + std::swap(L1, L2); + + // Choose the side further along in the sequence. + if ((L1 == LatticeState::Decremented || L1 == LatticeState::MightBeUsed) || + (L2 == LatticeState::MightBeUsed || + L2 == LatticeState::MightBeDecremented)) + return L2; + + // Otherwise, we don't know what happened, be conservative and return none. + return LatticeState::None; +} + +static inline TopDownRefCountState::LatticeState +MergeTopDownLatticeStates(TopDownRefCountState::LatticeState L1, + TopDownRefCountState::LatticeState L2) { + using LatticeState = TopDownRefCountState::LatticeState; + // If both states equal, return the first. + if (L1 == L2) + return L1; + + // If either are none, return None. + if (L1 == LatticeState::None || L2 == LatticeState::None) + return LatticeState::None; + + // Canonicalize. + if (unsigned(L1) > unsigned(L2)) + std::swap(L1, L2); + + // Choose the side further along in the sequence. + if ((L1 == LatticeState::Incremented || + L1 == LatticeState::MightBeDecremented) || + (L2 == LatticeState::MightBeDecremented || + L2 == LatticeState::MightBeUsed)) + return L2; + + // Otherwise, we don't know what happened, return none. + return LatticeState::None; +} + //===----------------------------------------------------------------------===// // ARCBBState Implementation //===----------------------------------------------------------------------===// /// Merge in the state of the successor basic block. This is currently a stub. void ARCBBState::mergeSuccBottomUp(ARCBBState &SuccBB) { + // For each entry in the other set, if our set has an entry with the same key, + // merge the entires. Otherwise, copy the entry and merge it with an empty + // entry. + for (auto MI : SuccBB.getBottomupStates()) { + auto Pair = PtrToBottomUpState.insert(MI); + // If we fail to merge, bail. + if (!Pair.first->second.merge(Pair.second ? BottomUpRefCountState() + : MI.second)) { + clear(); + return; + } + } + for (auto Pair : getBottomupStates()) { + if (SuccBB.PtrToBottomUpState.find(Pair.first) == + SuccBB.PtrToBottomUpState.end()) + // If we fail to merge, bail. + if (!Pair.second.merge(BottomUpRefCountState())) { + clear(); + return; + } + } } /// Initialize this BB with the state of the successor basic block. This is /// called on a basic block's state and then any other successors states are /// merged in. This is currently a stub. void ARCBBState::initSuccBottomUp(ARCBBState &SuccBB) { - + PtrToBottomUpState = SuccBB.PtrToBottomUpState; } /// Merge in the state of the predecessor basic block. This is currently a stub. void ARCBBState::mergePredTopDown(ARCBBState &PredBB) { + // For each entry in the other set, if our set has an entry with the same key, + // merge the entires. Otherwise, copy the entry and merge it with an empty + // entry. + for (auto MI : PredBB.getTopDownStates()) { + auto Pair = PtrToTopDownState.insert(MI); + // If we fail to merge, bail. + if (!Pair.first->second.merge(Pair.second ? TopDownRefCountState() + : MI.second)) { + clear(); + return; + } + } + for (auto Pair : getTopDownStates()) { + if (PredBB.PtrToTopDownState.find(Pair.first) == + PredBB.PtrToTopDownState.end()) + // If we fail to merge, bail. + if (!Pair.second.merge(TopDownRefCountState())) { + clear(); + return; + } + } } /// Initialize the state for this BB with the state of its predecessor /// BB. Used to create an initial state before we merge in other /// predecessors. This is currently a stub. void ARCBBState::initPredTopDown(ARCBBState &PredBB) { - + PtrToTopDownState = PredBB.PtrToTopDownState; } //===----------------------------------------------------------------------===// // Reference Count State Implementation //===----------------------------------------------------------------------===// -void TopDownRefCountState::merge(const TopDownRefCountState &Other) {} +bool TopDownRefCountState::merge(const TopDownRefCountState &Other) { + auto NewState = MergeTopDownLatticeStates(LatState, Other.LatState); + DEBUG(llvm::dbgs() << " Performing TopDown Merge.\n"); + DEBUG(llvm::dbgs() << " Left: " << LatState << "; Right: " + << Other.LatState << "; Result: " << NewState << "\n"); + LatState = NewState; + KnownSafe &= KnownSafe; -void BottomUpRefCountState::merge(const BottomUpRefCountState &Other) {} + // If we're doing a merge on a path that's previously seen a partial merge, + // conservatively drop the sequence, to avoid doing partial RR + // elimination. If the branch predicates for the two merge differ, mixing + // them is unsafe since they are not control dependent. + if (LatState == TopDownRefCountState::LatticeState::None || Partial || + Other.Partial) { + RefCountState::clear(); + DEBUG(llvm::dbgs() << " Found LatticeState::None or Partial. " + "Clearing State!\n"); + return false; + } + + // We should never have an argument path merge with a non-argument path. + if (!Argument.isNull()) { + RefCountState::clear(); + DEBUG( + llvm::dbgs() << " Can not merge Argument with Non-Argument " + "path... Bailing!\n"); + return false; + } + + Increments.insert(Other.Increments.begin(), Other.Increments.end()); + + Partial = InsertPts.size() != Other.InsertPts.size(); + for (auto *SI : Other.InsertPts) + Partial |= InsertPts.insert(SI); + + if (Partial) { + DEBUG(llvm::dbgs() << " Found partial, clearing state!\n"); + RefCountState::clear(); + return false; + } + + return true; +} + +bool BottomUpRefCountState::merge(const BottomUpRefCountState &Other) { + + auto NewState = MergeBottomUpLatticeStates(LatState, Other.LatState); + DEBUG(llvm::dbgs() << " Performing BottomUp Merge.\n"); + DEBUG(llvm::dbgs() << " Left: " << LatState << "; Right: " + << Other.LatState << "; Result: " << NewState << "\n"); + LatState = NewState; + KnownSafe &= KnownSafe; + + // If we're doing a merge on a path that's previously seen a partial merge, + // conservatively drop the sequence, to avoid doing partial RR + // elimination. If the branch predicates for the two merge differ, mixing + // them is unsafe since they are not control dependent. + if (LatState == BottomUpRefCountState::LatticeState::None || Partial || + Other.Partial) { + DEBUG(llvm::dbgs() << " Found LatticeState::None or Partial. " + "Clearing State!\n"); + RefCountState::clear(); + return false; + } + + Decrements.insert(Other.Decrements.begin(), Other.Decrements.end()); + + Partial = InsertPts.size() != Other.InsertPts.size(); + for (auto *SI : Other.InsertPts) + Partial |= InsertPts.insert(SI); + + if (Partial) { + DEBUG(llvm::dbgs() << " Found partial, clearing state!\n"); + RefCountState::clear(); + return false; + } + + return true; +} //===----------------------------------------------------------------------===// // Top Down Dataflow @@ -122,11 +297,10 @@ void BottomUpRefCountState::merge(const BottomUpRefCountState &Other) {} /// /// NestingDetected will be set to indicate that the block needs to be /// reanalyzed if code motion occurs. -static bool -processBBTopDown(ARCBBState &BBState, - BlotMapVector &DecToIncStateMap, - AliasAnalysis *AA) { +static bool processBBTopDown( + ARCBBState &BBState, + BlotMapVector &DecToIncStateMap, + AliasAnalysis *AA) { DEBUG(llvm::dbgs() << ">>>> Top Down!\n"); SILBasicBlock &BB = BBState.getBB(); @@ -140,7 +314,7 @@ processBBTopDown(ARCBBState &BBState, if (&BB == &*BB.getParent()->begin()) { auto Args = BB.getBBArgs(); auto SignatureParams = - BB.getParent()->getLoweredFunctionType()->getInterfaceParameters(); + BB.getParent()->getLoweredFunctionType()->getInterfaceParameters(); for (unsigned i = 0, e = Args.size(); i != e; ++i) { SILArgument *A = Args[i]; ParameterConvention P = SignatureParams[i].getConvention(); @@ -176,7 +350,7 @@ processBBTopDown(ARCBBState &BBState, NestingDetected |= State.initWithInst(&I); DEBUG(llvm::dbgs() << " REF COUNT INCREMENT! Known Safe: " - << (State.isKnownSafe()?"yes":"no") << "\n"); + << (State.isKnownSafe() ? "yes" : "no") << "\n"); // Continue processing in case this increment could be a CanUse for a // different pointer. @@ -198,15 +372,15 @@ processBBTopDown(ARCBBState &BBState, // Copy the current value of ref count state into the result map. DecToIncStateMap[&I] = RefCountState; DEBUG(llvm::dbgs() << " MATCHING INCREMENT:\n" - << RefCountState.getValue()); + << RefCountState.getValue()); // Clear the ref count state in case we see more operations on this // ref counted value. This is for safety reasons. RefCountState.clear(); } else { if (RefCountState.isTrackingRefCountInst()) { - DEBUG(llvm::dbgs() << " FAILED MATCH INCREMENT:\n" << - RefCountState.getValue()); + DEBUG(llvm::dbgs() << " FAILED MATCH INCREMENT:\n" + << RefCountState.getValue()); } else { DEBUG(llvm::dbgs() << " FAILED MATCH. NO INCREMENT.\n"); } @@ -224,8 +398,8 @@ processBBTopDown(ARCBBState &BBState, if (Op && OtherState.first == Op) continue; - // If we are tracking an argument, skip it. - if (!OtherState.second.isTrackingRefCountInst()) + // If the other state is not tracking anything, bail. + if (!OtherState.second.isTrackingRefCount()) continue; // Check if the instruction we are visiting could potentially decrement @@ -233,7 +407,7 @@ processBBTopDown(ARCBBState &BBState, // cause us to change states. If we do change states continue... if (OtherState.second.handlePotentialDecrement(&I, AA)) { DEBUG(llvm::dbgs() << " Found Potential Decrement:\n " - << OtherState.second.getValue()); + << OtherState.second.getValue()); continue; } @@ -241,7 +415,7 @@ processBBTopDown(ARCBBState &BBState, // could be used by the given instruction. if (OtherState.second.handlePotentialUser(&I, AA)) DEBUG(llvm::dbgs() << " Found Potential Use:\n " - << OtherState.second.getValue()); + << OtherState.second.getValue()); } } @@ -249,8 +423,8 @@ processBBTopDown(ARCBBState &BBState, } void -swift::arc::ARCSequenceDataflowEvaluator:: -mergePredecessors(ARCBBState &BBState, SILBasicBlock *BB) { +swift::arc::ARCSequenceDataflowEvaluator::mergePredecessors(ARCBBState &BBState, + SILBasicBlock *BB) { // For each successor of BB... unsigned i = 0; for (auto Pred : BB->getPreds()) { @@ -277,13 +451,18 @@ mergePredecessors(ARCBBState &BBState, SILBasicBlock *BB) { bool swift::arc::ARCSequenceDataflowEvaluator::processTopDown() { bool NestingDetected = false; + DEBUG(llvm::dbgs() << "<<<< Processing Bottom Up! >>>>\n"); + // For each BB in our reverse post order... for (auto *BB : reversed(PostOrder)) { + DEBUG(llvm::dbgs() << "Processing BB#: " << BBToPostOrderID[BB] << "\n"); + // Grab the BBState associated with it and set it to be the current BB. ARCBBState &BBState = TopDownBBStates[BB]; BBState.init(BB); + DEBUG(llvm::dbgs() << "Merging Predecessors!\n"); mergePredecessors(BBState, BB); // Then perform the basic block optimization. @@ -297,18 +476,16 @@ bool swift::arc::ARCSequenceDataflowEvaluator::processTopDown() { // Bottom Up Dataflow //===----------------------------------------------------------------------===// - /// Analyze a single BB for refcount inc/dec instructions. /// /// If anything was found it will be added to DecToIncStateMap. /// /// NestingDetected will be set to indicate that the block needs to be /// reanalyzed if code motion occurs. -static bool -processBBBottomUp(ARCBBState &BBState, - BlotMapVector &IncToDecStateMap, - AliasAnalysis *AA) { +static bool processBBBottomUp( + ARCBBState &BBState, + BlotMapVector &IncToDecStateMap, + AliasAnalysis *AA) { DEBUG(llvm::dbgs() << ">>>> Bottom Up!\n"); SILBasicBlock &BB = BBState.getBB(); @@ -337,7 +514,7 @@ processBBBottomUp(ARCBBState &BBState, NestingDetected |= State.initWithInst(&I); DEBUG(llvm::dbgs() << " REF COUNT DECREMENT! Known Safe: " - << (State.isKnownSafe()?"yes":"no") << "\n"); + << (State.isKnownSafe() ? "yes" : "no") << "\n"); // Continue on to see if our reference decrement could potentially affect // any other pointers via a use or a decrement. @@ -360,18 +537,18 @@ processBBBottomUp(ARCBBState &BBState, // Copy the current value of ref count state into the result map. IncToDecStateMap[&I] = RefCountState; DEBUG(llvm::dbgs() << " MATCHING DECREMENT:" - << RefCountState.getValue()); + << RefCountState.getValue()); // Clear the ref count state in case we see more operations on this // ref counted value. This is for safety reasons. RefCountState.clear(); } else { if (RefCountState.isTrackingRefCountInst()) { - DEBUG(llvm::dbgs() << " FAILED MATCH DECREMENT:" - << RefCountState.getValue()); + DEBUG(llvm::dbgs() + << " FAILED MATCH DECREMENT:" << RefCountState.getValue()); } else { DEBUG(llvm::dbgs() << " FAILED MATCH DECREMENT. Not tracking a " - "decrement.\n"); + "decrement.\n"); } } @@ -387,8 +564,8 @@ processBBBottomUp(ARCBBState &BBState, if (Op && OtherState.first == Op) continue; - // If we are tracking an argument, skip it. - if (!OtherState.second.isTrackingRefCountInst()) + // If this state is not tracking anything, skip it. + if (!OtherState.second.isTrackingRefCount()) continue; // Check if the instruction we are visiting could potentially decrement @@ -396,7 +573,7 @@ processBBBottomUp(ARCBBState &BBState, // cause us to change states. If we do change states continue... if (OtherState.second.handlePotentialDecrement(&I, AA)) { DEBUG(llvm::dbgs() << " Found Potential Decrement:\n " - << OtherState.second.getValue()); + << OtherState.second.getValue()); continue; } @@ -404,7 +581,7 @@ processBBBottomUp(ARCBBState &BBState, // could be used by the given instruction. if (OtherState.second.handlePotentialUser(&I, AA)) DEBUG(llvm::dbgs() << " Found Potential Use:\n " - << OtherState.second.getValue()); + << OtherState.second.getValue()); } } @@ -412,8 +589,8 @@ processBBBottomUp(ARCBBState &BBState, } void -swift::arc::ARCSequenceDataflowEvaluator:: -mergeSuccessors(ARCBBState &BBState, SILBasicBlock *BB) { +swift::arc::ARCSequenceDataflowEvaluator::mergeSuccessors(ARCBBState &BBState, + SILBasicBlock *BB) { // Grab the backedge set for our BB. auto &BackEdgeSet = BackedgeMap[BB]; @@ -449,13 +626,18 @@ mergeSuccessors(ARCBBState &BBState, SILBasicBlock *BB) { bool swift::arc::ARCSequenceDataflowEvaluator::processBottomUp() { bool NestingDetected = false; + DEBUG(llvm::dbgs() << "<<<< Processing Bottom Up! >>>>\n"); + // For each BB in our post order... for (auto *BB : PostOrder) { + DEBUG(llvm::dbgs() << "Processing BB#: " << BBToPostOrderID[BB] << "\n"); + // Grab the BBState associated with it and set it to be the current BB. ARCBBState &BBState = BottomUpBBStates[BB]; BBState.init(BB); + DEBUG(llvm::dbgs() << "Merging Successors!\n"); mergeSuccessors(BBState, BB); // Then perform the basic block optimization. @@ -509,8 +691,7 @@ bool swift::arc::ARCSequenceDataflowEvaluator::run() { //===----------------------------------------------------------------------===// bool swift::arc::performARCSequenceDataflow( - SILFunction &F, - AliasAnalysis *AA, + SILFunction &F, AliasAnalysis *AA, BlotMapVector &DecToIncStateMap, BlotMapVector &IncToDecStateMap) { diff --git a/lib/SILAnalysis/ReferenceCountState.h b/lib/SILAnalysis/ReferenceCountState.h index db7434d5215..bb086f72901 100644 --- a/lib/SILAnalysis/ReferenceCountState.h +++ b/lib/SILAnalysis/ReferenceCountState.h @@ -184,7 +184,7 @@ struct RefCountState { bool handlePotentialDecrement(SILInstruction *PotentialDecrement, AliasAnalysis *AA) { // If we are not tracking a ref count, just return false. - if (!isTrackingRefCountInst()) + if (!isTrackingRefCount()) return false; // If we can prove that Other can not use the pointer we are tracking, @@ -282,13 +282,13 @@ struct BottomUpRefCountState : public RefCountState { /// Is this ref count initialized and tracking a ref count ptr. bool isTrackingRefCount() const { - return !Decrements.empty(); + return Decrements.size(); } /// Are we tracking an instruction currently? This returns false when given an /// uninitialized ReferenceCountState. bool isTrackingRefCountInst() const { - return !Decrements.empty(); + return Decrements.size(); } /// Are we tracking a source of ref counts? This currently means that we are @@ -359,7 +359,7 @@ struct BottomUpRefCountState : public RefCountState { } } - void merge(const BottomUpRefCountState &Other); + bool merge(const BottomUpRefCountState &Other); }; //===----------------------------------------------------------------------===// @@ -507,7 +507,7 @@ struct TopDownRefCountState : public RefCountState { } } - void merge(const TopDownRefCountState &Other); + bool merge(const TopDownRefCountState &Other); }; } // end arc namespace diff --git a/test/SILPasses/global-arc-opts-loops.sil.gyb b/test/SILPasses/global-arc-opts-loops.sil.gyb new file mode 100644 index 00000000000..5c461892fd0 --- /dev/null +++ b/test/SILPasses/global-arc-opts-loops.sil.gyb @@ -0,0 +1,125 @@ +%# -*- mode: sil -*- +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: %gyb %s > %t/global-arc-opts-loops.sil +// RUN: sil-opt -global-arc-opts -verify %t/global-arc-opts-loops.sil | FileCheck %t/global-arc-opts-loops.sil + +%# Ignore the following admonition; it applies to the resulting .sil +%# test file only. +// DO NOT MODIFY THIS TEST FILE. IT IS AUTOMATICALLY GENERATED BY GYB. + +sil_stage canonical + +import Builtin + +// Utilities + +sil @user : $@thin (Builtin.NativeObject) -> () + +// Loop form 1 consists of CFG of the following form: +// +// BB0 -> BB1 +// BB1 -> BB1 +// BB1 -> BB2 +// +// Thus it is a simple loop without any interior basic blocks. +// +// We generate test retain release pairs at +% for i in range(16): + +// CHECK-LABEL: sil @loop_form_1_${i} : $@thin (Builtin.NativeObject) -> () { +sil @loop_form_1_${i} : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +bb0(%0 : $Builtin.NativeObject): +% if i & 1: +// CHECK-NEXT: strong_retain + strong_retain %0 : $Builtin.NativeObject +% end +// CHECK-NEXT: br + br bb1 + +// CHECK: bb1: +bb1: +% if (i & 2) and not (i & 4): +// CHECK-NEXT: strong_retain +% end +% if i & 2: + strong_retain %0 : $Builtin.NativeObject +% end +% if not (i & 2) and (i & 4): +// CHECK-NEXT: strong_release +% end +% if i & 4: + strong_release %0 : $Builtin.NativeObject +% end +// CHECK-NEXT: cond_br + cond_br undef, bb1, bb2 + +// CHECK: bb2: +bb2: +% if i & 8: +// CHECK-NEXT: strong_release + strong_release %0 : $Builtin.NativeObject +% end +%%1 = tuple () + return %1 : $() +} +% end + +// Loop form 2 consists of CFGs of the following form: +// +// BB0 -> BB1 +// BB1 -> BB2 +// BB2 -> BB1 +// BB2 -> BB3 +// +// I.e. a loop with a body of 2 and the jump into the loop not at the +// latch. + +% for i in range(3): +% for j in range(4): + +sil @loop_form_2_${i}_${j} : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0({{.*}}): +bb0(%0 : $Builtin.NativeObject): +% if i & 1: +// CHECK-NEXT: strong_retain + strong_retain %0 : $Builtin.NativeObject +% end + br bb1 + +// CHECK: bb1: +bb1: +% if j == 0: +// CHECK-NEXT: strong_retain + strong_retain %0 : $Builtin.NativeObject +% end +% if j == 1: +// CHECK-NEXT: strong_release + strong_release %0 : $Builtin.NativeObject +% end + br bb2 + +// CHECK: bb2: +bb2: +% if j == 2: +// CHECK-NEXT: strong_retain + strong_retain %0 : $Builtin.NativeObject +% end +% if j == 3: +// CHECK-NEXT: strong_release + strong_release %0 : $Builtin.NativeObject +% end + cond_br undef, bb1, bb3 + +// CHECK: bb3: +bb3: +% if i & 2: +// CHECK-NEXT: strong_release + strong_release %0 : $Builtin.NativeObject +% end +%%1 = tuple () + return %1 : $() +} + +% end diff --git a/test/SILPasses/globalarcopts.sil b/test/SILPasses/globalarcopts.sil index ccd3fc26714..9eb6d2a1036 100644 --- a/test/SILPasses/globalarcopts.sil +++ b/test/SILPasses/globalarcopts.sil @@ -4,6 +4,10 @@ sil_stage canonical import Builtin +// Utilities + +sil @user : $@thin (Builtin.NativeObject) -> () + ///////////////// // Basic Tests // ///////////////// @@ -94,8 +98,6 @@ bb0(%0 : $S, %1 : $Builtin.NativeObject): return %2 : $() } -sil @user : $@thin (Builtin.NativeObject) -> () - // CHECK-LABEL: sil @dont_delete_retain_over_decrement_use : $@thin (Builtin.NativeObject) -> () // CHECK: bb0 // CHECK: strong_retain @@ -310,8 +312,15 @@ bb0(%0 : $*Builtin.Int32): } // CHECK-LABEL: sil @silargument_retain_iterated : $@thin (@owned Builtin.NativeObject) -> () -// CHECK-NOT: strong_retain -// CHECK-NOT: strong_release +// CHECK: bb0 +// CHECK-NEXT: function_ref user +// CHECK-NEXT: function_ref @user +// CHECK-NEXT: apply +// CHECK-NEXT: apply +// CHECK-NEXT: strong_retain +// CHECK-NEXT: apply +// CHECK-NEXT: apply +// CHECK-NEXT: strong_release sil @silargument_retain_iterated : $@thin (@owned Builtin.NativeObject) -> () { bb0(%0 : $Builtin.NativeObject): %1 = function_ref @user : $@thin (Builtin.NativeObject) -> () @@ -441,3 +450,572 @@ bb0(%0 : $RetainUser, %1 : $Builtin.NativeObject): strong_release %0 : $RetainUser return %1 : $Builtin.NativeObject } + + +//////////////////// +// Multi-BB tests // +//////////////////// + +////////////// +// Hammocks // +////////////// + +// CHECK-LABEL: sil @hammock1 : $@thin (Builtin.NativeObject) -> () { +// CHECK-NOT: strong_retain +// CHECK-NOT: strong_release +sil @hammock1 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + br bb2 + +bb2: + strong_release %0 : $Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// This hammock can not be optimized. +// CHECK-LABEL: sil @hammock2 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NEXT: strong_retain +// CHECK: bb1: +// CHECK-NEXT: strong_retain +// CHECK: bb2: +// CHECK-NEXT: strong_release +sil @hammock2 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + strong_retain %0 : $Builtin.NativeObject + br bb2 + +bb2: + strong_release %0 : $Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +/// This hammock can't be optimized. +// CHECK-LABEL: sil @hammock3 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NEXT: strong_retain +// CHECK: bb1: +// CHECK-NEXT: strong_release +// CHECK: bb2: +// CHECK-NEXT: strong_release +sil @hammock3 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + strong_release %0 : $Builtin.NativeObject + br bb2 + +bb2: + strong_release %0 : $Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// This should not be optimizable. +// CHECK-LABEL: sil @hammock4 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NOT: strong_retain +// CHECK-NOT: strong_release +// CHECK: bb1: +// CHECK-NEXT: strong_retain +// CHECK: bb2: +// CHECK-NEXT: strong_release +sil @hammock4 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + cond_br undef, bb1, bb2 + +bb1: + strong_retain %0 : $Builtin.NativeObject + br bb2 + +bb2: + strong_release %0 : $Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil @hammock5 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NOT: strong_release +// CHECK-NOT: strong_retain +// CHECK: bb1: +// CHECK-NEXT: strong_release +// CHECK: bb2: +// CHECK-NEXT: strong_release +sil @hammock5 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + cond_br undef, bb1, bb2 + +bb1: + strong_release %0 : $Builtin.NativeObject + br bb2 + +bb2: + strong_release %0 : $Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil @hammock6 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NOT: strong_retain +// CHECK-NOT: strong_release +// CHECK: bb1: +// CHECK-NOT: strong_retain +// CHECK-NOT: strong_release +// CHECK: bb2: +// CHECK-NEXT: strong_release +sil @hammock6 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + cond_br undef, bb1, bb2 + +bb1: + br bb2 + +bb2: + strong_release %0 : $Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil @hammock7 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NEXT: strong_retain +// CHECK: bb1: +// CHECK-NEXT: strong_retain +// CHECK: bb2: +// CHECK-NOT: strong_retain +// CHECK-NOT: strong_release +sil @hammock7 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + strong_retain %0 : $Builtin.NativeObject + br bb2 + +bb2: + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil @hammock8 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NEXT: strong_retain +// CHECK-NOT: strong_release +// CHECK: bb1: +// CHECK-NEXT: strong_release +// CHECK-NOT: strong_retain +// CHECK: bb2: +// CHECK-NOT: strong_retain +// CHECK-NOT: strong_release +sil @hammock8 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + strong_release %0 : $Builtin.NativeObject + br bb2 + +bb2: + %9999 = tuple() + return %9999 : $() +} + +//////////////////// +// Double Hammock // +//////////////////// + +// Make sure we do not do anything in the presense of double partial +// applies. This is due to issues related to the two branches of the two +// diamonds not being control dependent. +// CHECK-LABEL: sil @double_hammock1 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NEXT: function_ref user +// CHECK-NEXT: function_ref @user +// CHECK-NEXT: strong_retain +// CHECK-NEXT: cond_br +// CHECK: bb1: +// CHECK-NEXT: apply +// CHECK-NEXT: bb2 +// CHECK: bb2: +// CHECK-NEXT: cond_br +// CHECK: bb3: +// CHECK-NEXT: apply +// CHECK-NEXT: br +// CHECK: bb4: +// CHECK-NEXT: strong_release +// CHECK-NEXT: tuple +// CHECK-NEXT: return +sil @double_hammock1 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + %1 = function_ref @user : $@thin (Builtin.NativeObject) -> () + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + apply %1(%0) : $@thin (Builtin.NativeObject) -> () + br bb2 + +bb2: + cond_br undef, bb3, bb4 + +bb3: + apply %1(%0) : $@thin (Builtin.NativeObject) -> () + br bb4 + +bb4: + strong_release %0 : $Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +////////////// +// Diamonds // +////////////// + +// CHECK-LABEL: sil @diamond1 : $@thin (Builtin.NativeObject) -> () { +// CHECK-NOT: strong_retain +// CHECK-NOT: strong_release +sil @diamond1 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + br bb3 + +bb2: + br bb3 + +bb3: + strong_release %0 : $Builtin.NativeObject + %1 = tuple() + return %1 : $() +} + +// CHECK-LABEL: sil @diamond2 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NEXT: strong_retain +// CHECK-NEXT: cond_br +// CHECK: bb1: +// CHECK-NEXT: br +// CHECK: bb2: +// CHECK-NEXT: strong_release +// CHECK-NEXT: br +// CHECK: bb3: +// CHECK-NEXT: tuple () +// CHECK-NEXT: return +sil @diamond2 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + br bb3 + +bb2: + strong_release %0 : $Builtin.NativeObject + br bb3 + +bb3: + %1 = tuple() + return %1 : $() +} + +// CHECK-LABEL: sil @diamond3 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NEXT: strong_retain +// CHECK-NEXT: cond_br +// CHECK: bb1: +// CHECK-NEXT: strong_release +// CHECK-NEXT: br +// CHECK: bb2: +// CHECK-NEXT: br +// CHECK: bb3: +// CHECK-NEXT: tuple () +// CHECK-NEXT: return +sil @diamond3 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + strong_release %0 : $Builtin.NativeObject + br bb3 + +bb2: + br bb3 + +bb3: + %1 = tuple() + return %1 : $() +} + +// CHECK-LABEL: sil @diamond4 : $@thin (Builtin.NativeObject) -> () { +// CHECK-NOT: strong_retain +// CHECK-NOT: strong_release +sil @diamond4 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + strong_release %0 : $Builtin.NativeObject + br bb3 + +bb2: + strong_release %0 : $Builtin.NativeObject + br bb3 + +bb3: + %1 = tuple() + return %1 : $() +} + +// CHECK-LABEL: sil @diamond5 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NEXT: strong_retain +// CHECK-NEXT: cond_br +// CHECK: bb1: +// CHECK-NEXT: strong_release +// CHECK-NEXT: br +// CHECK: bb2: +// CHECK-NEXT: br +// CHECK: bb3: +// CHECK-NEXT: strong_release +// CHECK-NEXT: tuple () +// CHECK-NEXT: return +sil @diamond5 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + strong_release %0 : $Builtin.NativeObject + br bb3 + +bb2: + br bb3 + +bb3: + strong_release %0 : $Builtin.NativeObject + %1 = tuple() + return %1 : $() +} + +// CHECK-LABEL: sil @diamond6 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NEXT: strong_retain +// CHECK-NEXT: cond_br +// CHECK: bb1: +// CHECK-NEXT: br +// CHECK: bb2: +// CHECK-NEXT: strong_release +// CHECK-NEXT: br +// CHECK: bb3: +// CHECK-NEXT: strong_release +// CHECK-NEXT: tuple () +// CHECK-NEXT: return +sil @diamond6 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + br bb3 + +bb2: + strong_release %0 : $Builtin.NativeObject + br bb3 + +bb3: + strong_release %0 : $Builtin.NativeObject + %1 = tuple() + return %1 : $() +} + +// CHECK-LABEL: sil @diamond7 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NEXT: cond_br +// CHECK: bb1: +// CHECK-NEXT: br +// CHECK: bb2: +// CHECK-NEXT: br +// CHECK: bb3: +// CHECK-NEXT: strong_release +// CHECK-NEXT: tuple () +// CHECK-NEXT: return +sil @diamond7 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + strong_release %0 : $Builtin.NativeObject + br bb3 + +bb2: + strong_release %0 : $Builtin.NativeObject + br bb3 + +bb3: + strong_release %0 : $Builtin.NativeObject + %1 = tuple() + return %1 : $() +} + +// CHECK-LABEL: sil @diamond8 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NEXT: cond_br +// CHECK: bb1: +// CHECK-NEXT: strong_retain +// CHECK-NEXT: br +// CHECK: bb2: +// CHECK-NEXT: br +// CHECK: bb3: +// CHECK-NEXT: strong_release +// CHECK-NEXT: tuple () +// CHECK-NEXT: return +sil @diamond8 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + cond_br undef, bb1, bb2 + +bb1: + strong_retain %0 : $Builtin.NativeObject + br bb3 + +bb2: + br bb3 + +bb3: + strong_release %0 : $Builtin.NativeObject + %1 = tuple() + return %1 : $() +} + +/// CHECK-LABEL: sil @diamond9 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NEXT: cond_br +// CHECK: bb1: +// CHECK-NEXT: br +// CHECK: bb2: +// CHECK-NEXT: strong_retain +// CHECK-NEXT: br +// CHECK: bb3: +// CHECK-NEXT: strong_release +// CHECK-NEXT: tuple () +// CHECK-NEXT: return +sil @diamond9 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + cond_br undef, bb1, bb2 + +bb1: + br bb3 + +bb2: + strong_retain %0 : $Builtin.NativeObject + br bb3 + +bb3: + strong_release %0 : $Builtin.NativeObject + %1 = tuple() + return %1 : $() +} + +// CHECK-LABEL: sil @diamond10 : $@thin (Builtin.NativeObject) -> () { +// CHECK-NOT: strong_retain +// CHECK-NOT: strong_release +sil @diamond10 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + cond_br undef, bb1, bb2 + +bb1: + strong_retain %0 : $Builtin.NativeObject + br bb3 + +bb2: + strong_retain %0 : $Builtin.NativeObject + br bb3 + +bb3: + strong_release %0 : $Builtin.NativeObject + %1 = tuple() + return %1 : $() +} + +/// CHECK-LABEL: sil @diamond11 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NEXT: strong_retain +// CHECK-NEXT: cond_br +// CHECK: bb1: +// CHECK-NEXT: strong_retain +// CHECK-NEXT: br +// CHECK: bb2: +// CHECK-NEXT: br +// CHECK: bb3: +// CHECK-NEXT: strong_release +// CHECK-NEXT: tuple () +// CHECK-NEXT: return +sil @diamond11 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + strong_retain %0 : $Builtin.NativeObject + br bb3 + +bb2: + br bb3 + +bb3: + strong_release %0 : $Builtin.NativeObject + %1 = tuple() + return %1 : $() +} + +/// CHECK-LABEL: sil @diamond12 : $@thin (Builtin.NativeObject) -> () { +// CHECK: bb0 +// CHECK-NEXT: strong_retain +// CHECK-NEXT: cond_br +// CHECK: bb1: +// CHECK-NEXT: br +// CHECK: bb2: +// CHECK-NEXT: strong_retain +// CHECK-NEXT: br +// CHECK: bb3: +// CHECK-NEXT: strong_release +// CHECK-NEXT: tuple () +// CHECK-NEXT: return +sil @diamond12 : $@thin (Builtin.NativeObject) -> () { +bb0(%0 : $Builtin.NativeObject): + strong_retain %0 : $Builtin.NativeObject + cond_br undef, bb1, bb2 + +bb1: + br bb3 + +bb2: + strong_retain %0 : $Builtin.NativeObject + br bb3 + +bb3: + strong_release %0 : $Builtin.NativeObject + %1 = tuple() + return %1 : $() +}