diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h --- a/clang/lib/CodeGen/CGCXXABI.h +++ b/clang/lib/CodeGen/CGCXXABI.h @@ -625,6 +625,8 @@ virtual std::pair LoadVTablePtr(CodeGenFunction &CGF, Address This, const CXXRecordDecl *RD) = 0; + + static bool isCXX14Aggregate(const CXXRecordDecl *RD); }; // Create an instance of a C++ ABI class: diff --git a/clang/lib/CodeGen/CGCXXABI.cpp b/clang/lib/CodeGen/CGCXXABI.cpp --- a/clang/lib/CodeGen/CGCXXABI.cpp +++ b/clang/lib/CodeGen/CGCXXABI.cpp @@ -310,3 +310,27 @@ return AddedStructorArgCounts(AddedArgs.Prefix.size(), AddedArgs.Suffix.size()); } + +bool CGCXXABI::isCXX14Aggregate(const CXXRecordDecl *RD) { + // For AArch64, we use the C++14 definition of an aggregate, so we also + // check for: + // No private or protected non static data members. + // No base classes + // No virtual functions + // Additionally, we need to ensure that there is a trivial copy assignment + // operator, a trivial destructor and no user-provided constructors. + if (RD->hasProtectedFields() || RD->hasPrivateFields()) + return false; + if (RD->getNumBases() > 0) + return false; + if (RD->isPolymorphic()) + return false; + if (RD->hasNonTrivialCopyAssignment()) + return false; + for (const CXXConstructorDecl *Ctor : RD->ctors()) + if (Ctor->isUserProvided()) + return false; + if (RD->hasNonTrivialDestructor()) + return false; + return true; +} diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -1070,30 +1070,6 @@ return isDeletingDtor(GD); } -static bool isCXX14Aggregate(const CXXRecordDecl *RD) { - // For AArch64, we use the C++14 definition of an aggregate, so we also - // check for: - // No private or protected non static data members. - // No base classes - // No virtual functions - // Additionally, we need to ensure that there is a trivial copy assignment - // operator, a trivial destructor and no user-provided constructors. - if (RD->hasProtectedFields() || RD->hasPrivateFields()) - return false; - if (RD->getNumBases() > 0) - return false; - if (RD->isPolymorphic()) - return false; - if (RD->hasNonTrivialCopyAssignment()) - return false; - for (const CXXConstructorDecl *Ctor : RD->ctors()) - if (Ctor->isUserProvided()) - return false; - if (RD->hasNonTrivialDestructor()) - return false; - return true; -} - bool MicrosoftCXXABI::classifyReturnType(CGFunctionInfo &FI) const { const CXXRecordDecl *RD = FI.getReturnType()->getAsCXXRecordDecl(); if (!RD) 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 @@ -5133,6 +5133,14 @@ Members += FldMembers; } + + const auto TT = CGT.getTarget().getTriple(); + // MSVC Windows on Arm64 considers a type not HFA if it is not an + // aggregate according to the C++14 spec. This is not consistent with the + // AAPCS64, but is defacto spec on that platform. + if (TT.isAArch64() && TT.isWindowsMSVCEnvironment() && + !CGCXXABI::isCXX14Aggregate(CXXRD)) + return false; } for (const auto *FD : RD->fields()) { diff --git a/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp b/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp --- a/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp @@ -468,3 +468,41 @@ // WIN64-LABEL: define dso_local void @"?g@C@pr30293@@QEAAXXZ"(%"struct.pr30293::C"* {{[^,]*}} %this) // WIN64: declare dso_local void @"?h@C@pr30293@@UEAAXUSmallWithDtor@@@Z"(i8*, i32) } + +namespace pr47611 { +// MSVC on Arm includes "isCXX14Aggregate" as part of its definition of +// Homogeneous Floating-point Aggregate (HFA). Additionally, it has a different +// handling of C++14 aggregates, which can lead to confusion. + +// Pod is a trivial HFA. +struct Pod { + double b[2]; +}; +// Not an aggregate according to C++14 spec => not HFA according to MSVC. +struct NotCXX14Aggregate { + NotCXX14Aggregate(); + Pod p; +}; +// NotPod is a C++14 aggregate. But not HFA, because it contains +// NotCXX14Aggregate (which itself is not HFA because it's not a C++14 +// aggregate). +struct NotPod { + NotCXX14Aggregate x; +}; +struct Empty {}; +// A class with a base is returned in standard registers by MSVC +struct HasEmptyBase : public Empty { + double b[2]; +}; +struct HasPodBase : public Pod {}; +// WOA64-LABEL: define dso_local %"struct.pr47611::Pod" @"?copy@pr47611@@YA?AUPod@1@PEAU21@@Z"(%"struct.pr47611::Pod"* %x) +Pod copy(Pod *x) { return *x; } // MSVC: ldp d0,d1,[x0], Clang: ldp d0,d1,[x0] +// WOA64-LABEL: define dso_local void @"?copy@pr47611@@YA?AUNotCXX14Aggregate@1@PEAU21@@Z"(%"struct.pr47611::NotCXX14Aggregate"* inreg noalias sret(%"struct.pr47611::NotCXX14Aggregate") align 8 %agg.result, %"struct.pr47611::NotCXX14Aggregate"* %x) +NotCXX14Aggregate copy(NotCXX14Aggregate *x) { return *x; } // MSVC: stp x8,x9,[x0], Clang: str q0,[x0] +// WOA64-LABEL: define dso_local [2 x i64] @"?copy@pr47611@@YA?AUNotPod@1@PEAU21@@Z"(%"struct.pr47611::NotPod"* %x) +NotPod copy(NotPod *x) { return *x; } +// WOA64-LABEL: define dso_local void @"?copy@pr47611@@YA?AUHasEmptyBase@1@PEAU21@@Z"(%"struct.pr47611::HasEmptyBase"* inreg noalias sret(%"struct.pr47611::HasEmptyBase") align 8 %agg.result, %"struct.pr47611::HasEmptyBase"* %x) +HasEmptyBase copy(HasEmptyBase *x) { return *x; } +// WOA64-LABEL: define dso_local void @"?copy@pr47611@@YA?AUHasPodBase@1@PEAU21@@Z"(%"struct.pr47611::HasPodBase"* inreg noalias sret(%"struct.pr47611::HasPodBase") align 8 %agg.result, %"struct.pr47611::HasPodBase"* %x) +HasPodBase copy(HasPodBase *x) { return *x; } +}; diff --git a/llvm/test/CodeGen/AArch64/arm64-windows-calls.ll b/llvm/test/CodeGen/AArch64/arm64-windows-calls.ll --- a/llvm/test/CodeGen/AArch64/arm64-windows-calls.ll +++ b/llvm/test/CodeGen/AArch64/arm64-windows-calls.ll @@ -98,3 +98,41 @@ %this1 = load %class.C*, %class.C** %this.addr, align 8 ret void } + +; The following tests correspond to tests in +; clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp + +; Pod is a trivial HFA +%struct.Pod = type { [2 x double] } +; Not an aggregate according to C++14 spec => not HFA according to MSVC +%struct.NotCXX14Aggregate = type { %struct.Pod } +; NotPod is a C++14 aggregate. But not HFA, because it contains +; NotCXX14Aggregate (which itself is not HFA because it's not a C++14 +; aggregate). +%struct.NotPod = type { %struct.NotCXX14Aggregate } + +define dso_local %struct.Pod @copy_pod(%struct.Pod* %x) { + %x1 = load %struct.Pod, %struct.Pod* %x, align 8 + ret %struct.Pod %x1 +; CHECK: ldp d0, d1, [x0] +} + +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) + +define dso_local void +@copy_notcxx14aggregate(%struct.NotCXX14Aggregate* inreg noalias sret(%struct.NotCXX14Aggregate) align 8 %agg.result, + %struct.NotCXX14Aggregate* %x) { + %1 = bitcast %struct.NotCXX14Aggregate* %agg.result to i8* + %2 = bitcast %struct.NotCXX14Aggregate* %x to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %1, i8* align 8 %2, i64 16, i1 false) + ret void +; CHECK: str q0, [x0] +} + +define dso_local [2 x i64] @copy_notpod(%struct.NotPod* %x) { + %x1 = bitcast %struct.NotPod* %x to [2 x i64]* + %x2 = load [2 x i64], [2 x i64]* %x1 + ret [2 x i64] %x2 +; CHECK: ldp x8, x1, [x0] +; CHECK: mov x0, x8 +}