Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
2f52f5c
Initial static library with implementation.
mikeash Sep 4, 2025
1bf3c6a
Use offset for mask value to get the right value when weakly referenced.
mikeash Sep 4, 2025
6620cf4
Macro-ize loading/saving registers.
mikeash Sep 4, 2025
a02db4f
Make inlined retain atomic. Don't smash x2.
mikeash Sep 5, 2025
82f42d1
Hacky compiler/runtime support.
mikeash Sep 5, 2025
0a13535
Improve and comment the RR implementations.
mikeash Sep 8, 2025
39ad94f
Automatically link RR library into clients.
mikeash Sep 10, 2025
b9d15b8
Conditionally emit the Inlined RR functions only on ARM64 targets.
mikeash Sep 11, 2025
e73e2c5
Save an instruction when saving registers on the slow path.
mikeash Sep 11, 2025
f81e350
Make RR symbols private_extern so they don't get exported from binari…
mikeash Sep 11, 2025
86e9c47
Correctly classify instructions calling the Inlined variants.
mikeash Sep 12, 2025
4de4672
Don't use GOTPAGE to reference non-GOT data.
mikeash Sep 12, 2025
a0fefb0
Make swiftCore target depend on swiftClientRetainRelease.
mikeash Sep 13, 2025
3d17243
Try plain PreserveMost.
mikeash Sep 15, 2025
830f5b6
Add ldxr/stxr implementation for targets without the cas instruction.
mikeash Sep 18, 2025
d1536e3
Fix ldxr/stxr implementation: proper release barrier, move mask load …
mikeash Sep 19, 2025
53dfdd3
Use ptrauth instructions when needed.
mikeash Sep 19, 2025
eb603cb
Don't use client RR on Embedded.
mikeash Sep 19, 2025
4753c7a
Comment the mask symbol and only emit it on ARM64.
mikeash Sep 22, 2025
29f4b28
Update test/IRGen/function_types.sil for new stuff.
mikeash Sep 23, 2025
9a0e39f
Apply preserve_most to swift_retain/release as well to improve slow-p…
mikeash Sep 24, 2025
e14d888
Avoid pushing a stack frame around the fast path of swift_retain/rele…
mikeash Sep 25, 2025
36b1a92
Test with standard CC for everything.
mikeash Sep 29, 2025
bd98554
Revert "Test with standard CC for everything."
mikeash Sep 29, 2025
c1f690a
Rename retain/releaseInlined to retain/releaseClient. We're not reall…
mikeash Sep 29, 2025
f3e5165
Add alignment directive to functions.
mikeash Sep 30, 2025
961023f
Inlined -> Client in LLVMARCOpts.h.
mikeash Sep 30, 2025
2a3c58a
Add Client versions of bridgeObjectRR.
mikeash Oct 1, 2025
c24fb77
Early out on tagged pointers in bridgeObjectRR.
mikeash Oct 2, 2025
d29ff46
Switch from __ARM_FEATURE_PAUTH to __has_feature(ptrauth_calls) to av…
mikeash Oct 2, 2025
4487e56
Disable clientRR in JIT mode.
mikeash Oct 3, 2025
9805175
Make bridgeObjectRetainClient return the original unmasked value.
mikeash Oct 3, 2025
59ffb8c
Put the slowpath mask in a special section.
mikeash Oct 6, 2025
ae3afdf
Add 'preservemost' aliases for swift_retain/release. Use weak definit…
mikeash Oct 6, 2025
44385ac
Use raw strings for asm in HeapObject.cpp.
mikeash Oct 6, 2025
d857de4
Add an option to disable client retain/release.
mikeash Oct 6, 2025
9a9e6c4
Fix alignment of slowpath mask.
mikeash Oct 6, 2025
c09846b
Mask off spare bits in bridgeObjectReleaseClient in the not-OBJC_INTE…
mikeash Oct 7, 2025
4d77e3b
Limit ClientRetainRelease to Darwin for the moment. We need more chan…
mikeash Oct 8, 2025
18d9fbd
Fix function_types.sil test to match new function names.
mikeash Oct 9, 2025
1c6dd5e
Don't musttail on WASM.
mikeash Oct 9, 2025
337951e
Set deployment versions, build flavor, and proper install component f…
mikeash Oct 10, 2025
81e7665
Correct attributes for bridgeObjectReleaseClient.
mikeash Oct 14, 2025
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
12 changes: 12 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,18 @@ option(SWIFT_BUILD_STDLIB_CXX_MODULE
"If not building stdlib, controls whether to build the Cxx module"
TRUE)

# The swiftClientRetainRelease library is currently only available for Darwin
# platforms.
if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS)
option(SWIFT_BUILD_CLIENT_RETAIN_RELEASE
"Build the swiftClientRetainRelease library"
TRUE)
else()
option(SWIFT_BUILD_CLIENT_RETAIN_RELEASE
"Build the swiftClientRetainRelease library"
FALSE)
endif()

# In many cases, the CMake build system needs to determine whether to include
# a directory, or perform other actions, based on whether the stdlib or SDK is
# being built at all -- statically or dynamically. Please note that these
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/IRGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@ class IRGenOptions {
// Whether to emit mergeable or non-mergeable traps.
unsigned MergeableTraps : 1;

/// Disable the use of swift_retain/releaseClient functions.
unsigned DisableClientRetainRelease : 1;

/// The number of threads for multi-threaded code generation.
unsigned NumThreads = 0;

Expand Down Expand Up @@ -653,6 +656,7 @@ class IRGenOptions {
EmitAsyncFramePushPopMetadata(true), EmitTypeMallocForCoroFrame(true),
AsyncFramePointerAll(false), UseProfilingMarkerThunks(false),
UseCoroCCX8664(false), UseCoroCCArm64(false), MergeableTraps(false),
DisableClientRetainRelease(false),
DebugInfoForProfiling(false), CmdArgs(),
SanitizeCoverage(llvm::SanitizerCoverageOptions()),
TypeInfoFilter(TypeInfoDumpFilter::All),
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -1457,6 +1457,10 @@ def mergeable_traps :
Flag<["-"], "mergeable-traps">,
HelpText<"Emit mergeable traps even in optimized builds">;

def disable_client_retain_release :
Flag<["-"], "disable-client-retain-release">,
HelpText<"Disable use of swift_retain/releaseClient functions">;

def enable_new_llvm_pass_manager :
Flag<["-"], "enable-new-llvm-pass-manager">,
HelpText<"Enable the new llvm pass manager">;
Expand Down
11 changes: 11 additions & 0 deletions include/swift/Runtime/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,17 @@ extern uintptr_t __COMPATIBILITY_LIBRARIES_CANNOT_CHECK_THE_IS_SWIFT_BIT_DIRECTL
// so changing this value is not sufficient.
#define SWIFT_DEFAULT_LLVM_CC llvm::CallingConv::C

// Define the calling convention for refcounting functions for targets where it
// differs from the standard calling convention. Currently this is only used for
// swift_retain, swift_release, and some internal helper functions that they
// call.
#if defined(__aarch64__)
#define SWIFT_REFCOUNT_CC SWIFT_CC_PreserveMost
#define SWIFT_REFCOUNT_CC_PRESERVEMOST 1
#else
#define SWIFT_REFCOUNT_CC
#endif

/// Should we use absolute function pointers instead of relative ones?
/// WebAssembly target uses it by default.
#ifndef SWIFT_COMPACT_ABSOLUTE_FUNCTION_POINTER
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Runtime/CustomRRABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ namespace swift {
template <typename Ret, typename Param>
Param returnTypeHelper(Ret (*)(Param)) {}

template <typename Ret, typename Param>
Param returnTypeHelper(SWIFT_REFCOUNT_CC Ret (*)(Param)) {}

#if defined(__LP64__) || defined(_LP64)
#define REGISTER_SUBSTITUTION_PREFIX ""
#define REGISTER_PREFIX "x"
Expand Down
2 changes: 2 additions & 0 deletions include/swift/Runtime/HeapObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ HeapObject* swift_allocEmptyBox();
/// It may also prove worthwhile to have this use a custom CC
/// which preserves a larger set of registers.
SWIFT_RUNTIME_EXPORT
SWIFT_REFCOUNT_CC
HeapObject *swift_retain(HeapObject *object);

SWIFT_RUNTIME_EXPORT
Expand Down Expand Up @@ -173,6 +174,7 @@ bool swift_isDeallocating(HeapObject *object);
/// - maybe a variant that can assume a non-null object
/// It's unlikely that a custom CC would be beneficial here.
SWIFT_RUNTIME_EXPORT
SWIFT_REFCOUNT_CC
void swift_release(HeapObject *object);

SWIFT_RUNTIME_EXPORT
Expand Down
34 changes: 34 additions & 0 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,22 @@ FUNCTION(NativeStrongRelease, Swift, swift_release, C_CC, AlwaysAvailable,
EFFECT(RuntimeEffect::RefCounting, RuntimeEffect::Deallocating),
UNKNOWN_MEMEFFECTS)

// void *swift_retain(void *ptr);
FUNCTION(NativeStrongRetainClient, Swift, swift_retainClient, SwiftRR_CC, AlwaysAvailable,
RETURNS(RefCountedPtrTy),
ARGS(RefCountedPtrTy),
ATTRS(NoUnwind, FirstParamReturned, WillReturn),
EFFECT(RuntimeEffect::RefCounting),
UNKNOWN_MEMEFFECTS)

// void swift_release(void *ptr);
FUNCTION(NativeStrongReleaseClient, Swift, swift_releaseClient, SwiftRR_CC, AlwaysAvailable,
RETURNS(VoidTy),
ARGS(RefCountedPtrTy),
ATTRS(NoUnwind),
EFFECT(RuntimeEffect::RefCounting, RuntimeEffect::Deallocating),
UNKNOWN_MEMEFFECTS)

// void *swift_retain_n(void *ptr, int32_t n);
FUNCTION(NativeStrongRetainN, Swift, swift_retain_n, C_CC, AlwaysAvailable,
RETURNS(RefCountedPtrTy),
Expand Down Expand Up @@ -420,6 +436,24 @@ FUNCTION(BridgeObjectStrongRelease, Swift, swift_bridgeObjectRelease,
EFFECT(RuntimeEffect::RefCounting, RuntimeEffect::Deallocating),
UNKNOWN_MEMEFFECTS)

// void *swift_bridgeObjectRetainClient(void *ptr);
FUNCTION(BridgeObjectStrongRetainClient, Swift, swift_bridgeObjectRetainClient,
SwiftRR_CC, AlwaysAvailable,
RETURNS(BridgeObjectPtrTy),
ARGS(BridgeObjectPtrTy),
ATTRS(NoUnwind, FirstParamReturned),
EFFECT(RuntimeEffect::RefCounting),
UNKNOWN_MEMEFFECTS)

// void *swift_bridgeObjectReleaseClient(void *ptr);
FUNCTION(BridgeObjectStrongReleaseClient, Swift, swift_bridgeObjectReleaseClient,
SwiftRR_CC, AlwaysAvailable,
RETURNS(VoidTy),
ARGS(BridgeObjectPtrTy),
ATTRS(NoUnwind),
EFFECT(RuntimeEffect::RefCounting, RuntimeEffect::Deallocating),
UNKNOWN_MEMEFFECTS)

// void *swift_nonatomic_bridgeObjectRetain(void *ptr);
FUNCTION(NonAtomicBridgeObjectStrongRetain, Swift, swift_nonatomic_bridgeObjectRetain,
C_CC, AlwaysAvailable,
Expand Down
3 changes: 3 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3939,6 +3939,9 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,

Opts.MergeableTraps = Args.hasArg(OPT_mergeable_traps);

Opts.DisableClientRetainRelease =
Args.hasArg(OPT_disable_client_retain_release);

Opts.EnableObjectiveCProtocolSymbolicReferences =
Args.hasFlag(OPT_enable_objective_c_protocol_symbolic_references,
OPT_disable_objective_c_protocol_symbolic_references,
Expand Down
59 changes: 40 additions & 19 deletions lib/IRGen/GenHeap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -997,11 +997,16 @@ void IRGenFunction::emitNativeStrongRetain(llvm::Value *value,
value = Builder.CreateBitCast(value, IGM.RefCountedPtrTy);

// Emit the call.
llvm::CallInst *call = Builder.CreateCall(
(atomicity == Atomicity::Atomic)
? IGM.getNativeStrongRetainFunctionPointer()
: IGM.getNativeNonAtomicStrongRetainFunctionPointer(),
value);
FunctionPointer function;
if (atomicity == Atomicity::Atomic &&
IGM.TargetInfo.HasSwiftClientRRLibrary &&
!getOptions().DisableClientRetainRelease)
function = IGM.getNativeStrongRetainClientFunctionPointer();
else if (atomicity == Atomicity::Atomic)
function = IGM.getNativeStrongRetainFunctionPointer();
else
function = IGM.getNativeNonAtomicStrongRetainFunctionPointer();
llvm::CallInst *call = Builder.CreateCall(function, value);
call->setDoesNotThrow();
call->addParamAttr(0, llvm::Attribute::Returned);
}
Expand Down Expand Up @@ -1257,10 +1262,16 @@ void IRGenFunction::emitNativeStrongRelease(llvm::Value *value,
Atomicity atomicity) {
if (doesNotRequireRefCounting(value))
return;
emitUnaryRefCountCall(*this, (atomicity == Atomicity::Atomic)
? IGM.getNativeStrongReleaseFn()
: IGM.getNativeNonAtomicStrongReleaseFn(),
value);
llvm::Constant *function;
if (atomicity == Atomicity::Atomic &&
IGM.TargetInfo.HasSwiftClientRRLibrary &&
!getOptions().DisableClientRetainRelease)
function = IGM.getNativeStrongReleaseClientFn();
else if (atomicity == Atomicity::Atomic)
function = IGM.getNativeStrongReleaseFn();
else
function = IGM.getNativeNonAtomicStrongReleaseFn();
emitUnaryRefCountCall(*this, function, value);
}

void IRGenFunction::emitNativeSetDeallocating(llvm::Value *value) {
Expand Down Expand Up @@ -1353,20 +1364,30 @@ void IRGenFunction::emitUnknownStrongRelease(llvm::Value *value,

void IRGenFunction::emitBridgeStrongRetain(llvm::Value *value,
Atomicity atomicity) {
emitUnaryRefCountCall(*this,
(atomicity == Atomicity::Atomic)
? IGM.getBridgeObjectStrongRetainFn()
: IGM.getNonAtomicBridgeObjectStrongRetainFn(),
value);
llvm::Constant *function;
if (atomicity == Atomicity::Atomic &&
IGM.TargetInfo.HasSwiftClientRRLibrary &&
!getOptions().DisableClientRetainRelease)
function = IGM.getBridgeObjectStrongRetainClientFn();
else if (atomicity == Atomicity::Atomic)
function = IGM.getBridgeObjectStrongRetainFn();
else
function = IGM.getNonAtomicBridgeObjectStrongRetainFn();
emitUnaryRefCountCall(*this, function, value);
}

void IRGenFunction::emitBridgeStrongRelease(llvm::Value *value,
Atomicity atomicity) {
emitUnaryRefCountCall(*this,
(atomicity == Atomicity::Atomic)
? IGM.getBridgeObjectStrongReleaseFn()
: IGM.getNonAtomicBridgeObjectStrongReleaseFn(),
value);
llvm::Constant *function;
if (atomicity == Atomicity::Atomic &&
IGM.TargetInfo.HasSwiftClientRRLibrary &&
!getOptions().DisableClientRetainRelease)
function = IGM.getBridgeObjectStrongReleaseClientFn();
else if (atomicity == Atomicity::Atomic)
function = IGM.getBridgeObjectStrongReleaseFn();
else
function = IGM.getNonAtomicBridgeObjectStrongReleaseFn();
emitUnaryRefCountCall(*this, function, value);
}

void IRGenFunction::emitErrorStrongRetain(llvm::Value *value) {
Expand Down
8 changes: 7 additions & 1 deletion lib/IRGen/IRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -564,8 +564,9 @@ IRGenModule::IRGenModule(IRGenerator &irgen,
InvariantMetadataID = getLLVMContext().getMDKindID("invariant.load");
InvariantNode = llvm::MDNode::get(getLLVMContext(), {});
DereferenceableID = getLLVMContext().getMDKindID("dereferenceable");

C_CC = getOptions().PlatformCCallingConvention;
SwiftRR_CC = llvm::CallingConv::PreserveMost;
// TODO: use "tinycc" on platforms that support it
DefaultCC = SWIFT_DEFAULT_LLVM_CC;

Expand Down Expand Up @@ -1724,6 +1725,11 @@ void IRGenModule::addLinkLibraries() {
registerLinkLibrary(
LinkLibrary{"objc", LibraryKind::Library, /*static=*/false});

if (TargetInfo.HasSwiftClientRRLibrary &&
!getOptions().DisableClientRetainRelease)
registerLinkLibrary(LinkLibrary{"swiftClientRetainRelease",
LibraryKind::Library, /*static=*/true});

// If C++ interop is enabled, add -lc++ on Darwin and -lstdc++ on linux.
// Also link with C++ bridging utility module (Cxx) and C++ stdlib overlay
// (std) if available.
Expand Down
1 change: 1 addition & 0 deletions lib/IRGen/IRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,7 @@ class IRGenModule {
llvm::CallingConv::ID SwiftCC; /// swift calling convention
llvm::CallingConv::ID SwiftAsyncCC; /// swift calling convention for async
llvm::CallingConv::ID SwiftCoroCC; /// swift calling convention for callee-allocated coroutines
llvm::CallingConv::ID SwiftRR_CC;

/// What kind of tail call should be used for async->async calls.
llvm::CallInst::TailCallKind AsyncTailCallKind;
Expand Down
8 changes: 8 additions & 0 deletions lib/IRGen/SwiftTargetInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ static void configureARM64(IRGenModule &IGM, const llvm::Triple &triple,
// half for the kernel.
target.SwiftRetainIgnoresNegativeValues = true;

// ARM64 Darwin has swiftClientRetainRelease, but not in Embedded mode. JIT
// mode can't load the static library, so disable it there as well.
if (triple.isOSDarwin() &&
!IGM.getSwiftModule()->getASTContext().LangOpts.hasFeature(
Feature::Embedded) &&
!IGM.getOptions().UseJIT)
target.HasSwiftClientRRLibrary = true;

target.UsableSwiftAsyncContextAddrIntrinsic = true;
}

Expand Down
3 changes: 3 additions & 0 deletions lib/IRGen/SwiftTargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ class SwiftTargetInfo {
/// "negative" pointer values.
bool SwiftRetainIgnoresNegativeValues = false;

/// True if the swiftClientRetainRelease static library is available.
bool HasSwiftClientRRLibrary = false;

bool UsableSwiftAsyncContextAddrIntrinsic = false;
};

Expand Down
7 changes: 7 additions & 0 deletions lib/LLVMPasses/LLVMARCOpts.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ inline RT_Kind classifyInstruction(const llvm::Instruction &I) {
.Case("__swift_" #TextualName, RT_ ## Name)
#include "LLVMSwift.def"

// Identify "Client" versions of reference counting entry points.
#define SWIFT_FUNC(Name, MemBehavior, TextualName) \
.Case("swift_" #TextualName "Client", RT_ ## Name)
#define SWIFT_INTERNAL_FUNC_NEVER_NONATOMIC(Name, MemBehavior, TextualName) \
.Case("__swift_" #TextualName "Client", RT_ ## Name)
#include "LLVMSwift.def"

// Support non-atomic versions of reference counting entry points.
#define SWIFT_FUNC(Name, MemBehavior, TextualName) \
.Case("swift_nonatomic_" #TextualName, RT_ ## Name)
Expand Down
5 changes: 4 additions & 1 deletion stdlib/public/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,10 @@ endif()
if(SWIFT_BUILD_STDLIB)
# These must be kept in dependency order so that any referenced targets
# exist at the time we look for them in add_swift_*.
if(SWIFT_BUILD_CLIENT_RETAIN_RELEASE)
add_subdirectory(ClientRetainRelease)
endif()

add_subdirectory(runtime)
add_subdirectory(stubs)
add_subdirectory(core)
Expand Down Expand Up @@ -366,4 +370,3 @@ endif()
if(SWIFT_BUILD_LIBEXEC)
add_subdirectory(libexec)
endif()

18 changes: 18 additions & 0 deletions stdlib/public/ClientRetainRelease/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
add_swift_target_library(swiftClientRetainRelease
STATIC DONT_EMBED_BITCODE NOSWIFTRT
RetainRelease.s
C_COMPILE_FLAGS ${SWIFT_RUNTIME_CXX_FLAGS}
$<$<BOOL:${SWIFT_STDLIB_ENABLE_OBJC_INTEROP}>:-DSWIFT_OBJC_INTEROP=1>
LINK_FLAGS ${SWIFT_RUNTIME_LINK_FLAGS}
SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS}

DEPLOYMENT_VERSION_OSX ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_OSX}
DEPLOYMENT_VERSION_IOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_IOS}
DEPLOYMENT_VERSION_TVOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_TVOS}
DEPLOYMENT_VERSION_WATCHOS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_WATCHOS}
DEPLOYMENT_VERSION_XROS ${COMPATIBILITY_MINIMUM_DEPLOYMENT_VERSION_XROS}

MACCATALYST_BUILD_FLAVOR "zippered"

INSTALL_IN_COMPONENT compiler
INSTALL_WITH_SHARED)
Loading