[clang] [llvm] [X86] Add WinCall Calling Convention and x86_64apx-windows(-gnu/-msvc) triplets (PR #215585)

via cfe-commits cfe-commits at lists.llvm.org
Tue Aug 11 13:20:36 PDT 2026


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/215585

>From a8f6c10f2a98af952e6216fcb0b52872a65b2bd1 Mon Sep 17 00:00:00 2001
From: trcrsired <uwgghhbcad at gmail.com>
Date: Sun, 23 Feb 2025 09:25:05 -0500
Subject: [PATCH 01/26] [X86] Initial Attempt to add a new calling convention:
 wincall

It is based on the proposal i made before but i want to test myself to
see whether there are some issues
https://developercommunity.visualstudio.com/t/I-present-a-novel-calling-convention-nam/10433601?q=wincall
---
 llvm/include/llvm/IR/CallingConv.h    |  3 +
 llvm/lib/Target/X86/X86CallingConv.td | 84 +++++++++++++++++++++++++++
 2 files changed, 87 insertions(+)

diff --git a/llvm/include/llvm/IR/CallingConv.h b/llvm/include/llvm/IR/CallingConv.h
index 55e32028e3ed0..00a175a451cf0 100644
--- a/llvm/include/llvm/IR/CallingConv.h
+++ b/llvm/include/llvm/IR/CallingConv.h
@@ -270,6 +270,9 @@ namespace CallingConv {
     /// Preserve X1-X15, X19-X29, SP, Z0-Z31, P0-P15.
     AArch64_SME_ABI_Support_Routines_PreserveMost_From_X1 = 111,
 
+    /// x86 wincall for APX to fix calling convention issues for Windows
+    X86_WinCall = 112,
+
     /// The highest possible ID. Must be some 2^k - 1.
     MaxID = 1023
   };
diff --git a/llvm/lib/Target/X86/X86CallingConv.td b/llvm/lib/Target/X86/X86CallingConv.td
index cf164acba9ec0..5b34303bfaa55 100644
--- a/llvm/lib/Target/X86/X86CallingConv.td
+++ b/llvm/lib/Target/X86/X86CallingConv.td
@@ -1039,6 +1039,89 @@ def CC_X86_64_Preserve_None : CallingConv<[
   CCDelegateTo<CC_X86_64_C>
 ]>;
 
+// New Calling convention used on Win64 with Intel APX for Windows 12+
+def CC_X86_Win64_WinCall : CallingConv<[
+  // FIXME: Handle varargs.
+
+  // Byval aggregates are passed by pointer
+  CCIfByVal<CCPassByVal<8, 8>>,
+
+  // Promote i1/v1i1 arguments to i8.
+  CCIfType<[i1, v1i1], CCPromoteToType<i8>>,
+
+  // The 'nest' parameter, if any, is passed in R10.
+  CCIfNest<CCAssignToReg<[R10]>>,
+
+  // A SwiftError is passed in R12.
+  CCIfSwiftError<CCIfType<[i64], CCAssignToReg<[R12]>>>,
+
+  // Pass SwiftSelf in a callee saved register.
+  CCIfSwiftSelf<CCIfType<[i64], CCAssignToReg<[R13]>>>,
+
+  // Pass SwiftAsync in an otherwise callee saved register so that calls to
+  // normal functions don't need to save it somewhere.
+  CCIfSwiftAsync<CCIfType<[i64], CCAssignToReg<[R14]>>>,
+
+  // The 'CFGuardTarget' parameter, if any, is passed in RAX.
+  CCIfCFGuardTarget<CCAssignToReg<[RAX]>>,
+
+  // 256 bit vectors are passed by pointer
+  CCIfType<[v32i8, v16i16, v8i32, v4i64, v16f16, v16bf16, v8f32, v4f64], CCPassIndirect<i64>>,
+
+  // 512 bit vectors are passed by pointer
+  CCIfType<[v64i8, v32i16, v16i32, v32f16, v32bf16, v16f32, v8f64, v8i64], CCPassIndirect<i64>>,
+
+  // Long doubles are passed by pointer
+  CCIfType<[f80], CCPassIndirect<i64>>,
+
+  // If SSE was disabled, pass FP values smaller than 64-bits as integers in
+  // GPRs or on the stack.
+  CCIfType<[f16], CCIfNotSubtarget<"hasSSE1()", CCBitConvertToType<i16>>>,
+  CCIfType<[f32], CCIfNotSubtarget<"hasSSE1()", CCBitConvertToType<i32>>>,
+  CCIfType<[f64], CCIfNotSubtarget<"hasSSE1()", CCBitConvertToType<i64>>>,
+
+  // The first 8 FP/Vector arguments are passed in XMM registers.
+  CCIfType<[f16, f32, f64, v16i8, v8i16, v4i32, v2i64, v8f16, v8bf16, v4f32, v2f64],
+           CCIfSubtarget<"hasSSE1()",
+           CCAssignToRegWithShadow<[XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7],
+                                   [RCX , RDX , R8  , R9  , R16, R17, R18, R19]>>>,
+  // 256 bit vectors are passed in YMM registers.
+  CCIfType<[v32i8, v16i16, v8i32, v4i64, v16f16, v16bf16, v8f32, v4f64],
+           CCIfSubtarget<"hasAVX()",
+            CCAssignToRegWithShadow<[YMM0, YMM1, YMM2, YMM3, YMM4, YMM5, YMM6, YMM7],
+                                   [RCX , RDX , R8  , R9  , R16, R17, R18, R19]>>>,
+  // 512 bit vectors are passed in ZMM registers.
+  CCIfType<[v64i8, v32i16, v16i32, v32f16, v32bf16, v16f32, v8f64, v8i64],
+           CCIfSubtarget<"hasAVX512()",
+            CCAssignToRegWithShadow<[ZMM0, ZMM1, ZMM2, ZMM3, ZMM4, ZMM5, ZMM6, ZMM7],
+                                   [RCX , RDX , R8  , R9  , R16, R17, R18, R19]>>>,
+  // 512 bit vectors are passed by pointer
+  CCIfType<[v64i8, v32i16, v16i32, v32f16, v32bf16, v16f32, v8f64, v8i64], CCPassIndirect<i64>>,
+
+  // The first 4 integer arguments are passed in integer registers.
+  CCIfType<[i8 ], CCAssignToRegWithShadow<[CL  , DL  , R8B , R9B , R16B, R17B, R18B, R19B],
+                                          [XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7]>>,
+  CCIfType<[i16], CCAssignToRegWithShadow<[CX  , DX  , R8W , R9W , R16W, R17W, R18W, R19W],
+                                          [XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7]>>,
+  CCIfType<[i32], CCAssignToRegWithShadow<[ECX , EDX , R8D , R9D , R16D, R17D, R18D, R19D],
+                                          [XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7]>>,
+  CCIfType<[i64], CCAssignToRegWithShadow<[RCX , RDX , R8  , R9  , R16,  R17,  R18,  R19],
+                                          [XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7]>>,
+
+  // Integer/FP values get stored in stack slots that are 8 bytes in size and
+  // 8-byte aligned if there are no more registers to hold them.
+  CCIfType<[i8, i16, i32, i64, f16, f32, f64], CCAssignToStack<8, 8>>,
+
+  // Vectors get 16-byte stack slots that are 16-byte aligned.
+  CCIfType<[v16i8, v8i16, v4i32, v2i64, v8f16, v8bf16, v4f32, v2f64], CCAssignToStack<16, 16>>,
+
+  // 256-bit vectors get 32-byte stack slots that are 32-byte aligned.
+  CCIfType<[v32i8, v16i16, v8i32, v4i64, v16f16, v16bf16, v8f32, v4f64], CCAssignToStack<32, 32>>,
+
+  // 512-bit vectors get 64-byte stack slots that are 64-byte aligned.
+  CCIfType<[v64i8, v32i16, v16i32, v8i64, v32f16, v32bf16, v16f32, v8f64], CCAssignToStack<64, 64>>
+]>;
+
 //===----------------------------------------------------------------------===//
 // X86 Root Argument Calling Conventions
 //===----------------------------------------------------------------------===//
@@ -1049,6 +1132,7 @@ def CC_X86_32 : CallingConv<[
   // MCU calling convention. Thus, this should be checked before isTargetMCU().
   CCIfCC<"CallingConv::X86_INTR", CCCustom<"CC_X86_Intr">>,
   CCIfSubtarget<"isTargetMCU()", CCDelegateTo<CC_X86_32_MCU>>,
+  CCIfCC<"CallingConv::X86_WinCall", CCDelegateTo<CC_X86_Win64_WinCall>>,
   CCIfCC<"CallingConv::X86_FastCall", CCDelegateTo<CC_X86_32_FastCall>>,
   CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<CC_X86_Win32_VectorCall>>,
   CCIfCC<"CallingConv::X86_ThisCall", CCDelegateTo<CC_X86_32_ThisCall>>,

>From 938fdc5d279f97e3390a7f9586c62c11c6db4146 Mon Sep 17 00:00:00 2001
From: trcrsired <uwgghhbcad at gmail.com>
Date: Mon, 24 Feb 2025 06:01:09 -0500
Subject: [PATCH 02/26] [X86] Add more random place changes for wincall

---
 llvm/include/llvm/AsmParser/LLToken.h               | 1 +
 llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h | 1 +
 llvm/lib/AsmParser/LLLexer.cpp                      | 1 +
 llvm/lib/AsmParser/LLParser.cpp                     | 1 +
 llvm/lib/IR/AsmWriter.cpp                           | 1 +
 llvm/lib/Target/X86/X86Subtarget.h                  | 1 +
 6 files changed, 6 insertions(+)

diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h
index a53d471f70271..d8316322a5929 100644
--- a/llvm/include/llvm/AsmParser/LLToken.h
+++ b/llvm/include/llvm/AsmParser/LLToken.h
@@ -141,6 +141,7 @@ enum Kind {
   kw_x86_fastcallcc,
   kw_x86_thiscallcc,
   kw_x86_vectorcallcc,
+  kw_x86_wincallcc,
   kw_x86_regcallcc,
   kw_arm_apcscc,
   kw_arm_aapcscc,
diff --git a/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h b/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h
index 09b9d947464ae..56ccb43172e67 100644
--- a/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h
+++ b/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h
@@ -65,6 +65,7 @@ enum class CallingConv : uint8_t {
   Eabi,
   Vectorcall,
   Regcall,
+  Wincall,
   Swift,      // Clang-only
   SwiftAsync, // Clang-only
 };
diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index c867a68518e4d..fda2bac2c0b0b 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -638,6 +638,7 @@ lltok::Kind LLLexer::LexIdentifier() {
   KEYWORD(x86_fastcallcc);
   KEYWORD(x86_thiscallcc);
   KEYWORD(x86_vectorcallcc);
+  KEYWORD(x86_wincallcc);
   KEYWORD(arm_apcscc);
   KEYWORD(arm_aapcscc);
   KEYWORD(arm_aapcs_vfpcc);
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 37103937c92a7..ee882135dcf0e 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -2228,6 +2228,7 @@ bool LLParser::parseOptionalCallingConv(unsigned &CC) {
   case lltok::kw_x86_regcallcc:  CC = CallingConv::X86_RegCall; break;
   case lltok::kw_x86_thiscallcc: CC = CallingConv::X86_ThisCall; break;
   case lltok::kw_x86_vectorcallcc:CC = CallingConv::X86_VectorCall; break;
+  case lltok::kw_x86_wincallcc:   CC = CallingConv::X86_WinCall; break;
   case lltok::kw_arm_apcscc:     CC = CallingConv::ARM_APCS; break;
   case lltok::kw_arm_aapcscc:    CC = CallingConv::ARM_AAPCS; break;
   case lltok::kw_arm_aapcs_vfpcc:CC = CallingConv::ARM_AAPCS_VFP; break;
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index 57e9cccdc0fb6..f3fb92e7146ac 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -310,6 +310,7 @@ static void PrintCallingConv(unsigned cc, raw_ostream &Out) {
   case CallingConv::GRAAL:         Out << "graalcc"; break;
   case CallingConv::CFGuard_Check: Out << "cfguard_checkcc"; break;
   case CallingConv::X86_StdCall:   Out << "x86_stdcallcc"; break;
+  case CallingConv::X86_WinCall:  Out << "x86_wincallcc"; break;
   case CallingConv::X86_FastCall:  Out << "x86_fastcallcc"; break;
   case CallingConv::X86_ThisCall:  Out << "x86_thiscallcc"; break;
   case CallingConv::X86_RegCall:   Out << "x86_regcallcc"; break;
diff --git a/llvm/lib/Target/X86/X86Subtarget.h b/llvm/lib/Target/X86/X86Subtarget.h
index 722076ca88c9c..25ca66e688972 100644
--- a/llvm/lib/Target/X86/X86Subtarget.h
+++ b/llvm/lib/Target/X86/X86Subtarget.h
@@ -355,6 +355,7 @@ class X86Subtarget final : public X86GenSubtargetInfo {
     case CallingConv::X86_StdCall:
     case CallingConv::X86_ThisCall:
     case CallingConv::X86_VectorCall:
+    case CallingConv::X86_WinCall:
     case CallingConv::Intel_OCL_BI:
       return isTargetWin64();
     // This convention allows using the Win64 convention on other targets.

>From 9d36dfc92b5cfe33a513f023b07ec18ddc115791 Mon Sep 17 00:00:00 2001
From: trcrsired <uwgghhbcad at gmail.com>
Date: Mon, 5 May 2025 17:21:58 +0800
Subject: [PATCH 03/26] call

---
 llvm/lib/Target/X86/X86CallingConv.td | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/X86/X86CallingConv.td b/llvm/lib/Target/X86/X86CallingConv.td
index 33cc1aceb5503..f68e7c6423318 100644
--- a/llvm/lib/Target/X86/X86CallingConv.td
+++ b/llvm/lib/Target/X86/X86CallingConv.td
@@ -1138,7 +1138,6 @@ def CC_X86_32 : CallingConv<[
   // MCU calling convention. Thus, this should be checked before isTargetMCU().
   CCIfCC<"CallingConv::X86_INTR", CCCustom<"CC_X86_Intr">>,
   CCIfSubtarget<"isTargetMCU()", CCDelegateTo<CC_X86_32_MCU>>,
-  CCIfCC<"CallingConv::X86_WinCall", CCDelegateTo<CC_X86_Win64_WinCall>>,
   CCIfCC<"CallingConv::X86_FastCall", CCDelegateTo<CC_X86_32_FastCall>>,
   CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<CC_X86_Win32_VectorCall>>,
   CCIfCC<"CallingConv::X86_ThisCall", CCDelegateTo<CC_X86_32_ThisCall>>,
@@ -1160,6 +1159,7 @@ def CC_X86_64 : CallingConv<[
   CCIfCC<"CallingConv::GHC", CCDelegateTo<CC_X86_64_GHC>>,
   CCIfCC<"CallingConv::HiPE", CCDelegateTo<CC_X86_64_HiPE>>,
   CCIfCC<"CallingConv::AnyReg", CCDelegateTo<CC_X86_64_AnyReg>>,
+  CCIfCC<"CallingConv::X86_WinCall", CCDelegateTo<CC_X86_Win64_WinCall>>,  
   CCIfCC<"CallingConv::Win64", CCDelegateTo<CC_X86_Win64_C>>,
   CCIfCC<"CallingConv::X86_64_SysV", CCDelegateTo<CC_X86_64_C>>,
   CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<CC_X86_Win64_VectorCall>>,

>From d3dd91b6300d61b33b64d9ee0d405421234b7db3 Mon Sep 17 00:00:00 2001
From: trcrsired <uwgghhbcad at gmail.com>
Date: Sun, 25 May 2025 18:29:56 +0800
Subject: [PATCH 04/26] Add experimental wincall changes to clang frontend

---
 clang/include/clang-c/Index.h            |  1 +
 clang/include/clang/Basic/Attr.td        |  7 +++++++
 clang/include/clang/Basic/AttrDocs.td    | 13 +++++++++++++
 clang/include/clang/Basic/LangOptions.h  |  3 ++-
 clang/include/clang/Basic/Specifiers.h   |  2 ++
 clang/include/clang/Basic/TokenKinds.def |  2 ++
 clang/include/clang/Driver/Options.td    |  6 ++++--
 clang/lib/AST/ASTContext.cpp             |  4 ++++
 clang/lib/AST/Expr.cpp                   |  1 +
 clang/lib/AST/ItaniumMangle.cpp          |  2 ++
 clang/lib/AST/Mangle.cpp                 |  2 ++
 clang/lib/AST/MicrosoftMangle.cpp        |  5 +++++
 clang/lib/AST/Type.cpp                   |  2 ++
 clang/lib/AST/TypePrinter.cpp            |  4 ++++
 clang/lib/Basic/Targets.cpp              |  2 +-
 clang/lib/CodeGen/CGCall.cpp             |  2 ++
 clang/lib/Driver/ToolChains/Clang.cpp    |  5 +++++
 clang/lib/Parse/ParseDecl.cpp            |  1 +
 clang/lib/Parse/ParseTentative.cpp       |  3 ++-
 clang/lib/Sema/SemaDeclAttr.cpp          |  6 ++++++
 clang/lib/Sema/SemaExpr.cpp              |  3 ++-
 clang/lib/Sema/SemaLambda.cpp            |  2 +-
 clang/lib/Sema/SemaType.cpp              |  3 +++
 23 files changed, 74 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index d30d15e53802a..c9096057f220c 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -3077,6 +3077,7 @@ enum CXCallingConv {
   CXCallingConv_RISCVVLSCall_16384 = 31,
   CXCallingConv_RISCVVLSCall_32768 = 32,
   CXCallingConv_RISCVVLSCall_65536 = 33,
+  CXCAllingConv_WinCall = 34,
 
   CXCallingConv_Invalid = 100,
   CXCallingConv_Unexposed = 200
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 06462b8a26bc0..dc8f675558320 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3230,6 +3230,13 @@ def StdCall : DeclOrTypeAttr {
   let Documentation = [StdCallDocs];
 }
 
+def WinCall : DeclOrTypeAttr {
+  let Spellings = [GCC<"wincall">, CustomKeyword<"__wincall">,
+                   CustomKeyword<"_wincall">];
+//  let Subjects = [Function, ObjCMethod];
+  let Documentation = [WinCallDocs];
+}
+
 def SwiftCall : DeclOrTypeAttr {
   let Spellings = [Clang<"swiftcall">];
 //  let Subjects = SubjectList<[Function]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 65d66dd398ad1..48164b43fbc39 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3394,6 +3394,19 @@ COM applications. See the documentation for `__stdcall`_ on MSDN.
   }];
 }
 
+def WinCallDocs : Documentation {
+  let Category = DocCatCallingConvs;
+  let Content = [{
+Wincall introduces a new calling convention for x86_64, designed to support Intel APX. 
+This convention addresses calling convention challenges for C++ types, such as `std::span`, 
+on the Windows x86_64 platform. 
+
+For detailed information, refer to the proposal for `__wincall`_ on Microsoft Developer Community.
+
+.. _`__wincall`: https://developercommunity.visualstudio.com/t/I-present-a-novel-calling-convention-nam/10433601?q=wincall
+  }];
+}
+
 def FastCallDocs : Documentation {
   let Category = DocCatCallingConvs;
   let Content = [{
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 491e8bee9fd5c..6e8e0ea4d411c 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -126,7 +126,8 @@ class LangOptionsBase {
     DCC_StdCall,
     DCC_VectorCall,
     DCC_RegCall,
-    DCC_RtdCall
+    DCC_RtdCall,
+    DCC_WinCall
   };
 
   enum AddrSpaceMapMangling { ASMM_Target, ASMM_On, ASMM_Off };
diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h
index 491badcc804e7..d2c4882d3f970 100644
--- a/clang/include/clang/Basic/Specifiers.h
+++ b/clang/include/clang/Basic/Specifiers.h
@@ -283,6 +283,7 @@ namespace clang {
     CC_X86VectorCall,      // __attribute__((vectorcall))
     CC_X86Pascal,          // __attribute__((pascal))
     CC_Win64,              // __attribute__((ms_abi))
+    CC_WinCall,            // __attribute__((wincall))
     CC_X86_64SysV,         // __attribute__((sysv_abi))
     CC_X86RegCall,         // __attribute__((regcall))
     CC_AAPCS,              // __attribute__((pcs("aapcs")))
@@ -322,6 +323,7 @@ namespace clang {
     case CC_X86StdCall:
     case CC_X86FastCall:
     case CC_X86ThisCall:
+    case CC_WinCall:
     case CC_X86RegCall:
     case CC_X86Pascal:
     case CC_X86VectorCall:
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 94e72fea56a68..9919386510766 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -619,6 +619,7 @@ KEYWORD(__fastcall                  , KEYALL)
 KEYWORD(__thiscall                  , KEYALL)
 KEYWORD(__regcall                   , KEYALL)
 KEYWORD(__vectorcall                , KEYALL)
+KEYWORD(__wincall                   , KEYALL)
 KEYWORD(__forceinline               , KEYMS)
 KEYWORD(__unaligned                 , KEYMS)
 KEYWORD(__super                     , KEYMS)
@@ -781,6 +782,7 @@ ALIAS("_ptr64"           , __ptr64      , KEYMSCOMPAT)
 ALIAS("_restrict"        , restrict     , KEYMSCOMPAT)
 ALIAS("_stdcall"         , __stdcall    , KEYMS | KEYBORLAND)
 ALIAS("_thiscall"        , __thiscall   , KEYMS)
+ALIAS("_wincall"         , __wincall    , KEYMS)
 ALIAS("_try"             , __try        , KEYMSCOMPAT)
 ALIAS("_vectorcall"      , __vectorcall , KEYMS)
 ALIAS("_unaligned"       , __unaligned  , KEYMSCOMPAT)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 22261621df092..e207574b992da 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -8397,9 +8397,9 @@ def fnative_half_arguments_and_returns : Flag<["-"], "fnative-half-arguments-and
   ImpliedByAnyOf<[open_cl.KeyPath, hlsl.KeyPath, hip.KeyPath]>;
 def fdefault_calling_conv_EQ : Joined<["-"], "fdefault-calling-conv=">,
   HelpText<"Set default calling convention">,
-  Values<"cdecl,fastcall,stdcall,vectorcall,regcall,rtdcall">,
+  Values<"cdecl,fastcall,stdcall,vectorcall,regcall,rtdcall,wincall">,
   NormalizedValuesScope<"LangOptions">,
-  NormalizedValues<["DCC_CDecl", "DCC_FastCall", "DCC_StdCall", "DCC_VectorCall", "DCC_RegCall", "DCC_RtdCall"]>,
+  NormalizedValues<["DCC_CDecl", "DCC_FastCall", "DCC_StdCall", "DCC_VectorCall", "DCC_RegCall", "DCC_RtdCall", "DCC_WinCall"]>,
   MarshallingInfoEnum<LangOpts<"DefaultCallingConv">, "DCC_None">;
 
 // These options cannot be marshalled, because they are used to set up the LangOptions defaults.
@@ -9092,6 +9092,8 @@ def _SLASH_Gz : CLFlag<"Gz">,
   HelpText<"Set __stdcall as a default calling convention">;
 def _SLASH_Gv : CLFlag<"Gv">,
   HelpText<"Set __vectorcall as a default calling convention">;
+def _SLASH_Gwincall : CLFlag<"Gwincall">,
+  HelpText<"Set __wincall as a default calling convention">;
 def _SLASH_Gregcall : CLFlag<"Gregcall">,
   HelpText<"Set __regcall as a default calling convention">;
 def _SLASH_Gregcall4 : CLFlag<"Gregcall4">,
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index b5417fcf20ddd..d04eccbf07439 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -13039,6 +13039,10 @@ CallingConv ASTContext::getDefaultCallingConvention(bool IsVariadic,
       if (!IsVariadic)
         return CC_X86StdCall;
       break;
+    case LangOptions::DCC_WinCall
+      if (!IsVariadic)
+        return CC_WinCall;
+      break;
     case LangOptions::DCC_VectorCall:
       // __vectorcall cannot be applied to variadic functions.
       if (!IsVariadic)
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index fe874ccd7b60f..40e4c58f359b7 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -796,6 +796,7 @@ std::string PredefinedExpr::ComputeName(PredefinedIdentKind IK,
       case CC_X86ThisCall: POut << "__thiscall "; break;
       case CC_X86VectorCall: POut << "__vectorcall "; break;
       case CC_X86RegCall: POut << "__regcall "; break;
+      case CC_WinCall: POut << "__wincall "; break;
       // Only bother printing the conventions that MSVC knows about.
       default: break;
       }
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 33a8728728574..d6643d34af0e1 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -3575,6 +3575,8 @@ StringRef CXXNameMangler::getCallingConvQualifierName(CallingConv CC) {
     return "swiftcall";
   case CC_SwiftAsync:
     return "swiftasynccall";
+  case CC_WinCall:
+    return "wincall";
   }
   llvm_unreachable("bad calling convention");
 }
diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp
index 9652fdbc4e125..b19f38bd3a7f6 100644
--- a/clang/lib/AST/Mangle.cpp
+++ b/clang/lib/AST/Mangle.cpp
@@ -115,6 +115,8 @@ static CCMangling getCallingConvMangling(const ASTContext &Context,
     return CCM_Std;
   case CC_X86VectorCall:
     return CCM_Vector;
+  case CC_X86WinCall:
+    return CCM_WinCall;
   }
 }
 
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index add737b762ccc..90d0379d812e7 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -3133,6 +3133,8 @@ void MicrosoftCXXNameMangler::mangleCallingConvention(CallingConv CC,
   //                      ::= H # __export __stdcall
   //                      ::= I # __fastcall
   //                      ::= J # __export __fastcall
+  //                      ::= K # __wincall
+  //                      ::= L # __export __wincall
   //                      ::= Q # __vectorcall
   //                      ::= S # __attribute__((__swiftcall__)) // Clang-only
   //                      ::= W # __attribute__((__swiftasynccall__))
@@ -3168,6 +3170,9 @@ void MicrosoftCXXNameMangler::mangleCallingConvention(CallingConv CC,
     case CC_X86FastCall:
       Out << 'I';
       return;
+    case CC_WinCall:
+      Out << 'K';
+      return;
     case CC_X86VectorCall:
       Out << 'Q';
       return;
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index df084dd9149a4..3709bd24482fe 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3582,6 +3582,8 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) {
     return "fastcall";
   case CC_X86ThisCall:
     return "thiscall";
+  case CC_WinCall:
+    return "wincall";
   case CC_X86Pascal:
     return "pascal";
   case CC_X86VectorCall:
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index cba1a2d98d660..6ef7adc90198a 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1084,6 +1084,9 @@ void TypePrinter::printFunctionAfter(const FunctionType::ExtInfo &Info,
     case CC_X86Pascal:
       OS << " __attribute__((pascal))";
       break;
+    case CC_WinCall:
+      OS << " __attribute__((wincall))";
+      break;
     case CC_AAPCS:
       OS << " __attribute__((pcs(\"aapcs\")))";
       break;
@@ -2053,6 +2056,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
   case attr::MSABI: OS << "ms_abi"; break;
   case attr::SysVABI: OS << "sysv_abi"; break;
   case attr::RegCall: OS << "regcall"; break;
+  case attr::WinCall: OS << "wincall"; break;
   case attr::Pcs: {
     OS << "pcs(";
    QualType t = T->getEquivalentType();
diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp
index 9889141ad2085..07f49b4370dfc 100644
--- a/clang/lib/Basic/Targets.cpp
+++ b/clang/lib/Basic/Targets.cpp
@@ -94,7 +94,7 @@ void addCygMingDefines(const LangOptions &Opts, MacroBuilder &Builder) {
     // Provide macros for all the calling convention keywords.  Provide both
     // single and double underscore prefixed variants.  These are available on
     // x64 as well as x86, even though they have no effect.
-    const char *CCs[] = {"cdecl", "stdcall", "fastcall", "thiscall", "pascal"};
+    const char *CCs[] = {"cdecl", "stdcall", "fastcall", "thiscall", "pascal", "wincall"};
     for (const char *CC : CCs) {
       std::string GCCSpelling = "__attribute__((__";
       GCCSpelling += CC;
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index bd920a2e3f2dd..5b35b600ff4fd 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -60,6 +60,8 @@ unsigned CodeGenTypes::ClangCallConvToLLVMCallConv(CallingConv CC) {
     return llvm::CallingConv::X86_RegCall;
   case CC_X86ThisCall:
     return llvm::CallingConv::X86_ThisCall;
+  case CC_WinCall:
+    return llvm::CallingConv::X86_WinCall;
   case CC_Win64:
     return llvm::CallingConv::Win64;
   case CC_X86_64SysV:
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index b0042b86ff421..870d6d06f6f22 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -8540,6 +8540,11 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType,
       ArchSupported = Arch == llvm::Triple::x86 || Arch == llvm::Triple::x86_64;
       DCCFlag = "-fdefault-calling-conv=regcall";
       break;
+      break;
+    case options::OPT__SLASH_Gwincall:
+      ArchSupported = Arch == llvm::Triple::x86_64;
+      DCCFlag = "-fdefault-calling-conv=wincall";
+      break;
     }
 
     // MSVC doesn't warn if /Gr or /Gz is used on x64, so we don't either.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 7a87cd2e340cc..2f40e5818a9a3 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -974,6 +974,7 @@ void Parser::ParseMicrosoftTypeAttributes(ParsedAttributes &attrs) {
     case tok::kw___regcall:
     case tok::kw___cdecl:
     case tok::kw___vectorcall:
+    case tok::kw___wincall:
     case tok::kw___ptr64:
     case tok::kw___w64:
     case tok::kw___ptr32:
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index cc02ee51618aa..d7b4765769e41 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -916,7 +916,7 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
       // '(' abstract-declarator ')'
       if (Tok.isOneOf(tok::kw___attribute, tok::kw___declspec, tok::kw___cdecl,
                       tok::kw___stdcall, tok::kw___fastcall, tok::kw___thiscall,
-                      tok::kw___regcall, tok::kw___vectorcall))
+                      tok::kw___regcall, tok::kw___vectorcall, tok::kw___wincall))
         return TPResult::True; // attributes indicate declaration
       TPResult TPR = TryParseDeclarator(mayBeAbstract, mayHaveIdentifier);
       if (TPR != TPResult::Ambiguous)
@@ -1238,6 +1238,7 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
   case tok::kw___thiscall:
   case tok::kw___regcall:
   case tok::kw___vectorcall:
+  case tok::kw___wincall:
   case tok::kw___w64:
   case tok::kw___sptr:
   case tok::kw___uptr:
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 8ce51cc2882bf..6341f9ba9dd4f 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5181,6 +5181,9 @@ static void handleCallConvAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   case ParsedAttr::AT_CDecl:
     D->addAttr(::new (S.Context) CDeclAttr(S.Context, AL));
     return;
+  case ParsedAttr::AT_WinCall:
+    D->addAttr(::new (S.Context) WinCallAttr(S.Context, AL));
+    return;
   case ParsedAttr::AT_Pascal:
     D->addAttr(::new (S.Context) PascalAttr(S.Context, AL));
     return;
@@ -5415,6 +5418,9 @@ bool Sema::CheckCallingConvAttr(const ParsedAttr &Attrs, CallingConv &CC,
   case ParsedAttr::AT_ThisCall:
     CC = CC_X86ThisCall;
     break;
+  case ParsedAttr::AT_WinCall:
+    CC = CC_WinCall;
+    break;
   case ParsedAttr::AT_Pascal:
     CC = CC_X86Pascal;
     break;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 452dbbfe23c5b..481f9b3d677b1 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18328,12 +18328,13 @@ static bool funcHasParameterSizeMangling(Sema &S, FunctionDecl *FD) {
   if (S.getLangOpts().CPlusPlus && !FD->isExternC())
     return false;
 
-  // Stdcall, fastcall, and vectorcall need this special treatment.
+  // Stdcall, fastcall, vectorcall and wincall need this special treatment.
   CallingConv CC = FD->getType()->castAs<FunctionType>()->getCallConv();
   switch (CC) {
   case CC_X86StdCall:
   case CC_X86FastCall:
   case CC_X86VectorCall:
+  case CC_X86WinCall:
     return true;
   default:
     break;
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index aad16290422f5..c71bd0a7364b8 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1625,7 +1625,7 @@ static void repeatForLambdaConversionFunctionCallingConvs(
   if (S.getLangOpts().MSVCCompat) {
     CallingConv Convs[] = {
         CC_C,        CC_X86StdCall, CC_X86FastCall, CC_X86VectorCall,
-        DefaultFree, DefaultMember, CallOpCC};
+        CC_WinCall,  DefaultFree, DefaultMember, CallOpCC};
     llvm::sort(Convs);
     llvm::iterator_range<CallingConv *> Range(std::begin(Convs),
                                               llvm::unique(Convs));
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 874e41ac0b90c..a75615b16d68c 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -126,6 +126,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr,
   case ParsedAttr::AT_CDecl:                                                   \
   case ParsedAttr::AT_FastCall:                                                \
   case ParsedAttr::AT_StdCall:                                                 \
+  case ParsedAttr::AT_WinCall:                                                 \
   case ParsedAttr::AT_ThisCall:                                                \
   case ParsedAttr::AT_RegCall:                                                 \
   case ParsedAttr::AT_Pascal:                                                  \
@@ -7512,6 +7513,8 @@ static Attr *getCCTypeAttr(ASTContext &Ctx, ParsedAttr &Attr) {
     return createSimpleAttr<FastCallAttr>(Ctx, Attr);
   case ParsedAttr::AT_StdCall:
     return createSimpleAttr<StdCallAttr>(Ctx, Attr);
+  case ParsedAttr::AT_WinCall:
+    return createSimpleAttr<WinCallAttr>(Ctx, Attr);
   case ParsedAttr::AT_ThisCall:
     return createSimpleAttr<ThisCallAttr>(Ctx, Attr);
   case ParsedAttr::AT_RegCall:

>From f9807460c4c15381c8e1d9dcb5a578a2aefc55b7 Mon Sep 17 00:00:00 2001
From: trcrsired <uwgghhbcad at gmail.com>
Date: Sun, 25 May 2025 18:33:57 +0800
Subject: [PATCH 05/26] Fix missing : in ASTContext.cpp

---
 clang/lib/AST/ASTContext.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index d04eccbf07439..1a0b892dbe219 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -13039,7 +13039,7 @@ CallingConv ASTContext::getDefaultCallingConvention(bool IsVariadic,
       if (!IsVariadic)
         return CC_X86StdCall;
       break;
-    case LangOptions::DCC_WinCall
+    case LangOptions::DCC_WinCall:
       if (!IsVariadic)
         return CC_WinCall;
       break;

>From 4a9ef9d40df32cafe842402cbd86d4f6d2339aea Mon Sep 17 00:00:00 2001
From: trcrsired <uwgghhbcad at gmail.com>
Date: Sun, 25 May 2025 18:36:27 +0800
Subject: [PATCH 06/26] CCM_WinCall was not defined. Fix

---
 clang/lib/AST/Mangle.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp
index b19f38bd3a7f6..f087fd558cfee 100644
--- a/clang/lib/AST/Mangle.cpp
+++ b/clang/lib/AST/Mangle.cpp
@@ -68,6 +68,7 @@ enum CCMangling {
   CCM_RegCall,
   CCM_Vector,
   CCM_Std,
+  CCM_WinCall,
   CCM_WasmMainArgcArgv
 };
 

>From b5d348aab21a82338c6701c9bfa5a68fa645768f Mon Sep 17 00:00:00 2001
From: trcrsired <uwgghhbcad at gmail.com>
Date: Sun, 25 May 2025 18:37:26 +0800
Subject: [PATCH 07/26] It is CC_WinCall not CCM_WinCall

---
 clang/lib/AST/Mangle.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp
index f087fd558cfee..2fad5a179ab14 100644
--- a/clang/lib/AST/Mangle.cpp
+++ b/clang/lib/AST/Mangle.cpp
@@ -116,7 +116,7 @@ static CCMangling getCallingConvMangling(const ASTContext &Context,
     return CCM_Std;
   case CC_X86VectorCall:
     return CCM_Vector;
-  case CC_X86WinCall:
+  case CC_WinCall:
     return CCM_WinCall;
   }
 }

>From 62d000098991b1a671fa784137277d5e102d1022 Mon Sep 17 00:00:00 2001
From: trcrsired <uwgghhbcad at gmail.com>
Date: Sun, 25 May 2025 18:41:29 +0800
Subject: [PATCH 08/26] WinCall, not X86WinCall

---
 clang/lib/Sema/SemaExpr.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 481f9b3d677b1..9107a1d902865 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18334,7 +18334,7 @@ static bool funcHasParameterSizeMangling(Sema &S, FunctionDecl *FD) {
   case CC_X86StdCall:
   case CC_X86FastCall:
   case CC_X86VectorCall:
-  case CC_X86WinCall:
+  case CC_WinCall:
     return true;
   default:
     break;

>From f0d259dc7ab70ebe3e3c95c9ca7efba0f59d2e3a Mon Sep 17 00:00:00 2001
From: trcrsired <uwgghhbcad at gmail.com>
Date: Sun, 25 May 2025 21:02:03 +0800
Subject: [PATCH 09/26] wincall

---
 clang/include/clang-c/Index.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index c9096057f220c..96e571b9f7936 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -3077,7 +3077,7 @@ enum CXCallingConv {
   CXCallingConv_RISCVVLSCall_16384 = 31,
   CXCallingConv_RISCVVLSCall_32768 = 32,
   CXCallingConv_RISCVVLSCall_65536 = 33,
-  CXCAllingConv_WinCall = 34,
+  CXCallingConv_WinCall = 34,
 
   CXCallingConv_Invalid = 100,
   CXCallingConv_Unexposed = 200

>From 4d41c627da4e304df33d39528a1de110c21dd7bd Mon Sep 17 00:00:00 2001
From: trcrsired <uwgghhbcad at gmail.com>
Date: Sun, 25 May 2025 23:18:42 +0800
Subject: [PATCH 10/26] remove tail space in attrdocs

---
 clang/include/clang/Basic/AttrDocs.td | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 48164b43fbc39..4e53f745d0f40 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3397,9 +3397,9 @@ COM applications. See the documentation for `__stdcall`_ on MSDN.
 def WinCallDocs : Documentation {
   let Category = DocCatCallingConvs;
   let Content = [{
-Wincall introduces a new calling convention for x86_64, designed to support Intel APX. 
-This convention addresses calling convention challenges for C++ types, such as `std::span`, 
-on the Windows x86_64 platform. 
+Wincall introduces a new calling convention for x86_64, designed to support Intel APX.
+This convention addresses calling convention challenges for C++ types, such as `std::span`,
+on the Windows x86_64 platform.
 
 For detailed information, refer to the proposal for `__wincall`_ on Microsoft Developer Community.
 

>From 1d58368ca94195606f97abc5c4aafe0e9974909c Mon Sep 17 00:00:00 2001
From: trcrsired <uwgghhbcad at gmail.com>
Date: Sun, 25 May 2025 23:27:58 +0800
Subject: [PATCH 11/26] upload for some changes

---
 clang/lib/Parse/ParseDeclCXX.cpp | 1 +
 clang/tools/libclang/CXType.cpp  | 1 +
 2 files changed, 2 insertions(+)

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 316bc30edf1f0..ab02db9b6a6a9 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1492,6 +1492,7 @@ bool Parser::isValidAfterTypeSpecifier(bool CouldBeBitfield) {
   case tok::kw___stdcall:    // struct foo {...} __stdcall    x;
   case tok::kw___thiscall:   // struct foo {...} __thiscall   x;
   case tok::kw___vectorcall: // struct foo {...} __vectorcall x;
+  case tok::kw___wincall:    // struct foo {...} __wincall    x;
     // We will diagnose these calling-convention specifiers on non-function
     // declarations later, so claim they are valid after a type specifier.
     return getLangOpts().MicrosoftExt;
diff --git a/clang/tools/libclang/CXType.cpp b/clang/tools/libclang/CXType.cpp
index ffa942d10669c..0a26799283d43 100644
--- a/clang/tools/libclang/CXType.cpp
+++ b/clang/tools/libclang/CXType.cpp
@@ -707,6 +707,7 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) {
       TCALLINGCONV(AArch64VectorCall);
       TCALLINGCONV(AArch64SVEPCS);
       TCALLINGCONV(Win64);
+      TCALLINGCONV(WinCall);
       TCALLINGCONV(X86_64SysV);
       TCALLINGCONV(AAPCS);
       TCALLINGCONV(AAPCS_VFP);

>From 103cc5deb9746500e37340cd08a75b1fee8e4882 Mon Sep 17 00:00:00 2001
From: trcrsired <uwgghhbcad at gmail.com>
Date: Mon, 26 May 2025 00:00:04 +0800
Subject: [PATCH 12/26] parser should parse wincall

---
 clang/lib/Parse/ParseDecl.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 2f40e5818a9a3..b7454ef23fb07 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -1030,6 +1030,7 @@ SourceLocation Parser::SkipExtendedMicrosoftTypeAttributes() {
     case tok::kw___thiscall:
     case tok::kw___cdecl:
     case tok::kw___vectorcall:
+    case tok::kw___wincall:
     case tok::kw___ptr32:
     case tok::kw___ptr64:
     case tok::kw___w64:
@@ -4037,6 +4038,7 @@ void Parser::ParseDeclarationSpecifiers(
     case tok::kw___thiscall:
     case tok::kw___regcall:
     case tok::kw___vectorcall:
+    case tok::kw___wincall:
       ParseMicrosoftTypeAttributes(DS.getAttributes());
       continue;
 
@@ -5654,6 +5656,7 @@ bool Parser::isTypeSpecifierQualifier() {
   case tok::kw___thiscall:
   case tok::kw___regcall:
   case tok::kw___vectorcall:
+  case tok::kw___wincall:
   case tok::kw___w64:
   case tok::kw___ptr64:
   case tok::kw___ptr32:
@@ -5934,6 +5937,7 @@ bool Parser::isDeclarationSpecifier(
   case tok::kw___thiscall:
   case tok::kw___regcall:
   case tok::kw___vectorcall:
+  case tok::kw___wincall:
   case tok::kw___w64:
   case tok::kw___sptr:
   case tok::kw___uptr:
@@ -6227,6 +6231,7 @@ void Parser::ParseTypeQualifierListOpt(
     case tok::kw___thiscall:
     case tok::kw___regcall:
     case tok::kw___vectorcall:
+    case tok::kw___wincall:
       if (AttrReqs & AR_DeclspecAttributesParsed) {
         ParseMicrosoftTypeAttributes(DS.getAttributes());
         continue;

>From d31d15fc05b8e9ebe5dfb11534c0b5ea37a992ec Mon Sep 17 00:00:00 2001
From: trcrsired <uwgghhbcad at gmail.com>
Date: Sat, 15 Nov 2025 09:55:21 +0800
Subject: [PATCH 13/26] Missing one case in parsedattr for wincall

---
 clang/lib/Sema/SemaDeclAttr.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 6d99dd6ec6faf..23bef3bb1ec7d 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7513,6 +7513,7 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
   case ParsedAttr::AT_CDecl:
   case ParsedAttr::AT_FastCall:
   case ParsedAttr::AT_ThisCall:
+  case ParsedAttr::AT_WinCall:
   case ParsedAttr::AT_Pascal:
   case ParsedAttr::AT_RegCall:
   case ParsedAttr::AT_SwiftCall:

>From da4e431be48beed2e7ddccc97a9a626b732d8546 Mon Sep 17 00:00:00 2001
From: trcrsired <uwgghhbcad at gmail.com>
Date: Sat, 15 Nov 2025 13:48:26 +0800
Subject: [PATCH 14/26] Add x86.h CC

---
 clang/lib/Basic/Targets/X86.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index e7da2622e78b5..c778bfc1005a7 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -931,6 +931,7 @@ class LLVM_LIBRARY_VISIBILITY WindowsX86_64TargetInfo
     case CC_SwiftAsync:
     case CC_X86RegCall:
     case CC_DeviceKernel:
+    case CC_WinCall:
       return CCCR_OK;
     default:
       return CCCR_Warning;

>From c08e44600eb13b57f318f057077615d26de4d660 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Tue, 11 Aug 2026 15:42:58 +0800
Subject: [PATCH 15/26] [X86][Clang] Fix wincall calling convention: @win
 suffix, independent registers, register returns

Fix several bugs in the wincall (x86_64 APX) calling convention so it
matches the spec in CLAUDE.md:

1. Symbol suffix
   Use a uniform '@win' suffix on wincall symbols instead of the stdcall
   '@N' parameter-size suffix, so the linker can catch calling convention
   mismatches without colliding with stdcall/vectorcall '@0'/'@16'
   decorations. Applies to all three ABIs:
   - C:            foo at win
   - C++ MS ABI:   ?foo@@YAXXZ at win  (mangled as 'A'/cdecl in the calling
                                     convention char, '@win' appended at the
                                     end of the mangled name)
   - C++ Itanium:  _Z4foov at win
   This avoids inventing a 'K' calling convention char that could collide
   with MSVC's real encodings.

2. Independent integer/FP register allocation
   Replace the Win64-style CCAssignToRegWithShadow pairing (which skipped
   e.g. XMM0 when RCX was used for an integer arg) with independent
   allocation so all 8 GPRs (RCX,RDX,R8,R9,R16-R19) and all 8 XMM/YMM/ZMM
   registers are used independently. This is safe across wincall<->stdcall
   calls because each function's convention is fixed at compile time and the
   @win suffix catches mismatches.

3. Return convention
   Add a dedicated RetCC_X86_Win64_WinCall (wincall previously had no
   return CC and silently fell back to Win64). Per the spec:
   - scalar <=64 bits -> RAX
   - scalar <=128 bits -> RAX (low) + RDX (high)
   - non-scalar (float/double/vector) -> XMM0
   - user-defined types of length 1,2,4,8,16,32,64,128 bits -> RAX (RDX for
     128 bits), and aggregates up to 32 bytes (e.g. 4x size_t, like
     std::string/std::vector) returned in RAX, RDX, RCX, R8.

4. Register pass/return of 16/32-byte aggregates
   In clang's WinX86_64ABIInfo::classify, pass/return aggregates that fit
   in 1,2,4,8,16 or 32 bytes directly in registers instead of by
   pointer/sret (MS x64 ABI behavior). __int128 is passed in two integer
   registers and returned in RAX+RDX; std::float128_t is handled the same
   way.

5. Vector arguments
   Remove the 256/512-bit CCPassIndirect rules that shadowed the YMM/ZMM
   register rules (dead code) so __m256 passes in YMM0-7 and __m512 in
   ZMM0-7 as the spec requires.
---
 clang/lib/AST/Mangle.cpp              |  7 +++
 clang/lib/AST/MicrosoftMangle.cpp     | 18 +++++--
 clang/lib/CodeGen/Targets/X86.cpp     | 32 ++++++++++++
 llvm/lib/Target/X86/X86CallingConv.td | 74 ++++++++++++++++++---------
 4 files changed, 102 insertions(+), 29 deletions(-)

diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp
index e2a05e59fe7c9..00b3d08c2179a 100644
--- a/clang/lib/AST/Mangle.cpp
+++ b/clang/lib/AST/Mangle.cpp
@@ -330,6 +330,13 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
   const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FT);
   if (CC == CCM_Vector)
     Out << '@';
+  if (CC == CCM_WinCall) {
+    // wincall symbols get a @win suffix so the linker can catch calling
+    // convention mismatches, like the @N parameter-size suffix does for
+    // stdcall on i386.
+    Out << "@win";
+    return;
+  }
   Out << '@';
   if (!Proto) {
     Out << '0';
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index 33559f57bd671..f62c797c4b35d 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -604,9 +604,14 @@ void MicrosoftCXXNameMangler::mangle(GlobalDecl GD, StringRef Prefix) {
   // <mangled-name> ::= ? <name> <type-encoding>
   Out << Prefix;
   mangleName(GD);
-  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
+  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
     mangleFunctionEncoding(GD, Context.shouldMangleDeclName(FD));
-  else if (const VarDecl *VD = dyn_cast<VarDecl>(D))
+    // __wincall symbols get a @win suffix so the linker can catch calling
+    // convention mismatches, like the @N parameter-size suffix does for
+    // stdcall on i386.
+    if (FD->getType()->castAs<FunctionType>()->getCallConv() == CC_WinCall)
+      Out << "@win";
+  } else if (const VarDecl *VD = dyn_cast<VarDecl>(D))
     mangleVariableEncoding(VD);
   else if (isa<MSGuidDecl>(D))
     // MSVC appears to mangle GUIDs as if they were variables of type
@@ -3363,6 +3368,11 @@ void MicrosoftCXXNameMangler::mangleCallingConvention(CallingConv CC,
   // that keyword. (It didn't actually export them, it just made them so
   // that they could be in a DLL and somebody from another module could call
   // them.)
+  //
+  // __wincall is mangled as if it were __cdecl ('A') here; the distinct
+  // "@win" suffix is appended to the whole mangled name in
+  // MicrosoftCXXNameMangler::mangle so the linker can catch calling
+  // convention mismatches.
 
   switch (CC) {
     default:
@@ -3370,6 +3380,7 @@ void MicrosoftCXXNameMangler::mangleCallingConvention(CallingConv CC,
     case CC_Win64:
     case CC_X86_64SysV:
     case CC_C:
+    case CC_WinCall:
       Out << 'A';
       return;
     case CC_X86Pascal:
@@ -3384,9 +3395,6 @@ void MicrosoftCXXNameMangler::mangleCallingConvention(CallingConv CC,
     case CC_X86FastCall:
       Out << 'I';
       return;
-    case CC_WinCall:
-      Out << 'K';
-      return;
     case CC_X86VectorCall:
       Out << 'Q';
       return;
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index f0d108f3279fd..7f92099b96575 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -3451,6 +3451,7 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, unsigned &FreeSSERegs,
                                       bool IsReturnType, unsigned CC) const {
   bool IsVectorCall = CC == llvm::CallingConv::X86_VectorCall;
   bool IsRegCall = CC == llvm::CallingConv::X86_RegCall;
+  bool IsWinCall = CC == llvm::CallingConv::X86_WinCall;
 
   if (Ty->isVoidType())
     return ABIArgInfo::getIgnore();
@@ -3473,6 +3474,21 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, unsigned &FreeSSERegs,
     if (RT->getDecl()->getDefinitionOrSelf()->hasFlexibleArrayMember())
       return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
                                      /*ByVal=*/false);
+
+    // wincall passes/returns aggregates that fit in 1, 2, 4, 8, 16 or 32 bytes
+    // (e.g. 4x size_t, like std::string/std::vector) directly in registers,
+    // instead of by pointer/sret like the MS x64 ABI.
+    if (IsWinCall && Width <= 256 && !Ty->isAnyComplexType() &&
+        !Ty->isMemberPointerType()) {
+      if (IsReturnType)
+        return ABIArgInfo::getDirect();
+      // Pass as an integer of the aggregate size when it fits in one register,
+      // otherwise expand it into its 8-byte parts.
+      if (Width <= 64)
+        return ABIArgInfo::getDirect(
+            llvm::IntegerType::get(getVMContext(), Width));
+      return ABIArgInfo::getExpand();
+    }
   }
 
   const Type *Base = nullptr;
@@ -3548,6 +3564,22 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, unsigned &FreeSSERegs,
     case BuiltinType::Int128:
     case BuiltinType::UInt128:
     case BuiltinType::Float128:
+      // wincall passes 128-bit integers in two integer registers and returns
+      // them in RAX (low) and RDX (high), per the spec.
+      if (IsWinCall && BT->getKind() != BuiltinType::Float128) {
+        if (IsReturnType)
+          return ABIArgInfo::getDirect();
+        return ABIArgInfo::getExpand();
+      }
+      if (IsWinCall) {
+        // std::float128_t is passed like __int128: in two integer registers.
+        if (IsReturnType)
+          return ABIArgInfo::getDirect(
+              llvm::FixedVectorType::get(
+                  llvm::Type::getInt64Ty(getVMContext()), 2));
+        return ABIArgInfo::getExpand();
+      }
+
       // If it's a parameter type, the normal ABI rule is that arguments larger
       // than 8 bytes are passed indirectly. GCC follows it. We follow it too,
       // even though it isn't particularly efficient.
diff --git a/llvm/lib/Target/X86/X86CallingConv.td b/llvm/lib/Target/X86/X86CallingConv.td
index 0e7986fe34767..32167bf43cda9 100644
--- a/llvm/lib/Target/X86/X86CallingConv.td
+++ b/llvm/lib/Target/X86/X86CallingConv.td
@@ -389,6 +389,43 @@ def RetCC_X86_Win64_C : CallingConv<[
   CCDelegateTo<RetCC_X86_64_C>
 ]>;
 
+// X86-Win64 wincall return-value convention.
+//
+// Per the wincall spec:
+//   - a scalar return value that fits in 64 bits is returned in RAX,
+//   - a scalar return value that fits in 128 bits is returned in RAX (low)
+//     and RDX (high),
+//   - non-scalar types (float, double, and vector types such as __m128)
+//     are returned in XMM0,
+//   - user-defined types of length 1, 2, 4, 8, 16, 32, 64 or 128 bits are
+//     returned in RAX (RDX for 128 bits).
+def RetCC_X86_Win64_WinCall : CallingConv<[
+  // The first 2 FP/Vector values are returned in XMM0/XMM1 (the spec says
+  // non-scalar types are returned in XMM0).
+  CCIfType<[f16, f32, f64, f128, v16i8, v8i16, v4i32, v2i64, v8f16, v8bf16, v4f32, v2f64],
+           CCIfSubtarget<"hasSSE1()",
+            CCAssignToReg<[XMM0, XMM1]>>>,
+
+  // 256-bit vectors are returned in YMM0.
+  CCIfType<[v32i8, v16i16, v8i32, v4i64, v16f16, v16bf16, v8f32, v4f64],
+           CCIfSubtarget<"hasAVX()",
+            CCAssignToReg<[YMM0]>>>,
+
+  // 512-bit vectors are returned in ZMM0.
+  CCIfType<[v64i8, v32i16, v16i32, v32f16, v32bf16, v16f32, v8f64, v8i64],
+           CCIfSubtarget<"hasAVX512()",
+            CCAssignToReg<[ZMM0]>>>,
+
+  // Scalars and user-defined types of length 1,2,4,8,16,32,64 or 128 bits are
+  // returned in RAX (and RDX for 128-bit values). Aggregates of up to 32 bytes
+  // (e.g. 4x size_t, like std::string/std::vector) are returned in
+  // RAX, RDX, RCX, R8.
+  CCIfType<[i8 ], CCAssignToReg<[AL, DL, CL, R8B]>>,
+  CCIfType<[i16], CCAssignToReg<[AX, DX, CX, R8W]>>,
+  CCIfType<[i32], CCAssignToReg<[EAX, EDX, ECX, R8D]>>,
+  CCIfType<[i64], CCAssignToReg<[RAX, RDX, RCX, R8]>>
+]>;
+
 // X86-64 vectorcall return-value convention.
 def RetCC_X86_64_Vectorcall : CallingConv<[
   // Vectorcall calling convention always returns FP values in XMMs.
@@ -483,6 +520,7 @@ def RetCC_X86_64 : CallingConv<[
   // Handle explicit CC selection
   CCIfCC<"CallingConv::Win64", CCDelegateTo<RetCC_X86_Win64_C>>,
   CCIfCC<"CallingConv::X86_64_SysV", CCDelegateTo<RetCC_X86_64_C>>,
+  CCIfCC<"CallingConv::X86_WinCall", CCDelegateTo<RetCC_X86_Win64_WinCall>>,
 
   // Handle Vectorcall CC
   CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<RetCC_X86_64_Vectorcall>>,
@@ -1083,12 +1121,6 @@ def CC_X86_Win64_WinCall : CallingConv<[
   // The 'CFGuardTarget' parameter, if any, is passed in RAX.
   CCIfCFGuardTarget<CCAssignToReg<[RAX]>>,
 
-  // 256 bit vectors are passed by pointer
-  CCIfType<[v32i8, v16i16, v8i32, v4i64, v16f16, v16bf16, v8f32, v4f64], CCPassIndirect<i64>>,
-
-  // 512 bit vectors are passed by pointer
-  CCIfType<[v64i8, v32i16, v16i32, v32f16, v32bf16, v16f32, v8f64, v8i64], CCPassIndirect<i64>>,
-
   // Long doubles are passed by pointer
   CCIfType<[f80], CCPassIndirect<i64>>,
 
@@ -1098,33 +1130,27 @@ def CC_X86_Win64_WinCall : CallingConv<[
   CCIfType<[f32], CCIfNotSubtarget<"hasSSE1()", CCBitConvertToType<i32>>>,
   CCIfType<[f64], CCIfNotSubtarget<"hasSSE1()", CCBitConvertToType<i64>>>,
 
-  // The first 8 FP/Vector arguments are passed in XMM registers.
+  // The first 8 FP/Vector arguments are passed in XMM registers. Integer and
+  // FP arguments are allocated independently (unlike Win64, which pairs each
+  // integer register with an XMM register and skips the partner); this uses
+  // all 8 GPRs and all 8 XMMs of the APX register file.
   CCIfType<[f16, f32, f64, v16i8, v8i16, v4i32, v2i64, v8f16, v8bf16, v4f32, v2f64],
            CCIfSubtarget<"hasSSE1()",
-           CCAssignToRegWithShadow<[XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7],
-                                   [RCX , RDX , R8  , R9  , R16, R17, R18, R19]>>>,
+           CCAssignToReg<[XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7]>>>,
   // 256 bit vectors are passed in YMM registers.
   CCIfType<[v32i8, v16i16, v8i32, v4i64, v16f16, v16bf16, v8f32, v4f64],
            CCIfSubtarget<"hasAVX()",
-            CCAssignToRegWithShadow<[YMM0, YMM1, YMM2, YMM3, YMM4, YMM5, YMM6, YMM7],
-                                   [RCX , RDX , R8  , R9  , R16, R17, R18, R19]>>>,
+            CCAssignToReg<[YMM0, YMM1, YMM2, YMM3, YMM4, YMM5, YMM6, YMM7]>>>,
   // 512 bit vectors are passed in ZMM registers.
   CCIfType<[v64i8, v32i16, v16i32, v32f16, v32bf16, v16f32, v8f64, v8i64],
            CCIfSubtarget<"hasAVX512()",
-            CCAssignToRegWithShadow<[ZMM0, ZMM1, ZMM2, ZMM3, ZMM4, ZMM5, ZMM6, ZMM7],
-                                   [RCX , RDX , R8  , R9  , R16, R17, R18, R19]>>>,
-  // 512 bit vectors are passed by pointer
-  CCIfType<[v64i8, v32i16, v16i32, v32f16, v32bf16, v16f32, v8f64, v8i64], CCPassIndirect<i64>>,
+            CCAssignToReg<[ZMM0, ZMM1, ZMM2, ZMM3, ZMM4, ZMM5, ZMM6, ZMM7]>>>,
 
-  // The first 4 integer arguments are passed in integer registers.
-  CCIfType<[i8 ], CCAssignToRegWithShadow<[CL  , DL  , R8B , R9B , R16B, R17B, R18B, R19B],
-                                          [XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7]>>,
-  CCIfType<[i16], CCAssignToRegWithShadow<[CX  , DX  , R8W , R9W , R16W, R17W, R18W, R19W],
-                                          [XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7]>>,
-  CCIfType<[i32], CCAssignToRegWithShadow<[ECX , EDX , R8D , R9D , R16D, R17D, R18D, R19D],
-                                          [XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7]>>,
-  CCIfType<[i64], CCAssignToRegWithShadow<[RCX , RDX , R8  , R9  , R16,  R17,  R18,  R19],
-                                          [XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7]>>,
+  // The first 8 integer arguments are passed in integer registers.
+  CCIfType<[i8 ], CCAssignToReg<[CL  , DL  , R8B , R9B , R16B, R17B, R18B, R19B]>>,
+  CCIfType<[i16], CCAssignToReg<[CX  , DX  , R8W , R9W , R16W, R17W, R18W, R19W]>>,
+  CCIfType<[i32], CCAssignToReg<[ECX , EDX , R8D , R9D , R16D, R17D, R18D, R19D]>>,
+  CCIfType<[i64], CCAssignToReg<[RCX , RDX , R8  , R9  , R16,  R17,  R18,  R19]>>,
 
   // Integer/FP values get stored in stack slots that are 8 bytes in size and
   // 8-byte aligned if there are no more registers to hold them.

>From bd737342723e4381d4df5c4a4124e2764e98604d Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Tue, 11 Aug 2026 20:48:01 +0800
Subject: [PATCH 16/26] [X86][Clang] Add x86_64apx sub-architecture with
 wincall as the Windows default

Treat x86_64apx as a sub-architecture of x86_64 (like arm64ec). The
x86_64apx-windows-msvc/x86_64apx-windows-gnu triples default to the
wincall calling convention and a 64 KiB section alignment passed to the
linker, so that Windows can move to 64 KiB pages (matching the NVIDIA
Grace CPU page size) by adopting this calling convention.

- Triple: add X86_64SubArch_apx, isX86_64APX()/isWindowsAPX(), and
  parse/getArchName support for "x86_64apx".
- X86 backend: ParseX86Triple enables the APX features (egpr, push2pop2,
  ppx, ndd, ccmp, nf, zu, jmpabs) for the sub-architecture so the
  backend works on the triple directly (previously only the clang
  frontend enabled them via initFeatureMap, which crashed the LiveVariables
  pass when R16-R19 were assigned as wincall argument registers).
- Clang frontend: x86_64apx targets enable the APX features by default.
- getDefaultCallingConv() returns CC_WinCall for isWindowsAPX() targets,
  so the wincall calling convention is the default.
- WinX86_64ABIInfo::classify: empty records in wincall take no register
  slots (ABIArgInfo::getIgnore()).
- Driver: MSVC and MinGW linkers get /section-alignment:0x10000 (MSVC) /
  --section-alignment=0x10000 (MinGW) by default for x86_64apx targets;
  a user -Wl section-alignment flag overrides it. The MSVC driver also
  passes /driver.
- Tests: Triple unit test, LLVM X86 wincall CC test, clang CodeGen tests
  for @win suffix / aggregates / empty structs, clang driver test, clang
  Sema test for the wincall attribute.
---
 clang/lib/Basic/Targets/X86.cpp               |  9 ++
 clang/lib/Basic/Targets/X86.h                 |  3 +
 clang/lib/CodeGen/Targets/X86.cpp             |  3 +
 clang/lib/Driver/ToolChains/MSVC.cpp          | 15 +++
 clang/lib/Driver/ToolChains/MinGW.cpp         | 13 +++
 clang/test/CodeGen/X86/wincall-abi.c          | 95 +++++++++++++++++++
 clang/test/CodeGen/wincall.c                  | 34 +++++++
 clang/test/CodeGenCXX/wincall-mangle.cpp      | 34 +++++++
 clang/test/Driver/wincall-x86_64apx.c         | 23 +++++
 clang/test/Sema/callingconv-wincall.c         | 10 ++
 llvm/include/llvm/TargetParser/Triple.h       | 14 +++
 .../X86/MCTargetDesc/X86MCTargetDesc.cpp      |  4 +
 llvm/lib/TargetParser/Triple.cpp              |  8 +-
 llvm/test/CodeGen/X86/wincall-cconv.ll        | 48 ++++++++++
 llvm/unittests/TargetParser/TripleTest.cpp    | 27 ++++++
 15 files changed, 339 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/CodeGen/X86/wincall-abi.c
 create mode 100644 clang/test/CodeGen/wincall.c
 create mode 100644 clang/test/CodeGenCXX/wincall-mangle.cpp
 create mode 100644 clang/test/Driver/wincall-x86_64apx.c
 create mode 100644 clang/test/Sema/callingconv-wincall.c
 create mode 100644 llvm/test/CodeGen/X86/wincall-cconv.ll

diff --git a/clang/lib/Basic/Targets/X86.cpp b/clang/lib/Basic/Targets/X86.cpp
index 8ab39b750dc99..6ed62b205c4b7 100644
--- a/clang/lib/Basic/Targets/X86.cpp
+++ b/clang/lib/Basic/Targets/X86.cpp
@@ -157,6 +157,15 @@ bool X86TargetInfo::initFeatureMap(
   if (getTriple().getArch() == llvm::Triple::x86_64)
     setFeatureEnabled(Features, "sse2", true);
 
+  // x86_64apx enables the APX features by default. LLVM's X86 backend models
+  // APX as the individual egpr/push2pop2/ppx/ndd/ccmp/nf/zu/jmpabs features
+  // (there is no single "apxf" feature), so enable each of them.
+  if (getTriple().isX86_64APX()) {
+    for (const char *Sub : {"egpr", "push2pop2", "ppx", "ndd", "ccmp", "nf",
+                            "zu", "jmpabs"})
+      setFeatureEnabled(Features, Sub, true);
+  }
+
   using namespace llvm::X86;
 
   SmallVector<StringRef, 16> CPUFeatures;
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index b37b3a4170270..00e0b93b12d1c 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -769,6 +769,9 @@ class LLVM_LIBRARY_VISIBILITY X86_64TargetInfo : public X86TargetInfo {
   }
 
   CallingConv getDefaultCallingConv() const override {
+    // x86_64apx targets default to the wincall calling convention.
+    if (getTriple().isWindowsAPX())
+      return CC_WinCall;
     return CC_C;
   }
 
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 7f92099b96575..8ebcc857bac8a 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -3480,6 +3480,9 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, unsigned &FreeSSERegs,
     // instead of by pointer/sret like the MS x64 ABI.
     if (IsWinCall && Width <= 256 && !Ty->isAnyComplexType() &&
         !Ty->isMemberPointerType()) {
+      // Empty C++ objects take no register slots.
+      if (isEmptyRecord(getContext(), Ty, /*AllowArrays=*/true))
+        return ABIArgInfo::getIgnore();
       if (IsReturnType)
         return ABIArgInfo::getDirect();
       // Pass as an integer of the aggregate size when it fits in one register,
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index 84bc2db6d2913..0eb6637687dcc 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -372,6 +372,21 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
                                            Output.getFilename() + "_dwo"));
   }
 
+  // x86_64apx targets default to a 64 KiB section alignment (matching the
+  // NVIDIA Grace CPU's 64 KiB page size) so that Windows can move to 64 KiB
+  // pages by adopting this calling convention. The user can still override it
+  // with their own -Wl,/section-alignment: flag.
+  if (TC.getTriple().isX86_64APX()) {
+    bool HasExplicitSectionAlignment = false;
+    for (Arg *A : Args.filtered(options::OPT_Wl_COMMA, options::OPT__SLASH_link))
+      if (StringRef(A->getValue()).contains_insensitive("section-alignment"))
+        HasExplicitSectionAlignment = true;
+    if (!HasExplicitSectionAlignment) {
+      CmdArgs.push_back("/section-alignment:0x10000");
+      CmdArgs.push_back("/driver");
+    }
+  }
+
   // Add filenames, libraries, and other linker inputs.
   for (const auto &Input : Inputs) {
     if (Input.isFilename()) {
diff --git a/clang/lib/Driver/ToolChains/MinGW.cpp b/clang/lib/Driver/ToolChains/MinGW.cpp
index 65120931d8181..28a883b49d402 100644
--- a/clang/lib/Driver/ToolChains/MinGW.cpp
+++ b/clang/lib/Driver/ToolChains/MinGW.cpp
@@ -168,6 +168,19 @@ void tools::MinGW::Linker::ConstructJob(Compilation &C, const JobAction &JA,
     CmdArgs.push_back("console");
   }
 
+  // x86_64apx targets default to a 64 KiB section alignment (matching the
+  // NVIDIA Grace CPU's 64 KiB page size) so that Windows can move to 64 KiB
+  // pages by adopting this calling convention. The user can still override it
+  // with their own -Wl,--section-alignment flag.
+  if (TC.getEffectiveTriple().isX86_64APX()) {
+    bool HasExplicitSectionAlignment = false;
+    for (Arg *A : Args.filtered(options::OPT_Wl_COMMA, options::OPT_Xlinker))
+      if (StringRef(A->getValue()).contains_insensitive("section-alignment"))
+        HasExplicitSectionAlignment = true;
+    if (!HasExplicitSectionAlignment)
+      CmdArgs.push_back("--section-alignment=0x10000");
+  }
+
   if (Args.hasArg(options::OPT_mdll))
     CmdArgs.push_back("--dll");
   else if (Args.hasArg(options::OPT_shared))
diff --git a/clang/test/CodeGen/X86/wincall-abi.c b/clang/test/CodeGen/X86/wincall-abi.c
new file mode 100644
index 0000000000000..8a7327e9136fc
--- /dev/null
+++ b/clang/test/CodeGen/X86/wincall-abi.c
@@ -0,0 +1,95 @@
+// RUN: %clang_cc1 -triple x86_64apx-unknown-windows-msvc -o - -emit-llvm %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64apx-unknown-windows-msvc -o - -S %s | FileCheck -check-prefix=ASM %s
+
+// wincall ABI for x86_64apx-windows targets:
+//   - empty objects take no register slots
+//   - aggregates <= 32 bytes are passed and returned in registers
+//   - larger aggregates use sret / indirect passing
+
+struct empty {};
+
+__attribute__((wincall)) void take_empty(struct empty e, int x);
+
+__attribute__((wincall)) struct empty make_empty(void);
+
+void call_empty(void) {
+  struct empty e;
+  take_empty(e, 42);
+  // CHECK-LABEL: define dso_local x86_wincallcc void @"\01call_empty at win"
+  // CHECK: call x86_wincallcc void @"\01take_empty at win"(i32 noundef 42)
+  make_empty();
+  // CHECK: call x86_wincallcc void @"\01make_empty at win"()
+  // CHECK: declare dso_local x86_wincallcc void @"\01take_empty at win"(i32 noundef)
+  // CHECK: declare dso_local x86_wincallcc void @"\01make_empty at win"()
+}
+
+__attribute__((wincall)) void take_empty2(struct empty e, int x) {}
+// CHECK-LABEL: define dso_local x86_wincallcc void @"\01take_empty2 at win"(i32 noundef %x)
+
+struct span2 {
+  unsigned long long *base;
+  unsigned long long len;
+};
+
+__attribute__((wincall)) struct span2 make_span(void) {
+  struct span2 s = {0, 1};
+  return s;
+}
+// CHECK-LABEL: define dso_local x86_wincallcc %struct.span2 @"\01make_span at win"
+// ASM-LABEL: make_span at win:
+// ASM: movq (%rsp), %rax
+// ASM: movq 8(%rsp), %rdx
+
+struct vec4 {
+  unsigned long long a, b, c, d;
+};
+
+__attribute__((wincall)) struct vec4 make_vec4(void) {
+  struct vec4 v = {1, 2, 3, 4};
+  return v;
+}
+// CHECK-LABEL: define dso_local x86_wincallcc %struct.vec4 @"\01make_vec4 at win"
+// ASM-LABEL: make_vec4 at win:
+// ASM: movq (%rsp), %rax
+// ASM: movq 8(%rsp), %rdx
+// ASM: movq 16(%rsp), %rcx
+// ASM: movq 24(%rsp), %r8
+
+struct big32 {
+  unsigned long long a[5];
+};
+
+__attribute__((wincall)) struct big32 make_big(void) {
+  struct big32 b = {{1, 2, 3, 4, 5}};
+  return b;
+}
+// CHECK-LABEL: define dso_local x86_wincallcc void @"\01make_big at win"(ptr dead_on_unwind noalias writable sret(%struct.big32) align 8 %agg.result)
+// ASM-LABEL: make_big at win:
+
+// FP and integer registers are allocated independently: a double between two
+// ints does not consume or skip an integer register.
+__attribute__((wincall)) void take_mixed(int a, double b, int c, double d,
+                                        int e, double f) {
+  volatile int sink1 = a + c + e;
+  volatile double sink2 = b + d + f;
+  (void)sink1;
+  (void)sink2;
+}
+// CHECK-LABEL: define dso_local x86_wincallcc void @"\01take_mixed at win"(i32 noundef %a, double noundef %b, i32 noundef %c, double noundef %d, i32 noundef %e, double noundef %f)
+void call_mixed(void) {
+  take_mixed(1, 2.0, 3, 4.0, 5, 6.0);
+  // CHECK-LABEL: define dso_local x86_wincallcc void @"\01call_mixed at win"
+  // CHECK: call x86_wincallcc void @"\01take_mixed at win"(i32 noundef 1, double noundef 2.000000e+00, i32 noundef 3, double noundef 4.000000e+00, i32 noundef 5, double noundef 6.000000e+00)
+}
+// ASM-LABEL: take_mixed at win:
+// ASM: movsd %xmm2, 48(%rsp)
+// ASM: movl %r8d, 44(%rsp)
+// ASM: movsd %xmm1, 32(%rsp)
+// ASM: movl %edx, 28(%rsp)
+// ASM: movsd %xmm0, 16(%rsp)
+// ASM: movl %ecx, 12(%rsp)
+
+__attribute__((wincall)) __int128 make_i128(void) { return (__int128)1 << 64 | 2; }
+// ASM-LABEL: make_i128 at win:
+// ASM: movl $2, %eax
+// ASM: movl $1, %edx
diff --git a/clang/test/CodeGen/wincall.c b/clang/test/CodeGen/wincall.c
new file mode 100644
index 0000000000000..61dbb1c6a8dda
--- /dev/null
+++ b/clang/test/CodeGen/wincall.c
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple x86_64apx-unknown-windows-msvc -emit-llvm  -o - %s | FileCheck -check-prefix=MSVC %s
+// RUN: %clang_cc1 -triple x86_64apx-unknown-windows-gnu -emit-llvm  -o - %s | FileCheck -check-prefix=GNU %s
+
+// The wincall calling convention is the default for x86_64apx-windows targets
+// and appends a @win suffix to the symbol so the linker can catch calling
+// convention mismatches.
+
+void plain(int, int, int);
+
+void __attribute__((wincall)) wc(int, int, int);
+
+void caller(void) {
+  // MSVC-LABEL: define dso_local x86_wincallcc void @"\01caller at win"
+  // GNU-LABEL: define dso_local x86_wincallcc void @"\01caller at win"
+  plain(1, 2, 3);
+  // MSVC: call x86_wincallcc void @"\01plain at win"(i32 noundef 1, i32 noundef 2, i32 noundef 3)
+  // GNU: call x86_wincallcc void @"\01plain at win"(i32 noundef 1, i32 noundef 2, i32 noundef 3)
+  wc(1, 2, 3);
+  // MSVC: call x86_wincallcc void @"\01wc at win"(i32 noundef 1, i32 noundef 2, i32 noundef 3)
+  // GNU: call x86_wincallcc void @"\01wc at win"(i32 noundef 1, i32 noundef 2, i32 noundef 3)
+}
+
+void plain(int a, int b, int c) {
+  // MSVC-LABEL: define dso_local x86_wincallcc void @"\01plain at win"
+  // GNU-LABEL: define dso_local x86_wincallcc void @"\01plain at win"
+  wc(a, b, c);
+  // MSVC: call x86_wincallcc void @"\01wc at win"
+  // GNU: call x86_wincallcc void @"\01wc at win"
+}
+
+void __attribute__((wincall)) wc(int a, int b, int c) {
+  // MSVC-LABEL: define dso_local x86_wincallcc void @"\01wc at win"
+  // GNU-LABEL: define dso_local x86_wincallcc void @"\01wc at win"
+}
diff --git a/clang/test/CodeGenCXX/wincall-mangle.cpp b/clang/test/CodeGenCXX/wincall-mangle.cpp
new file mode 100644
index 0000000000000..8efb01f729632
--- /dev/null
+++ b/clang/test/CodeGenCXX/wincall-mangle.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple x86_64apx-unknown-windows-gnu -o - -emit-llvm %s | FileCheck %s
+
+// wincall is the default calling convention for x86_64apx-windows targets.
+// C++ symbols get a @win suffix (both Itanium and MSVC-style manglings) so
+// that the linker can catch calling-convention mismatches, and function
+// pointer types carry the wincall vendor qualifier in the Itanium mangling.
+
+typedef void(__attribute__((wincall)) *W)(int);
+
+struct C {
+  void __attribute__((wincall)) m(int);
+};
+
+void C::m(int a) {
+  // CHECK-LABEL: define dso_local x86_wincallcc void @"\01_ZN1C1mEi at win"
+  (void)a;
+}
+
+void f(int);
+
+void g() {
+  // CHECK-LABEL: define dso_local x86_wincallcc void @"\01_Z1gv at win"
+  f(1);
+  // CHECK: call x86_wincallcc void @"\01_Z1fi at win"
+}
+
+// Function pointer types get a U7wincall vendor qualifier.
+template <typename T> T func_as_int(T x);
+W w;
+W test() {
+  // CHECK-LABEL: define dso_local x86_wincallcc noundef ptr @"\01_Z4testv at win"
+  // CHECK: call x86_wincallcc noundef ptr @"\01_Z11func_as_intIPU7wincallFviEET_S2_ at win"
+  return func_as_int<W>(w);
+}
diff --git a/clang/test/Driver/wincall-x86_64apx.c b/clang/test/Driver/wincall-x86_64apx.c
new file mode 100644
index 0000000000000..1d1680857917a
--- /dev/null
+++ b/clang/test/Driver/wincall-x86_64apx.c
@@ -0,0 +1,23 @@
+// x86_64apx-windows targets default to the wincall calling convention and pass
+// a 64 KiB section alignment to the linker.
+
+// RUN: %clang --target=x86_64apx-unknown-windows-msvc -### %s 2>&1 | FileCheck --check-prefix=MSVC-APX %s
+// RUN: %clang --target=x86_64apx-unknown-windows-gnu -### %s 2>&1 | FileCheck --check-prefix=GNU-APX %s
+
+// Non-APX Windows targets do not get the section alignment flag.
+// RUN: %clang --target=x86_64-unknown-windows-msvc -### %s 2>&1 | FileCheck --check-prefix=MSVC-PLAIN %s
+// RUN: %clang --target=x86_64-unknown-windows-gnu -### %s 2>&1 | FileCheck --check-prefix=GNU-PLAIN %s
+
+// MSVC-APX: link.exe"
+// MSVC-APX-SAME: "-nologo" "/section-alignment:0x10000" "/driver"
+// MSVC-PLAIN-NOT: section-alignment
+// GNU-APX: "--section-alignment=0x10000"
+// GNU-PLAIN-NOT: section-alignment
+
+// User-provided -Wl section-alignment overrides the default.
+// RUN: %clang --target=x86_64apx-unknown-windows-msvc -Wl,/section-alignment:0x20000 -### %s 2>&1 | FileCheck --check-prefix=MSVC-OVERRIDE %s
+// RUN: %clang --target=x86_64apx-unknown-windows-gnu -Wl,--section-alignment=0x20000 -### %s 2>&1 | FileCheck --check-prefix=GNU-OVERRIDE %s
+// MSVC-OVERRIDE-NOT: "/section-alignment:0x10000"
+// MSVC-OVERRIDE: "/section-alignment:0x20000"
+// GNU-OVERRIDE-NOT: --section-alignment=0x10000
+// GNU-OVERRIDE: --section-alignment=0x20000
diff --git a/clang/test/Sema/callingconv-wincall.c b/clang/test/Sema/callingconv-wincall.c
new file mode 100644
index 0000000000000..3ae621b0a7e94
--- /dev/null
+++ b/clang/test/Sema/callingconv-wincall.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64apx-pc-windows-msvc %s
+
+// wincall is the default calling convention on x86_64apx-windows targets.
+void __attribute__((wincall)) foo(void);
+void __attribute__((cdecl)) cdeclfoo(void);
+
+void (*pw)(void) = foo; // no error: plain function pointers are wincall by default
+void (*pc)(void) = cdeclfoo; // expected-error{{incompatible function pointer types}}
+
+void (__attribute__((wincall)) *pw2)(void) = foo; // no error: same calling convention
diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h
index 18634a8bbe6ff..9af359c373b30 100644
--- a/llvm/include/llvm/TargetParser/Triple.h
+++ b/llvm/include/llvm/TargetParser/Triple.h
@@ -161,6 +161,7 @@ class Triple {
     AArch64SubArch_lfi,
 
     X86_64SubArch_lfi,
+    X86_64SubArch_apx,
 
     KalimbaSubArch_v3,
     KalimbaSubArch_v4,
@@ -1009,6 +1010,19 @@ class Triple {
             getSubArch() == Triple::X86_64SubArch_lfi);
   }
 
+  /// Tests whether the target is x86-64 with Intel APX (wincall), i.e. the
+  /// x86_64apx architecture.
+  bool isX86_64APX() const {
+    return getArch() == Triple::x86_64 &&
+           getSubArch() == Triple::X86_64SubArch_apx;
+  }
+
+  /// Tests whether the target is the Windows x86_64apx target, which defaults
+  /// to the wincall calling convention.
+  bool isWindowsAPX() const {
+    return isX86_64APX() && isOSWindows();
+  }
+
   /// Tests whether the target supports the EHABI exception
   /// handling standard.
   bool isTargetEHABICompatible() const {
diff --git a/llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.cpp b/llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.cpp
index e6addc00971f5..6f56386e4f96c 100644
--- a/llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.cpp
+++ b/llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.cpp
@@ -59,6 +59,10 @@ std::string X86_MC::ParseX86Triple(const Triple &TT) {
   if (TT.isX32())
     FS += ",+x32";
 
+  if (TT.getSubArch() == Triple::X86_64SubArch_apx) {
+    FS += ",+egpr,+push2pop2,+ppx,+ndd,+ccmp,+nf,+zu,+jmpabs";
+  }
+
   return FS;
 }
 
diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp
index 1e421c428d8b4..5d11c352a01a1 100644
--- a/llvm/lib/TargetParser/Triple.cpp
+++ b/llvm/lib/TargetParser/Triple.cpp
@@ -198,6 +198,8 @@ StringRef Triple::getArchName(ArchType Kind, SubArchType SubArch) {
   case Triple::x86_64:
     if (SubArch == X86_64SubArch_lfi)
       return "x86_64_lfi";
+    if (SubArch == X86_64SubArch_apx)
+      return "x86_64apx";
     break;
   case Triple::spirv:
     switch (SubArch) {
@@ -598,7 +600,8 @@ Triple::ArchType Triple::parseArch(StringRef ArchName) {
           .Cases({"i386", "i486", "i586", "i686"}, Triple::x86)
           // FIXME: Do we need to support these?
           .Cases({"i786", "i886", "i986"}, Triple::x86)
-          .Cases({"amd64", "x86_64", "x86_64h", "x86_64_lfi"}, Triple::x86_64)
+          .Cases({"amd64", "x86_64", "x86_64h", "x86_64_lfi", "x86_64apx"},
+                 Triple::x86_64)
           .Cases({"powerpc", "powerpcspe", "ppc", "ppc32"}, Triple::ppc)
           .Cases({"powerpcle", "ppcle", "ppc32le"}, Triple::ppcle)
           .Cases({"powerpc64", "ppu", "ppc64"}, Triple::ppc64)
@@ -750,6 +753,9 @@ Triple::SubArchType Triple::parseSubArch(StringRef SubArchName) {
   if (SubArchName == "x86_64_lfi")
     return Triple::X86_64SubArch_lfi;
 
+  if (SubArchName == "x86_64apx")
+    return Triple::X86_64SubArch_apx;
+
   if (SubArchName.starts_with("spirv"))
     return StringSwitch<Triple::SubArchType>(SubArchName)
         .EndsWith("v1.0", Triple::SPIRVSubArch_v10)
diff --git a/llvm/test/CodeGen/X86/wincall-cconv.ll b/llvm/test/CodeGen/X86/wincall-cconv.ll
new file mode 100644
index 0000000000000..f3fe3b40a23f8
--- /dev/null
+++ b/llvm/test/CodeGen/X86/wincall-cconv.ll
@@ -0,0 +1,48 @@
+; RUN: llc -mtriple=x86_64apx-unknown-windows-msvc < %s | FileCheck %s
+
+; The wincall calling convention (x86_64 APX, default for x86_64apx-windows)
+; passes the first 8 integer args in RCX,RDX,R8,R9,R16,R17,R18,R19 and the
+; first 8 FP/vector args in XMM0-XMM7, allocated independently (no Win64-style
+; register pairing/skipping).
+
+declare x86_wincallcc void @wincall_thunk(i64, i64, i64, i64, i64, i64, i64, i64)
+
+; CHECK-LABEL: call_8_int:
+; CHECK:       subq $40, %rsp
+; CHECK-NEXT:  movq 80(%rsp), %r16
+; CHECK-NEXT:  movq 88(%rsp), %r17
+; CHECK-NEXT:  movq 96(%rsp), %r18
+; CHECK-NEXT:  movq 104(%rsp), %r19
+; CHECK-NEXT:  callq wincall_thunk
+; CHECK-NEXT:  addq $40, %rsp
+; CHECK-NEXT:  retq
+define void @call_8_int(i64 %a, i64 %b, i64 %c, i64 %d,
+                        i64 %e, i64 %f, i64 %g, i64 %h) nounwind {
+entry:
+  call x86_wincallcc void @wincall_thunk(i64 %a, i64 %b, i64 %c, i64 %d,
+                                         i64 %e, i64 %f, i64 %g, i64 %h)
+  ret void
+}
+
+; Callee side: the first 8 integer args arrive in RCX,RDX,R8,R9,R16,R17,R18,R19.
+; CHECK-LABEL: sum8:
+; CHECK:       addq %rdx, %rcx
+; CHECK-NEXT:  addq %r9, %r8
+; CHECK-NEXT:  addq %r8, %rcx
+; CHECK-NEXT:  addq %r17, %r16
+; CHECK-NEXT:  addq %r18, %r16
+; CHECK-NEXT:  addq %r16, %rcx
+; CHECK-NEXT:  addq %r19, %rax
+; CHECK-NEXT:  retq
+define x86_wincallcc i64 @sum8(i64 %a, i64 %b, i64 %c, i64 %d,
+                               i64 %e, i64 %f, i64 %g, i64 %h) nounwind {
+entry:
+  %s1 = add i64 %a, %b
+  %s2 = add i64 %s1, %c
+  %s3 = add i64 %s2, %d
+  %s4 = add i64 %s3, %e
+  %s5 = add i64 %s4, %f
+  %s6 = add i64 %s5, %g
+  %s7 = add i64 %s6, %h
+  ret i64 %s7
+}
diff --git a/llvm/unittests/TargetParser/TripleTest.cpp b/llvm/unittests/TargetParser/TripleTest.cpp
index 219e3ce9c0baf..7484cf46beb38 100644
--- a/llvm/unittests/TargetParser/TripleTest.cpp
+++ b/llvm/unittests/TargetParser/TripleTest.cpp
@@ -4027,4 +4027,31 @@ TEST(DataLayoutTest, CheriRISCV32) {
               testing::HasSubstr("A200-P200-G200"));
 }
 
+TEST(TripleTest, X86_64APX) {
+  {
+    Triple T = Triple("x86_64apx-pc-windows-msvc");
+    EXPECT_EQ(Triple::x86_64, T.getArch());
+    EXPECT_EQ(Triple::X86_64SubArch_apx, T.getSubArch());
+    EXPECT_TRUE(T.isX86_64APX());
+    EXPECT_TRUE(T.isWindowsAPX());
+  }
+  {
+    Triple T = Triple("x86_64apx-pc-linux-gnu");
+    EXPECT_EQ(Triple::x86_64, T.getArch());
+    EXPECT_EQ(Triple::X86_64SubArch_apx, T.getSubArch());
+    EXPECT_TRUE(T.isX86_64APX());
+    EXPECT_FALSE(T.isWindowsAPX());
+  }
+  {
+    Triple T;
+    T.setArch(Triple::x86_64, Triple::X86_64SubArch_apx);
+    EXPECT_EQ("x86_64apx", T.getArchName());
+  }
+  {
+    Triple T = Triple("x86_64-pc-windows-msvc");
+    EXPECT_FALSE(T.isX86_64APX());
+    EXPECT_FALSE(T.isWindowsAPX());
+  }
+}
+
 } // end anonymous namespace

>From a4b2e975b368d738ceb16b510eaca61f705468ea Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Tue, 11 Aug 2026 22:26:01 +0800
Subject: [PATCH 17/26] [Docs] Add documentation for the WinCall calling
 convention

Add a dedicated WinCall.md page describing the x86-64 wincall calling
convention (x86_wincallcc / CallingConv::X86_WinCall, CC ID 128):

- what WinCall is and when it is used (default for x86_64apx-windows-*
  triples, and the wincall attribute)
- the x86_64apx sub-architecture and the APX feature set it enables
- argument passing: 8 GPRs (RCX/RDX/R8/R9/R16-R19), 8 XMM/YMM/ZMM
  registers allocated independently of the integer registers, aggregate
  and empty-object rules
- return values per RetCC_X86_Win64_WinCall
- callee-saved registers (CSR_Win64_APX)
- the @win symbol decoration and the U7wincall Itanium vendor qualifier
- the 64 KiB section alignment passed by the clang driver

Also cross-reference WinCall from LangRef, CodeGenerator and UserGuides,
and replace the wincall attribute documentation in AttrDocs.td (removing
the unrelated Microsoft Developer Community proposal link) with an
accurate summary that points to the new page.
---
 clang/include/clang/Basic/AttrDocs.td |  26 ++-
 llvm/docs/CodeGenerator.md            |   5 +
 llvm/docs/LangRef.md                  |   4 +
 llvm/docs/UserGuides.md               |   6 +
 llvm/docs/WinCall.md                  | 239 ++++++++++++++++++++++++++
 5 files changed, 273 insertions(+), 7 deletions(-)
 create mode 100644 llvm/docs/WinCall.md

diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 6ab891bae59ab..2ed04e43272b4 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3752,13 +3752,25 @@ COM applications. See the documentation for `__stdcall`_ on MSDN.
 def WinCallDocs : Documentation {
   let Category = DocCatCallingConvs;
   let Content = [{
-Wincall introduces a new calling convention for x86_64, designed to support Intel APX.
-This convention addresses calling convention challenges for C++ types, such as `std::span`,
-on the Windows x86_64 platform.
-
-For detailed information, refer to the proposal for `__wincall`_ on Microsoft Developer Community.
-
-.. _`__wincall`: https://developercommunity.visualstudio.com/t/I-present-a-novel-calling-convention-nam/10433601?q=wincall
+The ``wincall`` attribute applies the WinCall calling convention to a
+function. WinCall is an x86-64-only calling convention for Windows targets
+that makes use of the registers added by Intel APX.
+
+Unlike the Microsoft x64 ABI, WinCall:
+- passes the first eight integer arguments in RCX, RDX, R8, R9, R16, R17,
+  R18, R19 (R16-R19 are APX registers),
+- passes the first eight floating-point/vector arguments in XMM0-XMM7
+  (YMM0-YMM7 for ``__m256``, ZMM0-ZMM7 for ``__m512``), allocated
+  independently of the integer registers,
+- passes/returns aggregates up to 32 bytes directly in registers instead of
+  by pointer, and
+- takes no register slots for empty C++ objects.
+
+On targets where WinCall is not supported the attribute is ignored with a
+warning.
+
+See the `LLVM WinCall documentation <https://llvm.org/docs/WinCall.html>`_ for
+the full specification.
   }];
 }
 
diff --git a/llvm/docs/CodeGenerator.md b/llvm/docs/CodeGenerator.md
index 32242b413c2a8..6aefe48b124f0 100644
--- a/llvm/docs/CodeGenerator.md
+++ b/llvm/docs/CodeGenerator.md
@@ -1903,6 +1903,11 @@ The following target-specific calling conventions are known to backend:
   others via stack. Callee is responsible for stack cleaning. This convention is
   used by MSVC by default for methods in its ABI (CC ID = 70).
 
+* **x86_WinCall** --- The WinCall calling convention for Windows x86-64 (APX).
+  It is the default calling convention for the ``x86_64apx-windows-*`` triples
+  and passes up to eight integer and eight FP/SIMD arguments in registers,
+  allocated independently. See {doc}`WinCall` for details (CC ID = 128).
+
 (X86 addressing mode)=
 
 #### Representing X86 addressing modes in MachineInstrs
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 3a616e8a29fcf..ff9836fd1d33e 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -535,6 +535,10 @@ More calling conventions can be added/defined on an as-needed basis, to
 support Pascal conventions or any other well-known target-independent
 convention.
 
+The target-specific calling conventions are documented in their respective
+target documentation. In particular, the X86 WinCall calling convention
+(spelled ``x86_wincallcc`` in IR) is described in {doc}`WinCall`.
+
 (visibilitystyles)=
 
 ### Visibility Styles
diff --git a/llvm/docs/UserGuides.md b/llvm/docs/UserGuides.md
index 108601427697c..0189e64c9efca 100644
--- a/llvm/docs/UserGuides.md
+++ b/llvm/docs/UserGuides.md
@@ -72,6 +72,7 @@ RISCV/RISCVVCIX
 SandboxIR
 Telemetry
 LFI
+WinCall
 AdminTasks
 Benchmarking
 CMakePrimer
@@ -402,3 +403,8 @@ yaml2obj
 - {doc}`LFI <LFI>`
 
   This document describes the Lightweight Fault Isolation (LFI) target in LLVM.
+
+- {doc}`WinCall <WinCall>`
+
+  This document describes the WinCall calling convention for Windows x86-64
+  (APX), including the `x86_64apx` sub-architecture.
diff --git a/llvm/docs/WinCall.md b/llvm/docs/WinCall.md
new file mode 100644
index 0000000000000..2049dcab3d443
--- /dev/null
+++ b/llvm/docs/WinCall.md
@@ -0,0 +1,239 @@
+# The WinCall Calling Convention
+
+## What WinCall is
+
+WinCall is an x86-64 **calling convention for Windows targets**. It is
+spelled ``x86_wincallcc`` in LLVM IR and corresponds to
+``CallingConv::X86_WinCall`` (CC ID 128).
+
+Its purpose is to exploit the 16 additional general-purpose registers
+(R16-R31) introduced by Intel APX. On a conventional Windows x64 ABI only
+four integer arguments can be passed in registers; WinCall doubles that to
+eight by using R16-R19 as argument registers. It also relaxes the aggregate
+passing and return rules of the Microsoft x64 ABI so that C++ types such as
+``std::span``, ``std::string`` and ``std::vector`` (a ``{pointer, size}``
+pair or a four-word object) can travel in registers.
+
+WinCall does **not** replace any existing ABI. Existing Windows APIs keep
+their ``stdcall``, ``cdecl`` and ``fastcall`` conventions; the convention is
+opt-in and only affects code that is explicitly built for it.
+
+## When WinCall is used
+
+WinCall applies in two situations:
+
+1. It is the **default calling convention** of the ``x86_64apx-windows-msvc``
+   and ``x86_64apx-windows-gnu`` target triples
+   (``X86_64TargetInfo::getDefaultCallingConv`` returns ``CC_WinCall`` for
+   ``getTriple().isWindowsAPX()``).
+
+2. It can be selected per-function with the ``wincall`` attribute
+   (``__attribute__((wincall))``, ``__wincall``, ``_wincall``), which maps to
+   ``CC_WinCall`` in the frontend.
+
+WinCall is **x86-64 only**. On 32-bit x86 or on any non-x86 target the
+attribute is ignored with a warning
+(``X86TargetInfo::checkCallingConvention`` does not accept it there).
+
+## The x86_64apx sub-architecture
+
+The ``x86_64apx`` triple component is a *sub-architecture* of ``x86_64``,
+modelled on how ``arm64ec`` is a sub-architecture of ``aarch64``:
+
+- ``Triple::X86_64SubArch_apx`` records it; ``getArch()`` still returns
+  ``Triple::x86_64``.
+- ``Triple::isX86_64APX()`` is true for any ``x86_64apx-*`` triple.
+- ``Triple::isWindowsAPX()`` is true for ``x86_64apx-*`` triples whose OS is
+  Windows; only these default to WinCall.
+- The name round-trips through ``Triple::getArchName()`` /
+  ``Triple::parseSubArch()``.
+
+The sub-architecture also turns on the APX instruction-set extensions. Both
+the clang frontend (``X86TargetInfo::initFeatureMap``) and the LLVM backend
+(``X86_MC::ParseX86Triple``) enable:
+
+```
++egpr,+push2pop2,+ppx,+ndd,+ccmp,+nf,+zu,+jmpabs
+```
+
+for ``x86_64apx`` targets. There is no single ``apxf`` feature bit; APX is
+modelled as this set of independent features. The backend must enable them
+from the triple itself (not just from the frontend), because backend passes
+size their register tables from ``X86RegisterInfo::getNumSupportedRegs()``,
+which returns the full register count only when ``egpr`` is enabled. If the
+registers were not enabled, WinCall would still assign R16-R19 as argument
+registers, and passes such as ``LiveVariables`` would index out of bounds.
+
+## Argument passing
+
+### Integer and pointer arguments
+
+The first **eight** integer-class arguments are passed in registers:
+
+| arg # | 1    | 2    | 3    | 4    | 5    | 6    | 7    | 8    |
+|-------|------|------|------|------|------|------|------|------|
+| i8    | CL   | DL   | R8B  | R9B  | R16B | R17B | R18B | R19B |
+| i16   | CX   | DX   | R8W  | R9W  | R16W | R17W | R18W | R19W |
+| i32   | ECX  | EDX  | R8D  | R9D  | R16D | R17D | R18D | R19D |
+| i64   | RCX  | RDX  | R8   | R9   | R16  | R17  | R18  | R19  |
+
+``i1`` arguments are promoted to ``i8`` first. Remaining integer arguments go
+on the stack in 8-byte, 8-byte-aligned slots.
+
+This list is used by ``bool``, all integer types, pointers, ``__m64``,
+``__int128``/``__uint128_t`` (which is split into two i64s), and
+``std::float128_t``.
+
+### Floating-point, SIMD and vector arguments
+
+The first **eight** FP/SIMD arguments are passed in XMM registers (or
+YMM/ZMM for wider vectors), **independently** of the integer registers:
+
+| arg # | 1    | 2    | 3    | 4    | 5    | 6    | 7    | 8    |
+|-------|------|------|------|------|------|------|------|------|
+| f16/f32/f64, 128-bit vectors | XMM0 | XMM1 | XMM2 | XMM3 | XMM4 | XMM5 | XMM6 | XMM7 |
+| 256-bit vectors (``__m256``) | YMM0 | YMM1 | YMM2 | YMM3 | YMM4 | YMM5 | YMM6 | YMM7 |
+| 512-bit vectors (``__m512``) | ZMM0 | ZMM1 | ZMM2 | ZMM3 | ZMM4 | ZMM5 | ZMM6 | ZMM7 |
+
+The 256-bit rules require AVX, the 512-bit rules require AVX-512. Stack slots
+are 16 bytes for 128-bit vectors, 32 bytes for 256-bit vectors and 64 bytes
+for 512-bit vectors, aligned to their size.
+
+### Independent integer/FP allocation
+
+Unlike the Microsoft x64 ABI, which pairs each integer register with an XMM
+register and *skips* the partner when the other class is used, WinCall
+allocates the integer and FP register lists **independently**. A function
+``f(int, double, int, double, int, double, ...)`` therefore uses
+ECX, EDX, R8D, ... for its ints and XMM0, XMM1, XMM2, ... for its doubles
+with no skipping.
+
+This is safe across wincall/non-wincall calls because each function's
+convention is fixed at compile time, and the ``@win`` symbol decoration (see
+below) lets the linker catch mismatches.
+
+### Aggregates
+
+Clang's ``WinX86_64ABIInfo::classify`` implements the aggregate rules for
+``CC_WinCall`` (this is a frontend rule layered on top of the IR-level
+convention):
+
+- A record that fits in **1, 2, 4, 8, 16 or 32 bytes** is passed **directly
+  in registers** (not by pointer/sret like the MS x64 ABI). A 4-``size_t``
+  struct — such as ``std::string`` or ``std::vector`` — therefore travels in
+  RCX, RDX, R8, R9.
+- A record of up to 64 bits is coerced to an integer of its size and uses
+  **one** GPR; a larger record (up to 32 bytes) is **expanded** into its
+  8-byte parts.
+- **Empty records** (``struct empty {}``) consume **no register slots**;
+  ``classify`` returns ``ABIArgInfo::getIgnore()`` for them.
+- Records larger than 32 bytes, records with a flexible array member, and
+  non-trivial C++ records (per ``getRecordArgABI``) are passed by reference.
+- ``f80`` (long double) is passed by pointer.
+- ``__int128`` is split into two GPRs.
+- Complex types and member pointers are handled as in the MS x64 ABI.
+
+## Return values
+
+``RetCC_X86_Win64_WinCall``:
+
+| Return type | Registers |
+|-------------|-----------|
+| Scalar (i8/i16/i32/i64), ``__m64`` | RAX (first value), RDX, RCX, R8 |
+| Scalar up to 128 bits (``__int128``, ``__uint128_t``, ``std::float128_t``) | RAX (low) + RDX (high) |
+| f16/f32/f64 and 128-bit vectors | XMM0 (first value), XMM1 |
+| 256-bit vectors | YMM0 |
+| 512-bit vectors | ZMM0 |
+| Aggregates up to 32 bytes | RAX, RDX, RCX, R8 (expanded) |
+| Aggregates larger than 32 bytes | hidden pointer (sret) |
+
+Empty records consume no return register. The state of unused bits in RAX or
+XMM0 is undefined.
+
+## Callee-saved registers
+
+WinCall keeps the Windows x64 caller-saved model. The callee-saved set is
+``CSR_Win64_APX`` (used whenever ``egpr`` is enabled, since the frontend
+always enables it for ``x86_64apx``):
+
+```
+RBX, RBP, RDI, RSI, R12, R13, R14, R15, R30, R31, XMM6-XMM15
+```
+
+R30 and R31 are APX registers preserved in addition to the standard Windows
+x64 set.
+
+## Symbol decoration: the ``@win`` suffix
+
+Every WinCall function gets a ``@win`` suffix appended to its symbol name, so
+that the linker and loader can detect a caller and callee compiled with
+mismatched calling conventions. This mirrors the ``@N`` parameter-size
+suffix of ``stdcall`` on i386, but uses a fixed tag that cannot collide with
+``stdcall``'s ``@0``/``@16`` decorations.
+
+The decoration is applied uniformly across all three name manglings
+(implemented in ``clang/lib/AST/Mangle.cpp`` for C, and via the suffix check
+in ``clang/lib/AST/MicrosoftMangle.cpp``; Itanium C++ names are decorated by
+the same ``@win`` suffix path):
+
+| Language / ABI | Symbol |
+|----------------|--------|
+| C              | ``foo at win`` |
+| C++ MS ABI     | ``?foo@@YAXH at Z@win`` |
+| C++ Itanium ABI | ``_Z3fooi at win`` |
+
+Additionally, the Itanium mangling encodes WinCall in function-pointer types
+as the vendor extended qualifier ``U7wincall``:
+
+```cpp
+using W = void (__attribute__((wincall)) *)(int);
+template <typename T> T id(T x);
+W w;
+// _Z2idIPU7wincallFviEET_S2_
+```
+
+## Section alignment
+
+For ``x86_64apx`` targets the clang driver passes a **64 KiB section
+alignment** to the linker by default, so that the OS can use 64 KiB pages
+(matching the page size of the NVIDIA Grace CPU) for code and data built with
+this convention:
+
+| Toolchain | Default flag |
+|-----------|--------------|
+| MSVC ``link.exe`` | ``/section-alignment:0x10000`` and ``/driver`` |
+| MinGW ``ld``      | ``--section-alignment=0x10000`` |
+
+The flags are added by the driver in ``clang/lib/Driver/ToolChains/MSVC.cpp``
+and ``MinGW.cpp``, not by the assembler. If the user passes their own
+section-alignment ``-Wl`` flag, the driver's default is suppressed in favour
+of the user's value.
+
+## Relation to "herbceptions" (deterministic exceptions)
+
+WinCall is designed so that Herb Sutter's proposed zero-overhead
+deterministic exceptions (P0709, "herbceptions") can represent ``std::error``
+as a two-register value passed/returned in RAX (domain pointer) and RDX
+(code), with the carry flag (CF) as the success/failure discriminant. This is
+the design intent of the convention; the discriminant-lowering support in
+LLVM is independent of WinCall and is not part of this calling convention
+itself.
+
+## Implementation notes
+
+- IR calling convention: ``x86_wincallcc`` / ``CallingConv::X86_WinCall``
+  (CC ID 128).
+- TableGen conventions in ``llvm/lib/Target/X86/X86CallingConv.td``:
+  ``CC_X86_Win64_WinCall`` (arguments) and ``RetCC_X86_Win64_WinCall``
+  (returns), dispatched from the root ``CC_X86_64`` convention.
+- Frontend ABI rules: ``WinX86_64ABIInfo::classify`` in
+  ``clang/lib/CodeGen/Targets/X86.cpp``.
+- Default CC: ``X86_64TargetInfo::getDefaultCallingConv`` in
+  ``clang/lib/Basic/Targets/X86.h``.
+- Symbol decoration: ``clang/lib/AST/Mangle.cpp`` and
+  ``clang/lib/AST/MicrosoftMangle.cpp``; vendor qualifier in
+  ``clang/lib/AST/ItaniumMangle.cpp``.
+- Triples: ``Triple::X86_64SubArch_apx`` in ``llvm/lib/TargetParser/Triple.*``.
+- Driver flags: ``clang/lib/Driver/ToolChains/MSVC.cpp`` and ``MinGW.cpp``.
+- Register tables must include the APX registers when ``egpr`` is enabled;
+  see ``X86RegisterInfo::getNumSupportedRegs``.

>From 6d262377d0c0d79b8e4d0705df90472b0cf70e45 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Tue, 11 Aug 2026 22:42:06 +0800
Subject: [PATCH 18/26] [Docs][WinCall] Add FAQ section to the WinCall
 documentation

Answer two questions that come up when reading the WinCall spec:

- R30/R31 are callee-saved under the Windows APX ABI (per Microsoft's
  x64 calling convention) but can still be used as scratch registers:
  the callee preserves them with pushp/popp in the prologue/epilogue,
  exactly like RBX/RDI/RSI etc. They are only reserved in functions that
  call setjmp/longjmp, where the Windows unwinder cannot restore the APX
  extended registers.
- A 4-pointer struct (32 bytes) is split into four GPR slots: returned
  in RAX/RDX/RCX/R8 and passed in RCX/RDX/R8/R9, with a following
  integer argument going to R16.
---
 llvm/docs/WinCall.md | 55 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/llvm/docs/WinCall.md b/llvm/docs/WinCall.md
index 2049dcab3d443..67c02787bdf0b 100644
--- a/llvm/docs/WinCall.md
+++ b/llvm/docs/WinCall.md
@@ -219,6 +219,61 @@ the design intent of the convention; the discriminant-lowering support in
 LLVM is independent of WinCall and is not part of this calling convention
 itself.
 
+## FAQ
+
+### R30 and R31 are callee-saved — can they still be used as scratch registers?
+
+Yes. R30 and R31 are callee-saved registers under the Windows APX ABI, so a
+callee may freely use them as scratch registers as long as it preserves them
+across the call. The compiler does exactly that: when a WinCall function uses
+R30 or R31 it pushes them in the prologue and pops them in the epilogue using
+the APX ``pushp``/``popp`` instructions:
+
+```asm
+use_r31 at win:
+        pushp   %r31           # preserve R31
+        movq    %rcx, %r31     # use R31 as a scratch register
+        callq   g
+        popp    %r31           # restore R31
+        retq
+```
+
+This mirrors how the other callee-saved registers (RBX, RDI, RSI, R12-R15,
+XMM6-XMM15) work on the standard Windows x64 ABI. The only restriction is in
+functions that call ``setjmp``/``longjmp``: because the Windows unwinder
+cannot restore the APX extended registers across a jump, clang reserves
+R30/R31 there (and warns on large functions), so they are not allocated.
+
+### Does a 4-pointer struct split into four registers?
+
+Yes. A struct of four pointers (32 bytes) is classified by
+``WinX86_64ABIInfo::classify`` as a direct record: it is returned in
+``RAX, RDX, RCX, R8`` and passed as an argument in ``RCX, RDX, R8, R9`` —
+four separate GPR slots, one per 8-byte field. At ``-O2`` the words move
+directly from the return registers to the argument registers with no stack
+round-trip:
+
+```asm
+caller at win:
+        callq   make at win       # returns the 4-pointer struct in RAX/RDX/RCX/R8
+        movq    %rcx, %r9      # save words 3,4 in scratch regs
+        movq    %r8,  %r10
+        movq    %rax, %rcx     # arg 1
+        movq    %rdx, %rdx     # arg 2
+        movq    %r9,  %r8      # arg 3
+        movq    %r10, %r9      # arg 4
+        callq   take at win
+```
+
+(With optimization disabled the four words are spilled to and reloaded from
+the stack frame between the two calls, but the argument registers are still
+RCX, RDX, R8, R9.)
+
+An integer argument following the struct is placed in the next free GPR
+(R16). This is what makes ``std::string`` and ``std::vector`` (four-word
+objects) travel entirely in registers under WinCall, unlike the MS x64 ABI
+which would pass them by pointer.
+
 ## Implementation notes
 
 - IR calling convention: ``x86_wincallcc`` / ``CallingConv::X86_WinCall``

>From 1303b2f156430deda281876ed5eba1722e593f97 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Tue, 11 Aug 2026 23:11:28 +0800
Subject: [PATCH 19/26] [Docs][WinCall] Add examples of empty objects,
 __uint128_t and std::span

Show how common C++ types are passed and returned under WinCall, with
codegen verified against the compiler:

- empty objects consume no register slots; the following argument takes
  the first GPR
- __uint128_t / __int128_t / std::float128_t are split into two GPRs
  (RCX+RDX in, RAX+RDX out)
- std::span / std::string_view ({pointer, size}) travel entirely in two
  registers instead of by pointer as in the MS x64 ABI
---
 llvm/docs/WinCall.md | 72 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 72 insertions(+)

diff --git a/llvm/docs/WinCall.md b/llvm/docs/WinCall.md
index 67c02787bdf0b..aed0c8459b6c4 100644
--- a/llvm/docs/WinCall.md
+++ b/llvm/docs/WinCall.md
@@ -219,6 +219,78 @@ the design intent of the convention; the discriminant-lowering support in
 LLVM is independent of WinCall and is not part of this calling convention
 itself.
 
+## Examples
+
+The following examples show how common C++ types are passed and returned
+under WinCall (``x86_64apx-windows-msvc``, optimized output).
+
+### Empty objects
+
+An empty object (``struct empty {}``) consumes **no register slots**. The
+``int`` that follows it takes the first GPR (ECX):
+
+```c
+struct empty {};
+
+__attribute__((wincall)) void f(struct empty e, int x) { sink(x); }
+```
+
+```asm
+f at win:
+        callq   sink at win        # x is forwarded from ECX; e took no register
+```
+
+An empty object as a return type also uses no register. This makes C++ types
+that contain empty base classes or members cheaper to pass and return than
+under the MS x64 ABI.
+
+### ``__uint128_t`` (and ``__int128_t``, ``std::float128_t``)
+
+A 128-bit integer is split into **two GPRs**: passed in RCX (low) + RDX
+(high), returned in RAX (low) + RDX (high).
+
+```c
+__attribute__((wincall)) __uint128_t f(__uint128_t v);
+```
+
+```asm
+f at win:
+        movq    %rcx, %rax      # low 64 bits: RCX -> RAX
+        retq                    # high 64 bits already in RDX
+```
+
+The same two-register rule applies to ``__int128_t`` and to
+``std::float128_t``.
+
+### ``std::span`` (two-word aggregates)
+
+A ``{pointer, size}`` aggregate such as ``std::span`` or
+``std::string_view`` is 16 bytes, so it is passed in **two** GPRs: the
+pointer in RCX and the length in RDX, and returned in RAX (pointer) + RDX
+(length).
+
+```c
+struct span { void *base; unsigned long long len; };
+
+__attribute__((wincall)) void f(struct span s);
+__attribute__((wincall)) struct span g(void);
+```
+
+```asm
+        callq   g at win           # returns {RAX = base, RDX = len}
+        movq    %rax, %rcx      # pass base in RCX (len already in RDX)
+        callq   f at win
+```
+
+Under the MS x64 ABI this same ``std::span`` argument would be passed by
+pointer; WinCall makes it a zero-cost, purely-register argument.
+
+### Four-word aggregates (``std::string`` / ``std::vector``)
+
+A four-word object such as ``std::string`` or ``std::vector`` (32 bytes) is
+passed in **four** GPRs (RCX, RDX, R8, R9) and returned in RAX, RDX, RCX,
+R8. See the FAQ below for the exact code.
+
 ## FAQ
 
 ### R30 and R31 are callee-saved — can they still be used as scratch registers?

>From 661ee7e2a97f23696683f740352528ee5524c138 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Tue, 11 Aug 2026 23:16:58 +0800
Subject: [PATCH 20/26] [Docs][WinCall] Clarify that classes with destructors
 are not passed in registers

std::string and std::vector have non-trivial destructors, so getRecordArgABI
makes clang pass them by pointer regardless of size. Remove them from the
register-passing examples and note the exemption explicitly in the
Aggregates section, the four-word aggregate example, and the FAQ.
---
 llvm/docs/WinCall.md | 29 ++++++++++++++++++-----------
 1 file changed, 18 insertions(+), 11 deletions(-)

diff --git a/llvm/docs/WinCall.md b/llvm/docs/WinCall.md
index aed0c8459b6c4..830b8f007aa27 100644
--- a/llvm/docs/WinCall.md
+++ b/llvm/docs/WinCall.md
@@ -11,8 +11,8 @@ Its purpose is to exploit the 16 additional general-purpose registers
 four integer arguments can be passed in registers; WinCall doubles that to
 eight by using R16-R19 as argument registers. It also relaxes the aggregate
 passing and return rules of the Microsoft x64 ABI so that C++ types such as
-``std::span``, ``std::string`` and ``std::vector`` (a ``{pointer, size}``
-pair or a four-word object) can travel in registers.
+``std::span`` (a ``{pointer, size}`` pair) and other small aggregates can
+travel in registers.
 
 WinCall does **not** replace any existing ABI. Existing Windows APIs keep
 their ``stdcall``, ``cdecl`` and ``fastcall`` conventions; the convention is
@@ -120,8 +120,7 @@ convention):
 
 - A record that fits in **1, 2, 4, 8, 16 or 32 bytes** is passed **directly
   in registers** (not by pointer/sret like the MS x64 ABI). A 4-``size_t``
-  struct — such as ``std::string`` or ``std::vector`` — therefore travels in
-  RCX, RDX, R8, R9.
+  struct therefore travels in RCX, RDX, R8, R9.
 - A record of up to 64 bits is coerced to an integer of its size and uses
   **one** GPR; a larger record (up to 32 bytes) is **expanded** into its
   8-byte parts.
@@ -129,6 +128,10 @@ convention):
   ``classify`` returns ``ABIArgInfo::getIgnore()`` for them.
 - Records larger than 32 bytes, records with a flexible array member, and
   non-trivial C++ records (per ``getRecordArgABI``) are passed by reference.
+  Note that this means C++ classes with user-declared or user-provided
+  destructors or copy/move constructors (e.g. ``std::string``,
+  ``std::vector``, ``std::unique_ptr``) are **not** passed in registers —
+  they are passed by pointer regardless of size.
 - ``f80`` (long double) is passed by pointer.
 - ``__int128`` is split into two GPRs.
 - Complex types and member pointers are handled as in the MS x64 ABI.
@@ -285,11 +288,13 @@ __attribute__((wincall)) struct span g(void);
 Under the MS x64 ABI this same ``std::span`` argument would be passed by
 pointer; WinCall makes it a zero-cost, purely-register argument.
 
-### Four-word aggregates (``std::string`` / ``std::vector``)
+### Four-word aggregates
 
-A four-word object such as ``std::string`` or ``std::vector`` (32 bytes) is
-passed in **four** GPRs (RCX, RDX, R8, R9) and returned in RAX, RDX, RCX,
-R8. See the FAQ below for the exact code.
+A plain 4-``size_t`` struct (32 bytes) is passed in **four** GPRs (RCX,
+RDX, R8, R9) and returned in RAX, RDX, RCX, R8. See the FAQ below for the
+exact code. (C++ classes such as ``std::string`` or ``std::vector`` that
+have a non-trivial destructor or copy/move constructor are *not* passed in
+registers — they are passed by pointer; see the Aggregates section above.)
 
 ## FAQ
 
@@ -342,9 +347,11 @@ the stack frame between the two calls, but the argument registers are still
 RCX, RDX, R8, R9.)
 
 An integer argument following the struct is placed in the next free GPR
-(R16). This is what makes ``std::string`` and ``std::vector`` (four-word
-objects) travel entirely in registers under WinCall, unlike the MS x64 ABI
-which would pass them by pointer.
+(R16). This is how a four-word aggregate such as a 4-``size_t`` struct
+travels entirely in registers under WinCall, unlike the MS x64 ABI which
+would pass it by pointer. (C++ classes with non-trivial destructors or
+copy/move constructors are exempt from this register passing; see the
+Aggregates section above.)
 
 ## Implementation notes
 

>From 0622d195f89a4997511cc936a79b5c24584fe040 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Tue, 11 Aug 2026 23:21:37 +0800
Subject: [PATCH 21/26] [Docs] add draft to wincall title

---
 llvm/docs/WinCall.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/docs/WinCall.md b/llvm/docs/WinCall.md
index 830b8f007aa27..98f89a9599e5c 100644
--- a/llvm/docs/WinCall.md
+++ b/llvm/docs/WinCall.md
@@ -1,4 +1,4 @@
-# The WinCall Calling Convention
+# The WinCall Calling Convention Draft
 
 ## What WinCall is
 

>From 5e38e7db07533c7037df35feae73ecc62e60254f Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Tue, 11 Aug 2026 23:37:48 +0800
Subject: [PATCH 22/26] [wincall] formatting issues detected by upstream

CI
---
 clang/lib/AST/Expr.cpp                  | 4 +++-
 clang/lib/AST/TypePrinter.cpp           | 4 +++-
 clang/lib/Basic/Targets.cpp             | 3 ++-
 clang/lib/Basic/Targets/X86.cpp         | 4 ++--
 clang/lib/CodeGen/Targets/X86.cpp       | 5 ++---
 clang/lib/Driver/ToolChains/MSVC.cpp    | 3 ++-
 clang/lib/Parse/ParseTentative.cpp      | 3 ++-
 clang/lib/Sema/SemaLambda.cpp           | 4 ++--
 llvm/include/llvm/TargetParser/Triple.h | 4 +---
 llvm/lib/AsmParser/LLParser.cpp         | 4 +++-
 llvm/lib/IR/AsmWriter.cpp               | 4 +++-
 11 files changed, 25 insertions(+), 17 deletions(-)

diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 01bc0c3e4b28b..9396679c1cae6 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -798,7 +798,9 @@ std::string PredefinedExpr::ComputeName(PredefinedIdentKind IK,
       case CC_X86ThisCall: POut << "__thiscall "; break;
       case CC_X86VectorCall: POut << "__vectorcall "; break;
       case CC_X86RegCall: POut << "__regcall "; break;
-      case CC_WinCall: POut << "__wincall "; break;
+      case CC_WinCall:
+        POut << "__wincall ";
+        break;
       // Only bother printing the conventions that MSVC knows about.
       default: break;
       }
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 079de42e744b4..90a89acb85692 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2115,7 +2115,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
   case attr::MSABI: OS << "ms_abi"; break;
   case attr::SysVABI: OS << "sysv_abi"; break;
   case attr::RegCall: OS << "regcall"; break;
-  case attr::WinCall: OS << "wincall"; break;
+  case attr::WinCall:
+    OS << "wincall";
+    break;
   case attr::Pcs: {
     OS << "pcs(";
    QualType t = T->getEquivalentType();
diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp
index 572957794d58d..9bb2e64209877 100644
--- a/clang/lib/Basic/Targets.cpp
+++ b/clang/lib/Basic/Targets.cpp
@@ -93,7 +93,8 @@ void addCygMingDefines(const LangOptions &Opts, MacroBuilder &Builder) {
     // Provide macros for all the calling convention keywords.  Provide both
     // single and double underscore prefixed variants.  These are available on
     // x64 as well as x86, even though they have no effect.
-    const char *CCs[] = {"cdecl", "stdcall", "fastcall", "thiscall", "pascal", "wincall"};
+    const char *CCs[] = {"cdecl",    "stdcall", "fastcall",
+                         "thiscall", "pascal",  "wincall"};
     for (const char *CC : CCs) {
       std::string GCCSpelling = "__attribute__((__";
       GCCSpelling += CC;
diff --git a/clang/lib/Basic/Targets/X86.cpp b/clang/lib/Basic/Targets/X86.cpp
index 6ed62b205c4b7..10b63ae320577 100644
--- a/clang/lib/Basic/Targets/X86.cpp
+++ b/clang/lib/Basic/Targets/X86.cpp
@@ -161,8 +161,8 @@ bool X86TargetInfo::initFeatureMap(
   // APX as the individual egpr/push2pop2/ppx/ndd/ccmp/nf/zu/jmpabs features
   // (there is no single "apxf" feature), so enable each of them.
   if (getTriple().isX86_64APX()) {
-    for (const char *Sub : {"egpr", "push2pop2", "ppx", "ndd", "ccmp", "nf",
-                            "zu", "jmpabs"})
+    for (const char *Sub :
+         {"egpr", "push2pop2", "ppx", "ndd", "ccmp", "nf", "zu", "jmpabs"})
       setFeatureEnabled(Features, Sub, true);
   }
 
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 8ebcc857bac8a..35d2494b2eb8c 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -3577,9 +3577,8 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, unsigned &FreeSSERegs,
       if (IsWinCall) {
         // std::float128_t is passed like __int128: in two integer registers.
         if (IsReturnType)
-          return ABIArgInfo::getDirect(
-              llvm::FixedVectorType::get(
-                  llvm::Type::getInt64Ty(getVMContext()), 2));
+          return ABIArgInfo::getDirect(llvm::FixedVectorType::get(
+              llvm::Type::getInt64Ty(getVMContext()), 2));
         return ABIArgInfo::getExpand();
       }
 
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index 0eb6637687dcc..ba844640736ee 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -378,7 +378,8 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
   // with their own -Wl,/section-alignment: flag.
   if (TC.getTriple().isX86_64APX()) {
     bool HasExplicitSectionAlignment = false;
-    for (Arg *A : Args.filtered(options::OPT_Wl_COMMA, options::OPT__SLASH_link))
+    for (Arg *A :
+         Args.filtered(options::OPT_Wl_COMMA, options::OPT__SLASH_link))
       if (StringRef(A->getValue()).contains_insensitive("section-alignment"))
         HasExplicitSectionAlignment = true;
     if (!HasExplicitSectionAlignment) {
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 31b802a058093..1618815f24118 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -937,7 +937,8 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
       // '(' abstract-declarator ')'
       if (Tok.isOneOf(tok::kw___attribute, tok::kw___declspec, tok::kw___cdecl,
                       tok::kw___stdcall, tok::kw___fastcall, tok::kw___thiscall,
-                      tok::kw___regcall, tok::kw___vectorcall, tok::kw___wincall))
+                      tok::kw___regcall, tok::kw___vectorcall,
+                      tok::kw___wincall))
         return TPResult::True; // attributes indicate declaration
       TPResult TPR = TryParseDeclarator(mayBeAbstract, mayHaveIdentifier);
       if (TPR != TPResult::Ambiguous)
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index ab1a3e91222ad..19bc8ef5e1171 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1649,8 +1649,8 @@ static void repeatForLambdaConversionFunctionCallingConvs(
   /// detecting the attribute by the time we get here.
   if (S.getLangOpts().MSVCCompat) {
     CallingConv Convs[] = {
-        CC_C,        CC_X86StdCall, CC_X86FastCall, CC_X86VectorCall,
-        CC_WinCall,  DefaultFree, DefaultMember, CallOpCC};
+        CC_C,       CC_X86StdCall, CC_X86FastCall, CC_X86VectorCall,
+        CC_WinCall, DefaultFree,   DefaultMember,  CallOpCC};
     llvm::sort(Convs);
     llvm::iterator_range<CallingConv *> Range(std::begin(Convs),
                                               llvm::unique(Convs));
diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h
index 9af359c373b30..0e53da52f23fb 100644
--- a/llvm/include/llvm/TargetParser/Triple.h
+++ b/llvm/include/llvm/TargetParser/Triple.h
@@ -1019,9 +1019,7 @@ class Triple {
 
   /// Tests whether the target is the Windows x86_64apx target, which defaults
   /// to the wincall calling convention.
-  bool isWindowsAPX() const {
-    return isX86_64APX() && isOSWindows();
-  }
+  bool isWindowsAPX() const { return isX86_64APX() && isOSWindows(); }
 
   /// Tests whether the target supports the EHABI exception
   /// handling standard.
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 98a9201528f96..60efaf8c56fef 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -2322,7 +2322,9 @@ bool LLParser::parseOptionalCallingConv(unsigned &CC) {
   case lltok::kw_x86_regcallcc:  CC = CallingConv::X86_RegCall; break;
   case lltok::kw_x86_thiscallcc: CC = CallingConv::X86_ThisCall; break;
   case lltok::kw_x86_vectorcallcc:CC = CallingConv::X86_VectorCall; break;
-  case lltok::kw_x86_wincallcc:   CC = CallingConv::X86_WinCall; break;
+  case lltok::kw_x86_wincallcc:
+    CC = CallingConv::X86_WinCall;
+    break;
   case lltok::kw_arm_apcscc:     CC = CallingConv::ARM_APCS; break;
   case lltok::kw_arm_aapcscc:    CC = CallingConv::ARM_AAPCS; break;
   case lltok::kw_arm_aapcs_vfpcc:CC = CallingConv::ARM_AAPCS_VFP; break;
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index 0a18dfaddc4dd..cd6ca86247a35 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -361,7 +361,9 @@ static void printCallingConv(unsigned cc, raw_ostream &Out) {
   case CallingConv::GRAAL:         Out << "graalcc"; break;
   case CallingConv::CFGuard_Check: Out << "cfguard_checkcc"; break;
   case CallingConv::X86_StdCall:   Out << "x86_stdcallcc"; break;
-  case CallingConv::X86_WinCall:  Out << "x86_wincallcc"; break;
+  case CallingConv::X86_WinCall:
+    Out << "x86_wincallcc";
+    break;
   case CallingConv::X86_FastCall:  Out << "x86_fastcallcc"; break;
   case CallingConv::X86_ThisCall:  Out << "x86_thiscallcc"; break;
   case CallingConv::X86_RegCall:   Out << "x86_regcallcc"; break;

>From 68052c9fe860961634be96f45a5551ef1f00f0d9 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Wed, 12 Aug 2026 00:21:15 +0800
Subject: [PATCH 23/26] [Docs][clang] indent issue try to fix it

---
 clang/include/clang/Basic/AttrDocs.td | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 2ed04e43272b4..c3628c93076d9 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3756,15 +3756,16 @@ The ``wincall`` attribute applies the WinCall calling convention to a
 function. WinCall is an x86-64-only calling convention for Windows targets
 that makes use of the registers added by Intel APX.
 
-Unlike the Microsoft x64 ABI, WinCall:
-- passes the first eight integer arguments in RCX, RDX, R8, R9, R16, R17,
-  R18, R19 (R16-R19 are APX registers),
-- passes the first eight floating-point/vector arguments in XMM0-XMM7
+Unlike the Microsoft x64 ABI, WinCall differs in the following ways.
+
+- It passes the first eight integer arguments in RCX, RDX, R8, R9, R16, R17,
+  R18, R19 (R16-R19 are APX registers).
+- It passes the first eight floating-point/vector arguments in XMM0-XMM7
   (YMM0-YMM7 for ``__m256``, ZMM0-ZMM7 for ``__m512``), allocated
-  independently of the integer registers,
-- passes/returns aggregates up to 32 bytes directly in registers instead of
-  by pointer, and
-- takes no register slots for empty C++ objects.
+  independently of the integer registers.
+- It passes/returns aggregates up to 32 bytes directly in registers instead
+  of by pointer.
+- It takes no register slots for empty C++ objects.
 
 On targets where WinCall is not supported the attribute is ignored with a
 warning.

>From 5b180c5d993e2d25a3f3bbf2a179678d07356d8e Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Wed, 12 Aug 2026 02:24:01 +0800
Subject: [PATCH 24/26] [skip ci] [Docs][WinCall] Document building dual-ABI
 DLLs

Explain how a single DLL binary can serve both WinCall and classic
(stdcall/MS x64/MinGW) callers by exposing two entry points per function:

- symbol names: export both foo and foo at win
- calling convention: forward arguments through a thin per-ABI thunk
  to one WinCall-compiled implementation
- long double: keep it out of the exported interface, or keep the
  classic 16-byte f80 layout in exported functions

Note that the long double size change does not by itself break
dual-ABI DLLs, since the calling-convention and symbol differences
already require the dual-entry-thunk design.
---
 llvm/docs/WinCall.md | 65 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/llvm/docs/WinCall.md b/llvm/docs/WinCall.md
index 98f89a9599e5c..906aac9647f43 100644
--- a/llvm/docs/WinCall.md
+++ b/llvm/docs/WinCall.md
@@ -212,6 +212,71 @@ and ``MinGW.cpp``, not by the assembler. If the user passes their own
 section-alignment ``-Wl`` flag, the driver's default is suppressed in favour
 of the user's value.
 
+## Building a DLL that works with both WinCall and the classic ABI
+
+A function's ABI is decided at the *call boundary*, not inside the function.
+A single DLL binary can therefore serve both WinCall callers and classic
+(``stdcall`` / MS x64 / plain MinGW) callers, but only by exposing **two entry
+points per function** — one per ABI — rather than one function with a
+compromise ABI. The three things that differ between the two worlds each need
+their own solution:
+
+| Aspect        | WinCall                        | Classic ABI                |
+|---------------|--------------------------------|----------------------------|
+| Symbol        | ``foo at win``                    | ``foo``                    |
+| Calling conv. | 8 GPRs, XMM0-7, aggregates in registers | 4 GPRs, XMM0-3, sret/byval |
+| ``long double`` | f64, 8 bytes                 | f80, 16 bytes              |
+
+### Symbol names
+
+Export **both** names for the same body: ``foo`` and ``foo at win``. On COFF this
+is two export entries pointing at the same RVA (or a one-line asm alias). The
+``@win`` name serves WinCall callers; the plain name serves classic callers.
+
+### Calling convention: forwarding thunks
+
+WinCall and the classic convention genuinely disagree on registers and stack.
+The standard technique is a thin **forwarding thunk** per ABI that converts
+the argument placement and jumps to a single implementation compiled with
+WinCall:
+
+```asm
+foo at win:            ; WinCall ABI: args in RCX,RDX,R8,R9,R16-R19, XMM0-7
+        jmp foo_impl
+
+foo:                ; classic ABI: args in RCX,RDX,R8,R9, XMM0-3
+        ...         ; convert the small subset that differs
+        jmp foo_impl
+```
+
+This is the same machinery as C++ ABI thunks or
+``-fdefault-calling-conv`` plus a per-function calling-convention attribute.
+In practice the thunk is written in assembly, or the exported function is
+marked with the classic attribute (e.g. ``__attribute__((stdcall))``) and the
+compiler emits the conversion.
+
+### ``long double``
+
+This is the one genuine limitation: a single binary stores one
+representation. The options are:
+
+1. **Keep ``long double`` out of the exported interface** (recommended). Most
+   Windows DLL APIs use ``double``/``int``/pointers/structs, so WinCall's f64
+   ``long double`` is invisible to classic callers and the dual-ABI DLL works
+   with both worlds.
+2. **Keep the classic 16-byte f80 ``long double`` in exported functions** that
+   must cross the boundary, while WinCall-internal code uses f64. This needs a
+   per-function (or per-TU) ``long double`` layout switch, so the exported
+   surface matches classic callers.
+3. **Pick f64 everywhere** and accept that only WinCall callers may use
+   ``long double`` in the API. Simple, but classic callers passing f80 will
+   misbehave on those functions.
+
+The ``long double`` size change therefore does **not** by itself break
+dual-ABI DLLs — the calling-convention and symbol differences already require
+the dual-entry-thunk design, and ``long double`` only matters if it is part of
+the exported interface.
+
 ## Relation to "herbceptions" (deterministic exceptions)
 
 WinCall is designed so that Herb Sutter's proposed zero-overhead

>From 04d022d28a3b2bf8f7d17dac18f6f884ee3ac456 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Wed, 12 Aug 2026 03:06:24 +0800
Subject: [PATCH 25/26] [X86][Clang] WinCall: f64 long double, vector
 complex/struct args, AVX-512 default, 64-byte stack

Refine the WinCall (x86_64apx-windows) ABI:

- long double is f64 on x86_64apx-windows-gnu (like the MSVC ABI) so the
  WinCall ABI never uses x87. Plain x86_64-windows-gnu keeps the f80/128-bit
  layout.
- _Complex float/double scalars are coerced to v2f32/v2f64 and passed and
  returned in XMM registers instead of by pointer/sret like the MS x64 ABI.
- A record with a single FP/SIMD member is passed like the scalar it wraps
  (in a vector register), as long as the record's size/alignment equals the
  member's natural size/alignment; if the user bumped the alignment so the
  struct is bigger than its member, it stays an aggregate.
- x86_64apx defaults to AVX-512F (APX/Evex implies AVX-512) in both the
  clang frontend and the X86 backend, and vzeroupper is disabled for
  x86_64apx so WinCall code stays EVEX-encoded without AVX->SSE transition
  penalties.
- x86_64apx-windows keeps a 64-byte stack alignment at every call site
  (instead of the classic 16-byte) so AVX-512 ZMM spills can use aligned
  moves without dynamic realignment; only the Windows apx triple gets this,
  other apx targets keep 16-byte and -mstack-alignment still overrides.
- Fix a missing CC_WinCall case in CGDebugInfo::getDwarfCC (CI -Wswitch
  error).
- Tests: new wincall-abi2.c for long double/complex/single-FP struct, update
  wincall-cconv.ll for the 64-byte-aligned frame. Update WinCall.md.
---
 clang/lib/Basic/Targets/X86.cpp               |  3 +
 clang/lib/Basic/Targets/X86.h                 |  8 +++
 clang/lib/CodeGen/CGDebugInfo.cpp             |  4 ++
 clang/lib/CodeGen/Targets/X86.cpp             | 61 +++++++++++++++++++
 clang/test/CodeGen/X86/wincall-abi2.c         | 54 ++++++++++++++++
 llvm/docs/WinCall.md                          | 16 +++++
 .../X86/MCTargetDesc/X86MCTargetDesc.cpp      |  3 +
 llvm/lib/Target/X86/X86Subtarget.cpp          |  6 ++
 llvm/lib/Target/X86/X86Subtarget.h            |  4 ++
 llvm/test/CodeGen/X86/wincall-cconv.ll        | 12 ++--
 10 files changed, 165 insertions(+), 6 deletions(-)
 create mode 100644 clang/test/CodeGen/X86/wincall-abi2.c

diff --git a/clang/lib/Basic/Targets/X86.cpp b/clang/lib/Basic/Targets/X86.cpp
index 10b63ae320577..5d7161f021d63 100644
--- a/clang/lib/Basic/Targets/X86.cpp
+++ b/clang/lib/Basic/Targets/X86.cpp
@@ -164,6 +164,9 @@ bool X86TargetInfo::initFeatureMap(
     for (const char *Sub :
          {"egpr", "push2pop2", "ppx", "ndd", "ccmp", "nf", "zu", "jmpabs"})
       setFeatureEnabled(Features, Sub, true);
+    // APX (EVEX) implies AVX-512, and WinCall expects every instruction to be
+    // EVEX-encoded so that no vzeroupper is needed at WinCall boundaries.
+    setFeatureEnabled(Features, "avx512f", true);
   }
 
   using namespace llvm::X86;
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index 00e0b93b12d1c..193dc89934f05 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -976,6 +976,14 @@ class LLVM_LIBRARY_VISIBILITY MinGWX86_64TargetInfo
 public:
   MinGWX86_64TargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts)
       : WindowsX86_64TargetInfo(Triple, Opts) {
+    if (Triple.isX86_64APX()) {
+      // WinCall unifies long double to f64 (like the MSVC ABI) so that the
+      // WinCall ABI never needs x87.
+      LongDoubleWidth = LongDoubleAlign = 64;
+      LongDoubleFormat = &llvm::APFloat::IEEEdouble();
+      HasFloat128 = true;
+      return;
+    }
     // Mingw64 rounds long double size and alignment up to 16 bytes, but sticks
     // with x86 FP ops. Weird.
     LongDoubleWidth = LongDoubleAlign = 128;
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 9df5792f69d73..c458a1f24266b 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1811,6 +1811,10 @@ static unsigned getDwarfCC(CallingConv CC) {
     return llvm::dwarf::DW_CC_BORLAND_pascal;
   case CC_Win64:
     return llvm::dwarf::DW_CC_LLVM_Win64;
+  case CC_WinCall:
+    // WinCall is the wincall convention on Windows x86-64; model it with the
+    // Win64 DWARF calling convention code.
+    return llvm::dwarf::DW_CC_LLVM_Win64;
   case CC_X86_64SysV:
     return llvm::dwarf::DW_CC_LLVM_X86_64SysV;
   case CC_AAPCS:
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 35d2494b2eb8c..e419b26ead099 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -3475,6 +3475,48 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, unsigned &FreeSSERegs,
       return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(),
                                      /*ByVal=*/false);
 
+    // WinCall passes a record that is a single FP/SIMD member exactly like the
+    // scalar it wraps (in a vector register), as long as the record's size and
+    // alignment match the member's natural size and alignment. If the user
+    // bumped the alignment so that the record is bigger than the member (e.g.
+    // a 16-byte-aligned struct holding one double), fall through to the normal
+    // aggregate rules.
+    if (IsWinCall && !Ty->isAnyComplexType() && !Ty->isMemberPointerType() &&
+        !RT->getDecl()->isUnion()) {
+      unsigned NumFields = 0;
+      const FieldDecl *SingleField = nullptr;
+      for (const FieldDecl *FD : RT->getDecl()->fields()) {
+        if (FD->isUnnamedBitField())
+          continue;
+        if (FD->isBitField()) {
+          NumFields = 0;
+          break;
+        }
+        ++NumFields;
+        SingleField = FD;
+      }
+      if (NumFields == 1 && SingleField) {
+        QualType FieldTy = SingleField->getType();
+        llvm::Type *FieldLLTy = CGT.ConvertType(FieldTy);
+        bool IsScalarFP =
+            FieldTy->isFloatingType() && !FieldTy->isComplexType();
+        bool IsVector = FieldTy->isVectorType();
+        if ((IsScalarFP || IsVector) &&
+            (FieldLLTy->isFloatingPointTy() || FieldLLTy->isVectorTy())) {
+          // The record must be exactly as big as the single member so that no
+          // padding/alignment is being carried by the struct.
+          if (getContext().getTypeSize(Ty) ==
+              getContext().getTypeSize(FieldTy)) {
+            if (IsReturnType)
+              return ABIArgInfo::getDirect(FieldLLTy);
+            if (Width <= 128)
+              return ABIArgInfo::getDirect(FieldLLTy);
+            return ABIArgInfo::getExpand();
+          }
+        }
+      }
+    }
+
     // wincall passes/returns aggregates that fit in 1, 2, 4, 8, 16 or 32 bytes
     // (e.g. 4x size_t, like std::string/std::vector) directly in registers,
     // instead of by pointer/sret like the MS x64 ABI.
@@ -3494,6 +3536,25 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, unsigned &FreeSSERegs,
     }
   }
 
+  // WinCall passes complex scalars in the vector registers: a complex value is
+  // just two elements of its component type, so coerce _Complex float/double
+  // to v2f32/v2f64 and pass/return them in XMM registers instead of by
+  // pointer/sret like the MS x64 ABI.
+  if (IsWinCall && Ty->isAnyComplexType()) {
+    QualType ElemTy = cast<ComplexType>(Ty)->getElementType();
+    llvm::Type *ElemLLTy = CGT.ConvertType(ElemTy);
+    if (llvm::FixedVectorType::isValidElementType(ElemLLTy)) {
+      auto *V2 = llvm::FixedVectorType::get(ElemLLTy, 2);
+      if (IsReturnType)
+        return ABIArgInfo::getDirect(V2);
+      // A 128-bit complex value fits in one XMM register; a wider one (e.g.
+      // long double complex) is expanded into its 64-bit parts.
+      if (Width <= 128)
+        return ABIArgInfo::getDirect(V2);
+      return ABIArgInfo::getExpand();
+    }
+  }
+
   const Type *Base = nullptr;
   uint64_t NumElts = 0;
   // vectorcall adds the concept of a homogenous vector aggregate, similar to
diff --git a/clang/test/CodeGen/X86/wincall-abi2.c b/clang/test/CodeGen/X86/wincall-abi2.c
new file mode 100644
index 0000000000000..65af11da4a223
--- /dev/null
+++ b/clang/test/CodeGen/X86/wincall-abi2.c
@@ -0,0 +1,54 @@
+// RUN: %clang_cc1 -triple x86_64apx-unknown-windows-msvc -o - -emit-llvm %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64apx-unknown-windows-msvc -o - -S %s | FileCheck -check-prefix=ASM %s
+// RUN: %clang_cc1 -triple x86_64apx-unknown-windows-gnu -o - -emit-llvm %s | FileCheck -check-prefix=GNU-LD %s
+
+// WinCall passes:
+//   - long double as f64 (no x87),
+//   - complex scalars in vector registers,
+//   - a single-FP-member struct in the vector register (only when the struct
+//     is exactly as big as its single member).
+
+// long double is f64 on x86_64apx-windows-gnu.
+// GNU-LD: define dso_local x86_wincallcc double @"\01f at win"(double noundef %v)
+__attribute__((wincall)) long double f(long double v) { return v; }
+
+// _Complex double travels in one XMM register.
+// CHECK: define dso_local x86_wincallcc <2 x double> @"\01f_cd at win"(<2 x double> noundef %v.coerce)
+// ASM-LABEL: f_cd at win:
+// ASM: vmovupd %xmm0, (%rsp)
+__attribute__((wincall)) _Complex double f_cd(_Complex double v) { return v; }
+
+// _Complex float travels in one XMM register.
+// CHECK: define dso_local x86_wincallcc <2 x float> @"\01f_cf at win"(<2 x float> noundef %v.coerce)
+// ASM-LABEL: f_cf at win:
+// ASM: vmovlpd %xmm0, (%rsp)
+__attribute__((wincall)) _Complex float f_cf(_Complex float v) { return v; }
+
+// A struct holding one double is passed like a double, in XMM0.
+// CHECK: define dso_local x86_wincallcc double @"\01f_od at win"(double %s.coerce)
+// ASM-LABEL: f_od at win:
+__attribute__((wincall)) double f_od(struct one_double { double d; } s) {
+  return s.d;
+}
+
+// A struct holding one float is passed like a float, in XMM0.
+// CHECK: define dso_local x86_wincallcc float @"\01f_of at win"(float %s.coerce)
+// ASM-LABEL: f_of at win:
+__attribute__((wincall)) float f_of(struct one_float { float f; } s) {
+  return s.f;
+}
+
+// A 16-byte-aligned struct holding one double is NOT treated as a scalar:
+// its size (16) is bigger than the member (8), so it stays an aggregate and
+// is expanded into its parts (the single double field; padding is dropped).
+// CHECK: define dso_local x86_wincallcc double @"\01f_ad at win"(double %s.0)
+__attribute__((wincall)) double f_ad(struct aligned_double { double d; } __attribute__((aligned(16))) s) {
+  return s.d;
+}
+
+// A two-double struct is a normal two-register aggregate.
+// CHECK: define dso_local x86_wincallcc double @"\01f_td at win"(double %s.0, double %s.1)
+// ASM-LABEL: f_td at win:
+__attribute__((wincall)) double f_td(struct two_double { double a, b; } s) {
+  return s.a;
+}
diff --git a/llvm/docs/WinCall.md b/llvm/docs/WinCall.md
index 906aac9647f43..41d8ef2fb80a6 100644
--- a/llvm/docs/WinCall.md
+++ b/llvm/docs/WinCall.md
@@ -212,6 +212,22 @@ and ``MinGW.cpp``, not by the assembler. If the user passes their own
 section-alignment ``-Wl`` flag, the driver's default is suppressed in favour
 of the user's value.
 
+## Stack alignment
+
+On ``x86_64apx-windows`` targets the stack is kept **64-byte aligned** at
+every call site (``X86Subtarget`` sets the stack alignment to 64 for
+``isWindowsAPX()`` targets, instead of the 16-byte alignment of the classic
+Windows ABI). This is a deliberate part of the WinCall ABI: it means the
+backend can use aligned 64-byte moves (``vmovaps``/``vmovdqa64``) for
+AVX-512 ZMM spills and aligned stack slots without dynamic stack realignment.
+
+This matters in practice because the classic Windows x64 ABI only guarantees
+16-byte stack alignment, which is not enough for 64-byte ZMM registers — this
+is why GCC still cannot support AVX-512 on Windows correctly. WinCall's 64-byte
+guarantee removes that limitation. Only ``x86_64apx-windows`` gets the 64-byte
+alignment; other ``x86_64apx`` targets (e.g. ``x86_64apx-linux``) keep the
+16-byte default, and a user-supplied ``-mstack-alignment`` still overrides it.
+
 ## Building a DLL that works with both WinCall and the classic ABI
 
 A function's ABI is decided at the *call boundary*, not inside the function.
diff --git a/llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.cpp b/llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.cpp
index 6f56386e4f96c..ec0377b5fb4a0 100644
--- a/llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.cpp
+++ b/llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.cpp
@@ -61,6 +61,9 @@ std::string X86_MC::ParseX86Triple(const Triple &TT) {
 
   if (TT.getSubArch() == Triple::X86_64SubArch_apx) {
     FS += ",+egpr,+push2pop2,+ppx,+ndd,+ccmp,+nf,+zu,+jmpabs";
+    // APX (EVEX) implies AVX-512; WinCall assumes every instruction is
+    // EVEX-encoded so no vzeroupper is needed at WinCall boundaries.
+    FS += ",+avx512f,-vzeroupper";
   }
 
   return FS;
diff --git a/llvm/lib/Target/X86/X86Subtarget.cpp b/llvm/lib/Target/X86/X86Subtarget.cpp
index ed2da3128b44a..23e04efaa68b7 100644
--- a/llvm/lib/Target/X86/X86Subtarget.cpp
+++ b/llvm/lib/Target/X86/X86Subtarget.cpp
@@ -292,6 +292,12 @@ void X86Subtarget::initSubtargetFeatures(StringRef CPU, StringRef TuneCPU,
   // following the i386 psABI, while on Illumos it is always 16 bytes.
   if (StackAlignOverride)
     stackAlignment = *StackAlignOverride;
+  else if (isWindowsAPX())
+    // WinCall (x86_64apx-windows) guarantees a 64-byte stack alignment so that
+    // AVX-512 (ZMM) spills and aligned moves can always be used without
+    // dynamic stack realignment (GCC still cannot support AVX-512 on Windows
+    // correctly because the classic ABI only aligns the stack to 16 bytes).
+    stackAlignment = Align(64);
   else if (isTargetDarwin() || isTargetLinux() || isTargetKFreeBSD() ||
            isTargetHurd() || Is64Bit)
     stackAlignment = Align(16);
diff --git a/llvm/lib/Target/X86/X86Subtarget.h b/llvm/lib/Target/X86/X86Subtarget.h
index ed81309f25376..e6700e1031e7c 100644
--- a/llvm/lib/Target/X86/X86Subtarget.h
+++ b/llvm/lib/Target/X86/X86Subtarget.h
@@ -290,6 +290,10 @@ class X86Subtarget final : public X86GenSubtargetInfo {
 
   const Triple &getTargetTriple() const { return TargetTriple; }
 
+  /// Tests whether the target is the Windows x86_64apx target, which defaults
+  /// to the WinCall calling convention.
+  bool isWindowsAPX() const { return TargetTriple.isWindowsAPX(); }
+
   bool isTargetDarwin() const { return TargetTriple.isOSDarwin(); }
   bool isTargetFreeBSD() const { return TargetTriple.isOSFreeBSD(); }
   bool isTargetDragonFly() const { return TargetTriple.isOSDragonFly(); }
diff --git a/llvm/test/CodeGen/X86/wincall-cconv.ll b/llvm/test/CodeGen/X86/wincall-cconv.ll
index f3fe3b40a23f8..cc2c620cd90ce 100644
--- a/llvm/test/CodeGen/X86/wincall-cconv.ll
+++ b/llvm/test/CodeGen/X86/wincall-cconv.ll
@@ -8,13 +8,13 @@
 declare x86_wincallcc void @wincall_thunk(i64, i64, i64, i64, i64, i64, i64, i64)
 
 ; CHECK-LABEL: call_8_int:
-; CHECK:       subq $40, %rsp
-; CHECK-NEXT:  movq 80(%rsp), %r16
-; CHECK-NEXT:  movq 88(%rsp), %r17
-; CHECK-NEXT:  movq 96(%rsp), %r18
-; CHECK-NEXT:  movq 104(%rsp), %r19
+; CHECK:       subq $56, %rsp
+; CHECK-NEXT:  movq 96(%rsp), %r16
+; CHECK-NEXT:  movq 104(%rsp), %r17
+; CHECK-NEXT:  movq 112(%rsp), %r18
+; CHECK-NEXT:  movq 120(%rsp), %r19
 ; CHECK-NEXT:  callq wincall_thunk
-; CHECK-NEXT:  addq $40, %rsp
+; CHECK-NEXT:  addq $56, %rsp
 ; CHECK-NEXT:  retq
 define void @call_8_int(i64 %a, i64 %b, i64 %c, i64 %d,
                         i64 %e, i64 %f, i64 %g, i64 %h) nounwind {

>From 8df837703a99ddc24ae561780547c1f95d21c6e3 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Wed, 12 Aug 2026 03:49:11 +0800
Subject: [PATCH 26/26] [skip ci] [Docs][WinCall] Clarify aggregate register
 passing for all sizes up to 32 bytes

The previous wording ('fits in 1, 2, 4, 8, 16 or 32 bytes') wrongly implied
only power-of-two sizes are passed in registers. Any trivial record up to
32 bytes is passed directly in registers: up to 64 bits uses one GPR, larger
records are expanded into their 8-byte parts (so a 3-size_t/24-byte struct
travels in RCX, RDX, R8).
---
 llvm/docs/WinCall.md | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/llvm/docs/WinCall.md b/llvm/docs/WinCall.md
index 41d8ef2fb80a6..dd980e3adf5aa 100644
--- a/llvm/docs/WinCall.md
+++ b/llvm/docs/WinCall.md
@@ -118,12 +118,12 @@ Clang's ``WinX86_64ABIInfo::classify`` implements the aggregate rules for
 ``CC_WinCall`` (this is a frontend rule layered on top of the IR-level
 convention):
 
-- A record that fits in **1, 2, 4, 8, 16 or 32 bytes** is passed **directly
-  in registers** (not by pointer/sret like the MS x64 ABI). A 4-``size_t``
-  struct therefore travels in RCX, RDX, R8, R9.
-- A record of up to 64 bits is coerced to an integer of its size and uses
-  **one** GPR; a larger record (up to 32 bytes) is **expanded** into its
-  8-byte parts.
+- A record of up to **32 bytes** is passed **directly in registers** (not by
+  pointer/sret like the MS x64 ABI): a record of up to 64 bits is coerced to
+  an integer of its size and uses **one** GPR; a larger record (e.g. 16, 24
+  or 32 bytes) is **expanded** into its 8-byte parts. A 4-``size_t`` struct
+  therefore travels in RCX, RDX, R8, R9, and a 3-``size_t`` (24-byte) struct
+  in RCX, RDX, R8.
 - **Empty records** (``struct empty {}``) consume **no register slots**;
   ``classify`` returns ``ABIArgInfo::getIgnore()`` for them.
 - Records larger than 32 bytes, records with a flexible array member, and



More information about the cfe-commits mailing list