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 @@ -1074,7 +1074,7 @@ return RD->getASTContext().getTypeSize(RD->getTypeForDecl()) > 128; } -static bool hasMicrosoftABIRestrictions(const CXXRecordDecl *RD) { +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. @@ -1083,19 +1083,19 @@ // 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 true; + return false; if (RD->getNumBases() > 0) - return true; + return false; if (RD->isPolymorphic()) - return true; + return false; if (RD->hasNonTrivialCopyAssignment()) - return true; + return false; for (const CXXConstructorDecl *Ctor : RD->ctors()) if (Ctor->isUserProvided()) - return true; + return false; if (RD->hasNonTrivialDestructor()) - return true; - return false; + return false; + return true; } bool MicrosoftCXXABI::classifyReturnType(CGFunctionInfo &FI) const { @@ -1104,20 +1104,21 @@ return false; bool isAArch64 = CGM.getTarget().getTriple().isAArch64(); - bool isSimple = !isAArch64 || !hasMicrosoftABIRestrictions(RD); + bool isTrivialForABI = + RD->canPassInRegisters() && !(isAArch64 && !isCXX14Aggregate(RD)); bool isIndirectReturn = - isAArch64 ? (!RD->canPassInRegisters() || - IsSizeGreaterThan128(RD)) - : !RD->isPOD(); + isAArch64 ? (!isTrivialForABI || IsSizeGreaterThan128(RD)) : !RD->isPOD(); bool isInstanceMethod = FI.isInstanceMethod(); - if (isIndirectReturn || !isSimple || isInstanceMethod) { + if (isIndirectReturn || isInstanceMethod) { CharUnits Align = CGM.getContext().getTypeAlignInChars(FI.getReturnType()); FI.getReturnInfo() = ABIArgInfo::getIndirect(Align, /*ByVal=*/false); FI.getReturnInfo().setSRetAfterThis(isInstanceMethod); + // On AArch64, use the `inreg` attribute if the object is considered to not + // be trivially copyable, or if this is an instance method struct return. FI.getReturnInfo().setInReg(isAArch64 && - !(isSimple && IsSizeGreaterThan128(RD))); + (isInstanceMethod || !isTrivialForABI)); return true; } 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 @@ -284,11 +284,13 @@ // LINUX: define {{.*}} void @_ZN5Class21thiscall_method_smallEv(%struct.Small* noalias sret align 4 %agg.result, %class.Class* %this) // WIN32: define {{.*}} x86_thiscallcc void @"?thiscall_method_small@Class@@QAE?AUSmall@@XZ"(%class.Class* %this, %struct.Small* noalias sret align 4 %agg.result) // WIN64: define linkonce_odr dso_local void @"?thiscall_method_small@Class@@QEAA?AUSmall@@XZ"(%class.Class* %this, %struct.Small* noalias sret align 4 %agg.result) + // WOA64: define linkonce_odr dso_local void @"?thiscall_method_small@Class@@QEAA?AUSmall@@XZ"(%class.Class* %this, %struct.Small* inreg noalias sret align 4 %agg.result) SmallWithCtor thiscall_method_small_with_ctor() { return SmallWithCtor(); } // LINUX: define {{.*}} void @_ZN5Class31thiscall_method_small_with_ctorEv(%struct.SmallWithCtor* noalias sret align 4 %agg.result, %class.Class* %this) // WIN32: define {{.*}} x86_thiscallcc void @"?thiscall_method_small_with_ctor@Class@@QAE?AUSmallWithCtor@@XZ"(%class.Class* %this, %struct.SmallWithCtor* noalias sret align 4 %agg.result) // WIN64: define linkonce_odr dso_local void @"?thiscall_method_small_with_ctor@Class@@QEAA?AUSmallWithCtor@@XZ"(%class.Class* %this, %struct.SmallWithCtor* noalias sret align 4 %agg.result) + // WOA64: define linkonce_odr dso_local void @"?thiscall_method_small_with_ctor@Class@@QEAA?AUSmallWithCtor@@XZ"(%class.Class* %this, %struct.SmallWithCtor* inreg noalias sret align 4 %agg.result) Small __cdecl cdecl_method_small() { return Small(); } // LINUX: define {{.*}} void @_ZN5Class18cdecl_method_smallEv(%struct.Small* noalias sret align 4 %agg.result, %class.Class* %this) @@ -299,6 +301,7 @@ // LINUX: define {{.*}} void @_ZN5Class16cdecl_method_bigEv(%struct.Big* noalias sret align 4 %agg.result, %class.Class* %this) // WIN32: define {{.*}} void @"?cdecl_method_big@Class@@QAA?AUBig@@XZ"(%class.Class* %this, %struct.Big* noalias sret align 4 %agg.result) // WIN64: define linkonce_odr dso_local void @"?cdecl_method_big@Class@@QEAA?AUBig@@XZ"(%class.Class* %this, %struct.Big* noalias sret align 4 %agg.result) + // WOA64: define linkonce_odr dso_local void @"?cdecl_method_big@Class@@QEAA?AUBig@@XZ"(%class.Class* %this, %struct.Big* inreg noalias sret align 4 %agg.result) void thiscall_method_arg(Empty s) {} // LINUX: define {{.*}} void @_ZN5Class19thiscall_method_argE5Empty(%class.Class* %this)