Skip to content

Commit 3b119c1

Browse files
committed
[IRGen] Add direct error return support for async functions
rdar://129359370 Second part of direct error support. This implements direct errors for async functions. Instead of always returning typed errors indirectly, we are returning them directly when possible.
1 parent e3c0bc8 commit 3b119c1

File tree

8 files changed

+403
-143
lines changed

8 files changed

+403
-143
lines changed

lib/IRGen/GenCall.cpp

Lines changed: 198 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2114,10 +2114,40 @@ void SignatureExpansion::expandAsyncReturnType() {
21142114
}
21152115
};
21162116

2117-
auto resultType = getSILFuncConventions().getSILResultType(
2118-
IGM.getMaximalTypeExpansionContext());
2117+
auto fnConv = getSILFuncConventions();
2118+
2119+
auto resultType =
2120+
fnConv.getSILResultType(IGM.getMaximalTypeExpansionContext());
21192121
auto &ti = IGM.getTypeInfo(resultType);
21202122
auto &native = ti.nativeReturnValueSchema(IGM);
2123+
2124+
if (!fnConv.hasIndirectSILResults() && !fnConv.hasIndirectSILErrorResults() &&
2125+
!native.requiresIndirect() && fnConv.funcTy->hasErrorResult() &&
2126+
fnConv.isTypedError()) {
2127+
auto errorType = getSILFuncConventions().getSILErrorType(
2128+
IGM.getMaximalTypeExpansionContext());
2129+
auto &errorTi = IGM.getTypeInfo(errorType);
2130+
auto &nativeError = errorTi.nativeReturnValueSchema(IGM);
2131+
if (!nativeError.shouldReturnTypedErrorIndirectly()) {
2132+
auto combined = combineResultAndTypedErrorType(IGM, native, nativeError);
2133+
2134+
if (combined.combinedTy->isVoidTy()) {
2135+
addErrorResult();
2136+
return;
2137+
}
2138+
2139+
if (auto *structTy = dyn_cast<llvm::StructType>(combined.combinedTy)) {
2140+
for (auto *elem : structTy->elements()) {
2141+
ParamIRTypes.push_back(elem);
2142+
}
2143+
} else {
2144+
ParamIRTypes.push_back(combined.combinedTy);
2145+
}
2146+
}
2147+
addErrorResult();
2148+
return;
2149+
}
2150+
21212151
if (native.requiresIndirect() || native.empty()) {
21222152
addErrorResult();
21232153
return;
@@ -2135,11 +2165,23 @@ void SignatureExpansion::expandAsyncReturnType() {
21352165
void SignatureExpansion::addIndirectThrowingResult() {
21362166
if (getSILFuncConventions().funcTy->hasErrorResult() &&
21372167
getSILFuncConventions().isTypedError()) {
2138-
auto resultType = getSILFuncConventions().getSILErrorType(
2139-
IGM.getMaximalTypeExpansionContext());
2140-
const TypeInfo &resultTI = IGM.getTypeInfo(resultType);
2141-
auto storageTy = resultTI.getStorageType();
2142-
ParamIRTypes.push_back(storageTy->getPointerTo());
2168+
auto resultType = getSILFuncConventions().getSILResultType(
2169+
IGM.getMaximalTypeExpansionContext());
2170+
auto &ti = IGM.getTypeInfo(resultType);
2171+
auto &native = ti.nativeReturnValueSchema(IGM);
2172+
2173+
auto errorType = getSILFuncConventions().getSILErrorType(
2174+
IGM.getMaximalTypeExpansionContext());
2175+
const TypeInfo &errorTI = IGM.getTypeInfo(errorType);
2176+
auto &nativeError = errorTI.nativeReturnValueSchema(IGM);
2177+
2178+
if (getSILFuncConventions().hasIndirectSILResults() ||
2179+
getSILFuncConventions().hasIndirectSILErrorResults() ||
2180+
native.requiresIndirect() ||
2181+
nativeError.shouldReturnTypedErrorIndirectly()) {
2182+
auto errorStorageTy = errorTI.getStorageType();
2183+
ParamIRTypes.push_back(errorStorageTy->getPointerTo());
2184+
}
21432185
}
21442186

21452187
}
@@ -2265,6 +2307,35 @@ void SignatureExpansion::expandAsyncAwaitType() {
22652307
IGM.getMaximalTypeExpansionContext());
22662308
auto &ti = IGM.getTypeInfo(resultType);
22672309
auto &native = ti.nativeReturnValueSchema(IGM);
2310+
2311+
if (!getSILFuncConventions().hasIndirectSILResults() &&
2312+
!getSILFuncConventions().hasIndirectSILErrorResults() &&
2313+
!native.requiresIndirect() && getSILFuncConventions().isTypedError()) {
2314+
auto errorType = getSILFuncConventions().getSILErrorType(
2315+
IGM.getMaximalTypeExpansionContext());
2316+
auto &errorTi = IGM.getTypeInfo(errorType);
2317+
auto &nativeError = errorTi.nativeReturnValueSchema(IGM);
2318+
if (!nativeError.shouldReturnTypedErrorIndirectly()) {
2319+
auto combined = combineResultAndTypedErrorType(IGM, native, nativeError);
2320+
2321+
if (combined.combinedTy->isVoidTy()) {
2322+
addErrorResult();
2323+
return;
2324+
}
2325+
2326+
if (auto *structTy = dyn_cast<llvm::StructType>(combined.combinedTy)) {
2327+
for (auto *elem : structTy->elements()) {
2328+
components.push_back(elem);
2329+
}
2330+
} else {
2331+
components.push_back(combined.combinedTy);
2332+
}
2333+
addErrorResult();
2334+
ResultIRType = llvm::StructType::get(IGM.getLLVMContext(), components);
2335+
return;
2336+
}
2337+
}
2338+
22682339
if (native.requiresIndirect() || native.empty()) {
22692340
addErrorResult();
22702341
ResultIRType = llvm::StructType::get(IGM.getLLVMContext(), components);
@@ -2278,7 +2349,6 @@ void SignatureExpansion::expandAsyncAwaitType() {
22782349
});
22792350

22802351
addErrorResult();
2281-
22822352
ResultIRType = llvm::StructType::get(IGM.getLLVMContext(), components);
22832353
}
22842354

@@ -2950,9 +3020,22 @@ class AsyncCallEmission final : public CallEmission {
29503020
setIndirectTypedErrorResultSlotArgsIndex(--LastArgWritten);
29513021
Args[LastArgWritten] = nullptr;
29523022
} else {
2953-
auto buf = IGF.getCalleeTypedErrorResultSlot(
2954-
fnConv.getSILErrorType(IGF.IGM.getMaximalTypeExpansionContext()));
2955-
Args[--LastArgWritten] = buf.getAddress();
3023+
auto silResultTy =
3024+
fnConv.getSILResultType(IGF.IGM.getMaximalTypeExpansionContext());
3025+
auto silErrorTy =
3026+
fnConv.getSILErrorType(IGF.IGM.getMaximalTypeExpansionContext());
3027+
3028+
auto &nativeSchema =
3029+
IGF.IGM.getTypeInfo(silResultTy).nativeReturnValueSchema(IGF.IGM);
3030+
auto &errorSchema =
3031+
IGF.IGM.getTypeInfo(silErrorTy).nativeReturnValueSchema(IGF.IGM);
3032+
3033+
if (nativeSchema.requiresIndirect() ||
3034+
errorSchema.shouldReturnTypedErrorIndirectly()) {
3035+
// Return the error indirectly.
3036+
auto buf = IGF.getCalleeTypedErrorResultSlot(silErrorTy);
3037+
Args[--LastArgWritten] = buf.getAddress();
3038+
}
29563039
}
29573040
}
29583041

@@ -3134,7 +3217,22 @@ class AsyncCallEmission final : public CallEmission {
31343217
errorType =
31353218
substConv.getSILErrorType(IGM.getMaximalTypeExpansionContext());
31363219

3137-
if (resultTys.size() == 1) {
3220+
SILFunctionConventions fnConv(getCallee().getOrigFunctionType(),
3221+
IGF.getSILModule());
3222+
3223+
// Get the natural IR type in the body of the function that makes
3224+
// the call. This may be different than the IR type returned by the
3225+
// call itself due to ABI type coercion.
3226+
auto resultType =
3227+
fnConv.getSILResultType(IGF.IGM.getMaximalTypeExpansionContext());
3228+
auto &nativeSchema =
3229+
IGF.IGM.getTypeInfo(resultType).nativeReturnValueSchema(IGF.IGM);
3230+
3231+
bool mayReturnErrorDirectly = mayReturnTypedErrorDirectly();
3232+
if (mayReturnErrorDirectly && !nativeSchema.requiresIndirect()) {
3233+
return emitToUnmappedExplosionWithDirectTypedError(resultType, result,
3234+
out);
3235+
} else if (resultTys.size() == 1) {
31383236
result = Builder.CreateExtractValue(result, numAsyncContextParams);
31393237
if (hasError) {
31403238
Address errorAddr = IGF.getCalleeErrorResultSlot(errorType,
@@ -3166,17 +3264,6 @@ class AsyncCallEmission final : public CallEmission {
31663264
result = resultAgg;
31673265
}
31683266

3169-
SILFunctionConventions fnConv(getCallee().getOrigFunctionType(),
3170-
IGF.getSILModule());
3171-
3172-
// Get the natural IR type in the body of the function that makes
3173-
// the call. This may be different than the IR type returned by the
3174-
// call itself due to ABI type coercion.
3175-
auto resultType =
3176-
fnConv.getSILResultType(IGF.IGM.getMaximalTypeExpansionContext());
3177-
auto &nativeSchema =
3178-
IGF.IGM.getTypeInfo(resultType).nativeReturnValueSchema(IGF.IGM);
3179-
31803267
// For ABI reasons the result type of the call might not actually match the
31813268
// expected result type.
31823269
//
@@ -3315,7 +3402,7 @@ void CallEmission::emitToUnmappedMemory(Address result) {
33153402
#ifndef NDEBUG
33163403
LastArgWritten = 0; // appease an assert
33173404
#endif
3318-
3405+
33193406
auto call = emitCallSite();
33203407

33213408
// Async calls need to store the error result that is passed as a parameter.
@@ -4403,32 +4490,21 @@ void CallEmission::emitToUnmappedExplosionWithDirectTypedError(
44034490
extractScalarResults(IGF, result->getType(), result, nativeExplosion);
44044491
auto values = nativeExplosion.claimAll();
44054492

4406-
auto convertIfNecessary = [&](llvm::Type *nativeTy,
4407-
llvm::Value *elt) -> llvm::Value * {
4408-
auto *eltTy = elt->getType();
4409-
if (nativeTy->isIntOrPtrTy() && eltTy->isIntOrPtrTy() &&
4410-
nativeTy->getPrimitiveSizeInBits() != eltTy->getPrimitiveSizeInBits()) {
4411-
if (nativeTy->isPointerTy() && eltTy == IGF.IGM.IntPtrTy) {
4412-
return IGF.Builder.CreateIntToPtr(elt, nativeTy);
4413-
}
4414-
return IGF.Builder.CreateTruncOrBitCast(elt, nativeTy);
4415-
}
4416-
return elt;
4417-
};
4418-
44194493
Explosion errorExplosion;
44204494
if (!errorSchema.empty()) {
44214495
if (auto *structTy =
44224496
dyn_cast<llvm::StructType>(errorSchema.getExpandedType(IGF.IGM))) {
44234497
for (unsigned i = 0, e = structTy->getNumElements(); i < e; ++i) {
44244498
llvm::Value *elt = values[combined.errorValueMapping[i]];
44254499
auto *nativeTy = structTy->getElementType(i);
4426-
elt = convertIfNecessary(nativeTy, elt);
4500+
elt = convertForAsyncDirect(IGF, elt, nativeTy, /*forExtraction*/ true);
44274501
errorExplosion.add(elt);
44284502
}
44294503
} else {
4430-
errorExplosion.add(convertIfNecessary(
4431-
combined.combinedTy, values[combined.errorValueMapping[0]]));
4504+
auto *converted =
4505+
convertForAsyncDirect(IGF, values[combined.errorValueMapping[0]],
4506+
combined.combinedTy, /*forExtraction*/ true);
4507+
errorExplosion.add(converted);
44324508
}
44334509

44344510
typedErrorExplosion =
@@ -4444,10 +4520,14 @@ void CallEmission::emitToUnmappedExplosionWithDirectTypedError(
44444520
dyn_cast<llvm::StructType>(nativeSchema.getExpandedType(IGF.IGM))) {
44454521
for (unsigned i = 0, e = structTy->getNumElements(); i < e; ++i) {
44464522
auto *nativeTy = structTy->getElementType(i);
4447-
resultExplosion.add(convertIfNecessary(nativeTy, values[i]));
4523+
auto *converted = convertForAsyncDirect(IGF, values[i], nativeTy,
4524+
/*forExtraction*/ true);
4525+
resultExplosion.add(converted);
44484526
}
44494527
} else {
4450-
resultExplosion.add(convertIfNecessary(combined.combinedTy, values[0]));
4528+
auto *converted = convertForAsyncDirect(
4529+
IGF, values[0], combined.combinedTy, /*forExtraction*/ true);
4530+
resultExplosion.add(converted);
44514531
}
44524532
out = nativeSchema.mapFromNative(IGF.IGM, IGF, resultExplosion, resultType);
44534533
}
@@ -5313,6 +5393,33 @@ llvm::Value* IRGenFunction::coerceValue(llvm::Value *value, llvm::Type *toTy,
53135393
return loaded;
53145394
}
53155395

5396+
llvm::Value *irgen::convertForAsyncDirect(IRGenFunction &IGF,
5397+
llvm::Value *value, llvm::Type *toTy,
5398+
bool forExtraction) {
5399+
auto &Builder = IGF.Builder;
5400+
auto *fromTy = value->getType();
5401+
if (toTy->isIntOrPtrTy() && fromTy->isIntOrPtrTy() && toTy != fromTy) {
5402+
5403+
if (toTy->isPointerTy()) {
5404+
if (fromTy->isPointerTy())
5405+
return Builder.CreateBitCast(value, toTy);
5406+
if (fromTy == IGF.IGM.IntPtrTy)
5407+
return Builder.CreateIntToPtr(value, toTy);
5408+
} else if (fromTy->isPointerTy()) {
5409+
if (toTy == IGF.IGM.IntPtrTy) {
5410+
return Builder.CreatePtrToInt(value, toTy);
5411+
}
5412+
}
5413+
5414+
if (forExtraction) {
5415+
return Builder.CreateTruncOrBitCast(value, toTy);
5416+
} else {
5417+
return Builder.CreateZExtOrBitCast(value, toTy);
5418+
}
5419+
}
5420+
return value;
5421+
}
5422+
53165423
void IRGenFunction::emitScalarReturn(llvm::Type *resultType,
53175424
Explosion &result) {
53185425
if (result.empty()) {
@@ -5754,32 +5861,18 @@ void IRGenFunction::emitScalarReturn(SILType returnResultType,
57545861
return;
57555862
}
57565863

5757-
auto convertIfNecessary = [&](llvm::Type *nativeTy,
5758-
llvm::Value *elt) -> llvm::Value * {
5759-
auto *eltTy = elt->getType();
5760-
if (nativeTy->isIntOrPtrTy() && eltTy->isIntOrPtrTy() &&
5761-
nativeTy->getPrimitiveSizeInBits() !=
5762-
eltTy->getPrimitiveSizeInBits()) {
5763-
assert(nativeTy->getPrimitiveSizeInBits() >
5764-
eltTy->getPrimitiveSizeInBits());
5765-
if (eltTy->isPointerTy()) {
5766-
return Builder.CreatePtrToInt(elt, nativeTy);
5767-
}
5768-
return Builder.CreateZExt(elt, nativeTy);
5769-
}
5770-
return elt;
5771-
};
5772-
57735864
if (auto *structTy = dyn_cast<llvm::StructType>(combinedTy)) {
57745865
nativeAgg = llvm::UndefValue::get(combinedTy);
57755866
for (unsigned i = 0, e = native.size(); i != e; ++i) {
57765867
llvm::Value *elt = native.claimNext();
57775868
auto *nativeTy = structTy->getElementType(i);
5778-
elt = convertIfNecessary(nativeTy, elt);
5869+
elt = convertForAsyncDirect(*this, elt, nativeTy,
5870+
/*forExtraction*/ false);
57795871
nativeAgg = Builder.CreateInsertValue(nativeAgg, elt, i);
57805872
}
57815873
} else {
5782-
nativeAgg = convertIfNecessary(combinedTy, native.claimNext());
5874+
nativeAgg = convertForAsyncDirect(*this, native.claimNext(), combinedTy,
5875+
/*forExtraction*/ false);
57835876
}
57845877
}
57855878

@@ -6089,6 +6182,51 @@ void irgen::emitAsyncReturn(IRGenFunction &IGF, AsyncContextLayout &asyncLayout,
60896182
SILFunctionConventions conv(fnType, IGF.getSILModule());
60906183
auto &nativeSchema =
60916184
IGM.getTypeInfo(funcResultTypeInContext).nativeReturnValueSchema(IGM);
6185+
6186+
if (fnType->hasErrorResult() && !conv.hasIndirectSILResults() &&
6187+
!conv.hasIndirectSILErrorResults() && !nativeSchema.requiresIndirect() &&
6188+
conv.isTypedError()) {
6189+
auto errorType = conv.getSILErrorType(IGM.getMaximalTypeExpansionContext());
6190+
auto &errorTI = IGM.getTypeInfo(errorType);
6191+
auto &nativeError = errorTI.nativeReturnValueSchema(IGM);
6192+
if (!nativeError.shouldReturnTypedErrorIndirectly()) {
6193+
assert(!error.empty() && "Direct error return must have error value");
6194+
auto *combinedTy =
6195+
combineResultAndTypedErrorType(IGM, nativeSchema, nativeError)
6196+
.combinedTy;
6197+
6198+
if (combinedTy->isVoidTy()) {
6199+
assert(result.empty() && "Unexpected result values");
6200+
} else {
6201+
if (auto *structTy = dyn_cast<llvm::StructType>(combinedTy)) {
6202+
llvm::Value *nativeAgg = llvm::UndefValue::get(structTy);
6203+
for (unsigned i = 0, e = result.size(); i != e; ++i) {
6204+
llvm::Value *elt = result.claimNext();
6205+
auto *nativeTy = structTy->getElementType(i);
6206+
elt = convertForAsyncDirect(IGF, elt, nativeTy,
6207+
/*forExtraction*/ false);
6208+
nativeAgg = IGF.Builder.CreateInsertValue(nativeAgg, elt, i);
6209+
}
6210+
Explosion out;
6211+
IGF.emitAllExtractValues(nativeAgg, structTy, out);
6212+
while (!out.empty()) {
6213+
nativeResultsStorage.push_back(out.claimNext());
6214+
}
6215+
} else {
6216+
auto *converted = convertForAsyncDirect(
6217+
IGF, result.claimNext(), combinedTy, /*forExtraction*/ false);
6218+
nativeResultsStorage.push_back(converted);
6219+
}
6220+
}
6221+
6222+
nativeResultsStorage.push_back(error.claimNext());
6223+
nativeResults = nativeResultsStorage;
6224+
6225+
emitAsyncReturn(IGF, asyncLayout, fnType, nativeResults);
6226+
return;
6227+
}
6228+
}
6229+
60926230
if (result.empty() && !nativeSchema.empty()) {
60936231
if (!nativeSchema.requiresIndirect())
60946232
// When we throw, we set the return values to undef.

lib/IRGen/GenCall.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,10 @@ namespace irgen {
274274
void forwardAsyncCallResult(IRGenFunction &IGF, CanSILFunctionType fnType,
275275
AsyncContextLayout &layout, llvm::CallInst *call);
276276

277+
/// Converts a value for async direct errors.
278+
llvm::Value *convertForAsyncDirect(IRGenFunction &IGF, llvm::Value *value,
279+
llvm::Type *toTy, bool forExtraction);
280+
277281
} // end namespace irgen
278282
} // end namespace swift
279283

0 commit comments

Comments
 (0)