diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -3394,6 +3394,7 @@ CXCallingConv_PreserveMost = 14, CXCallingConv_PreserveAll = 15, CXCallingConv_AArch64VectorCall = 16, + CXCallingConv_AArch64Darwin = 17, CXCallingConv_Invalid = 100, CXCallingConv_Unexposed = 200 diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1477,6 +1477,12 @@ let Documentation = [MSABIDocs]; } +def DarwinABI : DeclOrTypeAttr { + let Spellings = [GCC<"darwin_abi">]; +// let Subjects = [Function, ObjCMethod]; + let Documentation = [DarwinABIDocs]; +} + def MSP430Interrupt : InheritableAttr, TargetSpecificAttr { // NOTE: If you add any additional spellings, ARMInterrupt's, MipsInterrupt's // and AnyX86Interrupt's spellings must match. diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2252,6 +2252,15 @@ }]; } +def DarwinABIDocs : Documentation { + let Category = DocCatCallingConvs; + let Content = [{ +On Linux ARM64 targets, this attribute changes the calling convention of +a function to match the default convention used on Apple ARM64. This +attribute has no effect on Apple targets or non-Linux ARM64 targets. + }]; +} + def StdCallDocs : Documentation { let Category = DocCatCallingConvs; let Content = [{ diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h --- a/clang/include/clang/Basic/Specifiers.h +++ b/clang/include/clang/Basic/Specifiers.h @@ -266,6 +266,7 @@ ICIS_ListInit ///< Direct list-initialization. }; + // clang-format off /// CallingConv - Specifies the calling convention that a function uses. enum CallingConv { CC_C, // __attribute__((cdecl)) @@ -286,7 +287,9 @@ CC_PreserveMost, // __attribute__((preserve_most)) CC_PreserveAll, // __attribute__((preserve_all)) CC_AArch64VectorCall, // __attribute__((aarch64_vector_pcs)) + CC_AArch64Darwin, // __attribute__((darwin_abi)) }; + // clang-format on /// Checks whether the given calling convention supports variadic /// calls. Unprototyped calls also use the variadic call rules. diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2900,6 +2900,8 @@ return "sysv_abi"; case CC_Win64: return "ms_abi"; + case CC_AArch64Darwin: + return "darwin_abi"; case CC_Swift: return "swiftcall"; } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3112,6 +3112,7 @@ return *this; } +// clang-format off StringRef FunctionType::getNameForCallConv(CallingConv CC) { switch (CC) { case CC_C: return "cdecl"; @@ -3121,6 +3122,7 @@ case CC_X86Pascal: return "pascal"; case CC_X86VectorCall: return "vectorcall"; case CC_Win64: return "ms_abi"; + case CC_AArch64Darwin: return "darwin_abi"; case CC_X86_64SysV: return "sysv_abi"; case CC_X86RegCall : return "regcall"; case CC_AAPCS: return "aapcs"; @@ -3136,6 +3138,7 @@ llvm_unreachable("Invalid calling convention."); } +// clang-format on FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, QualType canonical, @@ -3548,6 +3551,7 @@ case attr::AArch64VectorPcs: case attr::Pascal: case attr::MSABI: + case attr::DarwinABI: case attr::SysVABI: case attr::IntelOclBicc: case attr::PreserveMost: diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -957,6 +957,9 @@ case CC_Win64: OS << " __attribute__((ms_abi))"; break; + case CC_AArch64Darwin: + OS << " __attribute__((darwin_abi))"; + break; case CC_X86_64SysV: OS << " __attribute__((sysv_abi))"; break; @@ -1640,6 +1643,7 @@ case attr::VectorCall: OS << "vectorcall"; break; case attr::Pascal: OS << "pascal"; break; case attr::MSABI: OS << "ms_abi"; break; + case attr::DarwinABI: OS << "darwin_abi"; break; case attr::SysVABI: OS << "sysv_abi"; break; case attr::RegCall: OS << "regcall"; break; case attr::Pcs: { diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -513,6 +513,7 @@ case CC_PreserveAll: case CC_OpenCLKernel: case CC_AArch64VectorCall: + case CC_AArch64Darwin: case CC_Win64: return CCCR_OK; default: diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -43,6 +43,7 @@ /***/ +// clang-format off unsigned CodeGenTypes::ClangCallConvToLLVMCallConv(CallingConv CC) { switch (CC) { default: return llvm::CallingConv::C; @@ -50,6 +51,7 @@ case CC_X86FastCall: return llvm::CallingConv::X86_FastCall; case CC_X86RegCall: return llvm::CallingConv::X86_RegCall; case CC_X86ThisCall: return llvm::CallingConv::X86_ThisCall; + case CC_AArch64Darwin: return llvm::CallingConv::AArch64Darwin; case CC_Win64: return llvm::CallingConv::Win64; case CC_X86_64SysV: return llvm::CallingConv::X86_64_SysV; case CC_AAPCS: return llvm::CallingConv::ARM_AAPCS; @@ -67,6 +69,7 @@ case CC_Swift: return llvm::CallingConv::Swift; } } +// clang-format on /// Derives the 'this' type for codegen purposes, i.e. ignoring method CVR /// qualification. Either or both of RD and MD may be null. A null RD indicates @@ -198,7 +201,8 @@ FTP); } -static CallingConv getCallingConventionForDecl(const Decl *D, bool IsWindows) { +static CallingConv getCallingConventionForDecl(const Decl *D, bool IsWindows, + bool IsDarwin) { // Set the appropriate calling convention for the Function. if (D->hasAttr()) return CC_X86StdCall; @@ -230,6 +234,9 @@ if (D->hasAttr()) return IsWindows ? CC_C : CC_Win64; + if (D->hasAttr()) + return IsDarwin ? CC_C : CC_AArch64Darwin; + if (D->hasAttr()) return IsWindows ? CC_X86_64SysV : CC_C; @@ -485,7 +492,9 @@ FunctionType::ExtInfo einfo; bool IsWindows = getContext().getTargetInfo().getTriple().isOSWindows(); - einfo = einfo.withCallingConv(getCallingConventionForDecl(MD, IsWindows)); + bool IsDarwin = getContext().getTargetInfo().getTriple().isOSDarwin(); + einfo = einfo.withCallingConv( + getCallingConventionForDecl(MD, IsWindows, IsDarwin)); if (getContext().getLangOpts().ObjCAutoRefCount && MD->hasAttr()) diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1230,6 +1230,8 @@ return llvm::dwarf::DW_CC_BORLAND_pascal; case CC_Win64: return llvm::dwarf::DW_CC_LLVM_Win64; + case CC_AArch64Darwin: + return llvm::dwarf::DW_CC_LLVM_AArch64Darwin; case CC_X86_64SysV: return llvm::dwarf::DW_CC_LLVM_X86_64SysV; case CC_AAPCS: diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -65,6 +65,20 @@ UseARMGuardVarABI(UseARMGuardVarABI), Use32BitVTableOffsetABI(false) { } + bool HasThisReturn(GlobalDecl GD) const override { + // Returns true if AArch64 Darwin ABI is explicitly used. + const bool IsCtorOrDtor = (isa(GD.getDecl()) || + (isa(GD.getDecl()) && + GD.getDtorType() != Dtor_Deleting)); + if (!IsCtorOrDtor) { + return false; + } + const auto *FTy = + cast(GD.getDecl())->getType()->getAs(); + const auto FCC = FTy->getCallConv(); + return FCC == CallingConv::CC_AArch64Darwin; + } + bool classifyReturnType(CGFunctionInfo &FI) const override; RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const override { diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -5446,9 +5446,16 @@ private: ABIKind getABIKind() const { return Kind; } bool isDarwinPCS() const { return Kind == DarwinPCS; } + bool isDarwinPCS(const unsigned CConv) const { + return isDarwinPCS() || CConv == llvm::CallingConv::AArch64Darwin; + } + bool isDarwinPCS(CGFunctionInfo const &FI) const { + return isDarwinPCS(FI.getCallingConvention()); + } - ABIArgInfo classifyReturnType(QualType RetTy, bool IsVariadic) const; - ABIArgInfo classifyArgumentType(QualType RetTy) const; + ABIArgInfo classifyReturnType(QualType RetTy, bool IsVariadic, + unsigned CConv) const; + ABIArgInfo classifyArgumentType(QualType RetTy, unsigned CConv) const; ABIArgInfo coerceIllegalVector(QualType Ty) const; bool isHomogeneousAggregateBaseType(QualType Ty) const override; bool isHomogeneousAggregateSmallEnough(const Type *Ty, @@ -5457,12 +5464,13 @@ bool isIllegalVectorType(QualType Ty) const; void computeInfo(CGFunctionInfo &FI) const override { + const unsigned CConv = FI.getCallingConvention(); if (!::classifyReturnType(getCXXABI(), FI, *this)) FI.getReturnInfo() = - classifyReturnType(FI.getReturnType(), FI.isVariadic()); + classifyReturnType(FI.getReturnType(), FI.isVariadic(), CConv); for (auto &it : FI.arguments()) - it.info = classifyArgumentType(it.type); + it.info = classifyArgumentType(it.type, CConv); } Address EmitDarwinVAArg(Address VAListAddr, QualType Ty, @@ -5660,7 +5668,8 @@ return getNaturalAlignIndirect(Ty, /*ByVal=*/false); } -ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType Ty) const { +ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType Ty, + unsigned CConv) const { Ty = useFirstFieldIfTransparentUnion(Ty); // Handle illegal vector types here. @@ -5676,7 +5685,7 @@ if (EIT->getNumBits() > 128) return getNaturalAlignIndirect(Ty); - return (isPromotableIntegerTypeForABI(Ty) && isDarwinPCS() + return (isPromotableIntegerTypeForABI(Ty) && isDarwinPCS(CConv) ? ABIArgInfo::getExtend(Ty) : ABIArgInfo::getDirect()); } @@ -5693,7 +5702,7 @@ uint64_t Size = getContext().getTypeSize(Ty); bool IsEmpty = isEmptyRecord(getContext(), Ty, true); if (IsEmpty || Size == 0) { - if (!getContext().getLangOpts().CPlusPlus || isDarwinPCS()) + if (!getContext().getLangOpts().CPlusPlus || isDarwinPCS(CConv)) return ABIArgInfo::getIgnore(); // GNU C mode. The only argument that gets ignored is an empty one with size @@ -5739,8 +5748,8 @@ return getNaturalAlignIndirect(Ty, /*ByVal=*/false); } -ABIArgInfo AArch64ABIInfo::classifyReturnType(QualType RetTy, - bool IsVariadic) const { +ABIArgInfo AArch64ABIInfo::classifyReturnType(QualType RetTy, bool IsVariadic, + unsigned CConv) const { if (RetTy->isVoidType()) return ABIArgInfo::getIgnore(); @@ -5763,7 +5772,7 @@ if (EIT->getNumBits() > 128) return getNaturalAlignIndirect(RetTy); - return (isPromotableIntegerTypeForABI(RetTy) && isDarwinPCS() + return (isPromotableIntegerTypeForABI(RetTy) && isDarwinPCS(CConv) ? ABIArgInfo::getExtend(RetTy) : ABIArgInfo::getDirect()); } @@ -5866,7 +5875,7 @@ Address AArch64ABIInfo::EmitAAPCSVAArg(Address VAListAddr, QualType Ty, CodeGenFunction &CGF) const { - ABIArgInfo AI = classifyArgumentType(Ty); + ABIArgInfo AI = classifyArgumentType(Ty, llvm::CallingConv::C); bool IsIndirect = AI.isIndirect(); llvm::Type *BaseTy = CGF.ConvertType(Ty); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -5612,6 +5612,7 @@ bool IsAArch64 = (TT.getArch() == llvm::Triple::aarch64 || TT.getArch() == llvm::Triple::aarch64_32); bool IsWindows = TT.isOSWindows(); + bool IsLinux = TT.isOSLinux(); bool IsMSVAStart = BuiltinID == Builtin::BI__builtin_ms_va_start; if (IsX64 || IsAArch64) { CallingConv CC = CC_C; @@ -5627,8 +5628,10 @@ // On x64 Windows, don't allow this in System V ABI functions. // (Yes, that means there's no corresponding way to support variadic // System V ABI functions on Windows.) + // On Apple ARM64 ABI functions, only allow this on AArch64/Unix. if ((IsWindows && CC == CC_X86_64SysV) || - (!IsWindows && CC == CC_Win64)) + (!IsWindows && CC == CC_Win64) || + (!IsLinux && CC == CC_AArch64Darwin)) return S.Diag(Fn->getBeginLoc(), diag::err_va_start_used_in_wrong_abi_function) << !IsWindows; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4465,6 +4465,9 @@ case ParsedAttr::AT_MSABI: D->addAttr(::new (S.Context) MSABIAttr(S.Context, AL)); return; + case ParsedAttr::AT_DarwinABI: + D->addAttr(::new (S.Context) DarwinABIAttr(S.Context, AL)); + return; case ParsedAttr::AT_SysVABI: D->addAttr(::new (S.Context) SysVABIAttr(S.Context, AL)); return; @@ -4633,6 +4636,10 @@ CC = Context.getTargetInfo().getTriple().isOSWindows() ? CC_C : CC_Win64; break; + case ParsedAttr::AT_DarwinABI: + CC = Context.getTargetInfo().getTriple().isOSDarwin() ? CC_C + : CC_AArch64Darwin; + break; case ParsedAttr::AT_SysVABI: CC = Context.getTargetInfo().getTriple().isOSWindows() ? CC_X86_64SysV : CC_C; @@ -7742,6 +7749,7 @@ case ParsedAttr::AT_SwiftCall: case ParsedAttr::AT_VectorCall: case ParsedAttr::AT_MSABI: + case ParsedAttr::AT_DarwinABI: case ParsedAttr::AT_SysVABI: case ParsedAttr::AT_Pcs: case ParsedAttr::AT_IntelOclBicc: diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -120,6 +120,7 @@ case ParsedAttr::AT_VectorCall: \ case ParsedAttr::AT_AArch64VectorPcs: \ case ParsedAttr::AT_MSABI: \ + case ParsedAttr::AT_DarwinABI: \ case ParsedAttr::AT_SysVABI: \ case ParsedAttr::AT_Pcs: \ case ParsedAttr::AT_IntelOclBicc: \ @@ -7301,6 +7302,8 @@ return createSimpleAttr(Ctx, Attr); case ParsedAttr::AT_MSABI: return createSimpleAttr(Ctx, Attr); + case ParsedAttr::AT_DarwinABI: + return createSimpleAttr(Ctx, Attr); case ParsedAttr::AT_SysVABI: return createSimpleAttr(Ctx, Attr); case ParsedAttr::AT_PreserveMost: diff --git a/clang/test/CodeGen/darwin_abi.c b/clang/test/CodeGen/darwin_abi.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/darwin_abi.c @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple aarch64-pc-linux %s -emit-llvm -o - |FileCheck %s +// Checks that correct LLVM IR is generated for functions that target +// the Darwin AArch64 ABI under Linux/AArch64. What we check: +// * sext/zext on return values and arguments +// * va_list forwarding from Darwin to Linux support + +// CHECK: define aarch64_darwincc signext i16 @f1(i16 signext +__attribute__((darwin_abi)) short f1(short a) { + return a + 1; +} + +// CHECK: define aarch64_darwincc zeroext i16 @f2(i16 zeroext +__attribute__((darwin_abi)) unsigned short f2(unsigned short a) { + return a + 1; +} + +// CHECK: define aarch64_darwincc void @foo(i32 %n, ...) +// CHECK: call void @llvm.va_start +void vfoo(int n, __builtin_va_list *va); +__attribute((darwin_abi)) void foo(int n, ...) { + __builtin_va_list va; + __builtin_va_start(va, n); + vfoo(n, &va); + __builtin_va_end(va); +} diff --git a/clang/test/CodeGen/darwin_abi_empty_structs.cpp b/clang/test/CodeGen/darwin_abi_empty_structs.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/darwin_abi_empty_structs.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 %s -S -emit-llvm -o - -O1 -triple aarch64-pc-linux | FileCheck %s +// Verify that "when passing parameters to a function, Apple platforms ignore +// empty structures unless those structures have a nontrivial destructor or +// copy constructor." when using darwin_abi + +struct Empty {}; + +__attribute__((darwin_abi)) void foo(int n, Empty E); +// CHECK: @_Z3bari(i32 %[[ARG:[[:alnum:]_]+]]) +void bar(int n) { + // CHECK: call aarch64_darwincc void @_Z3fooi5Empty(i32 %[[ARG]]) + return foo(n, Empty{}); +} + +// CHECK: declare aarch64_darwincc void @_Z3fooi5Empty(i32) diff --git a/clang/test/CodeGen/darwin_abi_vaarg.c b/clang/test/CodeGen/darwin_abi_vaarg.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/darwin_abi_vaarg.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -triple aarch64-pc-linux -emit-llvm %s -o - |FileCheck %s +// Check that va_arg used inside a function with the darwin_abi attribute still +// uses the Linux ABI lowering. + +// CHECK: define internal aarch64_darwincc i32 @vfoo(i32 %n, %struct.__va_list* %[[VA:[[:alnum:]_]+]]) +// CHECK: getelementptr inbounds %struct.__va_list, %struct.__va_list* %[[VA]], i32 0, i32 3 +__attribute__((darwin_abi, noinline)) static int vfoo(int n, __builtin_va_list va) { + int res = 0; + for (int i = 0; i < n; ++i) { + res += __builtin_va_arg(va, int); + } + return res; +} + +int foo(int n, ...) { + __builtin_va_list va; + __builtin_va_start(va, n); + const int res = vfoo(n, va); + __builtin_va_end(va); + return res; +} diff --git a/clang/test/CodeGen/debug-info-cc.c b/clang/test/CodeGen/debug-info-cc.c --- a/clang/test/CodeGen/debug-info-cc.c +++ b/clang/test/CodeGen/debug-info-cc.c @@ -2,26 +2,7 @@ // RUN: %clang_cc1 -triple x86_64-unknown-windows-msvc -o - -emit-llvm -debug-info-kind=limited %s | FileCheck %s --check-prefix=WINDOWS // RUN: %clang_cc1 -triple i386-pc-linux-gnu -o - -emit-llvm -debug-info-kind=limited %s | FileCheck %s --check-prefix=LINUX32 // RUN: %clang_cc1 -triple armv7--linux-gnueabihf -o - -emit-llvm -debug-info-kind=limited %s | FileCheck %s --check-prefix=ARM - -// enum CallingConv { -// CC_C, // __attribute__((cdecl)) -// CC_X86StdCall, // __attribute__((stdcall)) -// CC_X86FastCall, // __attribute__((fastcall)) -// CC_X86ThisCall, // __attribute__((thiscall)) -// CC_X86VectorCall, // __attribute__((vectorcall)) -// CC_X86Pascal, // __attribute__((pascal)) -// CC_Win64, // __attribute__((ms_abi)) -// CC_X86_64SysV, // __attribute__((sysv_abi)) -// CC_X86RegCall, // __attribute__((regcall)) -// CC_AAPCS, // __attribute__((pcs("aapcs"))) -// CC_AAPCS_VFP, // __attribute__((pcs("aapcs-vfp"))) -// CC_IntelOclBicc, // __attribute__((intel_ocl_bicc)) -// CC_SpirFunction, // default for OpenCL functions on SPIR target -// CC_OpenCLKernel, // inferred for OpenCL kernels -// CC_Swift, // __attribute__((swiftcall)) -// CC_PreserveMost, // __attribute__((preserve_most)) -// CC_PreserveAll, // __attribute__((preserve_all)) -// }; +// RUN: %clang_cc1 -triple aarch64-pc-linux -o - -emit-llvm -debug-info-kind=limited %s | FileCheck %s --check-prefix=AARCH64 #ifdef __x86_64__ @@ -118,3 +99,11 @@ return a+b; } #endif + +#if defined(__aarch64__) && defined(__linux__) +// AARCH64: !DISubprogram({{.*}}"add_darwinabi", {{.*}}type: ![[FTY:[0-9]+]] +// AARCH64: ![[FTY]] = !DISubroutineType({{.*}}cc: DW_CC_LLVM_AArch64Darwin, +__attribute__((darwin_abi)) int add_darwinabi(int a, int b) { + return a + b; +} +#endif diff --git a/clang/test/CodeGenCXX/darwinabi-returnthis.cpp b/clang/test/CodeGenCXX/darwinabi-returnthis.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/darwinabi-returnthis.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -triple aarch64-pc-linux-gnu -mconstructor-aliases %s -emit-llvm -O1 -o - | FileCheck %s + +struct A { + __attribute__((darwin_abi)) A(); + __attribute__((darwin_abi)) ~A(); +}; + +// CHECK: define aarch64_darwincc %struct.A* @_ZN1AC2Ev(%struct.A* readnone returned %[[THIS:.*]]) +A::A() { + // CHECK: ret %struct.A* %[[THIS]] +} + +// CHECK: define aarch64_darwincc %struct.A* @_ZN1AD2Ev(%struct.A* readnone returned %[[THIS:.*]]) +A::~A() { + // CHECK: ret %struct.A* %[[THIS]] +} diff --git a/clang/tools/libclang/CXType.cpp b/clang/tools/libclang/CXType.cpp --- a/clang/tools/libclang/CXType.cpp +++ b/clang/tools/libclang/CXType.cpp @@ -658,6 +658,7 @@ TCALLINGCONV(X86RegCall); TCALLINGCONV(X86VectorCall); TCALLINGCONV(AArch64VectorCall); + TCALLINGCONV(AArch64Darwin); TCALLINGCONV(Win64); TCALLINGCONV(X86_64SysV); TCALLINGCONV(AAPCS); diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def --- a/llvm/include/llvm/BinaryFormat/Dwarf.def +++ b/llvm/include/llvm/BinaryFormat/Dwarf.def @@ -788,6 +788,7 @@ HANDLE_DW_CC(0xc9, LLVM_PreserveMost) HANDLE_DW_CC(0xca, LLVM_PreserveAll) HANDLE_DW_CC(0xcb, LLVM_X86RegCall) +HANDLE_DW_CC(0xcc, LLVM_AArch64Darwin) // From GCC source code (include/dwarf2.h): This DW_CC_ value is not currently // generated by any toolchain. It is used internally to GDB to indicate OpenCL C // functions that have been compiled with the IBM XL C for OpenCL compiler and use diff --git a/llvm/include/llvm/IR/CallingConv.h b/llvm/include/llvm/IR/CallingConv.h --- a/llvm/include/llvm/IR/CallingConv.h +++ b/llvm/include/llvm/IR/CallingConv.h @@ -241,6 +241,8 @@ /// The remainder matches the regular calling convention. WASM_EmscriptenInvoke = 99, + AArch64Darwin = 100, + /// The highest possible calling convention ID. Must be some 2^k - 1. MaxID = 1023 }; diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -605,6 +605,7 @@ KEYWORD(intel_ocl_bicc); KEYWORD(x86_64_sysvcc); KEYWORD(win64cc); + KEYWORD(aarch64_darwincc); KEYWORD(x86_regcallcc); KEYWORD(webkit_jscc); KEYWORD(swiftcc); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1989,6 +1989,7 @@ Lex.Lex(); } +// clang-format off /// ParseOptionalCallingConv /// ::= /*empty*/ /// ::= 'ccc' @@ -2014,6 +2015,7 @@ /// ::= 'spir_kernel' /// ::= 'x86_64_sysvcc' /// ::= 'win64cc' +/// ::= 'aarch64_darwincc' /// ::= 'webkit_jscc' /// ::= 'anyregcc' /// ::= 'preserve_mostcc' @@ -2050,10 +2052,9 @@ 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; + case lltok::kw_aarch64_darwincc:CC = CallingConv::AArch64Darwin; break; case lltok::kw_aarch64_vector_pcs:CC = CallingConv::AArch64_VectorCall; break; - case lltok::kw_aarch64_sve_vector_pcs: - CC = CallingConv::AArch64_SVE_VectorCall; - break; + case lltok::kw_aarch64_sve_vector_pcs:CC = CallingConv::AArch64_SVE_VectorCall; break; case lltok::kw_msp430_intrcc: CC = CallingConv::MSP430_INTR; break; case lltok::kw_avr_intrcc: CC = CallingConv::AVR_INTR; break; case lltok::kw_avr_signalcc: CC = CallingConv::AVR_SIGNAL; break; @@ -2092,6 +2093,7 @@ Lex.Lex(); return false; } +// clang-format on /// ParseMetadataAttachment /// ::= !dbg !42 diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -152,6 +152,7 @@ kw_spir_func, kw_x86_64_sysvcc, kw_win64cc, + kw_aarch64_darwincc, kw_webkit_jscc, kw_anyregcc, kw_swiftcc, diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -341,6 +341,7 @@ return nullptr; } +// clang-format off static void PrintCallingConv(unsigned cc, raw_ostream &Out) { switch (cc) { default: Out << "cc" << cc; break; @@ -364,9 +365,7 @@ case CallingConv::ARM_AAPCS: Out << "arm_aapcscc"; break; case CallingConv::ARM_AAPCS_VFP: Out << "arm_aapcs_vfpcc"; break; case CallingConv::AArch64_VectorCall: Out << "aarch64_vector_pcs"; break; - case CallingConv::AArch64_SVE_VectorCall: - Out << "aarch64_sve_vector_pcs"; - break; + case CallingConv::AArch64_SVE_VectorCall: Out << "aarch64_sve_vector_pcs"; break; case CallingConv::MSP430_INTR: Out << "msp430_intrcc"; break; case CallingConv::AVR_INTR: Out << "avr_intrcc "; break; case CallingConv::AVR_SIGNAL: Out << "avr_signalcc "; break; @@ -374,6 +373,7 @@ case CallingConv::PTX_Device: Out << "ptx_device"; break; case CallingConv::X86_64_SysV: Out << "x86_64_sysvcc"; break; case CallingConv::Win64: Out << "win64cc"; break; + case CallingConv::AArch64Darwin: Out << "aarch64_darwincc"; break; case CallingConv::SPIR_FUNC: Out << "spir_func"; break; case CallingConv::SPIR_KERNEL: Out << "spir_kernel"; break; case CallingConv::Swift: Out << "swiftcc"; break; @@ -390,6 +390,7 @@ case CallingConv::AMDGPU_KERNEL: Out << "amdgpu_kernel"; break; } } +// clang-format on enum PrefixType { GlobalPrefix, diff --git a/llvm/lib/Target/AArch64/AArch64CallingConvention.cpp b/llvm/lib/Target/AArch64/AArch64CallingConvention.cpp --- a/llvm/lib/Target/AArch64/AArch64CallingConvention.cpp +++ b/llvm/lib/Target/AArch64/AArch64CallingConvention.cpp @@ -145,7 +145,9 @@ for (auto Reg : RegList) State.AllocateReg(Reg); - const Align SlotAlign = Subtarget.isTargetDarwin() ? Align(1) : Align(8); + const auto FCC = State.getMachineFunction().getFunction().getCallingConv(); + const Align SlotAlign = + Subtarget.isCallingConvDarwin(FCC) ? Align(1) : Align(8); return finishStackBlock(PendingMembers, LocVT, ArgFlags, State, SlotAlign); } diff --git a/llvm/lib/Target/AArch64/AArch64FastISel.cpp b/llvm/lib/Target/AArch64/AArch64FastISel.cpp --- a/llvm/lib/Target/AArch64/AArch64FastISel.cpp +++ b/llvm/lib/Target/AArch64/AArch64FastISel.cpp @@ -350,7 +350,8 @@ return CC_AArch64_GHC; if (CC == CallingConv::CFGuard_Check) return CC_AArch64_Win64_CFGuard_Check; - return Subtarget->isTargetDarwin() ? CC_AArch64_DarwinPCS : CC_AArch64_AAPCS; + return Subtarget->isCallingConvDarwin(CC) ? CC_AArch64_DarwinPCS + : CC_AArch64_AAPCS; } unsigned AArch64FastISel::fastMaterializeAlloca(const AllocaInst *AI) { diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h @@ -873,6 +873,7 @@ SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const; SDValue LowerAAPCS_VASTART(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerAAPCSFromDarwin_VASTART(SDValue Op, SelectionDAG &DAG) const; SDValue LowerDarwin_VASTART(SDValue Op, SelectionDAG &DAG) const; SDValue LowerWin64_VASTART(SDValue Op, SelectionDAG &DAG) const; SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const; diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -4079,6 +4079,11 @@ case CallingConv::AArch64_VectorCall: case CallingConv::AArch64_SVE_VectorCall: return CC_AArch64_AAPCS; + case CallingConv::AArch64Darwin: + if (!IsVarArg) + return CC_AArch64_DarwinPCS; + return Subtarget->isTargetILP32() ? CC_AArch64_DarwinPCS_ILP32_VarArg + : CC_AArch64_DarwinPCS_VarArg; } } @@ -4094,7 +4099,9 @@ SelectionDAG &DAG, SmallVectorImpl &InVals) const { MachineFunction &MF = DAG.getMachineFunction(); MachineFrameInfo &MFI = MF.getFrameInfo(); - bool IsWin64 = Subtarget->isCallingConvWin64(MF.getFunction().getCallingConv()); + const auto FCC = MF.getFunction().getCallingConv(); + const bool IsWin64 = Subtarget->isCallingConvWin64(FCC); + const bool IsDarwin = Subtarget->isCallingConvDarwin(FCC); // Assign locations to all of the incoming arguments. SmallVector ArgLocs; @@ -4278,7 +4285,7 @@ // varargs AArch64FunctionInfo *FuncInfo = MF.getInfo(); if (isVarArg) { - if (!Subtarget->isTargetDarwin() || IsWin64) { + if (!IsDarwin || IsWin64) { // The AAPCS variadic function ABI is identical to the non-variadic // one. As a result there may be more arguments in registers and we should // save them for future reference. @@ -6429,6 +6436,62 @@ return getAddr(BA, DAG); } +SDValue +AArch64TargetLowering::LowerAAPCSFromDarwin_VASTART(SDValue Op, + SelectionDAG &DAG) const { + // Linux/AArch64 va_list structure is (AArch64 Procedure Call Standard, + // section B.3.): + // typedef struct { + // void *stack; + // void *gr_top; + // void *vr_top; + // int gr_offs; + // int vr_offs; + // } va_list; + // Darwin/AArch64 va_list is just a pointer to the stack, as all variadic + // arguments are pushed to the stack. + // So we basically set stack as LowerDarwin_VASTART would do, and then set + // the gr_offs & vr_offs fields to zero. This will enforce AAPCS functions + // processing va_list objects to only get objects from the stack. + + MachineFunction &MF = DAG.getMachineFunction(); + AArch64FunctionInfo *FuncInfo = MF.getInfo(); + auto PtrVT = getPointerTy(DAG.getDataLayout()); + SDLoc DL(Op); + + SDValue Chain = Op.getOperand(0); + SDValue VAList = Op.getOperand(1); + const Value *SV = cast(Op.getOperand(2))->getValue(); + SmallVector MemOps; + + // void *__stack at offset 0 + SDValue Stack = DAG.getFrameIndex(FuncInfo->getVarArgsStackIndex(), PtrVT); + MemOps.push_back( + DAG.getStore(Chain, DL, Stack, VAList, MachinePointerInfo(SV), Align(8))); + + // void *__gr_top at offset 8. Won't be used. + const int GPRSize = 0; + + // void *__vr_top at offset 16. Won't be used. + const int FPRSize = 0; + + // int __gr_offs at offset 24 + SDValue GROffsAddr = + DAG.getNode(ISD::ADD, DL, PtrVT, VAList, DAG.getConstant(24, DL, PtrVT)); + MemOps.push_back( + DAG.getStore(Chain, DL, DAG.getConstant(-GPRSize, DL, MVT::i32), + GROffsAddr, MachinePointerInfo(SV, 24), Align(4))); + + // int __vr_offs at offset 28 + SDValue VROffsAddr = + DAG.getNode(ISD::ADD, DL, PtrVT, VAList, DAG.getConstant(28, DL, PtrVT)); + MemOps.push_back( + DAG.getStore(Chain, DL, DAG.getConstant(-FPRSize, DL, MVT::i32), + VROffsAddr, MachinePointerInfo(SV, 28), Align(4))); + + return DAG.getNode(ISD::TokenFactor, DL, MVT::Other, MemOps); +} + SDValue AArch64TargetLowering::LowerDarwin_VASTART(SDValue Op, SelectionDAG &DAG) const { AArch64FunctionInfo *FuncInfo = @@ -6529,11 +6592,19 @@ SelectionDAG &DAG) const { MachineFunction &MF = DAG.getMachineFunction(); - if (Subtarget->isCallingConvWin64(MF.getFunction().getCallingConv())) + const auto FCC = MF.getFunction().getCallingConv(); + if (Subtarget->isCallingConvWin64(FCC)) return LowerWin64_VASTART(Op, DAG); - else if (Subtarget->isTargetDarwin()) - return LowerDarwin_VASTART(Op, DAG); - else + else if (Subtarget->isCallingConvDarwin(FCC)) { + if (Subtarget->isTargetDarwin()) { + return LowerDarwin_VASTART(Op, DAG); + } + if (!Subtarget->isTargetWindows()) { + return LowerAAPCSFromDarwin_VASTART(Op, DAG); + } + report_fatal_error("can't lower Darwin vaarg if target OS isn't Darwin or " + "has an official AAPCS ABI (e.g. Linux)"); + } else return LowerAAPCS_VASTART(Op, DAG); } diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -74,7 +74,6 @@ const MCPhysReg * AArch64RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { assert(MF && "Invalid MachineFunction pointer."); - if (MF->getFunction().getCallingConv() == CallingConv::GHC) // GHC set of callee saved regs is empty as all those regs are // used for passing STG regs around @@ -87,22 +86,26 @@ if (MF->getSubtarget().isTargetDarwin()) return getDarwinCalleeSavedRegs(MF); - if (MF->getFunction().getCallingConv() == CallingConv::CFGuard_Check) + const auto FCC = MF->getFunction().getCallingConv(); + if (FCC == CallingConv::AArch64Darwin) { + return CSR_Darwin_AArch64_AAPCS_SaveList; + } + if (FCC == CallingConv::CFGuard_Check) return CSR_Win_AArch64_CFGuard_Check_SaveList; if (MF->getSubtarget().isTargetWindows()) return CSR_Win_AArch64_AAPCS_SaveList; - if (MF->getFunction().getCallingConv() == CallingConv::AArch64_VectorCall) + if (FCC == CallingConv::AArch64_VectorCall) return CSR_AArch64_AAVPCS_SaveList; - if (MF->getFunction().getCallingConv() == CallingConv::AArch64_SVE_VectorCall) + if (FCC == CallingConv::AArch64_SVE_VectorCall) return CSR_AArch64_SVE_AAPCS_SaveList; if (MF->getSubtarget().getTargetLowering() ->supportSwiftError() && MF->getFunction().getAttributes().hasAttrSomewhere( Attribute::SwiftError)) return CSR_AArch64_AAPCS_SwiftError_SaveList; - if (MF->getFunction().getCallingConv() == CallingConv::PreserveMost) + if (FCC == CallingConv::PreserveMost) return CSR_AArch64_RT_MostRegs_SaveList; - if (MF->getFunction().getCallingConv() == CallingConv::Win64) + if (FCC == CallingConv::Win64) // This is for OSes other than Windows; Windows is a separate case further // above. return CSR_AArch64_AAPCS_X18_SaveList; @@ -221,6 +224,9 @@ return getDarwinCallPreservedMask(MF, CC); } + if (CC == CallingConv::AArch64Darwin) { + return CSR_Darwin_AArch64_AAPCS_RegMask; + } if (CC == CallingConv::AArch64_VectorCall) return SCS ? CSR_AArch64_AAVPCS_SCS_RegMask : CSR_AArch64_AAVPCS_RegMask; if (CC == CallingConv::AArch64_SVE_VectorCall) @@ -291,7 +297,7 @@ // In case that the calling convention does not use the same register for // both, the function should return NULL (does not currently apply) assert(CC != CallingConv::GHC && "should not be GHC calling convention."); - if (MF.getSubtarget().isTargetDarwin()) + if (MF.getSubtarget().isCallingConvDarwin(CC)) return CSR_Darwin_AArch64_AAPCS_ThisReturn_RegMask; return CSR_AArch64_AAPCS_ThisReturn_RegMask; } diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.h b/llvm/lib/Target/AArch64/AArch64Subtarget.h --- a/llvm/lib/Target/AArch64/AArch64Subtarget.h +++ b/llvm/lib/Target/AArch64/AArch64Subtarget.h @@ -549,6 +549,13 @@ } } + bool isCallingConvDarwin(CallingConv::ID CC) const { + if (CC == CallingConv::AArch64Darwin) { + return true; + } + return isTargetDarwin(); + } + void mirFileLoaded(MachineFunction &MF) const override; // Return the known range for the bit length of SVE data registers. A value diff --git a/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp b/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp --- a/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp @@ -474,8 +474,8 @@ uint64_t StackOffset = Handler.StackUsed; if (F.isVarArg()) { auto &Subtarget = MF.getSubtarget(); - if (!Subtarget.isTargetDarwin()) { - // FIXME: we need to reimplement saveVarArgsRegisters from + if (!Subtarget.isCallingConvDarwin(MF.getFunction().getCallingConv())) { + // FIXME: we need to reimplement saveVarArgsRegisters from // AArch64ISelLowering. return false; } diff --git a/llvm/test/CodeGen/AArch64/darwin_abi.ll b/llvm/test/CodeGen/AArch64/darwin_abi.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/darwin_abi.ll @@ -0,0 +1,29 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=aarch64-pc-linux | FileCheck %s + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-pc-linux" + +define dso_local aarch64_darwincc signext i16 @f1(i16 signext %a) local_unnamed_addr #0 { +; CHECK-LABEL: f1: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: add w8, w0, #1 // =1 +; CHECK-NEXT: sxth w0, w8 +; CHECK-NEXT: ret +entry: + %add = add i16 %a, 1 + ret i16 %add +} + +define dso_local aarch64_darwincc zeroext i16 @f2(i16 zeroext %a) local_unnamed_addr #0 { +; CHECK-LABEL: f2: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: add w8, w0, #1 // =1 +; CHECK-NEXT: and w0, w8, #0xffff +; CHECK-NEXT: ret +entry: + %add = add i16 %a, 1 + ret i16 %add +} + +attributes #0 = { norecurse nounwind readnone "disable-tail-calls"="false" "frame-pointer"="non-leaf" "target-cpu"="generic" } diff --git a/llvm/test/CodeGen/AArch64/darwin_abi_vararg.ll b/llvm/test/CodeGen/AArch64/darwin_abi_vararg.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/darwin_abi_vararg.ll @@ -0,0 +1,39 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=aarch64-pc-linux | FileCheck %s + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-pc-linux" + +%struct.__va_list = type { i8*, i8*, i8*, i32, i32 } + +define dso_local aarch64_darwincc void @foo(i32 %n, ...) local_unnamed_addr #0 { +; CHECK-LABEL: foo: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: sub sp, sp, #48 // =48 +; CHECK-NEXT: stp x29, x30, [sp, #32] // 16-byte Folded Spill +; CHECK-NEXT: add x29, sp, #32 // =32 +; CHECK-NEXT: add x8, x29, #16 // =16 +; CHECK-NEXT: mov x1, sp +; CHECK-NEXT: str xzr, [sp, #24] +; CHECK-NEXT: str x8, [sp] +; CHECK-NEXT: bl vfoo +; CHECK-NEXT: ldp x29, x30, [sp, #32] // 16-byte Folded Reload +; CHECK-NEXT: add sp, sp, #48 // =48 +; CHECK-NEXT: ret +entry: + %va = alloca %struct.__va_list, align 8 + %0 = bitcast %struct.__va_list* %va to i8* + call void @llvm.va_start(i8* nonnull %0) + call void @vfoo(i32 %n, %struct.__va_list* nonnull %va) #1 + call void @llvm.va_end(i8* nonnull %0) + ret void +} + +declare void @llvm.va_start(i8*) #1 + +declare dso_local void @vfoo(i32, %struct.__va_list*) local_unnamed_addr #0 + +declare void @llvm.va_end(i8*) #1 + +attributes #0 = { nounwind "disable-tail-calls"="false" "frame-pointer"="non-leaf" "target-cpu"="generic" } +attributes #1 = { nounwind }