Skip to content

Commit 15c2192

Browse files
authored
Merge pull request #42101 from jckarter/async-let-reabstraction
SILGen: Emit async let entry points at correct abstraction level.
2 parents 8f54a15 + d451203 commit 15c2192

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
@@ -5619,6 +5619,12 @@ llvm::GlobalValue *irgen::emitAsyncFunctionPointer(IRGenModule &IGM,
56195619
llvm::Function *function,
56205620
LinkEntity entity,
56215621
Size size) {
5622+
auto afp = cast<llvm::GlobalVariable>(IGM.getAddrOfAsyncFunctionPointer(entity));
5623+
if (IGM.isAsyncFunctionPointerMarkedForPadding(afp)) {
5624+
size = std::max(size,
5625+
NumWords_AsyncLet * IGM.getPointerSize());
5626+
}
5627+
56225628
ConstantInitBuilder initBuilder(IGM);
56235629
ConstantStructBuilder builder(
56245630
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)