[IRGen] Only use a stub for swift_coroFrameAlloc when we need it

swift_coroFrameAlloc was introduced in the Swift 6.2 runtime. Give it
the appropriate availability in IRGen, so that it gets weak
availability when needed (per the deployment target). Then, only
create the stub function for calling into swift_coroFrameAlloc or
malloc (when the former isn't available) when we're back-deploying to
a runtime prior to Swift 6.2. This is a small code size/performance
win when allocating coroutine frames on Swift 6.2-or-newer platforms.

This has a side effect of fixing a bug in Embedded Swift, where the
swift_coroFrameAlloc was getting unconditionally set to have weak
external linkage despite behind defined in the same LLVM module
(because it comes from the standard library).

Fixes rdar://149695139 / issue #80947.
This commit is contained in:
Doug Gregor
2025-09-16 10:36:26 -07:00
parent 865c643cac
commit f7264e327f
9 changed files with 34 additions and 9 deletions

View File

@@ -82,6 +82,7 @@ FEATURE(ValueGenericType, (6, 2))
FEATURE(InitRawStructMetadata2, (6, 2))
FEATURE(CustomGlobalExecutors, (6, 2))
FEATURE(TaskExecutor, (6, 2))
FEATURE(TypedCoroAlloc, (6, 2))
FEATURE(Differentiation, FUTURE)
FEATURE(ClearSensitive, FUTURE)

View File

@@ -2059,7 +2059,8 @@ FUNCTION(Free, c, free, C_CC, AlwaysAvailable,
NO_ATTRS,
EFFECT(RuntimeEffect::Deallocating),
UNKNOWN_MEMEFFECTS)
FUNCTION(CoroFrameAlloc, Swift, swift_coroFrameAlloc, C_CC, AlwaysAvailable,
FUNCTION(CoroFrameAlloc, Swift, swift_coroFrameAlloc, C_CC,
TypedCoroAllocAvailability,
RETURNS(Int8PtrTy),
ARGS(SizeTy, Int64Ty),
NO_ATTRS,

View File

@@ -366,6 +366,8 @@ toolchains::Darwin::addArgsToLinkStdlib(ArgStringList &Arguments,
runtimeCompatibilityVersion = llvm::VersionTuple(5, 8);
} else if (value == "6.0") {
runtimeCompatibilityVersion = llvm::VersionTuple(6, 0);
} else if (value == "6.2") {
runtimeCompatibilityVersion = llvm::VersionTuple(6, 2);
} else if (value == "none") {
runtimeCompatibilityVersion = std::nullopt;
} else {

View File

@@ -3699,6 +3699,8 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
runtimeCompatibilityVersion = llvm::VersionTuple(5, 8);
} else if (version == "6.0") {
runtimeCompatibilityVersion = llvm::VersionTuple(6, 0);
} else if (version == "6.2") {
runtimeCompatibilityVersion = llvm::VersionTuple(6, 2);
} else {
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
versionArg->getAsString(Args), version);

View File

@@ -863,15 +863,20 @@ CanType irgen::getArgumentLoweringType(CanType type, SILParameterInfo paramInfo,
}
llvm::Constant *irgen::getCoroFrameAllocStubFn(IRGenModule &IGM) {
// If the coroutine allocation function is always available, call it directly.
auto coroAllocPtr = IGM.getCoroFrameAllocFn();
auto coroAllocFn = dyn_cast<llvm::Function>(coroAllocPtr);
if (coroAllocFn->getLinkage() != llvm::GlobalValue::ExternalWeakLinkage)
return coroAllocFn;
// Otherwise, create a stub function to call it when available, or malloc
// when it isn't.
return IGM.getOrCreateHelperFunction(
"__swift_coroFrameAllocStub", IGM.Int8PtrTy,
{IGM.SizeTy, IGM.Int64Ty},
[&](IRGenFunction &IGF) {
auto parameters = IGF.collectParameters();
auto *size = parameters.claimNext();
auto coroAllocPtr = IGF.IGM.getCoroFrameAllocFn();
auto coroAllocFn = dyn_cast<llvm::Function>(coroAllocPtr);
coroAllocFn->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
auto *coroFrameAllocFn = IGF.IGM.getOpaquePtr(coroAllocPtr);
auto *nullSwiftCoroFrameAlloc = IGF.Builder.CreateCmp(
llvm::CmpInst::Predicate::ICMP_NE, coroFrameAllocFn,

View File

@@ -902,6 +902,14 @@ namespace RuntimeConstants {
return RuntimeAvailability::AlwaysAvailable;
}
RuntimeAvailability TypedCoroAllocAvailability(ASTContext &context) {
auto featureAvailability = context.getTypedCoroAllocAvailability();
if (!isDeploymentAvailabilityContainedIn(context, featureAvailability)) {
return RuntimeAvailability::ConditionallyAvailable;
}
return RuntimeAvailability::AlwaysAvailable;
}
RuntimeAvailability ConcurrencyDiscardingTaskGroupAvailability(ASTContext &context) {
auto featureAvailability =
context.getConcurrencyDiscardingTaskGroupAvailability();

View File

@@ -1,5 +1,5 @@
// REQUIRES: OS=macosx || OS=iOS
// RUN: %target-swift-frontend -emit-irgen %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-%target-ptrsize-%target-ptrauth
// REQUIRES: OS=macosx
// RUN: %target-swift-frontend -target %target-cpu-apple-macosx15.0 -emit-irgen %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-%target-ptrsize-%target-ptrauth
import Builtin
@@ -44,6 +44,9 @@ unwind:
// CHECK-NEXT: unreachable
}
// CHECK-32: declare extern_weak ptr @swift_coroFrameAlloc(i32, i64)
// CHECK-64: declare extern_weak ptr @swift_coroFrameAlloc(i64, i64)
// CHECK-32: define linkonce_odr hidden ptr @__swift_coroFrameAllocStub(i32 %0, i64 %1) #1{{( comdat)?}} {
// CHECK-64: define linkonce_odr hidden ptr @__swift_coroFrameAllocStub(i64 %0, i64 %1) #1{{( comdat)?}} {
// CHECK: [[T0:%.*]] = icmp ne ptr @swift_coroFrameAlloc, null
@@ -59,9 +62,6 @@ unwind:
// CHECK-64: [[T2:%.*]] = call ptr @malloc(i64 %0)
// CHECK: ret ptr [[T2]]
// CHECK-32: declare extern_weak ptr @swift_coroFrameAlloc(i32, i64)
// CHECK-64: declare extern_weak ptr @swift_coroFrameAlloc(i64, i64)
// CHECK-LABEL: declare{{( dllimport)?}}{{( protected)?}} swiftcc void @"$sIetA_TC"
// CHECK-SAME: (ptr noalias dereferenceable([[BUFFER_SIZE]]), i1)

View File

@@ -0,0 +1,2 @@
// REQUIRES: OS=iOS
// RUN: %target-swift-frontend -target %target-cpu-apple-iphoneos16.0 -emit-irgen %S/yield_once_enable_emit_type_malloc_coro_frame.sil | %FileCheck %S/yield_once_enable_emit_type_malloc_coro_frame.sil --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-%target-ptrsize-%target-ptrauth

View File

@@ -0,0 +1,4 @@
// REQUIRES: OS=macosx
// RUN: %target-swift-frontend -target %target-cpu-apple-macosx26.0 -emit-irgen %S/yield_once_enable_emit_type_malloc_coro_frame.sil | %FileCheck %s
// CHECK-NOT: __swift_coroFrameAllocStub