diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -670,6 +670,7 @@ return cast(MemExpr->getMemberDecl()); // FIXME: Will eventually need to cope with member pointers. + // NOTE: Update makeTailCallIfSwiftAsync on fixing this. return nullptr; } 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,37 @@ }; } // 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 calleeQualType = CE->getCallee()->getType(); + const FunctionType *calleeType = nullptr; + if (calleeQualType->isFunctionPointerType() || + calleeQualType->isFunctionReferenceType() || + calleeQualType->isBlockPointerType() || + calleeQualType->isMemberFunctionPointerType()) { + calleeType = calleeQualType->getPointeeType()->castAs(); + } else if (auto *ty = dyn_cast(calleeQualType)) { + calleeType = ty; + } else if (auto CMCE = dyn_cast(CE)) { + if (auto methodDecl = CMCE->getMethodDecl()) { + // getMethodDecl() doesn't handle member pointers at the moment. + calleeType = methodDecl->getType()->castAs(); + } else { + return; + } + } else { + return; + if (calleeType->getCallConv() == CallingConv::CC_SwiftAsync && + (CurFnInfo->getASTCallingConvention() == CallingConv::CC_SwiftAsync)) { + auto CI = cast(&Builder.GetInsertBlock()->back()); + CI->setTailCallKind(llvm::CallInst::TCK_MustTail); + 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 +1234,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,184 @@ +// 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 --check-prefix=CHECK --check-prefix=CPPONLY +// RUN: %clang_cc1 -x c++ -triple arm64-apple-ios9 -target-cpu cyclone -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY +// RUN: %clang_cc1 -x c++ -triple armv7-apple-darwin9 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY +// RUN: %clang_cc1 -x c++ -triple armv7s-apple-ios9 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY +// RUN: %clang_cc1 -x c++ -triple armv7k-apple-ios9 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY + +// 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: musttail call swifttailcc void @{{.*}}async_leaf1 +// CHECK-NEXT: ret void +// CHECK: musttail 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: musttail call swifttailcc void @{{.*}}async_leaf1 +// CHECK: call swifttailcc void @{{.*}}async_leaf1 +// CHECK-NOT: ret void +// CHECK: musttail 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: musttail call swifttailcc void @{{.*}}async_leaf1 +// CHECK-NEXT: ret void +// CHECK: musttail call swifttailcc void @{{.*}}async_leaf2 +// CHECK-NEXT: ret void +// CHECK: musttail 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: musttail call swifttailcc void @{{.*}}async_leaf1 +// CHECK-NEXT: ret void +// CHECK: musttail 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: musttail call swifttailcc void @{{.*}}async_leaf1 +// CHECK-NEXT: ret void +// CHECK: musttail call swifttailcc void @{{.*}}async_leaf2 +// CHECK-NEXT: ret void +// CHECK: musttail 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; +} + +#if __cplusplus +struct S { + SWIFTASYNCCALL void (*fptr)(char * ASYNC_CONTEXT); + + SWIFTASYNCCALL void async_leaf_method(char * ASYNC_CONTEXT ctx) { + *ctx += 1; + } + SWIFTASYNCCALL void async_nonleaf_method1(char * ASYNC_CONTEXT ctx) { + return async_leaf_method(ctx); + } + SWIFTASYNCCALL void async_nonleaf_method2(char * ASYNC_CONTEXT ctx) { + return this->async_leaf_method(ctx); + } +}; + +SWIFTASYNCCALL void (S::*async_leaf_method_ptr)(char * ASYNC_CONTEXT) = &S::async_leaf_method; + +// CPPONLY-LABEL: swifttailcc void {{.*}}async_struct_field_and_methods +// CPPONLY: musttail call swifttailcc void %{{[0-9]+}} +// CPPONLY: musttail call swifttailcc void @{{.*}}async_nonleaf_method1 +// CPPONLY: musttail call swifttailcc void %{{[0-9]+}} +// CPPONLY: musttail call swifttailcc void @{{.*}}async_nonleaf_method2 +// CPPONLY-NOT: musttail call swifttailcc void @{{.*}}async_leaf_method +// ^ TODO: Member pointers should also work. +SWIFTASYNCCALL void async_struct_field_and_methods(int i, S &sref, S *sptr) { + char x = 'a'; + if (i == 0) { + return (*sref.fptr)(&x); + } else if (i == 1) { + return sref.async_nonleaf_method1(&x); + } else if (i == 2) { + return (*(sptr->fptr))(&x); + } else if (i == 3) { + return sptr->async_nonleaf_method2(&x); + } else if (i == 4) { + return (sref.*async_leaf_method_ptr)(&x); + } + return (sptr->*async_leaf_method_ptr)(&x); +} + +// CPPONLY-LABEL: define{{.*}} swifttailcc void @{{.*}}async_nonleaf_method1 +// CPPONLY: musttail call swifttailcc void @{{.*}}async_leaf_method + +// CPPONLY-LABEL: define{{.*}} swifttailcc void @{{.*}}async_nonleaf_method2 +// CPPONLY: musttail call swifttailcc void @{{.*}}async_leaf_method +#endif 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()