From 5e8bd88711d23b2a5db79f43580afebdfd10a394 Mon Sep 17 00:00:00 2001 From: Roman Levenstein Date: Wed, 16 Mar 2016 18:00:46 +0000 Subject: [PATCH] Add attributes for preserve_mostcc/preserve_allcc calling conventions to the C/C++ front-end Till now, preserve_mostcc/preserve_allcc calling convention attributes were only available at the LLVM IR level. This patch adds attributes for preserve_mostcc/preserve_allcc calling conventions to the C/C++ front-end. The code was mostly written by Juergen Ributzka. I just added support for the AArch64 target and tests. Differential Revision: http://reviews.llvm.org/D18025 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263647 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang-c/Index.h | 2 + include/clang/AST/Type.h | 2 + include/clang/Basic/Attr.td | 10 +++++ include/clang/Basic/AttrDocs.td | 67 +++++++++++++++++++++++++++++++ include/clang/Basic/Specifiers.h | 4 +- lib/AST/ItaniumMangle.cpp | 2 + lib/AST/Type.cpp | 6 +++ lib/AST/TypePrinter.cpp | 13 ++++++ lib/Basic/Targets.cpp | 4 ++ lib/CodeGen/CGCall.cpp | 8 ++++ lib/Sema/SemaDeclAttr.cpp | 13 +++++- lib/Sema/SemaType.cpp | 12 +++++- test/CodeGen/preserve-call-conv.c | 17 ++++++++ test/Sema/preserve-call-conv.c | 35 ++++++++++++++++ tools/libclang/CXType.cpp | 2 + 15 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 test/CodeGen/preserve-call-conv.c create mode 100644 test/Sema/preserve-call-conv.c diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 9c5c6807b2..a564af88b2 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -2971,6 +2971,8 @@ enum CXCallingConv { CXCallingConv_X86_64SysV = 11, CXCallingConv_X86VectorCall = 12, CXCallingConv_Swift = 13, + CXCallingConv_PreserveMost = 14, + CXCallingConv_PreserveAll = 15, CXCallingConv_Invalid = 100, CXCallingConv_Unexposed = 200 diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 6c3fd805df..2e02ad1ea0 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -3740,6 +3740,8 @@ public: attr_inteloclbicc, attr_ms_abi, attr_sysv_abi, + attr_preserve_most, + attr_preserve_all, attr_ptr32, attr_ptr64, attr_sptr, diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index f50cb296ad..f07f30d996 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1399,6 +1399,16 @@ def Pascal : InheritableAttr { let Documentation = [Undocumented]; } +def PreserveMost : InheritableAttr { + let Spellings = [GNU<"preserve_most">]; + let Documentation = [PreserveMostDocs]; +} + +def PreserveAll : InheritableAttr { + let Spellings = [GNU<"preserve_all">]; + let Documentation = [PreserveAllDocs]; +} + def Target : InheritableAttr { let Spellings = [GCC<"target">]; let Args = [StringArgument<"featuresStr">]; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 4e80a8db09..4e2df24d4d 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -2172,3 +2172,70 @@ a global variable of the class type. Therefor, the old code could keep using the old manged name and the new code will use the new mangled name with tags. }]; } + +def PreserveMostDocs : Documentation { + let Category = DocCatCallingConvs; + let Content = [{ +On X86-64 and AArch64 targets, this attribute changes the calling convention of +a function. The ``preserve_most`` calling convention attempts to make the code +in the caller as unintrusive as possible. This convention behaves identically +to the ``C`` calling convention on how arguments and return values are passed, +but it uses a different set of caller/callee-saved registers. This alleviates +the burden of saving and recovering a large register set before and after the +call in the caller. If the arguments are passed in callee-saved registers, +then they will be preserved by the callee across the call. This doesn't +apply for values returned in callee-saved registers. + +- On X86-64 the callee preserves all general purpose registers, except for + R11. R11 can be used as a scratch register. Floating-point registers + (XMMs/YMMs) are not preserved and need to be saved by the caller. + +The idea behind this convention is to support calls to runtime functions +that have a hot path and a cold path. The hot path is usually a small piece +of code that doesn't use many registers. The cold path might need to call out to +another function and therefore only needs to preserve the caller-saved +registers, which haven't already been saved by the caller. The +`preserve_most` calling convention is very similar to the ``cold`` calling +convention in terms of caller/callee-saved registers, but they are used for +different types of function calls. ``coldcc`` is for function calls that are +rarely executed, whereas `preserve_most` function calls are intended to be +on the hot path and definitely executed a lot. Furthermore ``preserve_most`` +doesn't prevent the inliner from inlining the function call. + +This calling convention will be used by a future version of the Objective-C +runtime and should therefore still be considered experimental at this time. +Although this convention was created to optimize certain runtime calls to +the Objective-C runtime, it is not limited to this runtime and might be used +by other runtimes in the future too. The current implementation only +supports X86-64 and AArch64, but the intention is to support more architectures +in the future. + }]; +} + +def PreserveAllDocs : Documentation { + let Category = DocCatCallingConvs; + let Content = [{ +On X86-64 and AArch64 targets, this attribute changes the calling convention of +a function. The ``preserve_all`` calling convention attempts to make the code +in the caller even less intrusive than the ``preserve_most`` calling convention. +This calling convention also behaves identical to the ``C`` calling convention +on how arguments and return values are passed, but it uses a different set of +caller/callee-saved registers. This removes the burden of saving and +recovering a large register set before and after the call in the caller. If +the arguments are passed in callee-saved registers, then they will be +preserved by the callee across the call. This doesn't apply for values +returned in callee-saved registers. + +- On X86-64 the callee preserves all general purpose registers, except for + R11. R11 can be used as a scratch register. Furthermore it also preserves + all floating-point registers (XMMs/YMMs). + +The idea behind this convention is to support calls to runtime functions +that don't need to call out to any other functions. + +This calling convention, like the ``preserve_most`` calling convention, will be +used by a future version of the Objective-C runtime and should be considered +experimental at this time. + }]; +} + diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h index 9be0d5aaaf..8857e0df56 100644 --- a/include/clang/Basic/Specifiers.h +++ b/include/clang/Basic/Specifiers.h @@ -239,7 +239,9 @@ namespace clang { CC_IntelOclBicc, // __attribute__((intel_ocl_bicc)) CC_SpirFunction, // default for OpenCL functions on SPIR target CC_SpirKernel, // inferred for OpenCL kernels on SPIR target - CC_Swift // __attribute__((swiftcall)) + CC_Swift, // __attribute__((swiftcall)) + CC_PreserveMost, // __attribute__((preserve_most)) + CC_PreserveAll, // __attribute__((preserve_all)) }; /// \brief Checks whether the given calling convention supports variadic diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 5e079960ae..2a517a5ccf 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -2165,6 +2165,8 @@ StringRef CXXNameMangler::getCallingConvQualifierName(CallingConv CC) { case CC_IntelOclBicc: case CC_SpirFunction: case CC_SpirKernel: + case CC_PreserveMost: + case CC_PreserveAll: // FIXME: we should be mangling all of the above. return ""; diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index efa2595f6d..74aa323dbc 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2656,6 +2656,8 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) { case CC_SpirFunction: return "spir_function"; case CC_SpirKernel: return "spir_kernel"; case CC_Swift: return "swiftcall"; + case CC_PreserveMost: return "preserve_most"; + case CC_PreserveAll: return "preserve_all"; } llvm_unreachable("Invalid calling convention."); @@ -2999,6 +3001,8 @@ bool AttributedType::isQualifier() const { case AttributedType::attr_swiftcall: case AttributedType::attr_vectorcall: case AttributedType::attr_inteloclbicc: + case AttributedType::attr_preserve_most: + case AttributedType::attr_preserve_all: case AttributedType::attr_ms_abi: case AttributedType::attr_sysv_abi: case AttributedType::attr_ptr32: @@ -3056,6 +3060,8 @@ bool AttributedType::isCallingConv() const { case attr_ms_abi: case attr_sysv_abi: case attr_inteloclbicc: + case attr_preserve_most: + case attr_preserve_all: return true; } llvm_unreachable("invalid attr kind"); diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 47f60acb1d..7c519fc638 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -726,6 +726,13 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, break; case CC_Swift: OS << " __attribute__((swiftcall))"; + break; + case CC_PreserveMost: + OS << " __attribute__((preserve_most))"; + break; + case CC_PreserveAll: + OS << " __attribute__((preserve_all))"; + break; } } @@ -1345,6 +1352,12 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, break; } case AttributedType::attr_inteloclbicc: OS << "inteloclbicc"; break; + case AttributedType::attr_preserve_most: + OS << "preserve_most"; + break; + case AttributedType::attr_preserve_all: + OS << "preserve_all"; + break; } OS << "))"; } diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index 797d052d0c..a151c59478 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -4095,6 +4095,8 @@ public: case CC_X86VectorCall: case CC_IntelOclBicc: case CC_X86_64Win64: + case CC_PreserveMost: + case CC_PreserveAll: return CCCR_OK; default: return CCCR_Warning; @@ -5545,6 +5547,8 @@ public: switch (CC) { case CC_C: case CC_Swift: + case CC_PreserveMost: + case CC_PreserveAll: return CCCR_OK; default: return CCCR_Warning; diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 7fb301c8c3..5e10f26ceb 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -57,6 +57,8 @@ static unsigned ClangCallConvToLLVMCallConv(CallingConv CC) { case CC_X86VectorCall: return llvm::CallingConv::X86_VectorCall; case CC_SpirFunction: return llvm::CallingConv::SPIR_FUNC; case CC_SpirKernel: return llvm::CallingConv::SPIR_KERNEL; + case CC_PreserveMost: return llvm::CallingConv::PreserveMost; + case CC_PreserveAll: return llvm::CallingConv::PreserveAll; } } @@ -187,6 +189,12 @@ static CallingConv getCallingConventionForDecl(const Decl *D, bool IsWindows) { if (D->hasAttr()) return IsWindows ? CC_X86_64SysV : CC_C; + if (D->hasAttr()) + return CC_PreserveMost; + + if (D->hasAttr()) + return CC_PreserveAll; + return CC_C; } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 0cae52a299..725b08a422 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -3799,7 +3799,14 @@ static void handleCallConvAttr(Sema &S, Decl *D, const AttributeList &Attr) { IntelOclBiccAttr(Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); return; - + case AttributeList::AT_PreserveMost: + D->addAttr(::new (S.Context) PreserveMostAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); + return; + case AttributeList::AT_PreserveAll: + D->addAttr(::new (S.Context) PreserveAllAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); + return; default: llvm_unreachable("unexpected attribute kind"); } @@ -3857,6 +3864,8 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, return true; } case AttributeList::AT_IntelOclBicc: CC = CC_IntelOclBicc; break; + case AttributeList::AT_PreserveMost: CC = CC_PreserveMost; break; + case AttributeList::AT_PreserveAll: CC = CC_PreserveAll; break; default: llvm_unreachable("unexpected attribute kind"); } @@ -5655,6 +5664,8 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_SysVABI: case AttributeList::AT_Pcs: case AttributeList::AT_IntelOclBicc: + case AttributeList::AT_PreserveMost: + case AttributeList::AT_PreserveAll: handleCallConvAttr(S, D, Attr); break; case AttributeList::AT_OpenCLKernel: diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 73c5bcc006..924cd07e02 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -112,7 +112,9 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, case AttributeList::AT_MSABI: \ case AttributeList::AT_SysVABI: \ case AttributeList::AT_Pcs: \ - case AttributeList::AT_IntelOclBicc + case AttributeList::AT_IntelOclBicc: \ + case AttributeList::AT_PreserveMost: \ + case AttributeList::AT_PreserveAll // Function type attributes. #define FUNCTION_TYPE_ATTRS_CASELIST \ @@ -4639,6 +4641,10 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) { return AttributeList::AT_MSABI; case AttributedType::attr_sysv_abi: return AttributeList::AT_SysVABI; + case AttributedType::attr_preserve_most: + return AttributeList::AT_PreserveMost; + case AttributedType::attr_preserve_all: + return AttributeList::AT_PreserveAll; case AttributedType::attr_ptr32: return AttributeList::AT_Ptr32; case AttributedType::attr_ptr64: @@ -5974,6 +5980,10 @@ static AttributedType::Kind getCCTypeAttrKind(AttributeList &Attr) { return AttributedType::attr_ms_abi; case AttributeList::AT_SysVABI: return AttributedType::attr_sysv_abi; + case AttributeList::AT_PreserveMost: + return AttributedType::attr_preserve_most; + case AttributeList::AT_PreserveAll: + return AttributedType::attr_preserve_all; } llvm_unreachable("unexpected attribute kind!"); } diff --git a/test/CodeGen/preserve-call-conv.c b/test/CodeGen/preserve-call-conv.c new file mode 100644 index 0000000000..6e91a8489b --- /dev/null +++ b/test/CodeGen/preserve-call-conv.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm < %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-unknown-unknown -emit-llvm < %s | FileCheck %s + +// Check that the preserve_most calling convention attribute at the source level +// is lowered to the corresponding calling convention attrribute at the LLVM IR +// level. +void foo() __attribute__((preserve_most)) { + // CHECK-LABEL: define preserve_mostcc void @foo() +} + +// Check that the preserve_most calling convention attribute at the source level +// is lowered to the corresponding calling convention attrribute at the LLVM IR +// level. +void boo() __attribute__((preserve_all)) { + // CHECK-LABEL: define preserve_allcc void @boo() +} + diff --git a/test/Sema/preserve-call-conv.c b/test/Sema/preserve-call-conv.c new file mode 100644 index 0000000000..f258f45ac5 --- /dev/null +++ b/test/Sema/preserve-call-conv.c @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 %s -fsyntax-only -triple x86_64-unknown-unknown -verify +// RUN: %clang_cc1 %s -fsyntax-only -triple arm64-unknown-unknown -verify +typedef void typedef_fun_t(int); + +void __attribute__((preserve_most)) foo(void *ptr) { +} + +void __attribute__((preserve_most(1))) foo1(void *ptr) { // expected-error {{'preserve_most' attribute takes no arguments}} +} + +void (__attribute__((preserve_most)) *pfoo1)(void *) = foo; + +void (__attribute__((cdecl)) *pfoo2)(void *) = foo; // expected-warning {{incompatible pointer types initializing 'void (*)(void *) __attribute__((cdecl))' with an expression of type 'void (void *) __attribute__((preserve_most))'}} +void (*pfoo3)(void *) = foo; // expected-warning {{incompatible pointer types initializing 'void (*)(void *)' with an expression of type 'void (void *) __attribute__((preserve_most))'}} + +typedef_fun_t typedef_fun_foo; // expected-note {{previous declaration is here}} +void __attribute__((preserve_most)) typedef_fun_foo(int x) { } // expected-error {{function declared 'preserve_most' here was previously declared without calling convention}} + +struct type_test_foo {} __attribute__((preserve_most)); // expected-warning {{'preserve_most' attribute only applies to functions and methods}} + +void __attribute__((preserve_all)) boo(void *ptr) { +} + +void __attribute__((preserve_all(1))) boo1(void *ptr) { // expected-error {{'preserve_all' attribute takes no arguments}} +} + +void (__attribute__((preserve_all)) *pboo1)(void *) = boo; + +void (__attribute__((cdecl)) *pboo2)(void *) = boo; // expected-warning {{incompatible pointer types initializing 'void (*)(void *) __attribute__((cdecl))' with an expression of type 'void (void *) __attribute__((preserve_all))'}} +void (*pboo3)(void *) = boo; // expected-warning {{incompatible pointer types initializing 'void (*)(void *)' with an expression of type 'void (void *) __attribute__((preserve_all))'}} + +typedef_fun_t typedef_fun_boo; // expected-note {{previous declaration is here}} +void __attribute__((preserve_all)) typedef_fun_boo(int x) { } // expected-error {{function declared 'preserve_all' here was previously declared without calling convention}} + +struct type_test_boo {} __attribute__((preserve_all)); // expected-warning {{'preserve_all' attribute only applies to functions and methods}} diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp index fce749c1e1..5cde236055 100644 --- a/tools/libclang/CXType.cpp +++ b/tools/libclang/CXType.cpp @@ -534,6 +534,8 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) { TCALLINGCONV(AAPCS_VFP); TCALLINGCONV(IntelOclBicc); TCALLINGCONV(Swift); + TCALLINGCONV(PreserveMost); + TCALLINGCONV(PreserveAll); case CC_SpirFunction: return CXCallingConv_Unexposed; case CC_SpirKernel: return CXCallingConv_Unexposed; break; -- 2.40.0