Index: include/clang/CodeGen/CGFunctionInfo.h =================================================================== --- include/clang/CodeGen/CGFunctionInfo.h +++ include/clang/CodeGen/CGFunctionInfo.h @@ -95,7 +95,6 @@ bool InReg : 1; // isDirect() || isExtend() || isIndirect() bool CanBeFlattened: 1; // isDirect() bool SignExt : 1; // isExtend() - bool SuppressSRet : 1; // isIndirect() bool canHavePaddingType() const { return isDirect() || isExtend() || isIndirect() || isExpand(); @@ -111,14 +110,13 @@ } ABIArgInfo(Kind K) - : TheKind(K), PaddingInReg(false), InReg(false), SuppressSRet(false) { + : TheKind(K), PaddingInReg(false), InReg(false) { } public: ABIArgInfo() : TypeData(nullptr), PaddingType(nullptr), DirectOffset(0), - TheKind(Direct), PaddingInReg(false), InReg(false), - SuppressSRet(false) {} + TheKind(Direct), PaddingInReg(false), InReg(false) {} static ABIArgInfo getDirect(llvm::Type *T = nullptr, unsigned Offset = 0, llvm::Type *Padding = nullptr, @@ -407,16 +405,6 @@ CanBeFlattened = Flatten; } - bool getSuppressSRet() const { - assert(isIndirect() && "Invalid kind!"); - return SuppressSRet; - } - - void setSuppressSRet(bool Suppress) { - assert(isIndirect() && "Invalid kind!"); - SuppressSRet = Suppress; - } - void dump() const; }; Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -1999,8 +1999,7 @@ // Attach attributes to sret. if (IRFunctionArgs.hasSRetArg()) { llvm::AttrBuilder SRETAttrs; - if (!RetAI.getSuppressSRet()) - SRETAttrs.addAttribute(llvm::Attribute::StructRet); + SRETAttrs.addAttribute(llvm::Attribute::StructRet); hasUsedSRet = true; if (RetAI.getInReg()) SRETAttrs.addAttribute(llvm::Attribute::InReg); Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -52,6 +52,10 @@ bool classifyReturnType(CGFunctionInfo &FI) const override; + bool passClassIndirect(const CXXRecordDecl *RD) const { + return !canCopyArgument(RD); + } + RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const override; bool isSRetParameterAfterThis() const override { return true; } @@ -1056,28 +1060,17 @@ if (!RD) return false; - CharUnits Align = CGM.getContext().getTypeAlignInChars(FI.getReturnType()); - if (FI.isInstanceMethod()) { - // If it's an instance method, aggregates are always returned indirectly via - // the second parameter. - FI.getReturnInfo() = ABIArgInfo::getIndirect(Align, /*ByVal=*/false); - FI.getReturnInfo().setSRetAfterThis(FI.isInstanceMethod()); + bool isAArch64 = CGM.getTarget().getTriple().isAArch64(); + bool isIndirectReturn = isAArch64 ? passClassIndirect(RD) : !RD->isPOD(); + bool isInstanceMethod = FI.isInstanceMethod(); - // aarch64-windows requires that instance methods use X1 for the return - // address. So for aarch64-windows we do not mark the - // return as SRet. - FI.getReturnInfo().setSuppressSRet(CGM.getTarget().getTriple().getArch() == - llvm::Triple::aarch64); - return true; - } else if (!RD->isPOD()) { - // If it's a free function, non-POD types are returned indirectly. + if (isIndirectReturn || isInstanceMethod) { + CharUnits Align = CGM.getContext().getTypeAlignInChars(FI.getReturnType()); FI.getReturnInfo() = ABIArgInfo::getIndirect(Align, /*ByVal=*/false); + FI.getReturnInfo().setSRetAfterThis(isInstanceMethod); + + FI.getReturnInfo().setInReg(isAArch64); - // aarch64-windows requires that non-POD, non-instance returns use X0 for - // the return address. So for aarch64-windows we do not mark the return as - // SRet. - FI.getReturnInfo().setSuppressSRet(CGM.getTarget().getTriple().getArch() == - llvm::Triple::aarch64); return true; } Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -5902,8 +5902,13 @@ !D->hasNonTrivialCopyConstructorForCall(); if (CCK == TargetInfo::CCK_MicrosoftWin64) { + bool isAArch64 = S.Context.getTargetInfo().getTriple().isAArch64(); + if (isAArch64 && !D->isAggregate()) + return false; + bool CopyCtorIsTrivial = false, CopyCtorIsTrivialForCall = false; bool DtorIsTrivialForCall = false; + bool CopyAssignmentIsTrivial = !isAArch64; // If a class has at least one non-deleted, trivial copy constructor, it // is passed according to the C ABI. Otherwise, it is passed indirectly. @@ -5929,6 +5934,9 @@ } } + if (isAArch64 && D->needsImplicitCopyAssignment()) + CopyAssignmentIsTrivial = D->hasTrivialCopyAssignment(); + if (D->needsImplicitDestructor()) { if (!D->defaultedDestructorIsDeleted() && D->hasTrivialDestructorForCall()) @@ -5939,7 +5947,8 @@ } // If the copy ctor and dtor are both trivial-for-calls, pass direct. - if (CopyCtorIsTrivialForCall && DtorIsTrivialForCall) + if (CopyCtorIsTrivialForCall && DtorIsTrivialForCall && + CopyAssignmentIsTrivial) return true; // If a class has a destructor, we'd really like to pass it indirectly @@ -5952,7 +5961,7 @@ // Note: This permits small classes with nontrivial destructors to be // passed in registers, which is non-conforming. - if (CopyCtorIsTrivial && + if (!isAArch64 && CopyCtorIsTrivial && S.getASTContext().getTypeSize(D->getTypeForDecl()) <= 64) return true; return false; Index: test/CodeGen/arm64-microsoft-arguments.cpp =================================================================== --- test/CodeGen/arm64-microsoft-arguments.cpp +++ test/CodeGen/arm64-microsoft-arguments.cpp @@ -1,25 +1,31 @@ // RUN: %clang_cc1 -triple aarch64-windows -ffreestanding -emit-llvm -O0 \ // RUN: -x c++ -o - %s | FileCheck %s -struct pod { int a, b, c, d, e; }; +// Return type size <= 8 bytes. +// CHECK: define {{.*}} i64 @{{.*}}f1{{.*}}() +struct S1 { int a, b; }; +S1 f1() { return S1{}; } -struct non_pod { - int a; - non_pod() {} -}; +// Return type size <= 16 bytes. +// CHECK: define {{.*}} [2 x i64] @{{.*}}f2{{.*}}() +struct S2 { int a, b, c, d; }; +S2 f2() { return S2{}; } -struct pod s; -struct non_pod t; +// Return type size > 16 bytes. +// CHECK: define {{.*}} void @{{.*}}f3{{.*}}(%struct.S3* noalias sret %agg.result) +struct S3 { int a, b, c, d, e; }; +S3 f3() { return S3{}; } -struct pod bar() { return s; } -struct non_pod foo() { return t; } -// CHECK: define {{.*}} void @{{.*}}bar{{.*}}(%struct.pod* noalias sret %agg.result) -// CHECK: define {{.*}} void @{{.*}}foo{{.*}}(%struct.non_pod* noalias %agg.result) +// Instance methods. +// CHECK: define {{.*}} void @{{.*}}inst@C{{.*}}(%class.C* %this, %class.A* inreg noalias sret %agg.result) +class A {}; -// Check instance methods. -struct pod2 { int x; }; -struct Baz { pod2 baz(); }; +class C { +public: + A inst(); +}; -int qux() { return Baz().baz().x; } -// CHECK: declare {{.*}} void @{{.*}}baz@Baz{{.*}}(%struct.Baz*, %struct.pod2*) +A C::inst() { + return A(); +} Index: test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp =================================================================== --- test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp +++ test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp @@ -164,8 +164,8 @@ // WIN64: define dso_local void @"?small_arg_with_dtor@@YAXUSmallWithDtor@@@Z"(i32 %s.coerce) {{.*}} { // WIN64: call void @"??1SmallWithDtor@@QEAA@XZ" // WIN64: } -// WOA64: define dso_local void @"?small_arg_with_dtor@@YAXUSmallWithDtor@@@Z"(i64 %s.coerce) {{.*}} { -// WOA64: call void @"??1SmallWithDtor@@QEAA@XZ" +// WOA64: define dso_local void @"?small_arg_with_dtor@@YAXUSmallWithDtor@@@Z"(%struct.SmallWithDtor* %s) {{.*}} { +// WOA64: call void @"??1SmallWithDtor@@QEAA@XZ"(%struct.SmallWithDtor* %s) // WOA64: } // FIXME: MSVC incompatible!