Skip to content

Commit de4ce5d

Browse files
authored
Rebase swiftasynccall's musttail support onto the [[clang::musttail]] logic (#86011)
The old logic expects the call to be the last thing we emitted, and since it kicks in before we emit cleanups, and since `swiftasynccall` functions always return void, that's likely to be true. "Likely" isn't very reassuring when we're talking about slapping attributes on random calls, though. And indeed, while I can't find any way to break the logic directly in current main, our previous (ongoing?) experiments with shortening argument temporary lifetimes definitely broke it wide open. So while this commit is prophylactic for now, it's clearly the right thing to do, and it can cherry-picked to other branches to fix problems.
1 parent 5a9bdd8 commit de4ce5d

File tree

2 files changed

+34
-15
lines changed

2 files changed

+34
-15
lines changed

clang/lib/CodeGen/CGStmt.cpp

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,10 +1341,8 @@ struct SaveRetExprRAII {
13411341
};
13421342
} // namespace
13431343

1344-
/// If we have 'return f(...);', where both caller and callee are SwiftAsync,
1345-
/// codegen it as 'tail call ...; ret void;'.
1346-
static void makeTailCallIfSwiftAsync(const CallExpr *CE, CGBuilderTy &Builder,
1347-
const CGFunctionInfo *CurFnInfo) {
1344+
/// Determine if the given call uses the swiftasync calling convention.
1345+
static bool isSwiftAsyncCallee(const CallExpr *CE) {
13481346
auto calleeQualType = CE->getCallee()->getType();
13491347
const FunctionType *calleeType = nullptr;
13501348
if (calleeQualType->isFunctionPointerType() ||
@@ -1359,18 +1357,12 @@ static void makeTailCallIfSwiftAsync(const CallExpr *CE, CGBuilderTy &Builder,
13591357
// getMethodDecl() doesn't handle member pointers at the moment.
13601358
calleeType = methodDecl->getType()->castAs<FunctionType>();
13611359
} else {
1362-
return;
1360+
return false;
13631361
}
13641362
} else {
1365-
return;
1366-
}
1367-
if (calleeType->getCallConv() == CallingConv::CC_SwiftAsync &&
1368-
(CurFnInfo->getASTCallingConvention() == CallingConv::CC_SwiftAsync)) {
1369-
auto CI = cast<llvm::CallInst>(&Builder.GetInsertBlock()->back());
1370-
CI->setTailCallKind(llvm::CallInst::TCK_MustTail);
1371-
Builder.CreateRetVoid();
1372-
Builder.ClearInsertionPoint();
1363+
return false;
13731364
}
1365+
return calleeType->getCallConv() == CallingConv::CC_SwiftAsync;
13741366
}
13751367

13761368
/// EmitReturnStmt - Note that due to GCC extensions, this can have an operand
@@ -1410,6 +1402,19 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) {
14101402
RunCleanupsScope cleanupScope(*this);
14111403
if (const auto *EWC = dyn_cast_or_null<ExprWithCleanups>(RV))
14121404
RV = EWC->getSubExpr();
1405+
1406+
// If we're in a swiftasynccall function, and the return expression is a
1407+
// call to a swiftasynccall function, mark the call as the musttail call.
1408+
std::optional<llvm::SaveAndRestore<const CallExpr *>> SaveMustTail;
1409+
if (RV && CurFnInfo &&
1410+
CurFnInfo->getASTCallingConvention() == CallingConv::CC_SwiftAsync) {
1411+
if (auto CE = dyn_cast<CallExpr>(RV)) {
1412+
if (isSwiftAsyncCallee(CE)) {
1413+
SaveMustTail.emplace(MustTailCall, CE);
1414+
}
1415+
}
1416+
}
1417+
14131418
// FIXME: Clean this up by using an LValue for ReturnTemp,
14141419
// EmitStoreThroughLValue, and EmitAnyExpr.
14151420
// Check if the NRVO candidate was not globalized in OpenMP mode.
@@ -1432,8 +1437,6 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) {
14321437
// for side effects.
14331438
if (RV) {
14341439
EmitAnyExpr(RV);
1435-
if (auto *CE = dyn_cast<CallExpr>(RV))
1436-
makeTailCallIfSwiftAsync(CE, Builder, CurFnInfo);
14371440
}
14381441
} else if (!RV) {
14391442
// Do nothing (return value is left uninitialized)

clang/test/CodeGen/swift-async-call-conv.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,19 @@ SWIFTASYNCCALL void async_struct_field_and_methods(int i, S &sref, S *sptr) {
182182
// CPPONLY-LABEL: define{{.*}} swifttailcc void @{{.*}}async_nonleaf_method2
183183
// CPPONLY: musttail call swifttailcc void @{{.*}}async_leaf_method
184184
#endif
185+
186+
// Passing this as an argument requires a coerce-and-expand operation,
187+
// which requires a temporary. Make sure that cleaning up that temporary
188+
// doesn't mess around with the musttail handling.
189+
struct coerce_and_expand {
190+
char a,b,c,d;
191+
};
192+
struct coerce_and_expand return_coerced(void);
193+
SWIFTASYNCCALL void take_coerced_async(struct coerce_and_expand);
194+
195+
// CHECK-LABEL: swifttailcc void @{{.*}}test_coerced
196+
SWIFTASYNCCALL void test_coerced() {
197+
// CHECK: musttail call swifttailcc void @{{.*}}take_coerced_async
198+
// CHECK-NEXT: ret void
199+
return take_coerced_async(return_coerced());
200+
}

0 commit comments

Comments
 (0)