Skip to content

[Runtime] Eliminate stack frames in swift_retain and swift_bridgeObjectRetain on ARM64. #61794

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions stdlib/public/SwiftShims/swift/shims/RefCount.h
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ class RefCounts {
// Out-of-line slow paths.

SWIFT_NOINLINE
void incrementSlow(RefCountBits oldbits, uint32_t inc) SWIFT_CC(PreserveMost);
HeapObject *incrementSlow(RefCountBits oldbits, uint32_t inc);

SWIFT_NOINLINE
void incrementNonAtomicSlow(RefCountBits oldbits, uint32_t inc);
Expand Down Expand Up @@ -799,14 +799,18 @@ class RefCounts {
}

// Increment the reference count.
//
// This returns the enclosing HeapObject so that it the result of this call
// can be directly returned from swift_retain. This makes the call to
// incrementSlow() a tail call.
SWIFT_ALWAYS_INLINE
void increment(uint32_t inc = 1) {
HeapObject *increment(uint32_t inc = 1) {
auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);

// constant propagation will remove this in swift_retain, it should only
// be present in swift_retain_n
if (inc != 1 && oldbits.isImmortal(true)) {
return;
return getHeapObject();
}

RefCountBits newbits;
Expand All @@ -815,11 +819,12 @@ class RefCounts {
bool fast = newbits.incrementStrongExtraRefCount(inc);
if (SWIFT_UNLIKELY(!fast)) {
if (oldbits.isImmortal(false))
return;
return getHeapObject();
return incrementSlow(oldbits, inc);
}
} while (!refCounts.compare_exchange_weak(oldbits, newbits,
std::memory_order_relaxed));
return getHeapObject();
}

SWIFT_ALWAYS_INLINE
Expand Down
6 changes: 6 additions & 0 deletions stdlib/public/SwiftShims/swift/shims/Visibility.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@
#define SWIFT_WEAK_IMPORT
#endif

#if __has_attribute(musttail)
#define SWIFT_MUSTTAIL [[clang::musttail]]
#else
#define SWIFT_MUSTTAIL
#endif

// Define the appropriate attributes for sharing symbols across
// image (executable / shared-library) boundaries.
//
Expand Down
8 changes: 6 additions & 2 deletions stdlib/public/runtime/HeapObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,12 @@ _swift_release_dealloc(HeapObject *object);
SWIFT_ALWAYS_INLINE
static HeapObject *_swift_retain_(HeapObject *object) {
SWIFT_RT_TRACK_INVOCATION(object, swift_retain);
if (isValidPointerForNativeRetain(object))
object->refCounts.increment(1);
if (isValidPointerForNativeRetain(object)) {
// Return the result of increment() to make the eventual call to
// incrementSlow a tail call, which avoids pushing a stack frame on the fast
// path on ARM64.
return object->refCounts.increment(1);
}
return object;
}

Expand Down
15 changes: 10 additions & 5 deletions stdlib/public/runtime/RefCount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
namespace swift {

template <typename RefCountBits>
void RefCounts<RefCountBits>::incrementSlow(RefCountBits oldbits,
uint32_t n) {
HeapObject *RefCounts<RefCountBits>::incrementSlow(RefCountBits oldbits,
uint32_t n) {
if (oldbits.isImmortal(false)) {
return;
return getHeapObject();
}
else if (oldbits.hasSideTable()) {
// Out-of-line slow path.
Expand All @@ -29,9 +29,14 @@ void RefCounts<RefCountBits>::incrementSlow(RefCountBits oldbits,
// Retain count overflow.
swift::swift_abortRetainOverflow();
}
return getHeapObject();
}
template void RefCounts<InlineRefCountBits>::incrementSlow(InlineRefCountBits oldbits, uint32_t n);
template void RefCounts<SideTableRefCountBits>::incrementSlow(SideTableRefCountBits oldbits, uint32_t n);
template HeapObject *
RefCounts<InlineRefCountBits>::incrementSlow(InlineRefCountBits oldbits,
uint32_t n);
template HeapObject *
RefCounts<SideTableRefCountBits>::incrementSlow(SideTableRefCountBits oldbits,
uint32_t n);

template <typename RefCountBits>
void RefCounts<RefCountBits>::incrementNonAtomicSlow(RefCountBits oldbits,
Expand Down
30 changes: 24 additions & 6 deletions stdlib/public/runtime/SwiftObject.mm
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,20 @@ static bool isBridgeObjectTaggedPointer(void *object) {
return (void*)(uintptr_t(object) & ~unTaggedNonNativeBridgeObjectBits);
}

#if SWIFT_OBJC_INTEROP
#if __arm64__
// Marking this as noinline allows swift_bridgeObjectRetain to avoid emitting
// a stack frame for the swift_retain path on ARM64. It makes for worse codegen
// on x86-64, though, so limit it to ARM64.
SWIFT_NOINLINE
#endif
static void *objcRetainAndReturn(void *object) {
auto const objectRef = toPlainObject_unTagged_bridgeObject(object);
objc_retain(static_cast<id>(objectRef));
return object;
}
#endif

void *swift::swift_bridgeObjectRetain(void *object) {
#if SWIFT_OBJC_INTEROP
if (isObjCTaggedPointer(object) || isBridgeObjectTaggedPointer(object))
Expand All @@ -584,14 +598,18 @@ static bool isBridgeObjectTaggedPointer(void *object) {

#if SWIFT_OBJC_INTEROP
if (!isNonNative_unTagged_bridgeObject(object)) {
swift_retain(static_cast<HeapObject *>(objectRef));
return object;
return swift_retain(static_cast<HeapObject *>(objectRef));
}
objc_retain(static_cast<id>(objectRef));
return object;

// Put the call to objc_retain in a separate function, tail-called here. This
// allows the fast path of swift_bridgeObjectRetain to avoid creating a stack
// frame on ARM64. We can't directly tail-call objc_retain, because
// swift_bridgeObjectRetain returns the pointer with objectPointerIsObjCBit
// set, so we have to make a non-tail call and then return the value with the
// bit set.
SWIFT_MUSTTAIL return objcRetainAndReturn(object);
#else
swift_retain(static_cast<HeapObject *>(objectRef));
return object;
return swift_retain(static_cast<HeapObject *>(objectRef));
#endif
}

Expand Down