Index: include/clang-c/Index.h =================================================================== --- include/clang-c/Index.h +++ include/clang-c/Index.h @@ -3211,6 +3211,7 @@ CXCallingConv_Swift = 13, CXCallingConv_PreserveMost = 14, CXCallingConv_PreserveAll = 15, + CXCallingConv_AArch64Win64 = 16, CXCallingConv_Invalid = 100, CXCallingConv_Unexposed = 200 Index: include/clang/Basic/BuiltinsAArch64.def =================================================================== --- include/clang/Basic/BuiltinsAArch64.def +++ include/clang/Basic/BuiltinsAArch64.def @@ -61,4 +61,9 @@ BUILTIN(__builtin_arm_wsr64, "vcC*LUi", "nc") BUILTIN(__builtin_arm_wsrp, "vcC*vC*", "nc") +// Win64-compatible va_list functions +BUILTIN(__builtin_ms_va_start, "vc*&.", "nt") +BUILTIN(__builtin_ms_va_end, "vc*&", "n") +BUILTIN(__builtin_ms_va_copy, "vc*&c*&", "n") + #undef BUILTIN Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -7993,6 +7993,8 @@ "'va_start' used in %select{System V|Win64}0 ABI function">; def err_ms_va_start_used_in_sysv_function : Error< "'__builtin_ms_va_start' used in System V ABI function">; +def err_ms_va_start_used_in_aapcs_function : Error< + "'__builtin_ms_va_start' used in AAPCS ABI function">; def warn_second_arg_of_va_start_not_last_named_param : Warning< "second argument to 'va_start' is not the last named parameter">, InGroup; Index: include/clang/Basic/Specifiers.h =================================================================== --- include/clang/Basic/Specifiers.h +++ include/clang/Basic/Specifiers.h @@ -247,6 +247,7 @@ CC_Swift, // __attribute__((swiftcall)) CC_PreserveMost, // __attribute__((preserve_most)) CC_PreserveAll, // __attribute__((preserve_all)) + CC_AArch64Win64, // __attribute__((ms_abi)) }; /// \brief Checks whether the given calling convention supports variadic Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -2539,6 +2539,7 @@ case CC_OpenCLKernel: case CC_PreserveMost: case CC_PreserveAll: + case CC_AArch64Win64: // FIXME: we should be mangling all of the above. return ""; Index: lib/AST/MicrosoftMangle.cpp =================================================================== --- lib/AST/MicrosoftMangle.cpp +++ lib/AST/MicrosoftMangle.cpp @@ -2124,6 +2124,7 @@ llvm_unreachable("Unsupported CC for mangling"); case CC_X86_64Win64: case CC_X86_64SysV: + case CC_AArch64Win64: case CC_C: Out << 'A'; break; case CC_X86Pascal: Out << 'C'; break; case CC_X86ThisCall: Out << 'E'; break; Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2641,6 +2641,7 @@ case CC_Swift: return "swiftcall"; case CC_PreserveMost: return "preserve_most"; case CC_PreserveAll: return "preserve_all"; + case CC_AArch64Win64: return "ms_abi"; } llvm_unreachable("Invalid calling convention."); Index: lib/AST/TypePrinter.cpp =================================================================== --- lib/AST/TypePrinter.cpp +++ lib/AST/TypePrinter.cpp @@ -742,6 +742,9 @@ case CC_PreserveAll: OS << " __attribute__((preserve_all))"; break; + case CC_AArch64Win64: + OS << " __attribute__((ms_abi))"; + break; } } Index: lib/Basic/Targets.cpp =================================================================== --- lib/Basic/Targets.cpp +++ lib/Basic/Targets.cpp @@ -6291,6 +6291,9 @@ LongDoubleWidth = LongDoubleAlign = SuitableAlign = 128; LongDoubleFormat = &llvm::APFloat::IEEEquad(); + // Make __builtin_ms_va_list available. + HasBuiltinMSVaList = true; + // {} in inline assembly are neon specifiers, not assembly variant // specifiers. NoAsmVariants = true; @@ -6474,6 +6477,7 @@ case CC_PreserveMost: case CC_PreserveAll: case CC_OpenCLKernel: + case CC_AArch64Win64: return CCCR_OK; default: return CCCR_Warning; Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -5273,6 +5273,31 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, const CallExpr *E) { + if (BuiltinID == AArch64::BI__builtin_ms_va_start || + BuiltinID == AArch64::BI__builtin_ms_va_end) + return EmitVAStartEnd(EmitMSVAListRef(E->getArg(0)).getPointer(), + BuiltinID == AArch64::BI__builtin_ms_va_start); + if (BuiltinID == AArch64::BI__builtin_ms_va_copy) { + // Lower this manually. We can't reliably determine whether or not any + // given va_copy() is for a Win64 va_list from the calling convention + // alone, because it's legal to do this from an AAPCS ABI function. + // With opaque pointer types, we won't have enough information in LLVM + // IR to determine this from the argument types, either. Best to do it + // now, while we have enough information. + Address DestAddr = EmitMSVAListRef(E->getArg(0)); + Address SrcAddr = EmitMSVAListRef(E->getArg(1)); + + llvm::Type *BPP = Int8PtrPtrTy; + + DestAddr = Address(Builder.CreateBitCast(DestAddr.getPointer(), BPP, "cp"), + DestAddr.getAlignment()); + SrcAddr = Address(Builder.CreateBitCast(SrcAddr.getPointer(), BPP, "ap"), + SrcAddr.getAlignment()); + + Value *ArgPtr = Builder.CreateLoad(SrcAddr, "ap.val"); + return Builder.CreateStore(ArgPtr, DestAddr); + } + unsigned HintID = static_cast(-1); switch (BuiltinID) { default: break; Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -64,6 +64,7 @@ case CC_PreserveMost: return llvm::CallingConv::PreserveMost; case CC_PreserveAll: return llvm::CallingConv::PreserveAll; case CC_Swift: return llvm::CallingConv::Swift; + case CC_AArch64Win64: return llvm::CallingConv::AArch64_Win64; } } @@ -191,7 +192,8 @@ FTP, FD); } -static CallingConv getCallingConventionForDecl(const Decl *D, bool IsWindows) { +static CallingConv getCallingConventionForDecl(const Decl *D, bool IsWindows, + llvm::Triple::ArchType Arch) { // Set the appropriate calling convention for the Function. if (D->hasAttr()) return CC_X86StdCall; @@ -218,7 +220,8 @@ return CC_IntelOclBicc; if (D->hasAttr()) - return IsWindows ? CC_C : CC_X86_64Win64; + return IsWindows ? CC_C : Arch == llvm::Triple::aarch64 ? CC_AArch64Win64 + : CC_X86_64Win64; if (D->hasAttr()) return IsWindows ? CC_X86_64SysV : CC_C; @@ -463,8 +466,11 @@ } FunctionType::ExtInfo einfo; - bool IsWindows = getContext().getTargetInfo().getTriple().isOSWindows(); - einfo = einfo.withCallingConv(getCallingConventionForDecl(MD, IsWindows)); + const llvm::Triple &TT = getContext().getTargetInfo().getTriple(); + bool IsWindows = TT.isOSWindows(); + llvm::Triple::ArchType Arch = TT.getArch(); + einfo = + einfo.withCallingConv(getCallingConventionForDecl(MD, IsWindows, Arch)); if (getContext().getLangOpts().ObjCAutoRefCount && MD->hasAttr()) Index: lib/CodeGen/CGDebugInfo.cpp =================================================================== --- lib/CodeGen/CGDebugInfo.cpp +++ lib/CodeGen/CGDebugInfo.cpp @@ -967,6 +967,7 @@ case CC_PreserveMost: case CC_PreserveAll: case CC_X86RegCall: + case CC_AArch64Win64: return 0; } return 0; Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -1440,6 +1440,9 @@ bool Sema::CheckAArch64BuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { + if (BuiltinID == AArch64::BI__builtin_ms_va_start) + return SemaBuiltinVAStart(BuiltinID, TheCall); + if (BuiltinID == AArch64::BI__builtin_arm_ldrex || BuiltinID == AArch64::BI__builtin_arm_ldaex || BuiltinID == AArch64::BI__builtin_arm_strex || @@ -3622,12 +3625,15 @@ static bool checkVAStartABI(Sema &S, unsigned BuiltinID, Expr *Fn) { const llvm::Triple &TT = S.Context.getTargetInfo().getTriple(); bool IsX64 = TT.getArch() == llvm::Triple::x86_64; + bool IsAArch64 = TT.getArch() == llvm::Triple::aarch64; bool IsWindows = TT.isOSWindows(); - bool IsMSVAStart = BuiltinID == X86::BI__builtin_ms_va_start; + bool IsMSVAStart = BuiltinID == X86::BI__builtin_ms_va_start || + BuiltinID == AArch64::BI__builtin_ms_va_start; + clang::CallingConv CC = CC_C; + if (const FunctionDecl *FD = S.getCurFunctionDecl()) + CC = FD->getType()->getAs()->getCallConv(); + if (IsX64) { - clang::CallingConv CC = CC_C; - if (const FunctionDecl *FD = S.getCurFunctionDecl()) - CC = FD->getType()->getAs()->getCallConv(); if (IsMSVAStart) { // Don't allow this in System V ABI functions. if (CC == CC_X86_64SysV || (!IsWindows && CC != CC_X86_64Win64)) @@ -3647,6 +3653,22 @@ return false; } + if (IsAArch64) { + if (IsMSVAStart) { + // Don't allow this in AAPCS functions. + if (!IsWindows && CC != CC_AArch64Win64) + return S.Diag(Fn->getLocStart(), + diag::err_ms_va_start_used_in_aapcs_function); + } else { + // On aarch64 Unix, don't allow this in Win64 ABI functions. + if (!IsWindows && CC == CC_AArch64Win64) + return S.Diag(Fn->getLocStart(), + diag::err_va_start_used_in_wrong_abi_function) + << !IsWindows; + } + return false; + } + if (IsMSVAStart) return S.Diag(Fn->getLocStart(), diag::err_x86_builtin_64_only); return false; Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -4279,8 +4279,13 @@ case AttributeList::AT_VectorCall: CC = CC_X86VectorCall; break; case AttributeList::AT_RegCall: CC = CC_X86RegCall; break; case AttributeList::AT_MSABI: - CC = Context.getTargetInfo().getTriple().isOSWindows() ? CC_C : - CC_X86_64Win64; + + CC = Context.getTargetInfo().getTriple().isOSWindows() + ? CC_C + : Context.getTargetInfo().getTriple().getArch() == + llvm::Triple::aarch64 + ? CC_AArch64Win64 + : CC_X86_64Win64; break; case AttributeList::AT_SysVABI: CC = Context.getTargetInfo().getTriple().isOSWindows() ? CC_X86_64SysV : Index: test/CodeGen/ms_abi_aarch64.c =================================================================== --- /dev/null +++ test/CodeGen/ms_abi_aarch64.c @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm < %s | FileCheck -check-prefix=LINUX %s +// RUN: %clang_cc1 -triple aarch64-pc-win32 -emit-llvm < %s | FileCheck -check-prefix=WIN64 %s + +void __attribute__((ms_abi)) f1(void); +void f2(void); +void f3(void) { + // LINUX-LABEL: define void @f3() + // WIN64-LABEL: define void @f3() + f1(); + // LINUX: call aarch64_win64cc void @f1() + // WIN64: call void @f1() + f2(); + // LINUX: call void @f2() + // WIN64: call void @f2() +} +// LINUX: declare aarch64_win64cc void @f1() +// LINUX: declare void @f2() +// WIN64: declare void @f1() +// WIN64: declare void @f2() + +// Win64 ABI varargs +void __attribute__((ms_abi)) f4(int a, ...) { + // LINUX-LABEL: define aarch64_win64cc void @f4 + // WIN64-LABEL: define void @f4 + __builtin_ms_va_list ap; + __builtin_ms_va_start(ap, a); + // LINUX: %[[AP:.*]] = alloca i8* + // LINUX: call void @llvm.va_start + // WIN64: %[[AP:.*]] = alloca i8* + // WIN64: call void @llvm.va_start + int b = __builtin_va_arg(ap, int); + // LINUX: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]] + // LINUX-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8 + // LINUX-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]] + // LINUX-NEXT: bitcast i8* %[[AP_CUR]] to i32* + // WIN64: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]] + // WIN64-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8 + // WIN64-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]] + // WIN64-NEXT: bitcast i8* %[[AP_CUR]] to i32* + __builtin_ms_va_list ap2; + __builtin_ms_va_copy(ap2, ap); + // LINUX: %[[AP_VAL:.*]] = load i8*, i8** %[[AP]] + // LINUX-NEXT: store i8* %[[AP_VAL]], i8** %[[AP2:.*]] + // WIN64: %[[AP_VAL:.*]] = load i8*, i8** %[[AP]] + // WIN64-NEXT: store i8* %[[AP_VAL]], i8** %[[AP2:.*]] + __builtin_ms_va_end(ap); + // LINUX: call void @llvm.va_end + // WIN64: call void @llvm.va_end +} + +// Let's verify that normal va_lists work right on Win64, too. +void f5(int a, ...) { + // WIN64-LABEL: define void @f5 + __builtin_va_list ap; + __builtin_va_start(ap, a); + // WIN64: %[[AP:.*]] = alloca i8* + // WIN64: call void @llvm.va_start + int b = __builtin_va_arg(ap, int); + // WIN64: %[[AP_CUR:.*]] = load i8*, i8** %[[AP]] + // WIN64-NEXT: %[[AP_NEXT:.*]] = getelementptr inbounds i8, i8* %[[AP_CUR]], i64 8 + // WIN64-NEXT: store i8* %[[AP_NEXT]], i8** %[[AP]] + // WIN64-NEXT: bitcast i8* %[[AP_CUR]] to i32* + __builtin_va_list ap2; + __builtin_va_copy(ap2, ap); + // WIN64: call void @llvm.va_copy + __builtin_va_end(ap); + // WIN64: call void @llvm.va_end +} Index: test/Sema/varargs-aarch64.c =================================================================== --- /dev/null +++ test/Sema/varargs-aarch64.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -triple aarch64-linux-gnu + +void f1(int a, ...) { + __builtin_ms_va_list ap; + __builtin_ms_va_start(ap, a); // expected-error {{'__builtin_ms_va_start' used in AAPCS ABI function}} +} + +void __attribute__((ms_abi)) f2(int a, ...) { + __builtin_va_list ap; + __builtin_va_start(ap, a); // expected-error {{'va_start' used in Win64 ABI function}} +} Index: tools/libclang/CXType.cpp =================================================================== --- tools/libclang/CXType.cpp +++ tools/libclang/CXType.cpp @@ -619,6 +619,7 @@ TCALLINGCONV(Swift); TCALLINGCONV(PreserveMost); TCALLINGCONV(PreserveAll); + TCALLINGCONV(AArch64Win64); case CC_SpirFunction: return CXCallingConv_Unexposed; case CC_OpenCLKernel: return CXCallingConv_Unexposed; break;