diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index b667d406dfa5e..6af41424ba89a 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -2986,6 +2986,7 @@ enum CXCallingConv { CXCallingConv_SwiftAsync = 17, CXCallingConv_AArch64SVEPCS = 18, CXCallingConv_M68kRTD = 19, + CXCallingConv_PreserveNone = 20, CXCallingConv_Invalid = 100, CXCallingConv_Unexposed = 200 diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 58838b01b4fd7..b2d5309e142c1 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2959,6 +2959,12 @@ def M68kRTD: DeclOrTypeAttr { let Documentation = [M68kRTDDocs]; } +def PreserveNone : DeclOrTypeAttr, TargetSpecificAttr { + let Spellings = [Clang<"preserve_none">]; + let Subjects = SubjectList<[FunctionLike]>; + let Documentation = [PreserveNoneDocs]; +} + def Target : InheritableAttr { let Spellings = [GCC<"target">]; let Args = [StringArgument<"featuresStr">]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index e02a1201e2ad7..041786f37fb8a 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -5506,6 +5506,23 @@ experimental at this time. }]; } +def PreserveNoneDocs : Documentation { + let Category = DocCatCallingConvs; + let Content = [{ +On X86-64 target, this attribute changes the calling convention of a function. +The ``preserve_none`` calling convention tries to preserve as few general +registers as possible. So all general registers are caller saved registers. It +also uses more general registers to pass arguments. This attribute doesn't +impact floating-point registers (XMMs/YMMs). Floating-point registers still +follow the c calling convention. + +- Only RSP and RBP are preserved by callee. + +- Register RDI, RSI, RDX, RCX, R8, R9, R11, R12, R13, R14, R15 and RAX now can + be used to pass function arguments. + }]; +} + def DeprecatedDocs : Documentation { let Category = DocCatDecl; let Content = [{ diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h index cf849b9b0f008..8586405825cfe 100644 --- a/clang/include/clang/Basic/Specifiers.h +++ b/clang/include/clang/Basic/Specifiers.h @@ -295,6 +295,7 @@ namespace clang { CC_AArch64SVEPCS, // __attribute__((aarch64_sve_pcs)) CC_AMDGPUKernelCall, // __attribute__((amdgpu_kernel)) CC_M68kRTD, // __attribute__((m68k_rtd)) + CC_PreserveNone, // __attribute__((preserve_none)) }; /// Checks whether the given calling convention supports variadic diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index c0add78bf90ff..1b6141580a81d 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -3443,6 +3443,7 @@ StringRef CXXNameMangler::getCallingConvQualifierName(CallingConv CC) { case CC_PreserveMost: case CC_PreserveAll: case CC_M68kRTD: + case CC_PreserveNone: // FIXME: we should be mangling all of the above. return ""; diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index c68254a459ccc..78dcd3f4007a5 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3438,6 +3438,7 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) { case CC_PreserveMost: return "preserve_most"; case CC_PreserveAll: return "preserve_all"; case CC_M68kRTD: return "m68k_rtd"; + case CC_PreserveNone: return "preserve_none"; } llvm_unreachable("Invalid calling convention."); @@ -3990,6 +3991,7 @@ bool AttributedType::isCallingConv() const { case attr::PreserveMost: case attr::PreserveAll: case attr::M68kRTD: + case attr::PreserveNone: return true; } llvm_unreachable("invalid attr kind"); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index c2ebe2c500174..7dcc4348f8e03 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1067,6 +1067,9 @@ void TypePrinter::printFunctionAfter(const FunctionType::ExtInfo &Info, case CC_M68kRTD: OS << " __attribute__((m68k_rtd))"; break; + case CC_PreserveNone: + OS << " __attribute__((preserve_none))"; + break; } } @@ -1911,6 +1914,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case attr::M68kRTD: OS << "m68k_rtd"; break; + case attr::PreserveNone: + OS << "preserve_none"; + break; case attr::NoDeref: OS << "noderef"; break; diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h index 0026d8781c8fa..d2232c7d5275a 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -777,6 +777,7 @@ class LLVM_LIBRARY_VISIBILITY X86_64TargetInfo : public X86TargetInfo { case CC_Win64: case CC_PreserveMost: case CC_PreserveAll: + case CC_PreserveNone: case CC_X86RegCall: case CC_OpenCLKernel: return CCCR_OK; @@ -854,6 +855,7 @@ class LLVM_LIBRARY_VISIBILITY WindowsX86_64TargetInfo case CC_IntelOclBicc: case CC_PreserveMost: case CC_PreserveAll: + case CC_PreserveNone: case CC_X86_64SysV: case CC_Swift: case CC_SwiftAsync: diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index bb61cf08bbfb4..cd26a3df78602 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -73,6 +73,7 @@ unsigned CodeGenTypes::ClangCallConvToLLVMCallConv(CallingConv CC) { case CC_Swift: return llvm::CallingConv::Swift; case CC_SwiftAsync: return llvm::CallingConv::SwiftTail; case CC_M68kRTD: return llvm::CallingConv::M68k_RTD; + case CC_PreserveNone: return llvm::CallingConv::PreserveNone; } } @@ -256,6 +257,9 @@ static CallingConv getCallingConventionForDecl(const ObjCMethodDecl *D, if (D->hasAttr()) return CC_M68kRTD; + if (D->hasAttr()) + return CC_PreserveNone; + return CC_C; } diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 81af8317300d9..c2c01439f2dc9 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1450,6 +1450,8 @@ static unsigned getDwarfCC(CallingConv CC) { return llvm::dwarf::DW_CC_LLVM_X86RegCall; case CC_M68kRTD: return llvm::dwarf::DW_CC_LLVM_M68kRTD; + case CC_PreserveNone: + return llvm::dwarf::DW_CC_LLVM_PreserveNone; } return 0; } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 069571fcf7864..d785714c4d811 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -5235,6 +5235,9 @@ static void handleCallConvAttr(Sema &S, Decl *D, const ParsedAttr &AL) { case ParsedAttr::AT_M68kRTD: D->addAttr(::new (S.Context) M68kRTDAttr(S.Context, AL)); return; + case ParsedAttr::AT_PreserveNone: + D->addAttr(::new (S.Context) PreserveNoneAttr(S.Context, AL)); + return; default: llvm_unreachable("unexpected attribute kind"); } @@ -5441,6 +5444,9 @@ bool Sema::CheckCallingConvAttr(const ParsedAttr &Attrs, CallingConv &CC, case ParsedAttr::AT_M68kRTD: CC = CC_M68kRTD; break; + case ParsedAttr::AT_PreserveNone: + CC = CC_PreserveNone; + break; default: llvm_unreachable("unexpected attribute kind"); } @@ -9559,6 +9565,7 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_AArch64SVEPcs: case ParsedAttr::AT_AMDGPUKernelCall: case ParsedAttr::AT_M68kRTD: + case ParsedAttr::AT_PreserveNone: handleCallConvAttr(S, D, AL); break; case ParsedAttr::AT_Suppress: diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index aee2c9c4a7ded..1e43e36016a66 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -137,7 +137,8 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr, case ParsedAttr::AT_IntelOclBicc: \ case ParsedAttr::AT_PreserveMost: \ case ParsedAttr::AT_PreserveAll: \ - case ParsedAttr::AT_M68kRTD + case ParsedAttr::AT_M68kRTD: \ + case ParsedAttr::AT_PreserveNone // Function type attributes. #define FUNCTION_TYPE_ATTRS_CASELIST \ @@ -7919,6 +7920,8 @@ static Attr *getCCTypeAttr(ASTContext &Ctx, ParsedAttr &Attr) { return createSimpleAttr(Ctx, Attr); case ParsedAttr::AT_M68kRTD: return createSimpleAttr(Ctx, Attr); + case ParsedAttr::AT_PreserveNone: + return createSimpleAttr(Ctx, Attr); } llvm_unreachable("unexpected attribute kind!"); } diff --git a/clang/test/CodeGen/debug-info-cc.c b/clang/test/CodeGen/debug-info-cc.c index a64515e31d1ae..2664bcd4cb6b2 100644 --- a/clang/test/CodeGen/debug-info-cc.c +++ b/clang/test/CodeGen/debug-info-cc.c @@ -22,6 +22,7 @@ // CC_SwiftAsync, // __attribute__((swiftasynccall)) // CC_PreserveMost, // __attribute__((preserve_most)) // CC_PreserveAll, // __attribute__((preserve_all)) +// CC_PreserveNone, // __attribute__((preserve_none)) // }; #ifdef __x86_64__ @@ -51,6 +52,12 @@ __attribute__((preserve_all)) int add_preserve_all(int a, int b) { return a+b; } +// LINUX: !DISubprogram({{.*}}"add_preserve_none", {{.*}}type: ![[FTY:[0-9]+]] +// LINUX: ![[FTY]] = !DISubroutineType({{.*}}cc: DW_CC_LLVM_PreserveNone, +__attribute__((preserve_none)) int add_preserve_none(int a, int b) { + return a+b; +} + // LINUX: !DISubprogram({{.*}}"add_swiftcall", {{.*}}type: ![[FTY:[0-9]+]] // LINUX: ![[FTY]] = !DISubroutineType({{.*}}cc: DW_CC_LLVM_Swift, __attribute__((swiftcall)) int add_swiftcall(int a, int b) { diff --git a/clang/test/CodeGen/preserve-call-conv.c b/clang/test/CodeGen/preserve-call-conv.c index e700c5cf12f53..74bf695e6f331 100644 --- a/clang/test/CodeGen/preserve-call-conv.c +++ b/clang/test/CodeGen/preserve-call-conv.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm < %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm < %s | FileCheck %s --check-prefixes=CHECK,X86-LINUX // RUN: %clang_cc1 -triple arm64-unknown-unknown -emit-llvm < %s | FileCheck %s // RUN: %clang_cc1 -triple x86_64-unknown-windows-msvc -emit-llvm %s -o - | FileCheck %s @@ -19,3 +19,9 @@ void boo(void) __attribute__((preserve_all)) { // CHECK-LABEL: define {{(dso_local )?}}preserve_allcc void @boo() } +// Check that the preserve_none calling convention attribute at the source level +// is lowered to the corresponding calling convention attrribute at the LLVM IR +// level. +void bar(void) __attribute__((preserve_none)) { + // X86-LINUX-LABEL: define {{(dso_local )?}}preserve_nonecc void @bar() +} diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index e476c15b35ded..1528388e3298e 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -163,6 +163,7 @@ // CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: PatchableFunctionEntry (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: Pointer (SubjectMatchRule_record_not_is_union) +// CHECK-NEXT: PreserveNone (SubjectMatchRule_hasType_functionType) // CHECK-NEXT: RandomizeLayout (SubjectMatchRule_record) // CHECK-NEXT: ReadOnlyPlacement (SubjectMatchRule_record) // CHECK-NEXT: ReleaseHandle (SubjectMatchRule_variable_is_parameter) diff --git a/clang/test/Sema/no_callconv.cpp b/clang/test/Sema/no_callconv.cpp index a8b3c91e0e3f6..c00930919306a 100644 --- a/clang/test/Sema/no_callconv.cpp +++ b/clang/test/Sema/no_callconv.cpp @@ -15,6 +15,7 @@ void __attribute__((swiftasynccall)) funcKK() {} // expected-error {{'swiftasync void __attribute__((pascal)) funcG() {} // expected-error {{'pascal' calling convention is not supported for this target}} void __attribute__((preserve_most)) funcL() {} // expected-error {{'preserve_most' calling convention is not supported for this target}} void __attribute__((preserve_all)) funcM() {} // expected-error {{'preserve_all' calling convention is not supported for this target}} +void __attribute__((preserve_none)) funcN() {} // expected-error {{'preserve_none' calling convention is not supported for this target}} void __attribute__((stdcall)) funcD() {} // expected-error {{'stdcall' calling convention is not supported for this target}} void __attribute__((fastcall)) funcE() {} // expected-error {{'fastcall' calling convention is not supported for this target}} void __attribute__((thiscall)) funcF() {} // expected-error {{'thiscall' calling convention is not supported for this target}} @@ -30,6 +31,7 @@ void __attribute__((swiftcall)) funcK() {} void __attribute__((swiftasynccall)) funcKK() {} void __attribute__((preserve_most)) funcL() {} void __attribute__((preserve_all)) funcM() {} +void __attribute__((preserve_none)) funcN() {} // Same function with different calling conventions. Error with a note pointing to the last decl. void __attribute__((preserve_all)) funcR(); // expected-note {{previous declaration is here}} diff --git a/clang/test/Sema/preserve-none-call-conv.c b/clang/test/Sema/preserve-none-call-conv.c new file mode 100644 index 0000000000000..4508095863de5 --- /dev/null +++ b/clang/test/Sema/preserve-none-call-conv.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 %s -fsyntax-only -triple x86_64-unknown-unknown -verify + +typedef void typedef_fun_t(int); + +void __attribute__((preserve_none)) boo(void *ptr) { +} + +void __attribute__((preserve_none(1))) boo1(void *ptr) { // expected-error {{'preserve_none' attribute takes no arguments}} +} + +void (__attribute__((preserve_none)) *pboo1)(void *) = boo; + +void (__attribute__((cdecl)) *pboo2)(void *) = boo; // expected-error {{incompatible function pointer types initializing 'void (*)(void *) __attribute__((cdecl))' with an expression of type 'void (void *) __attribute__((preserve_none))'}} +void (*pboo3)(void *) = boo; // expected-error {{incompatible function pointer types initializing 'void (*)(void *)' with an expression of type 'void (void *) __attribute__((preserve_none))'}} + +typedef_fun_t typedef_fun_boo; // expected-note {{previous declaration is here}} +void __attribute__((preserve_none)) typedef_fun_boo(int x) { } // expected-error {{function declared 'preserve_none' here was previously declared without calling convention}} + +struct type_test_boo {} __attribute__((preserve_none)); // expected-warning {{'preserve_none' attribute only applies to functions and function pointers}} diff --git a/clang/tools/libclang/CXType.cpp b/clang/tools/libclang/CXType.cpp index 3d620d3bfb260..292d524f00abd 100644 --- a/clang/tools/libclang/CXType.cpp +++ b/clang/tools/libclang/CXType.cpp @@ -679,6 +679,7 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) { TCALLINGCONV(PreserveMost); TCALLINGCONV(PreserveAll); TCALLINGCONV(M68kRTD); + TCALLINGCONV(PreserveNone); case CC_SpirFunction: return CXCallingConv_Unexposed; case CC_AMDGPUKernelCall: return CXCallingConv_Unexposed; case CC_OpenCLKernel: return CXCallingConv_Unexposed; diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 96bdba5b33993..fd2e3aacd0169 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -421,6 +421,13 @@ added in the future: This calling convention, like the `PreserveMost` calling convention, will be used by a future version of the ObjectiveC runtime and should be considered experimental at this time. +"``preserve_nonecc``" - The `PreserveNone` calling convention + This calling convention doesn't preserve any general registers. So all + general registers are caller saved registers. It also uses all general + registers to pass arguments. This attribute doesn't impact non-general + purpose registers (e.g. floating point registers, on X86 XMMs/YMMs). + Non-general purpose registers still follow the standard c calling + convention. Currently it is for x86_64 only. "``cxx_fast_tlscc``" - The `CXX_FAST_TLS` calling convention for access functions Clang generates an access function to access C++-style TLS. The access function generally has an entry block, an exit block and an initialization diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h index 147cf56c821aa..3c34706ee03e8 100644 --- a/llvm/include/llvm/AsmParser/LLToken.h +++ b/llvm/include/llvm/AsmParser/LLToken.h @@ -160,6 +160,7 @@ enum Kind { kw_swifttailcc, kw_preserve_mostcc, kw_preserve_allcc, + kw_preserve_nonecc, kw_ghccc, kw_x86_intrcc, kw_hhvmcc, diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def index d1abb1f361d3e..3a08eeaa791aa 100644 --- a/llvm/include/llvm/BinaryFormat/Dwarf.def +++ b/llvm/include/llvm/BinaryFormat/Dwarf.def @@ -1038,6 +1038,7 @@ HANDLE_DW_CC(0xc9, LLVM_PreserveMost) HANDLE_DW_CC(0xca, LLVM_PreserveAll) HANDLE_DW_CC(0xcb, LLVM_X86RegCall) HANDLE_DW_CC(0xcc, LLVM_M68kRTD) +HANDLE_DW_CC(0xcd, LLVM_PreserveNone) // From GCC source code (include/dwarf2.h): This DW_CC_ value is not currently // generated by any toolchain. It is used internally to GDB to indicate OpenCL // C functions that have been compiled with the IBM XL C for OpenCL compiler and diff --git a/llvm/include/llvm/IR/CallingConv.h b/llvm/include/llvm/IR/CallingConv.h index bca31b2572eb4..ef8aaf52f4e6a 100644 --- a/llvm/include/llvm/IR/CallingConv.h +++ b/llvm/include/llvm/IR/CallingConv.h @@ -86,6 +86,9 @@ namespace CallingConv { /// their stack. SwiftTail = 20, + /// Used for runtime calls that preserves none general registers. + PreserveNone = 21, + /// This is the start of the target-specific calling conventions, e.g. /// fastcall and thiscall on X86. FirstTargetCC = 64, diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index c8da3efbb68af..5d8a50eee1306 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -617,6 +617,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(anyregcc); KEYWORD(preserve_mostcc); KEYWORD(preserve_allcc); + KEYWORD(preserve_nonecc); KEYWORD(ghccc); KEYWORD(x86_intrcc); KEYWORD(hhvmcc); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index d5d4428bfbf9a..a91e2f690999e 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -2109,6 +2109,7 @@ void LLParser::parseOptionalDLLStorageClass(unsigned &Res) { /// ::= 'anyregcc' /// ::= 'preserve_mostcc' /// ::= 'preserve_allcc' +/// ::= 'preserve_nonecc' /// ::= 'ghccc' /// ::= 'swiftcc' /// ::= 'swifttailcc' @@ -2169,6 +2170,7 @@ bool LLParser::parseOptionalCallingConv(unsigned &CC) { case lltok::kw_anyregcc: CC = CallingConv::AnyReg; break; case lltok::kw_preserve_mostcc:CC = CallingConv::PreserveMost; break; case lltok::kw_preserve_allcc: CC = CallingConv::PreserveAll; break; + case lltok::kw_preserve_nonecc:CC = CallingConv::PreserveNone; break; case lltok::kw_ghccc: CC = CallingConv::GHC; break; case lltok::kw_swiftcc: CC = CallingConv::Swift; break; case lltok::kw_swifttailcc: CC = CallingConv::SwiftTail; break; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp index 76817fda441db..ef8ded8ebb66c 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp @@ -616,6 +616,9 @@ void DWARFTypePrinter::appendSubroutineNameAfter( case CallingConvention::DW_CC_LLVM_PreserveAll: OS << " __attribute__((preserve_all))"; break; + case CallingConvention::DW_CC_LLVM_PreserveNone: + OS << " __attribute__((preserve_none))"; + break; case CallingConvention::DW_CC_LLVM_X86RegCall: OS << " __attribute__((regcall))"; break; diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index 9a3f38c9f9708..35a98767a8be3 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -304,6 +304,7 @@ static void PrintCallingConv(unsigned cc, raw_ostream &Out) { case CallingConv::AnyReg: Out << "anyregcc"; break; case CallingConv::PreserveMost: Out << "preserve_mostcc"; break; case CallingConv::PreserveAll: Out << "preserve_allcc"; break; + case CallingConv::PreserveNone: Out << "preserve_nonecc"; break; case CallingConv::CXX_FAST_TLS: Out << "cxx_fast_tlscc"; break; case CallingConv::GHC: Out << "ghccc"; break; case CallingConv::Tail: Out << "tailcc"; break; diff --git a/llvm/lib/Target/X86/X86CallingConv.td b/llvm/lib/Target/X86/X86CallingConv.td index 16014d6a2f602..12178bcaf042d 100644 --- a/llvm/lib/Target/X86/X86CallingConv.td +++ b/llvm/lib/Target/X86/X86CallingConv.td @@ -1056,6 +1056,23 @@ def CC_Intel_OCL_BI : CallingConv<[ CCDelegateTo ]>; +def CC_X86_64_Preserve_None : CallingConv<[ + // We don't preserve general registers, so all of them can be used to pass + // arguments except + // - RBP frame pointer + // - R10 'nest' parameter + // - RBX base pointer + // - R16 - R31 these are not available everywhere + CCIfType<[i32], CCAssignToReg<[EDI, ESI, EDX, ECX, R8D, R9D, + R11D, R12D, R13D, R14D, R15D, EAX]>>, + + CCIfType<[i64], CCAssignToReg<[RDI, RSI, RDX, RCX, R8, R9, + R11, R12, R13, R14, R15, RAX]>>, + + // Otherwise it's the same as the regular C calling convention. + CCDelegateTo +]>; + //===----------------------------------------------------------------------===// // X86 Root Argument Calling Conventions //===----------------------------------------------------------------------===// @@ -1095,6 +1112,7 @@ def CC_X86_64 : CallingConv<[ CCIfCC<"CallingConv::X86_RegCall", CCIfSubtarget<"isTargetWin64()", CCDelegateTo>>, CCIfCC<"CallingConv::X86_RegCall", CCDelegateTo>, + CCIfCC<"CallingConv::PreserveNone", CCDelegateTo>, CCIfCC<"CallingConv::X86_INTR", CCCustom<"CC_X86_Intr">>, // Mingw64 and native Win64 use Win64 CC @@ -1184,6 +1202,7 @@ def CSR_64_AllRegs_AVX512 : CalleeSavedRegs<(sub (add CSR_64_MostRegs, RAX, (sequence "ZMM%u", 0, 31), (sequence "K%u", 0, 7)), (sequence "XMM%u", 0, 15))>; +def CSR_64_NoneRegs : CalleeSavedRegs<(add RBP)>; // Standard C + YMM6-15 def CSR_Win64_Intel_OCL_BI_AVX : CalleeSavedRegs<(add RBX, RBP, RDI, RSI, R12, diff --git a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp index 8c9bc759d6c0a..be8275c92e11a 100644 --- a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp +++ b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp @@ -1257,6 +1257,7 @@ static bool mayTailCallThisCC(CallingConv::ID CC) { case CallingConv::C: case CallingConv::Win64: case CallingConv::X86_64_SysV: + case CallingConv::PreserveNone: // Callee pop conventions: case CallingConv::X86_ThisCall: case CallingConv::X86_StdCall: @@ -1906,6 +1907,16 @@ SDValue X86TargetLowering::LowerFormalArguments( MRI.disableCalleeSavedRegister(Pair.first); } + if (CallingConv::PreserveNone == CallConv) + for (unsigned I = 0, E = Ins.size(); I != E; ++I) { + if (Ins[I].Flags.isSwiftSelf() || Ins[I].Flags.isSwiftAsync() || + Ins[I].Flags.isSwiftError()) { + errorUnsupported(DAG, dl, + "Swift attributes can't be used with preserve_none"); + break; + } + } + return Chain; } @@ -2557,6 +2568,16 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, InGlue = Chain.getValue(1); } + if (CallingConv::PreserveNone == CallConv) + for (unsigned I = 0, E = Outs.size(); I != E; ++I) { + if (Outs[I].Flags.isSwiftSelf() || Outs[I].Flags.isSwiftAsync() || + Outs[I].Flags.isSwiftError()) { + errorUnsupported(DAG, dl, + "Swift attributes can't be used with preserve_none"); + break; + } + } + // Handle result values, copying them out of physregs into vregs that we // return. return LowerCallResult(Chain, InGlue, CallConv, isVarArg, Ins, dl, DAG, diff --git a/llvm/lib/Target/X86/X86RegisterInfo.cpp b/llvm/lib/Target/X86/X86RegisterInfo.cpp index e76d0d7bf50e1..be0cf1596d0d9 100644 --- a/llvm/lib/Target/X86/X86RegisterInfo.cpp +++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp @@ -316,6 +316,8 @@ X86RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { if (HasAVX) return CSR_64_RT_AllRegs_AVX_SaveList; return CSR_64_RT_AllRegs_SaveList; + case CallingConv::PreserveNone: + return CSR_64_NoneRegs_SaveList; case CallingConv::CXX_FAST_TLS: if (Is64Bit) return MF->getInfo()->isSplitCSR() ? @@ -437,6 +439,8 @@ X86RegisterInfo::getCallPreservedMask(const MachineFunction &MF, if (HasAVX) return CSR_64_RT_AllRegs_AVX_RegMask; return CSR_64_RT_AllRegs_RegMask; + case CallingConv::PreserveNone: + return CSR_64_NoneRegs_RegMask; case CallingConv::CXX_FAST_TLS: if (Is64Bit) return CSR_64_TLS_Darwin_RegMask; diff --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll index 483024a250da0..ce6a6571ec144 100644 --- a/llvm/test/Bitcode/compatibility.ll +++ b/llvm/test/Bitcode/compatibility.ll @@ -394,6 +394,8 @@ declare preserve_mostcc void @f.preserve_mostcc() ; CHECK: declare preserve_mostcc void @f.preserve_mostcc() declare preserve_allcc void @f.preserve_allcc() ; CHECK: declare preserve_allcc void @f.preserve_allcc() +declare preserve_nonecc void @f.preserve_nonecc() +; CHECK: declare preserve_nonecc void @f.preserve_nonecc() declare swifttailcc void @f.swifttailcc() ; CHECK: declare swifttailcc void @f.swifttailcc() declare cc64 void @f.cc64() diff --git a/llvm/test/CodeGen/X86/dynamic-regmask-preserve-none.ll b/llvm/test/CodeGen/X86/dynamic-regmask-preserve-none.ll new file mode 100644 index 0000000000000..7dad3e1081c64 --- /dev/null +++ b/llvm/test/CodeGen/X86/dynamic-regmask-preserve-none.ll @@ -0,0 +1,88 @@ +; RUN: llc -mtriple=x86_64-apple-darwin -stop-after finalize-isel <%s | FileCheck %s + +; Check that the callee doesn't have calleeSavedRegisters. +define preserve_nonecc i64 @callee1(i64 %a0, i64 %b0, i64 %c0, i64 %d0, i64 %e0) nounwind { + %a1 = mul i64 %a0, %b0 + %a2 = mul i64 %a1, %c0 + %a3 = mul i64 %a2, %d0 + %a4 = mul i64 %a3, %e0 + ret i64 %a4 +} +; CHECK: name: callee1 +; CHECK-NOT: calleeSavedRegisters: +; CHECK: RET 0, $rax + +; Check that RegMask is csr_noregs. +define i64 @caller1(i64 %a0) nounwind { + %b1 = call preserve_nonecc i64 @callee1(i64 %a0, i64 %a0, i64 %a0, i64 %a0, i64 %a0) + %b2 = add i64 %b1, %a0 + ret i64 %b2 +} +; CHECK: name: caller1 +; CHECK: CALL64pcrel32 @callee1, csr_64_noneregs +; CHECK: RET 0, $rax + + +; Check that the callee doesn't have calleeSavedRegisters. +define preserve_nonecc {i64, i64} @callee2(i64 %a0, i64 %b0, i64 %c0, i64 %d0, i64 %e0) nounwind { + %a1 = mul i64 %a0, %b0 + %a2 = mul i64 %a1, %c0 + %a3 = mul i64 %a2, %d0 + %a4 = mul i64 %a3, %e0 + %b4 = insertvalue {i64, i64} undef, i64 %a3, 0 + %b5 = insertvalue {i64, i64} %b4, i64 %a4, 1 + ret {i64, i64} %b5 +} +; CHECK: name: callee2 +; CHECK-NOT: calleeSavedRegisters: +; CHECK: RET 0, $rax, $rdx + + +; Check that RegMask is csr_noregs. +define {i64, i64} @caller2(i64 %a0) nounwind { + %b1 = call preserve_nonecc {i64, i64} @callee2(i64 %a0, i64 %a0, i64 %a0, i64 %a0, i64 %a0) + ret {i64, i64} %b1 +} +; CHECK: name: caller2 +; CHECL: CALL64pcrel32 @callee2, csr_noregs +; CHECK: RET 0, $rax, $rdx + + +%struct.Large = type { i64, double, double } + +; Declare the callee with a sret parameter. +declare preserve_nonecc void @callee3(ptr noalias nocapture writeonly sret(%struct.Large) align 4 %a0, i64 %b0) nounwind; + +; Check that RegMask is csr_noregs. +define void @caller3(i64 %a0) nounwind { + %a1 = alloca %struct.Large, align 8 + call preserve_nonecc void @callee3(ptr nonnull sret(%struct.Large) align 8 %a1, i64 %a0) + ret void +} +; CHECK: name: caller3 +; CHECK: CALL64pcrel32 @callee3, csr_64_noneregs +; CHECK: RET 0 + + +; Check that the callee doesn't have calleeSavedRegisters. +define preserve_nonecc {i64, double} @callee4(i64 %a0, i64 %b0, i64 %c0, i64 %d0, i64 %e0) nounwind { + %a1 = mul i64 %a0, %b0 + %a2 = mul i64 %a1, %c0 + %a3 = mul i64 %a2, %d0 + %a4 = mul i64 %a3, %e0 + %b4 = insertvalue {i64, double} undef, i64 %a3, 0 + %b5 = insertvalue {i64, double} %b4, double 1.2, 1 + ret {i64, double} %b5 +} +; CHECK: name: callee4 +; CHECK-NOT: calleeSavedRegisters: +; CHECK: RET 0, $rax, $xmm0 + +; Check that RegMask is csr_noregs. +define {i64, double} @caller4(i64 %a0) nounwind { + %b1 = call preserve_nonecc {i64, double} @callee4(i64 %a0, i64 %a0, i64 %a0, i64 %a0, i64 %a0) + ret {i64, double} %b1 +} +; CHECK: name: caller4 +; CHECK: CALL64pcrel32 @callee4, csr_64_noneregs +; CHECK: RET 0, $rax, $xmm0 diff --git a/llvm/test/CodeGen/X86/ipra-reg-usage.ll b/llvm/test/CodeGen/X86/ipra-reg-usage.ll index 219b33b4297ae..d1b8be15a2d03 100644 --- a/llvm/test/CodeGen/X86/ipra-reg-usage.ll +++ b/llvm/test/CodeGen/X86/ipra-reg-usage.ll @@ -10,6 +10,13 @@ define preserve_allcc void @foo()#0 { } declare void @bar2() -@llvm.used = appending global [1 x ptr] [ptr @foo] +define preserve_nonecc void @foo2()#0 { +; CHECK: foo2 Clobbered Registers: $ah $al $ax $ch $cl $cs $cx $df $dh $di $dih $dil $dl $ds $dx $eax $ecx $edi $edx $eflags $eip $eiz $es $esi $esp $fpcw $fpsw $fs $fs_base $gs $gs_base $hax $hcx $hdi $hdx $hip $hsi $hsp $ip $mxcsr $rax $rcx $rdi $rdx $rflags $rip $riz $rsi $rsp $si $sih $sil $sp $sph $spl $ss $ssp $_eflags $cr0 $cr1 $cr2 $cr3 $cr4 $cr5 $cr6 $cr7 $cr8 $cr9 $cr10 $cr11 $cr12 $cr13 $cr14 $cr15 $dr0 $dr1 $dr2 $dr3 $dr4 $dr5 $dr6 $dr7 $dr8 $dr9 $dr10 $dr11 $dr12 $dr13 $dr14 $dr15 $fp0 $fp1 $fp2 $fp3 $fp4 $fp5 $fp6 $fp7 $mm0 $mm1 $mm2 $mm3 $mm4 $mm5 $mm6 $mm7 $r8 $r9 $r10 $r11 $st0 $st1 $st2 $st3 $st4 $st5 $st6 $st7 $xmm0 $xmm1 $xmm2 $xmm3 $xmm4 $xmm5 $xmm6 $xmm7 $xmm8 $xmm9 $xmm10 $xmm11 $xmm12 $xmm13 $xmm14 $xmm15 $r8b $r9b $r10b $r11b $r8bh $r9bh $r10bh $r11bh $r8d $r9d $r10d $r11d $r8w $r9w $r10w $r11w $r8wh $r9wh $r10wh $r11wh $ymm0 $ymm1 $ymm2 $ymm3 $ymm4 $ymm5 $ymm6 $ymm7 $ymm8 $ymm9 $ymm10 $ymm11 $ymm12 $ymm13 $ymm14 $ymm15 $k0 $k1 $k2 $k3 $k4 $k5 $k6 $k7 $xmm16 $xmm17 $xmm18 $xmm19 $xmm20 $xmm21 $xmm22 $xmm23 $xmm24 $xmm25 $xmm26 $xmm27 $xmm28 $xmm29 $xmm30 $xmm31 $ymm16 $ymm17 $ymm18 $ymm19 $ymm20 $ymm21 $ymm22 $ymm23 $ymm24 $ymm25 $ymm26 $ymm27 $ymm28 $ymm29 $ymm30 $ymm31 $zmm0 $zmm1 $zmm2 $zmm3 $zmm4 $zmm5 $zmm6 $zmm7 $zmm8 $zmm9 $zmm10 $zmm11 $zmm12 $zmm13 $zmm14 $zmm15 $zmm16 $zmm17 $zmm18 $zmm19 $zmm20 $zmm21 $zmm22 $zmm23 $zmm24 $zmm25 $zmm26 $zmm27 $zmm28 $zmm29 $zmm30 $zmm31 $k0_k1 $k2_k3 $k4_k5 $k6_k7 $tmmcfg $tmm0 $tmm1 $tmm2 $tmm3 $tmm4 $tmm5 $tmm6 $tmm7 $r16 $r17 $r18 $r19 $r20 $r21 $r22 $r23 $r24 $r25 $r26 $r27 $r28 $r29 $r30 $r31 $r16b $r17b $r18b $r19b $r20b $r21b $r22b $r23b $r24b $r25b $r26b $r27b $r28b $r29b $r30b $r31b $r16bh $r17bh $r18bh $r19bh $r20bh $r21bh $r22bh $r23bh $r24bh $r25bh $r26bh $r27bh $r28bh $r29bh $r30bh $r31bh $r16d $r17d $r18d $r19d $r20d $r21d $r22d $r23d $r24d $r25d $r26d $r27d $r28d $r29d $r30d $r31d $r16w $r17w $r18w $r19w $r20w $r21w $r22w $r23w $r24w $r25w $r26w $r27w $r28w $r29w $r30w $r31w $r16wh $r17wh $r18wh $r19wh $r20wh $r21wh $r22wh $r23wh $r24wh $r25wh $r26wh $r27wh $r28wh $r29wh $r30wh $r31wh + call void @bar1() + call void @bar2() + ret void +} + +@llvm.used = appending global [2 x ptr] [ptr @foo, ptr @foo2] attributes #0 = {nounwind} diff --git a/llvm/test/CodeGen/X86/ipra-transform.ll b/llvm/test/CodeGen/X86/ipra-transform.ll index 362af88123466..fc94865c4d29a 100644 --- a/llvm/test/CodeGen/X86/ipra-transform.ll +++ b/llvm/test/CodeGen/X86/ipra-transform.ll @@ -29,4 +29,23 @@ define preserve_allcc void @foo()#0 { define void @bar2() { ret void } + +define preserve_nonecc void @foo2()#0 { +; Due to preserve_nonecc foo2() will save above registers no matter IPRA is +; present or not. +; NOIPRA-LABEL: foo2: +; NOIPRA-NOT: pushq %r10 +; NOIPRA-NOT: pushq %r9 +; NOIPRA-NOT: pushq %r8 +; NOIPRA: callq bar1 +; CHECK: foo2: +; CHECK-NOT: pushq %r10 +; CHECK-NOT: pushq %r9 +; CHECK-NOT: pushq %r8 +; CHECK: callq bar1 + call void @bar1() + call void @bar2() + ret void +} + attributes #0 = {nounwind} diff --git a/llvm/test/CodeGen/X86/preserve_none_swift.ll b/llvm/test/CodeGen/X86/preserve_none_swift.ll new file mode 100644 index 0000000000000..9a1c15190c6a2 --- /dev/null +++ b/llvm/test/CodeGen/X86/preserve_none_swift.ll @@ -0,0 +1,16 @@ +; RUN: not llc -mtriple=x86_64 %s -o - 2>&1 | FileCheck %s + +; Swift attributes should not be used with preserve_none. + +declare preserve_nonecc void @foo(ptr swiftself) + +; CHECK: error: :0:0: in function bar void (ptr): Swift attributes can't be used with preserve_none +define preserve_nonecc void @bar(ptr swifterror) { + ret void +} + +; CHECK: error: :0:0: in function qux void (ptr): Swift attributes can't be used with preserve_none +define void @qux(ptr %addr) { + call preserve_nonecc void @foo(ptr swiftself %addr) + ret void +} diff --git a/llvm/test/CodeGen/X86/preserve_nonecc64-ret-double.ll b/llvm/test/CodeGen/X86/preserve_nonecc64-ret-double.ll new file mode 100644 index 0000000000000..22f0931a1446e --- /dev/null +++ b/llvm/test/CodeGen/X86/preserve_nonecc64-ret-double.ll @@ -0,0 +1,125 @@ +; RUN: llc < %s -mtriple=x86_64-apple-darwin -mcpu=corei7 | FileCheck --check-prefixes=ALL %s +; RUN: llc < %s -mtriple=x86_64-apple-darwin -mcpu=corei7-avx | FileCheck --check-prefixes=ALL,AVX %s + +; Don't need to preserve registers before using them. +define preserve_nonecc double @preserve_nonecc1() nounwind { +entry: +;ALL-LABEL: preserve_nonecc1 +;ALL-NOT: movq %rax +;ALL-NOT: movq %rbx +;ALL-NOT: movq %rcx +;ALL-NOT: movq %rdx +;ALL-NOT: movq %rsi +;ALL-NOT: movq %rdi +;ALL-NOT: movq %r8 +;ALL-NOT: movq %r9 +;ALL-NOT: movq %r10 +;ALL-NOT: movq %r11 +;ALL-NOT: movq %r12 +;ALL-NOT: movq %r13 +;ALL-NOT: movq %r14 +;ALL-NOT: movq %r15 +;ALL-NOT: movaps %xmm1 +;ALL-NOT: movaps %xmm0 +;AVX-NOT: vmovups %ymm1 +;AVX-NOT: vmovups %ymm0 +;ALL-NOT: movq {{.*}}, %rax +;ALL-NOT: movq {{.*}}, %rbx +;ALL-NOT: movq {{.*}}, %rcx +;ALL-NOT: movq {{.*}}, %rdx +;ALL-NOT: movq {{.*}}, %rsi +;ALL-NOT: movq {{.*}}, %rdi +;ALL-NOT: movq {{.*}}, %r8 +;ALL-NOT: movq {{.*}}, %r9 +;ALL-NOT: movq {{.*}}, %r10 +;ALL-NOT: movq {{.*}}, %r11 +;ALL-NOT: movq {{.*}}, %r12 +;ALL-NOT: movq {{.*}}, %r13 +;ALL-NOT: movq {{.*}}, %r14 +;ALL-NOT: movq {{.*}}, %r15 +;ALL-NOT: movaps {{.*}} %xmm0 +;ALL-NOT: movaps {{.*}} %xmm1 +;AVX-NOT: vmovups {{.*}} %ymm0 +;AVX-NOT: vmovups {{.*}} %ymm1 + call void asm sideeffect "", "~{rax},~{rbx},~{rcx},~{rdx},~{rsi},~{rdi},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{rbp},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{xmm8},~{xmm9},~{xmm10},~{xmm11},~{xmm12},~{xmm13},~{xmm14},~{xmm15}"() + ret double 0. +} + +; Save/restore live registers across preserve_none function call. +declare preserve_nonecc double @bar_double(i64, i64) +define void @preserve_nonecc2() nounwind { +entry: +;ALL-LABEL: preserve_nonecc2 +;ALL: movq %rax +;ALL: movq %rcx +;ALL: movq %rdx +;ALL: movq %r8 +;ALL: movq %r9 +;ALL: movq %r10 +;ALL: movq %r11 +;ALL: movaps %xmm0 +;ALL: movaps %xmm1 +;ALL: movaps %xmm2 +;ALL: movaps %xmm3 +;ALL: movaps %xmm4 +;ALL: movaps %xmm5 +;ALL: movaps %xmm6 +;ALL: movaps %xmm7 +;ALL: movaps %xmm8 +;ALL: movaps %xmm9 +;ALL: movaps %xmm10 +;ALL: movaps %xmm11 +;ALL: movaps %xmm12 +;ALL: movaps %xmm13 +;ALL: movaps %xmm14 +;ALL: movaps %xmm15 +;ALL: movq {{.*}}, %rax +;ALL: movq {{.*}}, %rcx +;ALL: movq {{.*}}, %rdx +;ALL: movq {{.*}}, %r8 +;ALL: movq {{.*}}, %r9 +;ALL: movq {{.*}}, %r10 +;ALL: movq {{.*}}, %r11 +;ALL: movaps {{.*}} %xmm0 +;ALL: movaps {{.*}} %xmm1 +;ALL: movaps {{.*}} %xmm2 +;ALL: movaps {{.*}} %xmm3 +;ALL: movaps {{.*}} %xmm4 +;ALL: movaps {{.*}} %xmm5 +;ALL: movaps {{.*}} %xmm6 +;ALL: movaps {{.*}} %xmm7 +;ALL: movaps {{.*}} %xmm8 +;ALL: movaps {{.*}} %xmm9 +;ALL: movaps {{.*}} %xmm10 +;ALL: movaps {{.*}} %xmm11 +;ALL: movaps {{.*}} %xmm12 +;ALL: movaps {{.*}} %xmm13 +;ALL: movaps {{.*}} %xmm14 +;ALL: movaps {{.*}} %xmm15 + %a0 = call i64 asm sideeffect "", "={rax}"() nounwind + %a1 = call i64 asm sideeffect "", "={rcx}"() nounwind + %a2 = call i64 asm sideeffect "", "={rdx}"() nounwind + %a3 = call i64 asm sideeffect "", "={r8}"() nounwind + %a4 = call i64 asm sideeffect "", "={r9}"() nounwind + %a5 = call i64 asm sideeffect "", "={r10}"() nounwind + %a6 = call i64 asm sideeffect "", "={r11}"() nounwind + %a10 = call <2 x double> asm sideeffect "", "={xmm0}"() nounwind + %a11 = call <2 x double> asm sideeffect "", "={xmm1}"() nounwind + %a12 = call <2 x double> asm sideeffect "", "={xmm2}"() nounwind + %a13 = call <2 x double> asm sideeffect "", "={xmm3}"() nounwind + %a14 = call <2 x double> asm sideeffect "", "={xmm4}"() nounwind + %a15 = call <2 x double> asm sideeffect "", "={xmm5}"() nounwind + %a16 = call <2 x double> asm sideeffect "", "={xmm6}"() nounwind + %a17 = call <2 x double> asm sideeffect "", "={xmm7}"() nounwind + %a18 = call <2 x double> asm sideeffect "", "={xmm8}"() nounwind + %a19 = call <2 x double> asm sideeffect "", "={xmm9}"() nounwind + %a20 = call <2 x double> asm sideeffect "", "={xmm10}"() nounwind + %a21 = call <2 x double> asm sideeffect "", "={xmm11}"() nounwind + %a22 = call <2 x double> asm sideeffect "", "={xmm12}"() nounwind + %a23 = call <2 x double> asm sideeffect "", "={xmm13}"() nounwind + %a24 = call <2 x double> asm sideeffect "", "={xmm14}"() nounwind + %a25 = call <2 x double> asm sideeffect "", "={xmm15}"() nounwind + call preserve_nonecc double @bar_double(i64 1, i64 2) + call void asm sideeffect "", "{rax},{rcx},{rdx},{r8},{r9},{r10},{r11},{xmm0},{xmm1},{xmm2},{xmm3},{xmm4},{xmm5},{xmm6},{xmm7},{xmm8},{xmm9},{xmm10},{xmm11},{xmm12},{xmm13},{xmm14},{xmm15}"(i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, <2 x double> %a10, <2 x double> %a11, <2 x double> %a12, <2 x double> %a13, <2 x double> %a14, <2 x double> %a15, <2 x double> %a16, <2 x double> %a17, <2 x double> %a18, <2 x double> %a19, <2 x double> %a20, <2 x double> %a21, <2 x double> %a22, <2 x double> %a23, <2 x double> %a24, <2 x double> %a25) + ret void +} diff --git a/llvm/test/CodeGen/X86/preserve_nonecc64.ll b/llvm/test/CodeGen/X86/preserve_nonecc64.ll new file mode 100644 index 0000000000000..9526b4b939f8f --- /dev/null +++ b/llvm/test/CodeGen/X86/preserve_nonecc64.ll @@ -0,0 +1,86 @@ +; RUN: sed -e "s/RETTYPE/void/;s/RETVAL//" %s | llc -mtriple=x86_64-apple-darwin -mcpu=corei7 | FileCheck --check-prefixes=ALL %s +; RUN: sed -e "s/RETTYPE/i32/;s/RETVAL/undef/" %s | llc -mtriple=x86_64-apple-darwin -mcpu=corei7 | FileCheck --check-prefixes=ALL %s +; RUN: sed -e "s/RETTYPE/\{i64\,i64\}/;s/RETVAL/undef/" %s | llc -mtriple=x86_64-apple-darwin -mcpu=corei7 | FileCheck --check-prefixes=ALL %s +; +; RUN: sed -e "s/RETTYPE/void/;s/RETVAL//" %s | llc -mtriple=x86_64-apple-darwin -mcpu=corei7-avx | FileCheck --check-prefixes=ALL %s +; RUN: sed -e "s/RETTYPE/i32/;s/RETVAL/undef/" %s | llc -mtriple=x86_64-apple-darwin -mcpu=corei7-avx | FileCheck --check-prefixes=ALL %s +; RUN: sed -e "s/RETTYPE/\{i64\,i64\}/;s/RETVAL/undef/" %s | llc -mtriple=x86_64-apple-darwin -mcpu=corei7-avx | FileCheck --check-prefixes=ALL %s + +; We don't need to save registers before using them inside preserve_none function. +define preserve_nonecc RETTYPE @preserve_nonecc1(i64, i64, double, double) nounwind { +entry: +;ALL-LABEL: preserve_nonecc1 +;ALL: pushq %rbp +;ALL-NEXT: InlineAsm Start +;ALL-NEXT: InlineAsm End +;ALL-NEXT: popq %rbp +;ALL-NEXT: retq + call void asm sideeffect "", "~{rax},~{rbx},~{rcx},~{rdx},~{rsi},~{rdi},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{rbp},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{xmm8},~{xmm9},~{xmm10},~{xmm11},~{xmm12},~{xmm13},~{xmm14},~{xmm15}"() + ret RETTYPE RETVAL +} + +; When calling a preserve_none function, all live registers must be saved and +; restored around the function call. +declare preserve_nonecc RETTYPE @bar(i64, i64, double, double) +define void @preserve_nonecc2() nounwind { +entry: +;ALL-LABEL: preserve_nonecc2 +;ALL: movq %rax +;ALL: movq %rdx +;ALL: movq %r11 +;ALL: movaps %xmm2 +;ALL: movaps %xmm3 +;ALL: movaps %xmm4 +;ALL: movaps %xmm5 +;ALL: movaps %xmm6 +;ALL: movaps %xmm7 +;ALL: movaps %xmm8 +;ALL: movaps %xmm9 +;ALL: movaps %xmm10 +;ALL: movaps %xmm11 +;ALL: movaps %xmm12 +;ALL: movaps %xmm13 +;ALL: movaps %xmm14 +;ALL: movaps %xmm15 +;ALL: movq {{.*}}, %rax +;ALL: movq {{.*}}, %rdx +;ALL: movq {{.*}}, %r11 +;ALL: movaps {{.*}} %xmm2 +;ALL: movaps {{.*}} %xmm3 +;ALL: movaps {{.*}} %xmm4 +;ALL: movaps {{.*}} %xmm5 +;ALL: movaps {{.*}} %xmm6 +;ALL: movaps {{.*}} %xmm7 +;ALL: movaps {{.*}} %xmm8 +;ALL: movaps {{.*}} %xmm9 +;ALL: movaps {{.*}} %xmm10 +;ALL: movaps {{.*}} %xmm11 +;ALL: movaps {{.*}} %xmm12 +;ALL: movaps {{.*}} %xmm13 +;ALL: movaps {{.*}} %xmm14 +;ALL: movaps {{.*}} %xmm15 + %a0 = call i64 asm sideeffect "", "={rax}"() nounwind + %a1 = call i64 asm sideeffect "", "={rcx}"() nounwind + %a2 = call i64 asm sideeffect "", "={rdx}"() nounwind + %a3 = call i64 asm sideeffect "", "={r8}"() nounwind + %a4 = call i64 asm sideeffect "", "={r9}"() nounwind + %a5 = call i64 asm sideeffect "", "={r10}"() nounwind + %a6 = call i64 asm sideeffect "", "={r11}"() nounwind + %a10 = call <2 x double> asm sideeffect "", "={xmm2}"() nounwind + %a11 = call <2 x double> asm sideeffect "", "={xmm3}"() nounwind + %a12 = call <2 x double> asm sideeffect "", "={xmm4}"() nounwind + %a13 = call <2 x double> asm sideeffect "", "={xmm5}"() nounwind + %a14 = call <2 x double> asm sideeffect "", "={xmm6}"() nounwind + %a15 = call <2 x double> asm sideeffect "", "={xmm7}"() nounwind + %a16 = call <2 x double> asm sideeffect "", "={xmm8}"() nounwind + %a17 = call <2 x double> asm sideeffect "", "={xmm9}"() nounwind + %a18 = call <2 x double> asm sideeffect "", "={xmm10}"() nounwind + %a19 = call <2 x double> asm sideeffect "", "={xmm11}"() nounwind + %a20 = call <2 x double> asm sideeffect "", "={xmm12}"() nounwind + %a21 = call <2 x double> asm sideeffect "", "={xmm13}"() nounwind + %a22 = call <2 x double> asm sideeffect "", "={xmm14}"() nounwind + %a23 = call <2 x double> asm sideeffect "", "={xmm15}"() nounwind + call preserve_nonecc RETTYPE @bar(i64 1, i64 2, double 3.0, double 4.0) + call void asm sideeffect "", "{rax},{rcx},{rdx},{r8},{r9},{r10},{r11},{xmm2},{xmm3},{xmm4},{xmm5},{xmm6},{xmm7},{xmm8},{xmm9},{xmm10},{xmm11},{xmm12},{xmm13},{xmm14},{xmm15}"(i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, <2 x double> %a10, <2 x double> %a11, <2 x double> %a12, <2 x double> %a13, <2 x double> %a14, <2 x double> %a15, <2 x double> %a16, <2 x double> %a17, <2 x double> %a18, <2 x double> %a19, <2 x double> %a20, <2 x double> %a21, <2 x double> %a22, <2 x double> %a23) + ret void +} diff --git a/llvm/test/CodeGen/X86/preserve_nonecc_call.ll b/llvm/test/CodeGen/X86/preserve_nonecc_call.ll new file mode 100644 index 0000000000000..e4ad056913c5d --- /dev/null +++ b/llvm/test/CodeGen/X86/preserve_nonecc_call.ll @@ -0,0 +1,127 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4 +; RUN: llc -mtriple=x86_64-unknown-unknown -mcpu=corei7 < %s | FileCheck %s + +; This test checks various function call behaviors between preserve_none and +; normal calling conventions. + +declare preserve_nonecc void @callee(ptr) + +; Normal caller calls preserve_none callee. Will not generated tail call because +; of incompatible calling convention. Callee saved registers are saved/restored +; around the call. +define void @caller1(ptr %a) { +; CHECK-LABEL: caller1: +; CHECK: # %bb.0: +; CHECK-NEXT: pushq %r15 +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: pushq %r14 +; CHECK-NEXT: .cfi_def_cfa_offset 24 +; CHECK-NEXT: pushq %r13 +; CHECK-NEXT: .cfi_def_cfa_offset 32 +; CHECK-NEXT: pushq %r12 +; CHECK-NEXT: .cfi_def_cfa_offset 40 +; CHECK-NEXT: pushq %rbx +; CHECK-NEXT: .cfi_def_cfa_offset 48 +; CHECK-NEXT: .cfi_offset %rbx, -48 +; CHECK-NEXT: .cfi_offset %r12, -40 +; CHECK-NEXT: .cfi_offset %r13, -32 +; CHECK-NEXT: .cfi_offset %r14, -24 +; CHECK-NEXT: .cfi_offset %r15, -16 +; CHECK-NEXT: callq callee@PLT +; CHECK-NEXT: popq %rbx +; CHECK-NEXT: .cfi_def_cfa_offset 40 +; CHECK-NEXT: popq %r12 +; CHECK-NEXT: .cfi_def_cfa_offset 32 +; CHECK-NEXT: popq %r13 +; CHECK-NEXT: .cfi_def_cfa_offset 24 +; CHECK-NEXT: popq %r14 +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: popq %r15 +; CHECK-NEXT: .cfi_def_cfa_offset 8 +; CHECK-NEXT: retq + tail call preserve_nonecc void @callee(ptr %a) + ret void +} + +; Preserve_none caller calls preserve_none callee. Same function body. +; The tail call is preserved. No registers are saved/restored around the call. +; Actually a simple jmp instruction is generated. +define preserve_nonecc void @caller2(ptr %a) { +; CHECK-LABEL: caller2: +; CHECK: # %bb.0: +; CHECK-NEXT: jmp callee@PLT # TAILCALL + tail call preserve_nonecc void @callee(ptr %a) + ret void +} + +; Preserve_none function can use more registers to pass parameters. +declare preserve_nonecc i64 @callee_with_many_param2(i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i64 %a9, i64 %a10, i64 %a11) +define preserve_nonecc i64 @callee_with_many_param(i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i64 %a9, i64 %a10, i64 %a11, i64 %a12) { +; CHECK-LABEL: callee_with_many_param: +; CHECK: # %bb.0: +; CHECK-NEXT: pushq %rax +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: movq %rsi, %rdi +; CHECK-NEXT: movq %rdx, %rsi +; CHECK-NEXT: movq %rcx, %rdx +; CHECK-NEXT: movq %r8, %rcx +; CHECK-NEXT: movq %r9, %r8 +; CHECK-NEXT: movq %r11, %r9 +; CHECK-NEXT: movq %r12, %r11 +; CHECK-NEXT: movq %r13, %r12 +; CHECK-NEXT: movq %r14, %r13 +; CHECK-NEXT: movq %r15, %r14 +; CHECK-NEXT: movq %rax, %r15 +; CHECK-NEXT: callq callee_with_many_param2@PLT +; CHECK-NEXT: popq %rcx +; CHECK-NEXT: .cfi_def_cfa_offset 8 +; CHECK-NEXT: retq + %ret = call preserve_nonecc i64 @callee_with_many_param2(i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i64 %a9, i64 %a10, i64 %a11, i64 %a12) + ret i64 %ret +} + +define i64 @caller3() { +; CHECK-LABEL: caller3: +; CHECK: # %bb.0: +; CHECK-NEXT: pushq %r15 +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: pushq %r14 +; CHECK-NEXT: .cfi_def_cfa_offset 24 +; CHECK-NEXT: pushq %r13 +; CHECK-NEXT: .cfi_def_cfa_offset 32 +; CHECK-NEXT: pushq %r12 +; CHECK-NEXT: .cfi_def_cfa_offset 40 +; CHECK-NEXT: pushq %rbx +; CHECK-NEXT: .cfi_def_cfa_offset 48 +; CHECK-NEXT: .cfi_offset %rbx, -48 +; CHECK-NEXT: .cfi_offset %r12, -40 +; CHECK-NEXT: .cfi_offset %r13, -32 +; CHECK-NEXT: .cfi_offset %r14, -24 +; CHECK-NEXT: .cfi_offset %r15, -16 +; CHECK-NEXT: movl $1, %edi +; CHECK-NEXT: movl $2, %esi +; CHECK-NEXT: movl $3, %edx +; CHECK-NEXT: movl $4, %ecx +; CHECK-NEXT: movl $5, %r8d +; CHECK-NEXT: movl $6, %r9d +; CHECK-NEXT: movl $7, %r11d +; CHECK-NEXT: movl $8, %r12d +; CHECK-NEXT: movl $9, %r13d +; CHECK-NEXT: movl $10, %r14d +; CHECK-NEXT: movl $11, %r15d +; CHECK-NEXT: movl $12, %eax +; CHECK-NEXT: callq callee_with_many_param@PLT +; CHECK-NEXT: popq %rbx +; CHECK-NEXT: .cfi_def_cfa_offset 40 +; CHECK-NEXT: popq %r12 +; CHECK-NEXT: .cfi_def_cfa_offset 32 +; CHECK-NEXT: popq %r13 +; CHECK-NEXT: .cfi_def_cfa_offset 24 +; CHECK-NEXT: popq %r14 +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: popq %r15 +; CHECK-NEXT: .cfi_def_cfa_offset 8 +; CHECK-NEXT: retq + %ret = call preserve_nonecc i64 @callee_with_many_param(i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 7, i64 8, i64 9, i64 10, i64 11, i64 12) + ret i64 %ret +} diff --git a/llvm/test/CodeGen/X86/preserve_nonecc_musttail.ll b/llvm/test/CodeGen/X86/preserve_nonecc_musttail.ll new file mode 100644 index 0000000000000..77f5a8bd75ac8 --- /dev/null +++ b/llvm/test/CodeGen/X86/preserve_nonecc_musttail.ll @@ -0,0 +1,11 @@ +; RUN: not llc -mtriple=x86_64-unknown-unknown -mcpu=corei7 %s -o - 2>&1 | FileCheck %s + +; Incompatible calling convention causes following error message. + +; CHECK: cannot guarantee tail call due to mismatched calling conv + +declare preserve_nonecc void @callee(ptr) +define void @caller(ptr %a) { + musttail call preserve_nonecc void @callee(ptr %a) + ret void +}