[llvm] [clang] New calling convention preserve_none (PR #76868)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 31 15:37:39 PST 2024


https://github.com/weiguozhi updated https://github.com/llvm/llvm-project/pull/76868

>From 90e14918a0eb13e2187f8548416ac72491d966c1 Mon Sep 17 00:00:00 2001
From: Guozhi Wei <carrot at google.com>
Date: Thu, 21 Dec 2023 19:04:44 +0000
Subject: [PATCH 1/6] New calling convention preserve_none

The new calling convention preserve_none is the opposite side of
existing preserve_all. It tries to preserve as few general registers as
possible. So all general registers are caller saved registers. It can
also uses more general registers to pass arguments. This attribute
doesn't impact floating-point registers. Floating-point registers still
follow the c calling convention.

Currently preserve_none is supported on X86-64 only.
---
 clang/include/clang-c/Index.h                 |  1 +
 clang/include/clang/Basic/Attr.td             |  5 ++
 clang/include/clang/Basic/AttrDocs.td         | 17 ++++
 clang/include/clang/Basic/Specifiers.h        |  1 +
 clang/lib/AST/ItaniumMangle.cpp               |  1 +
 clang/lib/AST/Type.cpp                        |  2 +
 clang/lib/AST/TypePrinter.cpp                 |  6 ++
 clang/lib/Basic/Targets/X86.h                 |  2 +
 clang/lib/CodeGen/CGCall.cpp                  |  4 +
 clang/lib/CodeGen/CGDebugInfo.cpp             |  2 +
 clang/lib/Sema/SemaDeclAttr.cpp               |  7 ++
 clang/lib/Sema/SemaType.cpp                   |  5 +-
 clang/test/CodeGen/debug-info-cc.c            |  7 ++
 clang/test/CodeGen/preserve-call-conv.c       |  8 +-
 clang/test/Sema/no_callconv.cpp               |  2 +
 clang/test/Sema/preserve-none-call-conv.c     | 19 ++++
 clang/tools/libclang/CXType.cpp               |  1 +
 llvm/docs/LangRef.rst                         |  6 ++
 llvm/include/llvm/AsmParser/LLToken.h         |  1 +
 llvm/include/llvm/BinaryFormat/Dwarf.def      |  1 +
 llvm/include/llvm/IR/CallingConv.h            |  3 +
 llvm/lib/AsmParser/LLLexer.cpp                |  1 +
 llvm/lib/AsmParser/LLParser.cpp               |  2 +
 llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp |  3 +
 llvm/lib/IR/AsmWriter.cpp                     |  1 +
 llvm/lib/Target/X86/X86CallingConv.td         | 18 ++++
 llvm/lib/Target/X86/X86ISelLoweringCall.cpp   |  1 +
 llvm/lib/Target/X86/X86RegisterInfo.cpp       |  4 +
 llvm/test/Bitcode/compatibility.ll            |  2 +
 .../X86/dynamic-regmask-preserve-none.ll      | 88 +++++++++++++++++++
 llvm/test/CodeGen/X86/ipra-reg-usage.ll       |  9 +-
 llvm/test/CodeGen/X86/ipra-transform.ll       | 19 ++++
 .../X86/preserve_nonecc64-ret-double.ll       | 85 ++++++++++++++++++
 llvm/test/CodeGen/X86/preserve_nonecc64.ll    | 86 ++++++++++++++++++
 34 files changed, 417 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/Sema/preserve-none-call-conv.c
 create mode 100644 llvm/test/CodeGen/X86/dynamic-regmask-preserve-none.ll
 create mode 100644 llvm/test/CodeGen/X86/preserve_nonecc64-ret-double.ll
 create mode 100644 llvm/test/CodeGen/X86/preserve_nonecc64.ll

diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index 64ab3378957c7..c241a8ccc7dfa 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -2981,6 +2981,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 db17211747b17..87e2ce91a0afb 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2868,6 +2868,11 @@ def M68kRTD: DeclOrTypeAttr {
   let Documentation = [M68kRTDDocs];
 }
 
+def PreserveNone : DeclOrTypeAttr {
+  let Spellings = [Clang<"preserve_none">];
+  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 98a7ecc7fd7df..a43e00c07e141 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -5494,6 +5494,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 87f29c8ae10bd..410be857dee71 100644
--- a/clang/include/clang/Basic/Specifiers.h
+++ b/clang/include/clang/Basic/Specifiers.h
@@ -293,6 +293,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 b1678479888eb..e692ef3412d01 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -3442,6 +3442,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 160a725939ccd..aae8834b97bce 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3409,6 +3409,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.");
@@ -3889,6 +3890,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 f694124292736..7bc4499efc13a 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1047,6 +1047,9 @@ void TypePrinter::printFunctionAfter(const FunctionType::ExtInfo &Info,
     case CC_M68kRTD:
       OS << " __attribute__((m68k_rtd))";
       break;
+    case CC_PreserveNone:
+      OS << " __attribute__((preserve_none))";
+      break;
     }
   }
 
@@ -1885,6 +1888,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 0ab1c10833db2..cdb4b23bc5b43 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -772,6 +772,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;
@@ -849,6 +850,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 51a43b5f85b3c..e7773076c203e 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<M68kRTDAttr>())
     return CC_M68kRTD;
 
+  if (D->hasAttr<PreserveNoneAttr>())
+    return CC_PreserveNone;
+
   return CC_C;
 }
 
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 236d53bee4e8f..78970e784f6f6 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 af8b90ecfed97..6f124c9045f40 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5219,6 +5219,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");
   }
@@ -5425,6 +5428,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");
   }
 
@@ -9355,6 +9361,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 a376f20fa4f4e..68b9e37f3a928 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                                           \
@@ -7852,6 +7853,8 @@ static Attr *getCCTypeAttr(ASTContext &Ctx, ParsedAttr &Attr) {
     return createSimpleAttr<PreserveAllAttr>(Ctx, Attr);
   case ParsedAttr::AT_M68kRTD:
     return createSimpleAttr<M68kRTDAttr>(Ctx, Attr);
+  case ParsedAttr::AT_PreserveNone:
+    return createSimpleAttr<PreserveNoneAttr>(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/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..2f2fed1765dc1
--- /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 methods}}
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 7f4a316a21ace..9344acb5d8301 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -416,6 +416,12 @@ 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 floating-point
+    registers (XMMs/YMMs). Floating-point registers still follow the c calling
+    convention.
 "``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 3a522c239ad59..ab553da5a2461 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 fb9e1ba875e1f..382c9799a4afc 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1985,6 +1985,7 @@ void LLParser::parseOptionalDLLStorageClass(unsigned &Res) {
 ///   ::= 'anyregcc'
 ///   ::= 'preserve_mostcc'
 ///   ::= 'preserve_allcc'
+///   ::= 'preserve_nonecc'
 ///   ::= 'ghccc'
 ///   ::= 'swiftcc'
 ///   ::= 'swifttailcc'
@@ -2045,6 +2046,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 20242d958b6b4..d97927b92a6c6 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp
@@ -617,6 +617,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 95cdec722062e..afd2845133c09 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..9d2984e4c12a7 100644
--- a/llvm/lib/Target/X86/X86CallingConv.td
+++ b/llvm/lib/Target/X86/X86CallingConv.td
@@ -1056,6 +1056,22 @@ def CC_Intel_OCL_BI : CallingConv<[
   CCDelegateTo<CC_X86_32_C>
 ]>;
 
+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
+  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<CC_X86_64_C>
+]>;
+
 //===----------------------------------------------------------------------===//
 // X86 Root Argument Calling Conventions
 //===----------------------------------------------------------------------===//
@@ -1095,6 +1111,7 @@ def CC_X86_64 : CallingConv<[
   CCIfCC<"CallingConv::X86_RegCall",
     CCIfSubtarget<"isTargetWin64()", CCDelegateTo<CC_X86_Win64_RegCall>>>,
   CCIfCC<"CallingConv::X86_RegCall", CCDelegateTo<CC_X86_SysV64_RegCall>>,
+  CCIfCC<"CallingConv::PreserveNone", CCDelegateTo<CC_X86_64_Preserve_None>>,
   CCIfCC<"CallingConv::X86_INTR", CCCustom<"CC_X86_Intr">>,
 
   // Mingw64 and native Win64 use Win64 CC
@@ -1184,6 +1201,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 b8b5421b90050..d022b487e387e 100644
--- a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
+++ b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
@@ -1288,6 +1288,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:
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<X86MachineFunctionInfo>()->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()
 
- at 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
+}
+
+ at 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_nonecc64-ret-double.ll b/llvm/test/CodeGen/X86/preserve_nonecc64-ret-double.ll
new file mode 100644
index 0000000000000..8977ecf835257
--- /dev/null
+++ b/llvm/test/CodeGen/X86/preserve_nonecc64-ret-double.ll
@@ -0,0 +1,85 @@
+; 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:     movaps %xmm1
+;ALL-NOT:     movaps %xmm0
+;AVX-NOT:     vmovups %ymm1
+;AVX-NOT:     vmovups %ymm0
+;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 %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 {{.*}}, %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
+}

>From bf0c2b8e18d97315a40f89960caf1ed3fc842f9f Mon Sep 17 00:00:00 2001
From: Guozhi Wei <carrot at google.com>
Date: Mon, 8 Jan 2024 19:36:19 +0000
Subject: [PATCH 2/6] Update the comment and document.

---
 llvm/docs/LangRef.rst                 | 5 +++--
 llvm/lib/Target/X86/X86CallingConv.td | 7 ++++---
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 9344acb5d8301..373c1cef937f7 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -419,8 +419,9 @@ added in the future:
 "``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 floating-point
-    registers (XMMs/YMMs). Floating-point registers still follow the c calling
+    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.
 "``cxx_fast_tlscc``" - The `CXX_FAST_TLS` calling convention for access functions
     Clang generates an access function to access C++-style TLS. The access
diff --git a/llvm/lib/Target/X86/X86CallingConv.td b/llvm/lib/Target/X86/X86CallingConv.td
index 9d2984e4c12a7..12178bcaf042d 100644
--- a/llvm/lib/Target/X86/X86CallingConv.td
+++ b/llvm/lib/Target/X86/X86CallingConv.td
@@ -1059,9 +1059,10 @@ def CC_Intel_OCL_BI : CallingConv<[
 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
+  //   - 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]>>,
 

>From 5479cf791abdf7f733e275e1f4269b2f838466e7 Mon Sep 17 00:00:00 2001
From: Guozhi Wei <carrot at google.com>
Date: Fri, 12 Jan 2024 19:40:26 +0000
Subject: [PATCH 3/6] New calling convention preserve_none

Specify it has a FunctionLike subject.
Specify it as a target specific attribute.
---
 clang/include/clang/Basic/Attr.td                              | 3 ++-
 .../test/Misc/pragma-attribute-supported-attributes-list.test  | 1 +
 clang/test/Sema/preserve-none-call-conv.c                      | 2 +-
 3 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 87e2ce91a0afb..dd63244ca8b32 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2868,8 +2868,9 @@ def M68kRTD: DeclOrTypeAttr {
   let Documentation = [M68kRTDDocs];
 }
 
-def PreserveNone : DeclOrTypeAttr {
+def PreserveNone : DeclOrTypeAttr, TargetSpecificAttr<TargetAnyX86> {
   let Spellings = [Clang<"preserve_none">];
+  let Subjects = SubjectList<[FunctionLike]>;
   let Documentation = [PreserveNoneDocs];
 }
 
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 7b0cda0bca078..261696b2978fe 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -161,6 +161,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/preserve-none-call-conv.c b/clang/test/Sema/preserve-none-call-conv.c
index 2f2fed1765dc1..4508095863de5 100644
--- a/clang/test/Sema/preserve-none-call-conv.c
+++ b/clang/test/Sema/preserve-none-call-conv.c
@@ -16,4 +16,4 @@ void (*pboo3)(void *) = boo; // expected-error {{incompatible function pointer t
 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 methods}}
+struct type_test_boo {} __attribute__((preserve_none));  // expected-warning {{'preserve_none' attribute only applies to functions and function pointers}}

>From dc1bc55634045adb5b0ab465b16dcf73f1e77144 Mon Sep 17 00:00:00 2001
From: Guozhi Wei <carrot at google.com>
Date: Wed, 17 Jan 2024 19:05:15 +0000
Subject: [PATCH 4/6] New calling convention preserve_none

Report error when swift attribute is used with preserve_none at the same
time.
---
 llvm/lib/Target/X86/X86ISelLoweringCall.cpp  | 20 ++++++++++++++++++++
 llvm/test/CodeGen/X86/preserve_none_swift.ll | 16 ++++++++++++++++
 2 files changed, 36 insertions(+)
 create mode 100644 llvm/test/CodeGen/X86/preserve_none_swift.ll

diff --git a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
index d022b487e387e..9add15c27fca0 100644
--- a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
+++ b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
@@ -1935,6 +1935,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;
 }
 
@@ -2586,6 +2596,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/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: <unknown>: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: <unknown>: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
+}

>From d4013a5aad9ac065e53ed8d82986e493c5fe9323 Mon Sep 17 00:00:00 2001
From: Guozhi Wei <carrot at google.com>
Date: Wed, 31 Jan 2024 21:49:08 +0000
Subject: [PATCH 5/6] New calling convention preserve_none

Add more test cases.
---
 llvm/docs/LangRef.rst                         |   2 +-
 .../X86/preserve_nonecc64-ret-double.ll       |  12 ++
 llvm/test/CodeGen/X86/preserve_nonecc_call.ll | 131 ++++++++++++++++++
 .../CodeGen/X86/preserve_nonecc_musttail.ll   |  11 ++
 4 files changed, 155 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/X86/preserve_nonecc_call.ll
 create mode 100644 llvm/test/CodeGen/X86/preserve_nonecc_musttail.ll

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 373c1cef937f7..69fbaf68e6b5f 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -422,7 +422,7 @@ added in the future:
     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.
+    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/test/CodeGen/X86/preserve_nonecc64-ret-double.ll b/llvm/test/CodeGen/X86/preserve_nonecc64-ret-double.ll
index 8977ecf835257..a09829c245998 100644
--- a/llvm/test/CodeGen/X86/preserve_nonecc64-ret-double.ll
+++ b/llvm/test/CodeGen/X86/preserve_nonecc64-ret-double.ll
@@ -22,6 +22,12 @@ 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
@@ -39,6 +45,12 @@ entry:
 ;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
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..f3877e75f8914
--- /dev/null
+++ b/llvm/test/CodeGen/X86/preserve_nonecc_call.ll
@@ -0,0 +1,131 @@
+; 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 at 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 at PLT # TAILCALL
+  tail call preserve_nonecc void @callee(ptr %a)
+  ret void
+}
+
+; Preserve_none function can use more registers to pass parameters.
+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:    addq %rdi, %rsi
+; CHECK-NEXT:    addq %rdx, %rcx
+; CHECK-NEXT:    addq %rsi, %rcx
+; CHECK-NEXT:    leaq (%r8,%r9), %rdx
+; CHECK-NEXT:    addq %r11, %rdx
+; CHECK-NEXT:    addq %rcx, %rdx
+; CHECK-NEXT:    leaq (%r12,%r13), %rcx
+; CHECK-NEXT:    addq %r14, %rcx
+; CHECK-NEXT:    addq %r15, %rcx
+; CHECK-NEXT:    addq %rdx, %rcx
+; CHECK-NEXT:    addq %rcx, %rax
+; CHECK-NEXT:    retq
+  %v1 = add i64 %a1, %a2
+  %v2 = add i64 %v1, %a3
+  %v3 = add i64 %v2, %a4
+  %v4 = add i64 %v3, %a5
+  %v5 = add i64 %v4, %a6
+  %v6 = add i64 %v5, %a7
+  %v7 = add i64 %v6, %a8
+  %v8 = add i64 %v7, %a9
+  %v9 = add i64 %v8, %a10
+  %v10 = add i64 %v9, %a11
+  %v11 = add i64 %v10, %a12
+  ret i64 %v11
+}
+
+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 at 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
+}

>From b1844ca0ead427a81fea16c3e9736634e432586e Mon Sep 17 00:00:00 2001
From: Guozhi Wei <carrot at google.com>
Date: Wed, 31 Jan 2024 23:37:01 +0000
Subject: [PATCH 6/6] New calling convention preserve_none

Enhance a test case.
---
 .../X86/preserve_nonecc64-ret-double.ll       | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/llvm/test/CodeGen/X86/preserve_nonecc64-ret-double.ll b/llvm/test/CodeGen/X86/preserve_nonecc64-ret-double.ll
index a09829c245998..22f0931a1446e 100644
--- a/llvm/test/CodeGen/X86/preserve_nonecc64-ret-double.ll
+++ b/llvm/test/CodeGen/X86/preserve_nonecc64-ret-double.ll
@@ -5,10 +5,38 @@
 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



More information about the llvm-commits mailing list