This is part of a series of commits to remove reference forwarding for some of the ARC entry points. rdar://22724641.

After this commit, swift_retain will return no reference and LLVMARCContract pass is modified NOT to rewrite
swift_retain_noresult to old swift_retain which forwarded the reference.

Swift SVN r32075
This commit is contained in:
Xin Tong
2015-09-18 20:35:39 +00:00
parent 090e73c728
commit bc3fe169b4
10 changed files with 90 additions and 223 deletions

View File

@@ -137,7 +137,6 @@ extern "C" void swift_slowDealloc(void *ptr, size_t bytes, size_t alignMask);
/// Atomically increments the retain count of an object.
///
/// \param object - may be null, in which case this is a no-op
/// \return its argument value exactly
///
/// POSSIBILITIES: We may end up wanting a bunch of different variants:
/// - the general version which correctly handles null values, swift
@@ -147,16 +146,15 @@ extern "C" void swift_slowDealloc(void *ptr, size_t bytes, size_t alignMask);
/// - maybe a variant that can assume a non-null object
/// It may also prove worthwhile to have this use a custom CC
/// which preserves a larger set of registers.
extern "C" HeapObject *swift_retain(HeapObject *object);
extern "C" void swift_retain(HeapObject *object);
extern "C" void swift_retain_noresult(HeapObject *object);
extern "C" HeapObject *swift_retain_n(HeapObject *object, uint32_t n);
extern "C" void swift_retain_n(HeapObject *object, uint32_t n);
static inline HeapObject *_swift_retain_inlined(HeapObject *object) {
static inline void _swift_retain_inlined(HeapObject *object) {
if (object) {
object->refCount.increment();
}
return object;
}
/// Atomically increments the reference count of an object, unless it has
@@ -327,7 +325,9 @@ public:
swift_release(object);
}
SwiftRAII(const SwiftRAII &other) : object(swift_retain(*other)) {
SwiftRAII(const SwiftRAII &other) {
swift_retain(*other);
object = *other;
;
}
SwiftRAII(SwiftRAII &&other) : object(*other) {
@@ -336,7 +336,8 @@ public:
SwiftRAII &operator=(const SwiftRAII &other) {
if (object)
swift_release(object);
object = swift_retain(*other);
swift_retain(*other);
object = *other;
return *this;
}
SwiftRAII &operator=(SwiftRAII &&other) {

View File

@@ -26,8 +26,8 @@ extern "C" HeapObject *(*_swift_allocObject)(HeapMetadata const *metadata,
extern "C" BoxPair::Return (*_swift_allocBox)(Metadata const *type);
extern "C" HeapObject *(*_swift_retain)(HeapObject *object);
extern "C" HeapObject *(*_swift_retain_n)(HeapObject *object, uint32_t n);
extern "C" void (*_swift_retain)(HeapObject *object);
extern "C" void (*_swift_retain_n)(HeapObject *object, uint32_t n);
extern "C" HeapObject *(*_swift_tryRetain)(HeapObject *object);
extern "C" bool (*_swift_isDeallocating)(HeapObject *object);
extern "C" void (*_swift_release)(HeapObject *object);

View File

@@ -129,7 +129,8 @@ private:
auto &M = getModule();
auto AttrList = AttributeSet::get(
M.getContext(), AttributeSet::FunctionIndex, Attribute::NoUnwind);
Retain = M.getOrInsertFunction("swift_retain", AttrList, ObjectPtrTy,
Retain = M.getOrInsertFunction("swift_retain", AttrList,
Type::getVoidTy(M.getContext()),
ObjectPtrTy, nullptr);
return Retain.get();
}
@@ -174,7 +175,8 @@ private:
auto *Int32Ty = Type::getInt32Ty(M.getContext());
auto AttrList = AttributeSet::get(
M.getContext(), AttributeSet::FunctionIndex, Attribute::NoUnwind);
RetainN = M.getOrInsertFunction("swift_retain_n", AttrList, ObjectPtrTy,
RetainN = M.getOrInsertFunction("swift_retain_n", AttrList,
Type::getVoidTy(M.getContext()),
ObjectPtrTy, Int32Ty, nullptr);
return RetainN.get();
}

View File

@@ -34,7 +34,6 @@ STATISTIC(NumRetainReleasesEliminatedByMergingIntoRetainReleaseN,
namespace {
struct LocalState {
Value *CurrentLocalUpdate;
TinyPtrVector<CallInst *> RetainList;
TinyPtrVector<CallInst *> ReleaseList;
};
@@ -46,9 +45,6 @@ struct LocalState {
///
/// Optimizations include:
///
/// - Lowering "retain no return" calls to swift_retain (which return the
/// retained argument) to lower register pressure.
///
/// - Merging together retain and release calls into retain_n, release_n
/// - calls.
///
@@ -63,18 +59,6 @@ class SwiftARCContractImpl {
/// The entry point builder that is used to construct ARC entry points.
ARCEntryPointBuilder B;
/// Since all of the calls are canonicalized, we know that we can just walk
/// through the function and collect the interesting heap object definitions
/// by getting the argument to these functions.
DenseMap<Value *, TinyPtrVector<Instruction *>> DefsOfValue;
/// Keep track of which order we see values in since iteration over a densemap
/// isn't in a deterministic order, and isn't efficient anyway.
///
/// TODO: Maybe this should be merged into DefsOfValue in a MapVector?
SmallVector<Value *, 16> DefOrder;
public:
SwiftARCContractImpl(Function &InF) : Changed(false), F(InF), B(F) {}
@@ -82,12 +66,6 @@ public:
bool run();
private:
/// Perform single basic block optimizations.
///
/// This means changing retain_no_return into retains, finding return values,
/// and merging retains, releases.
void performSingleBBOpts();
/// Perform the RRN Optimization given the current state that we are
/// tracking. This is called at the end of BBs and if we run into an unknown
/// call.
@@ -107,22 +85,11 @@ performRRNOptimization(DenseMap<Value *, LocalState> &PtrToLocalStateMap) {
if (RetainList.size() > 1) {
// Create the retainN call right by the first retain.
B.setInsertPoint(RetainList[0]);
auto &CI = *B.createRetainN(RetainList[0]->getArgOperand(0),
RetainList.size());
// Change the Local Entry to be the new retainN call.
P.second.CurrentLocalUpdate = &CI;
// Change GlobalEntry to track the new retainN instruction instead of
// the last retain that was seen.
TinyPtrVector<Instruction *> &GlobalEntry = DefsOfValue[ArgVal];
GlobalEntry.pop_back();
GlobalEntry.push_back(&CI);
B.createRetainN(RetainList[0]->getArgOperand(0), RetainList.size());
// Replace all uses of the retain instructions with our new retainN and
// then delete them.
for (auto *Inst : RetainList) {
Inst->replaceAllUsesWith(&CI);
Inst->eraseFromParent();
NumRetainReleasesEliminatedByMergingIntoRetainReleaseN++;
}
@@ -150,11 +117,9 @@ performRRNOptimization(DenseMap<Value *, LocalState> &PtrToLocalStateMap) {
}
}
void SwiftARCContractImpl::performSingleBBOpts() {
// Do a first pass over the function, collecting all interesting definitions.
// In this pass, we rewrite any intra-block uses that we can, since the
// SSAUpdater doesn't handle them.
bool SwiftARCContractImpl::run() {
// intra-BB retain/release merging.
DenseMap<Value *, LocalState> PtrToLocalStateMap;
for (BasicBlock &BB : F) {
for (auto II = BB.begin(), IE = BB.end(); II != IE; ) {
@@ -172,38 +137,11 @@ void SwiftARCContractImpl::performSingleBBOpts() {
case RT_Retain:
llvm_unreachable("This should be canonicalized away!");
case RT_RetainNoResult: {
auto *ArgVal = cast<CallInst>(Inst).getArgOperand(0);
B.setInsertPoint(&Inst);
CallInst &CI = *B.createRetain(ArgVal);
Inst.eraseFromParent();
if (!isa<Instruction>(ArgVal) && !isa<Argument>(ArgVal))
continue;
TinyPtrVector<Instruction *> &GlobalEntry = DefsOfValue[ArgVal];
// If this is the first definition of a value for the argument that
// we've seen, keep track of it in DefOrder.
if (GlobalEntry.empty())
DefOrder.push_back(ArgVal);
auto *CI = cast<CallInst>(&Inst);
auto *ArgVal = CI->getArgOperand(0);
LocalState &LocalEntry = PtrToLocalStateMap[ArgVal];
// Check to see if there is already an entry for this basic block. If
// there is another local entry, switch to using the local value and
// remove the previous value from the GlobalEntry.
if (LocalEntry.CurrentLocalUpdate) {
Changed = true;
CI.setArgOperand(0, LocalEntry.CurrentLocalUpdate);
assert(GlobalEntry.back() == LocalEntry.CurrentLocalUpdate &&
"Local/Global mismatch?");
GlobalEntry.pop_back();
}
LocalEntry.CurrentLocalUpdate = &CI;
LocalEntry.RetainList.push_back(&CI);
GlobalEntry.push_back(&CI);
LocalEntry.RetainList.push_back(CI);
continue;
}
case RT_Release: {
@@ -226,22 +164,9 @@ void SwiftARCContractImpl::performSingleBBOpts() {
case RT_CheckUnowned:
case RT_ObjCRelease:
case RT_ObjCRetain:
// Just remap any uses in the value.
break;
}
// Check to see if there are any uses of a value in the LocalUpdates
// map. If so, remap it now to the locally defined version.
for (unsigned i = 0, e = Inst.getNumOperands(); i != e; ++i) {
auto Iter = PtrToLocalStateMap.find(Inst.getOperand(i));
if (Iter != PtrToLocalStateMap.end()) {
if (Value *V = Iter->second.CurrentLocalUpdate) {
Changed = true;
Inst.setOperand(i, V);
}
}
}
if (Kind != RT_Unknown)
continue;
@@ -271,64 +196,6 @@ void SwiftARCContractImpl::performSingleBBOpts() {
performRRNOptimization(PtrToLocalStateMap);
PtrToLocalStateMap.clear();
}
}
bool SwiftARCContractImpl::run() {
// Perform single BB optimizations and gather information in prepration for
// the multiple BB optimizations.
performSingleBBOpts();
// Now that we've collected all of the interesting heap object values that are
// passed into argument-returning functions, rewrite uses of these pointers
// with optimized lifetime-shorted versions of it.
for (Value *Ptr : DefOrder) {
// If Ptr is an instruction, remember its block. If not, use the entry
// block as its block (it must be an argument, constant, etc).
BasicBlock *PtrBlock;
if (auto *PI = dyn_cast<Instruction>(Ptr))
PtrBlock = PI->getParent();
else
PtrBlock = &F.getEntryBlock();
TinyPtrVector<Instruction *> &Defs = DefsOfValue[Ptr];
// This is the same problem as SSA construction, so we just use LLVM's
// SSAUpdater, with each retain as a definition of the virtual value.
SSAUpdater Updater;
Updater.Initialize(Ptr->getType(), Ptr->getName());
// Set the return value of each of these calls as a definition of the
// virtual value.
for (auto *D : Defs)
Updater.AddAvailableValue(D->getParent(), D);
// If we didn't add a definition for Ptr's block, then Ptr itself is
// available in its block.
if (!Updater.HasValueForBlock(PtrBlock))
Updater.AddAvailableValue(PtrBlock, Ptr);
// Rewrite uses of Ptr to their optimized forms.
//
// NOTE: We are assuming that our Ptrs are not constants meaning that we
// know that users can not be constant expressions.
for (auto UI = Ptr->user_begin(), E = Ptr->user_end(); UI != E; ) {
// Make sure to increment the use iterator before potentially rewriting
// it.
Use &U = UI.getUse();
++UI;
// If the use is in the same block that defines it and the User is not a
// PHI node, then this is a local use that shouldn't be rewritten.
auto *User = cast<Instruction>(U.getUser());
if (User->getParent() == PtrBlock && !isa<PHINode>(User))
continue;
// Otherwise, change it if profitable!
Updater.RewriteUse(U);
if (U.get() != Ptr)
Changed = true;
}
}
return Changed;
}

View File

@@ -95,7 +95,8 @@ swift::swift_getErrorValue(const SwiftError *errorObject,
SwiftError *
swift::swift_errorRetain(SwiftError *object) {
return static_cast<SwiftError*>(swift_retain(object));
swift_retain(object);
return static_cast<SwiftError*>(object);
}
void swift::swift_errorRelease(SwiftError *object) {

View File

@@ -281,24 +281,23 @@ swift::swift_retain_noresult(HeapObject *object) {
swift_retain(object);
}
HeapObject *swift::swift_retain(HeapObject *object) {
void swift::swift_retain(HeapObject *object) {
SWIFT_RETAIN();
return _swift_retain(object);
_swift_retain(object);
}
static HeapObject *_swift_retain_(HeapObject *object) {
return _swift_retain_inlined(object);
static void _swift_retain_(HeapObject *object) {
_swift_retain_inlined(object);
}
auto swift::_swift_retain = _swift_retain_;
HeapObject *swift::swift_retain_n(HeapObject *object, uint32_t n) {
void swift::swift_retain_n(HeapObject *object, uint32_t n) {
SWIFT_RETAIN();
return _swift_retain_n(object, n);
_swift_retain_n(object, n);
}
static HeapObject *_swift_retain_n_(HeapObject *object, uint32_t n) {
static void _swift_retain_n_(HeapObject *object, uint32_t n) {
if (object) {
object->refCount.increment(n);
}
return object;
}
auto swift::_swift_retain_n = _swift_retain_n_;

View File

@@ -273,7 +273,8 @@ template <class Impl, class T> struct RetainableBoxBase {
struct SwiftRetainableBox :
RetainableBoxBase<SwiftRetainableBox, HeapObject*> {
static HeapObject *retain(HeapObject *obj) {
return swift_retain(obj);
swift_retain(obj);
return obj;
}
static void release(HeapObject *obj) {
@@ -443,7 +444,8 @@ struct UnknownRetainableBox : RetainableBoxBase<UnknownRetainableBox, void*> {
#if SWIFT_OBJC_INTEROP
return swift_unknownRetain(obj);
#else
return swift_retain(static_cast<HeapObject *>(obj));
swift_retain(static_cast<HeapObject *>(obj));
return static_cast<HeapObject *>(obj);
#endif
}

View File

@@ -569,8 +569,10 @@ static bool usesNativeSwiftReferenceCounting_unowned(const void *object) {
void *swift::swift_unknownRetain_n(void *object, int n) {
void *objc_ret = nullptr;
if (isObjCTaggedPointerOrNull(object)) return object;
if (usesNativeSwiftReferenceCounting_allocated(object))
return swift_retain_n(static_cast<HeapObject *>(object), n);
if (usesNativeSwiftReferenceCounting_allocated(object)) {
swift_retain_n(static_cast<HeapObject *>(object), n);
return static_cast<HeapObject *>(object);
}
for (int i = 0; i < n; ++i)
objc_ret = objc_retain(static_cast<id>(object));
return objc_ret;
@@ -586,8 +588,10 @@ void swift::swift_unknownRelease_n(void *object, int n) {
void *swift::swift_unknownRetain(void *object) {
if (isObjCTaggedPointerOrNull(object)) return object;
if (usesNativeSwiftReferenceCounting_allocated(object))
return swift_retain(static_cast<HeapObject *>(object));
if (usesNativeSwiftReferenceCounting_allocated(object)) {
swift_retain(static_cast<HeapObject *>(object));
return static_cast<HeapObject *>(object);
}
return objc_retain(static_cast<id>(object));
}
@@ -627,11 +631,14 @@ void *swift::swift_bridgeObjectRetain(void *object) {
auto const objectRef = toPlainObject_unTagged_bridgeObject(object);
#if SWIFT_OBJC_INTEROP
if (!isNonNative_unTagged_bridgeObject(object))
return swift_retain(static_cast<HeapObject *>(objectRef));
if (!isNonNative_unTagged_bridgeObject(object)) {
swift_retain(static_cast<HeapObject *>(objectRef));
return static_cast<HeapObject *>(objectRef);
}
return objc_retain(static_cast<id>(objectRef));
#else
return swift_retain(static_cast<HeapObject *>(objectRef));
swift_retain(static_cast<HeapObject *>(objectRef));
return static_cast<HeapObject *>(objectRef);
#endif
}
@@ -662,13 +669,16 @@ void *swift::swift_bridgeObjectRetain_n(void *object, int n) {
#if SWIFT_OBJC_INTEROP
void *objc_ret = nullptr;
if (!isNonNative_unTagged_bridgeObject(object))
return swift_retain_n(static_cast<HeapObject *>(objectRef), n);
if (!isNonNative_unTagged_bridgeObject(object)) {
swift_retain_n(static_cast<HeapObject *>(objectRef), n);
return static_cast<HeapObject *>(objectRef);
}
for (int i = 0;i < n; ++i)
objc_ret = objc_retain(static_cast<id>(objectRef));
return objc_ret;
#else
return swift_retain_n(static_cast<HeapObject *>(objectRef), n);
swift_retain_n(static_cast<HeapObject *>(objectRef), n);
return static_cast<HeapObject *>(objectRef);
#endif
}

View File

@@ -8,7 +8,7 @@ target triple = "x86_64-apple-macosx10.9"
declare %swift.refcounted* @swift_allocObject(%swift.heapmetadata* , i64, i64) nounwind
declare void @swift_release(%swift.refcounted* nocapture)
declare %swift.refcounted* @swift_retain(%swift.refcounted* ) nounwind
declare void @swift_retain(%swift.refcounted* ) nounwind
declare void @swift_retain_noresult(%swift.refcounted* nocapture) nounwind
declare void @swift_fixLifetime(%swift.refcounted*)
declare void @noread_user(%swift.refcounted*) readnone
@@ -23,32 +23,22 @@ entry:
ret void
}
; CHECK-LABEL: define void @swift_contractNoresultTest
; CHECK: call %swift.refcounted* @swift_retain(%swift.refcounted* %A), !dbg
define void @swift_contractNoresultTest(%swift.refcounted* %A) {
tail call void @swift_retain_noresult(%swift.refcounted* %A), !dbg !0
tail call void @swift_release(%swift.refcounted* %A) nounwind
ret void
}
; CHECK-LABEL: define %swift.refcounted* @swift_contractRetainN(%swift.refcounted* %A) {
; CHECK: entry:
; CHECK-NEXT: br i1 undef
; CHECK: bb1:
; CHECK-NEXT: [[RET1:%.+]] = tail call %swift.refcounted* @swift_retain_n(%swift.refcounted* %A, i32 2)
; CHECK-NEXT: call void @noread_user(%swift.refcounted* [[RET1]])
; CHECK-NEXT: call void @noread_user(%swift.refcounted* [[RET1]])
; CHECK-NEXT: tail call void @swift_retain_n(%swift.refcounted* %A, i32 2)
; CHECK-NEXT: call void @noread_user(%swift.refcounted* %A)
; CHECK-NEXT: call void @noread_user(%swift.refcounted* %A)
; CHECK-NEXT: br label %bb3
; CHECK: bb2:
; CHECK-NEXT: call void @noread_user(%swift.refcounted* %A)
; CHECK-NEXT: [[RET2:%.+]] = tail call %swift.refcounted* @swift_retain(%swift.refcounted* %A)
; CHECK-NEXT: call void @noread_user(%swift.refcounted* [[RET2]])
; CHECK-NEXT: tail call void @swift_retain_noresult(%swift.refcounted* %A)
; CHECK-NEXT: call void @noread_user(%swift.refcounted* %A)
; CHECK-NEXT: br label %bb3
; CHECK: bb3:
; CHECK-NEXT: [[PHIRESULT:%.+]] = phi %swift.refcounted* [ [[RET2]], %bb2 ], [ [[RET1]], %bb1 ]
; CHECK-NEXT: [[RET3:%.+]] = tail call %swift.refcounted* @swift_retain(%swift.refcounted* [[PHIRESULT]])
; CHECK-NEXT: ret %swift.refcounted* [[RET3]]
; CHECK-NEXT: tail call void @swift_retain_noresult(%swift.refcounted* %A)
; CHECK-NEXT: ret %swift.refcounted* %A
define %swift.refcounted* @swift_contractRetainN(%swift.refcounted* %A) {
entry:
br i1 undef, label %bb1, label %bb2
@@ -163,27 +153,26 @@ bb3:
; But do make sure that we can form retainN, releaseN in between such uses
; CHECK-LABEL: define %swift.refcounted* @swift_contractRetainNInterleavedWithUnknown(%swift.refcounted* %A) {
; CHECK: bb1:
; CHECK: [[RET1:%.*]] = tail call %swift.refcounted* @swift_retain(%swift.refcounted* %A)
; CHECK-NEXT: call void @user(%swift.refcounted* [[RET1]])
; CHECK-NEXT: [[RET2:%.*]] = tail call %swift.refcounted* @swift_retain_n(%swift.refcounted* [[RET1]], i32 3)
; CHECK-NEXT: call void @noread_user(%swift.refcounted* [[RET2]])
; CHECK-NEXT: call void @noread_user(%swift.refcounted* [[RET2]])
; CHECK-NEXT: call void @user(%swift.refcounted* [[RET2]])
; CHECK-NEXT: [[RET3:%.*]] = tail call %swift.refcounted* @swift_retain_n(%swift.refcounted* [[RET2]], i32 2)
; CHECK-NEXT: call void @user(%swift.refcounted* [[RET3]])
; CHECK-NEXT: [[RET4:%.*]] = tail call %swift.refcounted* @swift_retain(%swift.refcounted* [[RET3]])
; CHECK: tail call void @swift_retain_noresult(%swift.refcounted* %A)
; CHECK-NEXT: call void @user(%swift.refcounted* %A)
; CHECK-NEXT: tail call void @swift_retain_n(%swift.refcounted* %A, i32 3)
; CHECK-NEXT: call void @noread_user(%swift.refcounted* %A)
; CHECK-NEXT: call void @noread_user(%swift.refcounted* %A)
; CHECK-NEXT: call void @user(%swift.refcounted* %A)
; CHECK-NEXT: tail call void @swift_retain_n(%swift.refcounted* %A, i32 2)
; CHECK-NEXT: call void @user(%swift.refcounted* %A)
; CHECK-NEXT: tail call void @swift_retain_noresult(%swift.refcounted* %A)
; CHECK-NEXT: br label %bb3
; CHECK: bb2:
; CHECK-NEXT: call void @user(%swift.refcounted* %A)
; CHECK-NEXT: [[RET5:%.*]] = tail call %swift.refcounted* @swift_retain(%swift.refcounted* %A)
; CHECK-NEXT: call void @user(%swift.refcounted* [[RET5]])
; CHECK-NEXT: tail call void @swift_retain_noresult(%swift.refcounted* %A)
; CHECK-NEXT: call void @user(%swift.refcounted* %A)
; CHECK-NEXT: br label %bb3
; CHECK: bb3:
; CHECK-NEXT: [[PHIRET:%.*]] = phi %swift.refcounted* [ [[RET5]], %bb2 ], [ [[RET4]], %bb1 ]
; CHECK-NEXT: [[FINALRET:%.*]] = tail call %swift.refcounted* @swift_retain(%swift.refcounted* [[PHIRET]])
; CHECK-NEXT: ret %swift.refcounted* [[FINALRET]]
; CHECK-NEXT: tail call void @swift_retain_noresult(%swift.refcounted* %A)
; CHECK-NEXT: ret %swift.refcounted* %A
define %swift.refcounted* @swift_contractRetainNInterleavedWithUnknown(%swift.refcounted* %A) {
entry:
br i1 undef, label %bb1, label %bb2
@@ -248,17 +237,17 @@ bb3:
; CHECK-LABEL: define %swift.refcounted* @swift_contractRetainReleaseNInterleavedWithUnknown(%swift.refcounted* %A) {
; CHECK: bb1:
; CHECK-NEXT: [[RET1:%.*]] = tail call %swift.refcounted* @swift_retain(%swift.refcounted* %A)
; CHECK-NEXT: call void @user(%swift.refcounted* [[RET1]])
; CHECK-NEXT: call void @noread_user(%swift.refcounted* [[RET1]])
; CHECK-NEXT: tail call void @swift_release_n(%swift.refcounted* [[RET1]], i32 2)
; CHECK-NEXT: call void @user(%swift.refcounted* [[RET1]])
; CHECK-NEXT: [[RET2:%.*]] = tail call %swift.refcounted* @swift_retain_n(%swift.refcounted* [[RET1]], i32 2)
; CHECK-NEXT: tail call void @swift_release(%swift.refcounted* [[RET2]])
; CHECK-NEXT: call void @user(%swift.refcounted* [[RET2]])
; CHECK-NEXT: call void @noread_user(%swift.refcounted* [[RET2]])
; CHECK-NEXT: tail call void @swift_release_n(%swift.refcounted* [[RET2]], i32 2)
; CHECK-NEXT: call void @user(%swift.refcounted* [[RET2]])
; CHECK-NEXT: tail call void @swift_retain_noresult(%swift.refcounted* %A)
; CHECK-NEXT: call void @user(%swift.refcounted* %A)
; CHECK-NEXT: call void @noread_user(%swift.refcounted* %A)
; CHECK-NEXT: tail call void @swift_release_n(%swift.refcounted* %A, i32 2)
; CHECK-NEXT: call void @user(%swift.refcounted* %A)
; CHECK-NEXT: tail call void @swift_retain_n(%swift.refcounted* %A, i32 2)
; CHECK-NEXT: tail call void @swift_release(%swift.refcounted* %A)
; CHECK-NEXT: call void @user(%swift.refcounted* %A)
; CHECK-NEXT: call void @noread_user(%swift.refcounted* %A)
; CHECK-NEXT: tail call void @swift_release_n(%swift.refcounted* %A, i32 2)
; CHECK-NEXT: call void @user(%swift.refcounted* %A)
; CHECK-NEXT: br label %bb3
; CHECK: bb2:
; CHECK-NEXT: call void @user(%swift.refcounted* %A)
@@ -267,9 +256,8 @@ bb3:
; CHECK-NEXT: br label %bb3
; CHECK: bb3:
; CHECK-NEXT: [[PHIRET:%.*]] = phi %swift.refcounted* [ %A, %bb2 ], [ [[RET2]], %bb1 ]
; CHECK-NEXT: tail call void @swift_release(%swift.refcounted* [[PHIRET]])
; CHECK-NEXT: ret %swift.refcounted* [[PHIRET]]
; CHECK-NEXT: tail call void @swift_release(%swift.refcounted* %A)
; CHECK-NEXT: ret %swift.refcounted* %A
define %swift.refcounted* @swift_contractRetainReleaseNInterleavedWithUnknown(%swift.refcounted* %A) {
entry:
br i1 undef, label %bb1, label %bb2

View File

@@ -59,8 +59,7 @@ TEST(RefcountingTest, retain_release) {
size_t value = 0;
auto object = allocTestObject(&value, 1);
EXPECT_EQ(0u, value);
auto retainResult = swift_retain(object);
EXPECT_EQ(object, retainResult);
swift_retain(object);
EXPECT_EQ(0u, value);
swift_release(object);
EXPECT_EQ(0u, value);
@@ -103,10 +102,8 @@ TEST(RefcountingTest, retain_release_n) {
size_t value = 0;
auto object = allocTestObject(&value, 1);
EXPECT_EQ(0u, value);
auto retainResult = swift_retain_n(object, 32);
EXPECT_EQ(object, retainResult);
retainResult = swift_retain(object);
EXPECT_EQ(object, retainResult);
swift_retain_n(object, 32);
swift_retain(object);
EXPECT_EQ(0u, value);
EXPECT_EQ(34u, swift_retainCount(object));
swift_release_n(object, 31);