diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -66,9 +66,7 @@ case CC_PreserveMost: return llvm::CallingConv::PreserveMost; case CC_PreserveAll: return llvm::CallingConv::PreserveAll; case CC_Swift: return llvm::CallingConv::Swift; - // [FIXME: swiftasynccc] Update to SwiftAsync once LLVM support lands. - case CC_SwiftAsync: - return llvm::CallingConv::Swift; + case CC_SwiftAsync: return llvm::CallingConv::SwiftTail; } } diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -1145,6 +1145,24 @@ }; } // namespace +/// If we have 'return f(...);', where both caller and callee are SwiftAsync, +/// codegen it as 'tail call ...; ret void;'. +static void makeTailCallIfSwiftAsync(const CallExpr *CE, CGBuilderTy &Builder, + const CGFunctionInfo *CurFnInfo) { + auto callee = CE->getDirectCallee(); + if (!callee) + return; + auto calleeType = callee->getFunctionType(); + if (calleeType->getCallConv() == CallingConv::CC_SwiftAsync + && (CurFnInfo->getASTCallingConvention() == + CallingConv::CC_SwiftAsync)) { + auto CI = cast(&Builder.GetInsertBlock()->back()); + CI->setTailCallKind(llvm::CallInst::TCK_Tail); + Builder.CreateRetVoid(); + Builder.ClearInsertionPoint(); + } +} + /// EmitReturnStmt - Note that due to GCC extensions, this can have an operand /// if the function returns void, or may be missing one if the function returns /// non-void. Fun stuff :). @@ -1203,8 +1221,11 @@ } else if (!ReturnValue.isValid() || (RV && RV->getType()->isVoidType())) { // Make sure not to return anything, but evaluate the expression // for side effects. - if (RV) + if (RV) { EmitAnyExpr(RV); + if (auto *CE = dyn_cast(RV)) + ::makeTailCallIfSwiftAsync(CE, Builder, CurFnInfo); + } } else if (!RV) { // Do nothing (return value is left uninitialized) } else if (FnRetTy->isReferenceType()) { diff --git a/clang/test/CodeGen/64bit-swiftcall.c b/clang/test/CodeGen/64bit-swiftcall.c --- a/clang/test/CodeGen/64bit-swiftcall.c +++ b/clang/test/CodeGen/64bit-swiftcall.c @@ -4,9 +4,11 @@ // REQUIRES: aarch64-registered-target,x86-registered-target #define SWIFTCALL __attribute__((swiftcall)) +#define SWIFTASYNCCALL __attribute__((swiftasynccall)) #define OUT __attribute__((swift_indirect_result)) #define ERROR __attribute__((swift_error_result)) #define CONTEXT __attribute__((swift_context)) +#define ASYNC_CONTEXT __attribute__((swift_async_context)) // CHECK-DAG: %struct.atomic_padded = type { { %struct.packed, [7 x i8] } } // CHECK-DAG: %struct.packed = type <{ i64, i8 }> @@ -31,9 +33,15 @@ SWIFTCALL void context_1(CONTEXT void *self) {} // CHECK-LABEL: define {{.*}} void @context_1(i8* swiftself +SWIFTASYNCCALL void async_context_1(ASYNC_CONTEXT void *ctx) {} +// CHECK-LABEL: define {{.*}} void @async_context_1(i8* swiftasync + SWIFTCALL void context_2(void *arg0, CONTEXT void *self) {} // CHECK-LABEL: define {{.*}} void @context_2(i8*{{.*}}, i8* swiftself +SWIFTASYNCCALL void async_context_2(void *arg0, ASYNC_CONTEXT void *ctx) {} +// CHECK-LABEL: define {{.*}} void @async_context_2(i8*{{.*}}, i8* swiftasync + SWIFTCALL void context_error_1(CONTEXT int *self, ERROR float **error) {} // CHECK-LABEL: define {{.*}} void @context_error_1(i32* swiftself{{.*}}, float** swifterror %0) // CHECK: [[TEMP:%.*]] = alloca float*, align 8 diff --git a/clang/test/CodeGen/arm-swiftcall.c b/clang/test/CodeGen/arm-swiftcall.c --- a/clang/test/CodeGen/arm-swiftcall.c +++ b/clang/test/CodeGen/arm-swiftcall.c @@ -27,9 +27,15 @@ SWIFTCALL void context_1(CONTEXT void *self) {} // CHECK-LABEL: define{{.*}} void @context_1(i8* swiftself +SWIFTASYNCCALL void async_context_1(ASYNC_CONTEXT void *self) {} +// CHECK-LABEL: define{{.*}} void @async_context_1(i8* swiftasync + SWIFTCALL void context_2(void *arg0, CONTEXT void *self) {} // CHECK-LABEL: define{{.*}} void @context_2(i8*{{.*}}, i8* swiftself +SWIFTASYNCCALL void async_context_2(void *arg0, ASYNC_CONTEXT void *self) {} +// CHECK-LABEL: define{{.*}} void @async_context_2(i8*{{.*}}, i8* swiftasync + SWIFTCALL void context_error_1(CONTEXT int *self, ERROR float **error) {} // CHECK-LABEL: define{{.*}} void @context_error_1(i32* swiftself{{.*}}, float** swifterror %0) // CHECK: [[TEMP:%.*]] = alloca float*, align 4 @@ -55,9 +61,6 @@ SWIFTCALL void context_error_2(short s, CONTEXT int *self, ERROR float **error) {} // CHECK-LABEL: define{{.*}} void @context_error_2(i16{{.*}}, i32* swiftself{{.*}}, float** swifterror %0) -SWIFTASYNCCALL void async_context_1(ASYNC_CONTEXT void *self) {} -// CHECK-LABEL: define {{.*}} void @async_context_1(i8* swiftasync - /*****************************************************************************/ /********************************** LOWERING *********************************/ /*****************************************************************************/ diff --git a/clang/test/CodeGen/swift-async-call-conv.c b/clang/test/CodeGen/swift-async-call-conv.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/swift-async-call-conv.c @@ -0,0 +1,138 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -target-cpu core2 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios9 -target-cpu cyclone -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple armv7-apple-darwin9 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple armv7s-apple-ios9 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple armv7k-apple-ios9 -emit-llvm -o - %s | FileCheck %s + +// RUN: %clang_cc1 -x c++ -triple x86_64-apple-darwin10 -target-cpu core2 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -x c++ -triple arm64-apple-ios9 -target-cpu cyclone -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -x c++ -triple armv7-apple-darwin9 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -x c++ -triple armv7s-apple-ios9 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -x c++ -triple armv7k-apple-ios9 -emit-llvm -o - %s | FileCheck %s + +// Test tail call behavior when a swiftasynccall function is called +// from another swiftasynccall function. + +#define SWIFTCALL __attribute__((swiftcall)) +#define SWIFTASYNCCALL __attribute__((swiftasynccall)) +#define ASYNC_CONTEXT __attribute__((swift_async_context)) + +// CHECK-LABEL: swifttailcc void {{.*}}async_leaf1{{.*}}(i8* swiftasync +SWIFTASYNCCALL void async_leaf1(char * ASYNC_CONTEXT ctx) { + *ctx += 1; +} + +// CHECK-LABEL: swifttailcc void {{.*}}async_leaf2{{.*}}(i8* swiftasync +SWIFTASYNCCALL void async_leaf2(char * ASYNC_CONTEXT ctx) { + *ctx += 2; +} + +#if __cplusplus + #define MYBOOL bool +#else + #define MYBOOL _Bool +#endif + +// CHECK-LABEL: swifttailcc void {{.*}}async_branch{{.*}}i8* swiftasync +// CHECK: tail call swifttailcc void @{{.*}}async_leaf1 +// CHECK-NEXT: ret void +// CHECK: tail call swifttailcc void @{{.*}}async_leaf2 +// CHECK-NEXT: ret void +SWIFTASYNCCALL void async_branch(MYBOOL b, char * ASYNC_CONTEXT ctx) { + if (b) { + return async_leaf1(ctx); + } else { + return async_leaf2(ctx); + } +} + +// CHECK-LABEL: swifttailcc void {{.*}}async_not_all_tail +// CHECK-NOT: tail call swifttailcc void @{{.*}}async_leaf1 +// CHECK: call swifttailcc void @{{.*}}async_leaf1 +// CHECK-NOT: ret void +// CHECK: tail call swifttailcc void @{{.*}}async_leaf2 +// CHECK-NEXT: ret void +SWIFTASYNCCALL void async_not_all_tail(char * ASYNC_CONTEXT ctx) { + async_leaf1(ctx); + return async_leaf2(ctx); +} + +// CHECK-LABEL: swifttailcc void {{.*}}async_loop +// CHECK: tail call swifttailcc void @{{.*}}async_leaf1 +// CHECK-NEXT: ret void +// CHECK: tail call swifttailcc void @{{.*}}async_leaf2 +// CHECK-NEXT: ret void +// CHECK: tail call swifttailcc void @{{.*}}async_loop +// CHECK-NEXT: ret void +SWIFTASYNCCALL void async_loop(unsigned u, char * ASYNC_CONTEXT ctx) { + if (u == 0) { + return async_leaf1(ctx); + } else if (u == 1) { + return async_leaf2(ctx); + } + return async_loop(u - 2, ctx); +} + +// Forward-declaration + mutual recursion is okay. + +SWIFTASYNCCALL void async_mutual_loop2(unsigned u, char * ASYNC_CONTEXT ctx); + +// CHECK-LABEL: swifttailcc void {{.*}}async_mutual_loop1 +// CHECK: tail call swifttailcc void @{{.*}}async_leaf1 +// CHECK-NEXT: ret void +// CHECK: tail call swifttailcc void @{{.*}}async_leaf2 +// CHECK-NEXT: ret void +// There is some bugginess around FileCheck's greediness/matching, +// so skipping the check for async_mutual_loop2 here. +SWIFTASYNCCALL void async_mutual_loop1(unsigned u, char * ASYNC_CONTEXT ctx) { + if (u == 0) { + return async_leaf1(ctx); + } else if (u == 1) { + return async_leaf2(ctx); + } + return async_mutual_loop2(u - 2, ctx); +} + +// CHECK-LABEL: swifttailcc void {{.*}}async_mutual_loop2 +// CHECK: tail call swifttailcc void @{{.*}}async_leaf1 +// CHECK-NEXT: ret void +// CHECK: tail call swifttailcc void @{{.*}}async_leaf2 +// CHECK-NEXT: ret void +// CHECK: tail call swifttailcc void @{{.*}}async_mutual_loop1 +// CHECK-NEXT: ret void +SWIFTASYNCCALL void async_mutual_loop2(unsigned u, char * ASYNC_CONTEXT ctx) { + if (u == 0) { + return async_leaf1(ctx); + } else if (u == 1) { + return async_leaf2(ctx); + } + return async_mutual_loop1(u - 2, ctx); +} + +// When swiftasynccall functions are called by non-swiftasynccall functions, +// the call isn't marked as a tail call. + +// CHECK-LABEL: swiftcc i8 {{.*}}sync_calling_async +// CHECK-NOT: tail call +// CHECK: call swifttailcc void @{{.*}}async_branch +// CHECK-NOT: tail call +// CHECK: call swifttailcc void @{{.*}}async_loop +SWIFTCALL char sync_calling_async(MYBOOL b, unsigned u) { + char x = 'a'; + async_branch(b, &x); + async_loop(u, &x); + return x; +} + +// CHECK-LABEL: i8 {{.*}}c_calling_async +// CHECK-NOT: tail call +// CHECK: call swifttailcc void @{{.*}}async_branch +// CHECK-NOT: tail call +// CHECK: call swifttailcc void @{{.*}}async_loop +char c_calling_async(MYBOOL b, unsigned u) { + char x = 'a'; + async_branch(b, &x); + async_loop(u, &x); + return x; +} + diff --git a/clang/test/CodeGen/swift-call-conv.c b/clang/test/CodeGen/swift-call-conv.c --- a/clang/test/CodeGen/swift-call-conv.c +++ b/clang/test/CodeGen/swift-call-conv.c @@ -6,3 +6,5 @@ void __attribute__((__swiftcall__)) f(void) {} // CHECK-LABEL: define dso_local swiftcc void @f() +void __attribute__((__swiftasynccall__)) f_async(void) {} +// CHECK-LABEL: define dso_local swifttailcc void @f_async()