[g-arc-opts] Implement the merge functions to complete Stage 2 of global arc opts.

Also fixed a small bug related to how we handle arguments. We were not
clearing the state of the argument when we saw a potential decrement of
the argument.

<rdar://problem/17013194>

Swift SVN r18706
This commit is contained in:
Michael Gottesman
2014-06-05 04:35:14 +00:00
parent 814274a05d
commit c2b2e52260
5 changed files with 941 additions and 56 deletions

View File

@@ -15,6 +15,7 @@
#include "swift/SIL/SILValue.h" #include "swift/SIL/SILValue.h"
#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SetVector.h"
namespace swift { namespace swift {
@@ -39,10 +40,10 @@ bool canUseValue(SILInstruction *User, SILValue Ptr, AliasAnalysis *AA);
/// insertion pts, and decrement insertion pts. /// insertion pts, and decrement insertion pts.
struct ARCMatchingSet { struct ARCMatchingSet {
SILValue Ptr; SILValue Ptr;
llvm::SmallPtrSet<SILInstruction *, 8> Increments; llvm::SetVector<SILInstruction *> Increments;
llvm::SmallPtrSet<SILInstruction *, 8> IncrementInsertPts; llvm::SetVector<SILInstruction *> IncrementInsertPts;
llvm::SmallPtrSet<SILInstruction *, 8> Decrements; llvm::SetVector<SILInstruction *> Decrements;
llvm::SmallPtrSet<SILInstruction *, 8> DecrementInsertPts; llvm::SetVector<SILInstruction *> DecrementInsertPts;
void clear() { void clear() {
Ptr = SILValue(); Ptr = SILValue();

View File

@@ -76,41 +76,216 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
} }
} // end namespace llvm } // 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 // ARCBBState Implementation
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// Merge in the state of the successor basic block. This is currently a stub. /// Merge in the state of the successor basic block. This is currently a stub.
void ARCBBState::mergeSuccBottomUp(ARCBBState &SuccBB) { 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 /// 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 /// called on a basic block's state and then any other successors states are
/// merged in. This is currently a stub. /// merged in. This is currently a stub.
void ARCBBState::initSuccBottomUp(ARCBBState &SuccBB) { void ARCBBState::initSuccBottomUp(ARCBBState &SuccBB) {
PtrToBottomUpState = SuccBB.PtrToBottomUpState;
} }
/// Merge in the state of the predecessor basic block. This is currently a stub. /// Merge in the state of the predecessor basic block. This is currently a stub.
void ARCBBState::mergePredTopDown(ARCBBState &PredBB) { 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 /// Initialize the state for this BB with the state of its predecessor
/// BB. Used to create an initial state before we merge in other /// BB. Used to create an initial state before we merge in other
/// predecessors. This is currently a stub. /// predecessors. This is currently a stub.
void ARCBBState::initPredTopDown(ARCBBState &PredBB) { void ARCBBState::initPredTopDown(ARCBBState &PredBB) {
PtrToTopDownState = PredBB.PtrToTopDownState;
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Reference Count State Implementation // 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<TopDownRefCountState>::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<TopDownRefCountState>::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<TopDownRefCountState>::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<BottomUpRefCountState>::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<BottomUpRefCountState>::clear();
return false;
}
return true;
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Top Down Dataflow // Top Down Dataflow
@@ -122,10 +297,9 @@ void BottomUpRefCountState::merge(const BottomUpRefCountState &Other) {}
/// ///
/// NestingDetected will be set to indicate that the block needs to be /// NestingDetected will be set to indicate that the block needs to be
/// reanalyzed if code motion occurs. /// reanalyzed if code motion occurs.
static bool static bool processBBTopDown(
processBBTopDown(ARCBBState &BBState, ARCBBState &BBState,
BlotMapVector<SILInstruction *, BlotMapVector<SILInstruction *, TopDownRefCountState> &DecToIncStateMap,
TopDownRefCountState> &DecToIncStateMap,
AliasAnalysis *AA) { AliasAnalysis *AA) {
DEBUG(llvm::dbgs() << ">>>> Top Down!\n"); DEBUG(llvm::dbgs() << ">>>> Top Down!\n");
@@ -176,7 +350,7 @@ processBBTopDown(ARCBBState &BBState,
NestingDetected |= State.initWithInst(&I); NestingDetected |= State.initWithInst(&I);
DEBUG(llvm::dbgs() << " REF COUNT INCREMENT! Known Safe: " 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 // Continue processing in case this increment could be a CanUse for a
// different pointer. // different pointer.
@@ -205,8 +379,8 @@ processBBTopDown(ARCBBState &BBState,
RefCountState.clear(); RefCountState.clear();
} else { } else {
if (RefCountState.isTrackingRefCountInst()) { if (RefCountState.isTrackingRefCountInst()) {
DEBUG(llvm::dbgs() << " FAILED MATCH INCREMENT:\n" << DEBUG(llvm::dbgs() << " FAILED MATCH INCREMENT:\n"
RefCountState.getValue()); << RefCountState.getValue());
} else { } else {
DEBUG(llvm::dbgs() << " FAILED MATCH. NO INCREMENT.\n"); DEBUG(llvm::dbgs() << " FAILED MATCH. NO INCREMENT.\n");
} }
@@ -224,8 +398,8 @@ processBBTopDown(ARCBBState &BBState,
if (Op && OtherState.first == Op) if (Op && OtherState.first == Op)
continue; continue;
// If we are tracking an argument, skip it. // If the other state is not tracking anything, bail.
if (!OtherState.second.isTrackingRefCountInst()) if (!OtherState.second.isTrackingRefCount())
continue; continue;
// Check if the instruction we are visiting could potentially decrement // Check if the instruction we are visiting could potentially decrement
@@ -249,8 +423,8 @@ processBBTopDown(ARCBBState &BBState,
} }
void void
swift::arc::ARCSequenceDataflowEvaluator:: swift::arc::ARCSequenceDataflowEvaluator::mergePredecessors(ARCBBState &BBState,
mergePredecessors(ARCBBState &BBState, SILBasicBlock *BB) { SILBasicBlock *BB) {
// For each successor of BB... // For each successor of BB...
unsigned i = 0; unsigned i = 0;
for (auto Pred : BB->getPreds()) { for (auto Pred : BB->getPreds()) {
@@ -277,13 +451,18 @@ mergePredecessors(ARCBBState &BBState, SILBasicBlock *BB) {
bool swift::arc::ARCSequenceDataflowEvaluator::processTopDown() { bool swift::arc::ARCSequenceDataflowEvaluator::processTopDown() {
bool NestingDetected = false; bool NestingDetected = false;
DEBUG(llvm::dbgs() << "<<<< Processing Bottom Up! >>>>\n");
// For each BB in our reverse post order... // For each BB in our reverse post order...
for (auto *BB : reversed(PostOrder)) { 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. // Grab the BBState associated with it and set it to be the current BB.
ARCBBState &BBState = TopDownBBStates[BB]; ARCBBState &BBState = TopDownBBStates[BB];
BBState.init(BB); BBState.init(BB);
DEBUG(llvm::dbgs() << "Merging Predecessors!\n");
mergePredecessors(BBState, BB); mergePredecessors(BBState, BB);
// Then perform the basic block optimization. // Then perform the basic block optimization.
@@ -297,17 +476,15 @@ bool swift::arc::ARCSequenceDataflowEvaluator::processTopDown() {
// Bottom Up Dataflow // Bottom Up Dataflow
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// Analyze a single BB for refcount inc/dec instructions. /// Analyze a single BB for refcount inc/dec instructions.
/// ///
/// If anything was found it will be added to DecToIncStateMap. /// If anything was found it will be added to DecToIncStateMap.
/// ///
/// NestingDetected will be set to indicate that the block needs to be /// NestingDetected will be set to indicate that the block needs to be
/// reanalyzed if code motion occurs. /// reanalyzed if code motion occurs.
static bool static bool processBBBottomUp(
processBBBottomUp(ARCBBState &BBState, ARCBBState &BBState,
BlotMapVector<SILInstruction *, BlotMapVector<SILInstruction *, BottomUpRefCountState> &IncToDecStateMap,
BottomUpRefCountState> &IncToDecStateMap,
AliasAnalysis *AA) { AliasAnalysis *AA) {
DEBUG(llvm::dbgs() << ">>>> Bottom Up!\n"); DEBUG(llvm::dbgs() << ">>>> Bottom Up!\n");
SILBasicBlock &BB = BBState.getBB(); SILBasicBlock &BB = BBState.getBB();
@@ -337,7 +514,7 @@ processBBBottomUp(ARCBBState &BBState,
NestingDetected |= State.initWithInst(&I); NestingDetected |= State.initWithInst(&I);
DEBUG(llvm::dbgs() << " REF COUNT DECREMENT! Known Safe: " 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 // Continue on to see if our reference decrement could potentially affect
// any other pointers via a use or a decrement. // any other pointers via a use or a decrement.
@@ -367,8 +544,8 @@ processBBBottomUp(ARCBBState &BBState,
RefCountState.clear(); RefCountState.clear();
} else { } else {
if (RefCountState.isTrackingRefCountInst()) { if (RefCountState.isTrackingRefCountInst()) {
DEBUG(llvm::dbgs() << " FAILED MATCH DECREMENT:" DEBUG(llvm::dbgs()
<< RefCountState.getValue()); << " FAILED MATCH DECREMENT:" << RefCountState.getValue());
} else { } else {
DEBUG(llvm::dbgs() << " FAILED MATCH DECREMENT. Not tracking a " 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) if (Op && OtherState.first == Op)
continue; continue;
// If we are tracking an argument, skip it. // If this state is not tracking anything, skip it.
if (!OtherState.second.isTrackingRefCountInst()) if (!OtherState.second.isTrackingRefCount())
continue; continue;
// Check if the instruction we are visiting could potentially decrement // Check if the instruction we are visiting could potentially decrement
@@ -412,8 +589,8 @@ processBBBottomUp(ARCBBState &BBState,
} }
void void
swift::arc::ARCSequenceDataflowEvaluator:: swift::arc::ARCSequenceDataflowEvaluator::mergeSuccessors(ARCBBState &BBState,
mergeSuccessors(ARCBBState &BBState, SILBasicBlock *BB) { SILBasicBlock *BB) {
// Grab the backedge set for our BB. // Grab the backedge set for our BB.
auto &BackEdgeSet = BackedgeMap[BB]; auto &BackEdgeSet = BackedgeMap[BB];
@@ -449,13 +626,18 @@ mergeSuccessors(ARCBBState &BBState, SILBasicBlock *BB) {
bool swift::arc::ARCSequenceDataflowEvaluator::processBottomUp() { bool swift::arc::ARCSequenceDataflowEvaluator::processBottomUp() {
bool NestingDetected = false; bool NestingDetected = false;
DEBUG(llvm::dbgs() << "<<<< Processing Bottom Up! >>>>\n");
// For each BB in our post order... // For each BB in our post order...
for (auto *BB : PostOrder) { 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. // Grab the BBState associated with it and set it to be the current BB.
ARCBBState &BBState = BottomUpBBStates[BB]; ARCBBState &BBState = BottomUpBBStates[BB];
BBState.init(BB); BBState.init(BB);
DEBUG(llvm::dbgs() << "Merging Successors!\n");
mergeSuccessors(BBState, BB); mergeSuccessors(BBState, BB);
// Then perform the basic block optimization. // Then perform the basic block optimization.
@@ -509,8 +691,7 @@ bool swift::arc::ARCSequenceDataflowEvaluator::run() {
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
bool swift::arc::performARCSequenceDataflow( bool swift::arc::performARCSequenceDataflow(
SILFunction &F, SILFunction &F, AliasAnalysis *AA,
AliasAnalysis *AA,
BlotMapVector<SILInstruction *, TopDownRefCountState> &DecToIncStateMap, BlotMapVector<SILInstruction *, TopDownRefCountState> &DecToIncStateMap,
BlotMapVector<SILInstruction *, BottomUpRefCountState> &IncToDecStateMap) { BlotMapVector<SILInstruction *, BottomUpRefCountState> &IncToDecStateMap) {

View File

@@ -184,7 +184,7 @@ struct RefCountState {
bool handlePotentialDecrement(SILInstruction *PotentialDecrement, bool handlePotentialDecrement(SILInstruction *PotentialDecrement,
AliasAnalysis *AA) { AliasAnalysis *AA) {
// If we are not tracking a ref count, just return false. // If we are not tracking a ref count, just return false.
if (!isTrackingRefCountInst()) if (!isTrackingRefCount())
return false; return false;
// If we can prove that Other can not use the pointer we are tracking, // If we can prove that Other can not use the pointer we are tracking,
@@ -282,13 +282,13 @@ struct BottomUpRefCountState : public RefCountState<BottomUpRefCountState> {
/// Is this ref count initialized and tracking a ref count ptr. /// Is this ref count initialized and tracking a ref count ptr.
bool isTrackingRefCount() const { bool isTrackingRefCount() const {
return !Decrements.empty(); return Decrements.size();
} }
/// Are we tracking an instruction currently? This returns false when given an /// Are we tracking an instruction currently? This returns false when given an
/// uninitialized ReferenceCountState. /// uninitialized ReferenceCountState.
bool isTrackingRefCountInst() const { bool isTrackingRefCountInst() const {
return !Decrements.empty(); return Decrements.size();
} }
/// Are we tracking a source of ref counts? This currently means that we are /// Are we tracking a source of ref counts? This currently means that we are
@@ -359,7 +359,7 @@ struct BottomUpRefCountState : public RefCountState<BottomUpRefCountState> {
} }
} }
void merge(const BottomUpRefCountState &Other); bool merge(const BottomUpRefCountState &Other);
}; };
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@@ -507,7 +507,7 @@ struct TopDownRefCountState : public RefCountState<TopDownRefCountState> {
} }
} }
void merge(const TopDownRefCountState &Other); bool merge(const TopDownRefCountState &Other);
}; };
} // end arc namespace } // end arc namespace

View File

@@ -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

View File

@@ -4,6 +4,10 @@ sil_stage canonical
import Builtin import Builtin
// Utilities
sil @user : $@thin (Builtin.NativeObject) -> ()
///////////////// /////////////////
// Basic Tests // // Basic Tests //
///////////////// /////////////////
@@ -94,8 +98,6 @@ bb0(%0 : $S, %1 : $Builtin.NativeObject):
return %2 : $() return %2 : $()
} }
sil @user : $@thin (Builtin.NativeObject) -> ()
// CHECK-LABEL: sil @dont_delete_retain_over_decrement_use : $@thin (Builtin.NativeObject) -> () // CHECK-LABEL: sil @dont_delete_retain_over_decrement_use : $@thin (Builtin.NativeObject) -> ()
// CHECK: bb0 // CHECK: bb0
// CHECK: strong_retain // CHECK: strong_retain
@@ -310,8 +312,15 @@ bb0(%0 : $*Builtin.Int32):
} }
// CHECK-LABEL: sil @silargument_retain_iterated : $@thin (@owned Builtin.NativeObject) -> () // CHECK-LABEL: sil @silargument_retain_iterated : $@thin (@owned Builtin.NativeObject) -> ()
// CHECK-NOT: strong_retain // CHECK: bb0
// CHECK-NOT: strong_release // 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) -> () { sil @silargument_retain_iterated : $@thin (@owned Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject): bb0(%0 : $Builtin.NativeObject):
%1 = function_ref @user : $@thin (Builtin.NativeObject) -> () %1 = function_ref @user : $@thin (Builtin.NativeObject) -> ()
@@ -441,3 +450,572 @@ bb0(%0 : $RetainUser, %1 : $Builtin.NativeObject):
strong_release %0 : $RetainUser strong_release %0 : $RetainUser
return %1 : $Builtin.NativeObject 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 : $()
}