Skip to content

Commit d451203

Browse files
committed
SILGen: Emit async let entry points at correct abstraction level.
Avoid a reabstraction thunk every time an async let entry point is emitted, by setting the context abstraction level while we emit the implicit closure. Adjust some surrounding logic that breaks when we do this: - When lowering a non-throwing function type against a throwing abstraction pattern, include the error type in the lowered substituted type. Async throwing and nonthrowing functions would require another thunk to convert away the throwingness of the async context, which would defeat the purpose. - Adjust the code in IRGen that pads the initial context size for `async let` entry points so that it works when the entry point has not yet emitted, by marking the async function pointer to be padded later if it isn't defined yet.
1 parent a620902 commit d451203

File tree

9 files changed

+135
-39
lines changed

9 files changed

+135
-39
lines changed

lib/IRGen/GenCall.cpp

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3897,23 +3897,58 @@ emitRetconCoroutineEntry(IRGenFunction &IGF, CanSILFunctionType fnType,
38973897
IGF.setEarliestInsertionPoint(pt);
38983898
}
38993899

3900+
void IRGenModule::addAsyncCoroIDMapping(llvm::GlobalVariable *asyncFunctionPointer,
3901+
llvm::CallInst *coro_id_builtin) {
3902+
AsyncCoroIDsForPadding[asyncFunctionPointer] = coro_id_builtin;
3903+
}
3904+
3905+
llvm::CallInst *
3906+
IRGenModule::getAsyncCoroIDMapping(llvm::GlobalVariable *asyncFunctionPointer) {
3907+
auto found = AsyncCoroIDsForPadding.find(asyncFunctionPointer);
3908+
if (found == AsyncCoroIDsForPadding.end())
3909+
return nullptr;
3910+
return found->second;
3911+
}
3912+
3913+
void IRGenModule::markAsyncFunctionPointerForPadding(
3914+
llvm::GlobalVariable *asyncFunctionPointer) {
3915+
AsyncCoroIDsForPadding[asyncFunctionPointer] = nullptr;
3916+
}
3917+
3918+
bool IRGenModule::isAsyncFunctionPointerMarkedForPadding(
3919+
llvm::GlobalVariable *asyncFunctionPointer) {
3920+
auto found = AsyncCoroIDsForPadding.find(asyncFunctionPointer);
3921+
if (found == AsyncCoroIDsForPadding.end())
3922+
return false;
3923+
return found->second == nullptr;
3924+
}
3925+
39003926
void irgen::emitAsyncFunctionEntry(IRGenFunction &IGF,
39013927
const AsyncContextLayout &layout,
39023928
LinkEntity asyncFunction,
39033929
unsigned asyncContextIndex) {
39043930
auto &IGM = IGF.IGM;
39053931
auto size = layout.getSize();
39063932
auto asyncFuncPointerVar = cast<llvm::GlobalVariable>(IGM.getAddrOfAsyncFunctionPointer(asyncFunction));
3933+
bool isPadded = IGM
3934+
.isAsyncFunctionPointerMarkedForPadding(asyncFuncPointerVar);
39073935
auto asyncFuncPointer = IGF.Builder.CreateBitOrPointerCast(
39083936
asyncFuncPointerVar, IGM.Int8PtrTy);
3937+
3938+
if (isPadded) {
3939+
size = std::max(layout.getSize(),
3940+
NumWords_AsyncLet * IGM.getPointerSize());
3941+
}
3942+
39093943
auto *id = IGF.Builder.CreateIntrinsicCall(
39103944
llvm::Intrinsic::coro_id_async,
39113945
{llvm::ConstantInt::get(IGM.Int32Ty, size.getValue()),
39123946
llvm::ConstantInt::get(IGM.Int32Ty, 16),
39133947
llvm::ConstantInt::get(IGM.Int32Ty, asyncContextIndex),
39143948
asyncFuncPointer});
3915-
auto inserted = IGM.AsyncCoroIDs.insert({asyncFuncPointerVar, id});
3916-
assert(inserted.second);
3949+
3950+
IGM.addAsyncCoroIDMapping(asyncFuncPointerVar, id);
3951+
39173952
// Call 'llvm.coro.begin', just for consistency with the normal pattern.
39183953
// This serves as a handle that we can pass around to other intrinsics.
39193954
auto hdl = IGF.Builder.CreateIntrinsicCall(

lib/IRGen/GenConcurrency.cpp

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ llvm::Value *irgen::emitBuiltinStartAsyncLet(IRGenFunction &IGF,
181181
llvm::Value *localContextInfo,
182182
llvm::Value *localResultBuffer,
183183
SubstitutionMap subs) {
184+
localContextInfo = IGF.Builder.CreateBitCast(localContextInfo,
185+
IGF.IGM.OpaquePtrTy);
186+
184187
// stack allocate AsyncLet, and begin lifetime for it (until EndAsyncLet)
185188
auto ty = llvm::ArrayType::get(IGF.IGM.Int8PtrTy, NumWords_AsyncLet);
186189
auto address = IGF.createAlloca(ty, Alignment(Alignment_AsyncLet));
@@ -204,31 +207,35 @@ llvm::Value *irgen::emitBuiltinStartAsyncLet(IRGenFunction &IGF,
204207
auto deploymentAvailability
205208
= AvailabilityContext::forDeploymentTarget(IGF.IGM.Context);
206209
if (!deploymentAvailability.isContainedIn(
207-
IGF.IGM.Context.getSwift57Availability())) {
210+
IGF.IGM.Context.getSwift57Availability()))
211+
{
208212
auto taskAsyncFunctionPointer
209213
= cast<llvm::GlobalVariable>(taskFunction->stripPointerCasts());
210214

211-
auto taskAsyncIDIter = IGF.IGM.AsyncCoroIDs.find(taskAsyncFunctionPointer);
212-
assert(taskAsyncIDIter != IGF.IGM.AsyncCoroIDs.end()
213-
&& "async let entry point not emitted locally");
214-
auto taskAsyncID = taskAsyncIDIter->second;
215-
216-
// Pad out the initial context size in the async function pointer record
217-
// and ID intrinsic so that it will never fit in the preallocated space.
218-
uint64_t origSize = cast<llvm::ConstantInt>(taskAsyncID->getArgOperand(0))
219-
->getValue().getLimitedValue();
220-
221-
uint64_t paddedSize = std::max(origSize,
215+
if (auto taskAsyncID
216+
= IGF.IGM.getAsyncCoroIDMapping(taskAsyncFunctionPointer)) {
217+
// If the entry point function has already been emitted, retroactively
218+
// pad out the initial context size in the async function pointer record
219+
// and ID intrinsic so that it will never fit in the preallocated space.
220+
uint64_t origSize = cast<llvm::ConstantInt>(taskAsyncID->getArgOperand(0))
221+
->getValue().getLimitedValue();
222+
223+
uint64_t paddedSize = std::max(origSize,
222224
(NumWords_AsyncLet * IGF.IGM.getPointerSize()).getValue());
223-
auto paddedSizeVal = llvm::ConstantInt::get(IGF.IGM.Int32Ty, paddedSize);
224-
taskAsyncID->setArgOperand(0, paddedSizeVal);
225-
226-
auto origInit = taskAsyncFunctionPointer->getInitializer();
227-
auto newInit = llvm::ConstantStruct::get(
225+
auto paddedSizeVal = llvm::ConstantInt::get(IGF.IGM.Int32Ty, paddedSize);
226+
taskAsyncID->setArgOperand(0, paddedSizeVal);
227+
228+
auto origInit = taskAsyncFunctionPointer->getInitializer();
229+
auto newInit = llvm::ConstantStruct::get(
228230
cast<llvm::StructType>(origInit->getType()),
229231
origInit->getAggregateElement(0u),
230232
paddedSizeVal);
231-
taskAsyncFunctionPointer->setInitializer(newInit);
233+
taskAsyncFunctionPointer->setInitializer(newInit);
234+
} else {
235+
// If it hasn't been emitted yet, mark it to get the padding when it does
236+
// get emitted.
237+
IGF.IGM.markAsyncFunctionPointerForPadding(taskAsyncFunctionPointer);
238+
}
232239
}
233240

234241
llvm::CallInst *call;

lib/IRGen/GenMeta.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5611,6 +5611,12 @@ llvm::GlobalValue *irgen::emitAsyncFunctionPointer(IRGenModule &IGM,
56115611
llvm::Function *function,
56125612
LinkEntity entity,
56135613
Size size) {
5614+
auto afp = cast<llvm::GlobalVariable>(IGM.getAddrOfAsyncFunctionPointer(entity));
5615+
if (IGM.isAsyncFunctionPointerMarkedForPadding(afp)) {
5616+
size = std::max(size,
5617+
NumWords_AsyncLet * IGM.getPointerSize());
5618+
}
5619+
56145620
ConstantInitBuilder initBuilder(IGM);
56155621
ConstantStructBuilder builder(
56165622
initBuilder.beginStruct(IGM.AsyncFunctionPointerTy));

lib/IRGen/IRGenModule.h

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -893,10 +893,18 @@ class IRGenModule {
893893
std::string GetObjCSectionName(StringRef Section, StringRef MachOAttributes);
894894
void SetCStringLiteralSection(llvm::GlobalVariable *GV, ObjCLabelType Type);
895895

896-
// Mapping of AsyncFunctionPointer records to their corresponding
897-
// `@llvm.coro.id.async` intrinsic tag in the function implementation.
898-
llvm::DenseMap<llvm::GlobalVariable*, llvm::CallInst*> AsyncCoroIDs;
899-
896+
void addAsyncCoroIDMapping(llvm::GlobalVariable *asyncFunctionPointer,
897+
llvm::CallInst *coro_id_builtin);
898+
899+
llvm::CallInst *getAsyncCoroIDMapping(
900+
llvm::GlobalVariable *asyncFunctionPointer);
901+
902+
void markAsyncFunctionPointerForPadding(
903+
llvm::GlobalVariable *asyncFunctionPointer);
904+
905+
bool isAsyncFunctionPointerMarkedForPadding(
906+
llvm::GlobalVariable *asyncFunctionPointer);
907+
900908
private:
901909
Size PtrSize;
902910
Size AtomicBoolSize;
@@ -911,7 +919,18 @@ class IRGenModule {
911919
llvm::PointerType *EnumValueWitnessTablePtrTy = nullptr;
912920

913921
llvm::DenseMap<llvm::Type *, SpareBitVector> SpareBitsForTypes;
914-
922+
923+
// Mapping of AsyncFunctionPointer records to their corresponding
924+
// `@llvm.coro.id.async` intrinsic tag in the function implementation.
925+
// This is used for a runtime bug workaround where we need to pad the initial
926+
// context size for tasks used as `async let` entry points.
927+
//
928+
// An entry in the map may have a null value, to indicate that a not-yet-
929+
// emitted async function pointer should get the padding applied when it is
930+
// emitted.
931+
llvm::DenseMap<llvm::GlobalVariable*, llvm::CallInst*> AsyncCoroIDsForPadding;
932+
933+
915934
//--- Types -----------------------------------------------------------------
916935
public:
917936
const ProtocolInfo &getProtocolInfo(ProtocolDecl *D, ProtocolInfoKind kind);

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1933,7 +1933,16 @@ static CanSILFunctionType getSILFunctionType(
19331933
}
19341934

19351935
// Map 'throws' to the appropriate error convention.
1936-
if (substFnInterfaceType->getExtInfo().isThrowing() && !foreignInfo.error &&
1936+
// Give the type an error argument whether the substituted type semantically
1937+
// `throws` or if the abstraction pattern specifies a Swift function type
1938+
// that also throws. This prevents the need for a second possibly-thunking
1939+
// conversion when using a non-throwing function in more abstract throwing
1940+
// context.
1941+
bool isThrowing = substFnInterfaceType->getExtInfo().isThrowing();
1942+
if (auto origFnType = origType.getAs<AnyFunctionType>()) {
1943+
isThrowing |= origFnType->getExtInfo().isThrowing();
1944+
}
1945+
if (isThrowing && !foreignInfo.error &&
19371946
!foreignInfo.async) {
19381947
assert(!origType.isForeign()
19391948
&& "using native Swift error convention for foreign type!");

lib/SILGen/SILGenApply.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5948,10 +5948,11 @@ SILGenFunction::emitCoroutineAccessor(SILLocation loc, SILDeclRef accessor,
59485948
ManagedValue SILGenFunction::emitAsyncLetStart(
59495949
SILLocation loc,
59505950
SILValue taskOptions,
5951-
Type functionType, ManagedValue taskFunction,
5951+
AbstractClosureExpr *asyncLetEntryPoint,
59525952
SILValue resultBuf) {
59535953
ASTContext &ctx = getASTContext();
5954-
Type resultType = functionType->castTo<FunctionType>()->getResult();
5954+
Type resultType = asyncLetEntryPoint->getType()
5955+
->castTo<FunctionType>()->getResult();
59555956
Type replacementTypes[] = {resultType};
59565957
auto startBuiltin = cast<FuncDecl>(
59575958
getBuiltinValueDecl(ctx, ctx.getIdentifier("startAsyncLet")));
@@ -5966,8 +5967,15 @@ ManagedValue SILGenFunction::emitAsyncLetStart(
59665967
AbstractionPattern origParam(
59675968
startBuiltin->getGenericSignature().getCanonicalSignature(),
59685969
origParamType);
5969-
taskFunction = emitSubstToOrigValue(
5970-
loc, taskFunction, origParam, substParamType);
5970+
5971+
auto conversion = Conversion::getSubstToOrig(origParam, substParamType,
5972+
getLoweredType(origParam, substParamType));
5973+
ConvertingInitialization convertingInit(conversion, SGFContext());
5974+
auto taskFunction = emitRValue(asyncLetEntryPoint,
5975+
SGFContext(&convertingInit))
5976+
.getAsSingleValue(*this, loc);
5977+
taskFunction = emitSubstToOrigValue(loc, taskFunction,
5978+
origParam, substParamType);
59715979

59725980
auto apply = B.createBuiltin(
59735981
loc,

lib/SILGen/SILGenDecl.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,8 +1212,7 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD,
12121212
if (auto tryExpr = dyn_cast<TryExpr>(init))
12131213
init = tryExpr->getSubExpr();
12141214
init = cast<CallExpr>(init)->getFn();
1215-
assert(isa<AutoClosureExpr>(init) &&
1216-
"Could not find async let autoclosure");
1215+
auto initClosure = cast<AutoClosureExpr>(init);
12171216
bool isThrowing = init->getType()->castTo<AnyFunctionType>()->isThrowing();
12181217

12191218
// Allocate space to receive the child task's result.
@@ -1240,8 +1239,7 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD,
12401239
alet = emitAsyncLetStart(
12411240
loc,
12421241
options.forward(*this), // options is B.createManagedOptionalNone
1243-
init->getType(),
1244-
emitRValue(init).getScalarValue(),
1242+
initClosure,
12451243
resultBufPtr
12461244
).forward(*this);
12471245
}

lib/SILGen/SILGenFunction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1456,7 +1456,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
14561456

14571457
ManagedValue emitAsyncLetStart(SILLocation loc,
14581458
SILValue taskOptions,
1459-
Type functionType, ManagedValue taskFunction,
1459+
AbstractClosureExpr *asyncLetEntryPoint,
14601460
SILValue resultBuf);
14611461

14621462
void emitFinishAsyncLet(SILLocation loc, SILValue asyncLet, SILValue resultBuf);
Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %target-swift-frontend -emit-ir -target x86_64-apple-macos99.99 %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-sans-workaround %s
2-
// RUN: %target-swift-frontend -emit-ir -target x86_64-apple-macos12.3 %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-with-workaround %s
1+
// RUN: %target-swift-frontend -emit-ir -target %target-cpu-apple-macos99.99 %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-sans-workaround %s
2+
// RUN: %target-swift-frontend -emit-ir -target %target-cpu-apple-macos12.3 %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-with-workaround %s
33

44
// REQUIRES: OS=macosx
55

@@ -11,12 +11,26 @@
1111
// initial context sizes of any async functions used as `async let` entry points
1212
// to ensure that the preallocated space is never used for the initial context.
1313

14-
// CHECK: [[ASYNC_LET_ENTRY:@"\$sSiIeghHd_Sis5Error_pIegHrzo_TRTATu"]]
14+
// CHECK: [[ASYNC_LET_ENTRY_FOO:@"\$s32async_let_back_deploy_workaround3foo1x1yS2i_SitYaFSiyYaYbcfu_TATu"]]
1515
// CHECK-with-workaround-SAME: = internal {{.*}} %swift.async_func_pointer <{ {{.*}}, i32 6{{[0-9][0-9]}} }>
1616
// CHECK-sans-workaround-SAME: = internal {{.*}} %swift.async_func_pointer <{ {{.*}}, i32 {{[0-9][0-9]}} }>
1717

18-
// CHECK: swift_asyncLet_begin{{.*}}[[ASYNC_LET_ENTRY]]
18+
// CHECK: [[ASYNC_LET_ENTRY_BAR:@"\$s32async_let_back_deploy_workaround3bar1x1yS2i_SitYaFSiyYaYbcfu_Tu"]]
19+
// CHECK-with-workaround-SAME: = internal {{.*}} %swift.async_func_pointer <{ {{.*}}, i32 6{{[0-9][0-9]}} }>
20+
// CHECK-sans-workaround-SAME: = internal {{.*}} %swift.async_func_pointer <{ {{.*}}, i32 {{[0-9][0-9]}} }>
21+
22+
// CHECK-LABEL: define {{.*}}3foo
23+
// CHECK: swift_asyncLet_begin{{.*}}[[ASYNC_LET_ENTRY_FOO]]
1924
public func foo(x: Int, y: Int) async -> Int {
2025
async let z = x + y
2126
return await z
2227
}
28+
29+
@_silgen_name("bar_work") func bar_work() -> Int
30+
31+
// CHECK-LABEL: define {{.*}}3bar
32+
// CHECK: swift_asyncLet_begin{{.*}}[[ASYNC_LET_ENTRY_BAR]]
33+
public func bar(x: Int, y: Int) async -> Int {
34+
async let z = bar_work()
35+
return await z
36+
}

0 commit comments

Comments
 (0)