Index: include/clang/CodeGen/CGFunctionInfo.h =================================================================== --- include/clang/CodeGen/CGFunctionInfo.h +++ include/clang/CodeGen/CGFunctionInfo.h @@ -82,9 +82,10 @@ }; Kind TheKind; bool PaddingInReg : 1; - bool InAllocaSRet : 1; // isInAlloca + bool InAllocaSRet : 1; // isInAlloca() bool IndirectByVal : 1; // isIndirect() bool IndirectRealign : 1; // isIndirect() + bool SRetAfterThis : 1; // isIndirect() bool InReg : 1; // isDirect() || isExtend() || isIndirect() ABIArgInfo(Kind K) @@ -129,6 +130,7 @@ AI.setIndirectAlign(Alignment); AI.setIndirectByVal(ByVal); AI.setIndirectRealign(Realign); + AI.setSRetAfterThis(false); AI.setPaddingType(Padding); return AI; } @@ -233,6 +235,15 @@ IndirectRealign = IR; } + bool isSRetAfterThis() const { + assert(isIndirect() && "Invalid kind!"); + return SRetAfterThis; + } + void setSRetAfterThis(bool AfterThis) { + assert(isIndirect() && "Invalid kind!"); + SRetAfterThis = AfterThis; + } + unsigned getInAllocaFieldIndex() const { assert(isInAlloca() && "Invalid kind!"); return AllocaFieldIndex; Index: lib/CodeGen/CGCXXABI.h =================================================================== --- lib/CodeGen/CGCXXABI.h +++ lib/CodeGen/CGCXXABI.h @@ -114,6 +114,10 @@ /// Returns how an argument of the given record type should be passed. virtual RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const = 0; + /// Returns true if the implicit 'sret' parameter comes after the implicit + /// 'this' parameter of C++ instance methods. + virtual bool isSRetParameterAfterThis() const { return false; } + /// Find the LLVM type used to represent the given member pointer /// type. virtual llvm::Type * Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -922,6 +922,7 @@ bool Inserted = FunctionsBeingProcessed.insert(&FI); (void)Inserted; assert(Inserted && "Recursively being processed?"); + bool SwapThisWithSRet = false; SmallVector argTypes; llvm::Type *resultType = 0; @@ -955,6 +956,8 @@ llvm::Type *ty = ConvertType(ret); unsigned addressSpace = Context.getTargetAddressSpace(ret); argTypes.push_back(llvm::PointerType::get(ty, addressSpace)); + + SwapThisWithSRet = retAI.isSRetAfterThis(); break; } @@ -1014,6 +1017,9 @@ if (llvm::StructType *ArgStruct = FI.getArgStruct()) argTypes.push_back(ArgStruct->getPointerTo()); + if (SwapThisWithSRet) + std::swap(argTypes[0], argTypes[1]); + bool Erased = FunctionsBeingProcessed.erase(&FI); (void)Erased; assert(Erased && "Not in set?"); @@ -1128,6 +1134,7 @@ QualType RetTy = FI.getReturnType(); unsigned Index = 1; + bool SwapThisWithSRet = false; const ABIArgInfo &RetAI = FI.getReturnInfo(); switch (RetAI.getKind()) { case ABIArgInfo::Extend: @@ -1155,10 +1162,12 @@ SRETAttrs.addAttribute(llvm::Attribute::StructRet); if (RetAI.getInReg()) SRETAttrs.addAttribute(llvm::Attribute::InReg); - PAL.push_back(llvm:: - AttributeSet::get(getLLVMContext(), Index, SRETAttrs)); + SwapThisWithSRet = RetAI.isSRetAfterThis(); + PAL.push_back(llvm::AttributeSet::get( + getLLVMContext(), SwapThisWithSRet ? 2 : Index, SRETAttrs)); - ++Index; + if (!SwapThisWithSRet) + ++Index; // sret disables readnone and readonly FuncAttrs.removeAttribute(llvm::Attribute::ReadOnly) .removeAttribute(llvm::Attribute::ReadNone); @@ -1180,6 +1189,11 @@ const ABIArgInfo &AI = I.info; llvm::AttrBuilder Attrs; + // Skip over the sret parameter when it comes second. We already handled it + // above. + if (Index == 2 && SwapThisWithSRet) + ++Index; + if (AI.getPaddingType()) { if (AI.getPaddingInReg()) PAL.push_back(llvm::AttributeSet::get(getLLVMContext(), Index, @@ -1322,13 +1336,20 @@ assert(ArgStruct->getType() == FI.getArgStruct()->getPointerTo()); } - // Name the struct return argument. - if (CGM.ReturnTypeUsesSRet(FI)) { + // Name the struct return parameter, which can come first or second. + const ABIArgInfo &RetAI = FI.getReturnInfo(); + bool SwapThisWithSRet = false; + if (RetAI.isIndirect()) { + SwapThisWithSRet = RetAI.isSRetAfterThis(); + if (SwapThisWithSRet) + ++AI; AI->setName("agg.result"); - AI->addAttr(llvm::AttributeSet::get(getLLVMContext(), - AI->getArgNo() + 1, + AI->addAttr(llvm::AttributeSet::get(getLLVMContext(), AI->getArgNo() + 1, llvm::Attribute::NoAlias)); - ++AI; + if (SwapThisWithSRet) + --AI; // Go back to the beginning for 'this'. + else + ++AI; // Skip the sret parameter. } // Track if we received the parameter as a pointer (indirect, byval, or @@ -1556,6 +1577,9 @@ } ++AI; + + if (ArgNo == 1 && SwapThisWithSRet) + ++AI; // Skip the sret parameter. } if (FI.usesInAlloca()) @@ -1798,13 +1822,15 @@ break; case ABIArgInfo::Indirect: { + auto AI = CurFn->arg_begin(); + if (RetAI.isSRetAfterThis()) + ++AI; switch (getEvaluationKind(RetTy)) { case TEK_Complex: { ComplexPairTy RT = EmitLoadOfComplex(MakeNaturalAlignAddrLValue(ReturnValue, RetTy), EndLoc); - EmitStoreOfComplex(RT, - MakeNaturalAlignAddrLValue(CurFn->arg_begin(), RetTy), + EmitStoreOfComplex(RT, MakeNaturalAlignAddrLValue(AI, RetTy), /*isInit*/ true); break; } @@ -1813,7 +1839,7 @@ break; case TEK_Scalar: EmitStoreOfScalar(Builder.CreateLoad(ReturnValue), - MakeNaturalAlignAddrLValue(CurFn->arg_begin(), RetTy), + MakeNaturalAlignAddrLValue(AI, RetTy), /*isInit*/ true); break; } @@ -2576,13 +2602,19 @@ // If the call returns a temporary with struct return, create a temporary // alloca to hold the result, unless one is given to us. llvm::Value *SRetPtr = 0; - if (CGM.ReturnTypeUsesSRet(CallInfo) || RetAI.isInAlloca()) { + bool SwapThisWithSRet = false; + if (RetAI.isIndirect() || RetAI.isInAlloca()) { SRetPtr = ReturnValue.getValue(); if (!SRetPtr) SRetPtr = CreateMemTemp(RetTy); - if (CGM.ReturnTypeUsesSRet(CallInfo)) { + if (RetAI.isIndirect()) { Args.push_back(SRetPtr); + SwapThisWithSRet = RetAI.isSRetAfterThis(); + if (SwapThisWithSRet) + IRArgNo = 1; checkArgMatches(SRetPtr, IRArgNo, IRFuncTy); + if (SwapThisWithSRet) + IRArgNo = 0; } else { llvm::Value *Addr = Builder.CreateStructGEP(ArgMemory, RetAI.getInAllocaFieldIndex()); @@ -2598,6 +2630,10 @@ const ABIArgInfo &ArgInfo = info_it->info; RValue RV = I->RV; + // Skip 'sret' if it came second. + if (IRArgNo == 1 && SwapThisWithSRet) + ++IRArgNo; + CharUnits TypeAlign = getContext().getTypeAlignInChars(I->Ty); // Insert a padding argument to ensure proper alignment. @@ -2784,6 +2820,9 @@ } } + if (SwapThisWithSRet) + std::swap(Args[0], Args[1]); + if (ArgMemory) { llvm::Value *Arg = ArgMemory; llvm::Type *LastParamTy = Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -617,7 +617,10 @@ !hasScalarEvaluationKind(CurFnInfo->getReturnType())) { // Indirect aggregate return; emit returned value directly into sret slot. // This reduces code size, and affects correctness in C++. - ReturnValue = CurFn->arg_begin(); + auto AI = CurFn->arg_begin(); + if (CurFnInfo->getReturnInfo().isSRetAfterThis()) + ++AI; + ReturnValue = AI; } else if (CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::InAlloca && !hasScalarEvaluationKind(CurFnInfo->getReturnType())) { // Load the sret pointer from the argument struct and return into that. Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -46,6 +46,8 @@ RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const override; + bool isSRetParameterAfterThis() const override { return true; } + StringRef GetPureVirtualCallName() override { return "_purecall"; } // No known support for deleted functions in MSVC yet, so this choice is // arbitrary. Index: lib/CodeGen/TargetInfo.cpp =================================================================== --- lib/CodeGen/TargetInfo.cpp +++ lib/CodeGen/TargetInfo.cpp @@ -992,13 +992,13 @@ FI.getReturnInfo() = classifyReturnType(FI.getReturnType(), State, FI.isInstanceMethod()); - // On win32, use the x86_cdeclmethodcc convention for cdecl methods that use - // sret. This convention swaps the order of the first two parameters behind - // the scenes to match MSVC. + // On win32, swap the order of the first two parameters for instance methods + // which are sret behind the scenes to match MSVC. if (IsWin32StructABI && FI.isInstanceMethod() && - FI.getCallingConvention() == llvm::CallingConv::C && - FI.getReturnInfo().isIndirect()) - FI.setEffectiveCallingConvention(llvm::CallingConv::X86_CDeclMethod); + FI.getReturnInfo().isIndirect()) { + assert(FI.arg_size() >= 1 && "instance method should have this"); + FI.getReturnInfo().setSRetAfterThis(true); + } bool UsedInAlloca = false; for (auto &I : FI.arguments()) { @@ -2768,6 +2768,14 @@ QualType RetTy = FI.getReturnType(); FI.getReturnInfo() = classify(RetTy, true); + // On win64, swap the order of the first two parameters for instance methods + // which are sret behind the scenes to match MSVC. + if (FI.getReturnInfo().isIndirect() && FI.isInstanceMethod() && + getCXXABI().isSRetParameterAfterThis()) { + assert(FI.arg_size() >= 1 && "instance method should have this"); + FI.getReturnInfo().setSRetAfterThis(true); + } + for (auto &I : FI.arguments()) I.info = classify(I.type, false); } Index: test/CodeGenCXX/microsoft-abi-cdecl-method-sret.cpp =================================================================== --- test/CodeGenCXX/microsoft-abi-cdecl-method-sret.cpp +++ test/CodeGenCXX/microsoft-abi-cdecl-method-sret.cpp @@ -19,9 +19,9 @@ S C::cdecl_sret() { return S(); } S C::byval_and_sret(S a) { return S(); } -// CHECK: define x86_cdeclmethodcc void @"\01?variadic_sret@C@@QAA?AUS@@PBDZZ"(%struct.S* noalias sret %agg.result, %struct.C* %this, i8* %f, ...) -// CHECK: define x86_cdeclmethodcc void @"\01?cdecl_sret@C@@QAA?AUS@@XZ"(%struct.S* noalias sret %agg.result, %struct.C* %this) -// CHECK: define x86_cdeclmethodcc void @"\01?byval_and_sret@C@@QAA?AUS@@U2@@Z"(%struct.S* noalias sret %agg.result, %struct.C* %this, %struct.S* byval align 4 %a) +// CHECK: define void @"\01?variadic_sret@C@@QAA?AUS@@PBDZZ"(%struct.C* %this, %struct.S* noalias sret %agg.result, i8* %f, ...) +// CHECK: define void @"\01?cdecl_sret@C@@QAA?AUS@@XZ"(%struct.C* %this, %struct.S* noalias sret %agg.result) +// CHECK: define void @"\01?byval_and_sret@C@@QAA?AUS@@U2@@Z"(%struct.C* %this, %struct.S* noalias sret %agg.result, %struct.S* byval align 4 %a) int main() { C c; @@ -30,6 +30,6 @@ c.byval_and_sret(S()); } // CHECK-LABEL: define i32 @main() -// CHECK: call x86_cdeclmethodcc void {{.*}} @"\01?variadic_sret@C@@QAA?AUS@@PBDZZ" -// CHECK: call x86_cdeclmethodcc void @"\01?cdecl_sret@C@@QAA?AUS@@XZ" -// CHECK: call x86_cdeclmethodcc void @"\01?byval_and_sret@C@@QAA?AUS@@U2@@Z" +// CHECK: call void {{.*}} @"\01?variadic_sret@C@@QAA?AUS@@PBDZZ" +// CHECK: call void @"\01?cdecl_sret@C@@QAA?AUS@@XZ" +// CHECK: call void @"\01?byval_and_sret@C@@QAA?AUS@@U2@@Z" 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 @@ -201,19 +201,19 @@ public: Small thiscall_method_small() { return Small(); } // LINUX: define {{.*}} void @_ZN5Class21thiscall_method_smallEv(%struct.Small* noalias sret %agg.result, %class.Class* %this) - // WIN32: define {{.*}} x86_thiscallcc void @"\01?thiscall_method_small@Class@@QAE?AUSmall@@XZ"(%struct.Small* noalias sret %agg.result, %class.Class* %this) + // WIN32: define {{.*}} x86_thiscallcc void @"\01?thiscall_method_small@Class@@QAE?AUSmall@@XZ"(%class.Class* %this, %struct.Small* noalias sret %agg.result) SmallWithCtor thiscall_method_small_with_ctor() { return SmallWithCtor(); } // LINUX: define {{.*}} void @_ZN5Class31thiscall_method_small_with_ctorEv(%struct.SmallWithCtor* noalias sret %agg.result, %class.Class* %this) - // WIN32: define {{.*}} x86_thiscallcc void @"\01?thiscall_method_small_with_ctor@Class@@QAE?AUSmallWithCtor@@XZ"(%struct.SmallWithCtor* noalias sret %agg.result, %class.Class* %this) + // WIN32: define {{.*}} x86_thiscallcc void @"\01?thiscall_method_small_with_ctor@Class@@QAE?AUSmallWithCtor@@XZ"(%class.Class* %this, %struct.SmallWithCtor* noalias sret %agg.result) Small __cdecl cdecl_method_small() { return Small(); } // LINUX: define {{.*}} void @_ZN5Class18cdecl_method_smallEv(%struct.Small* noalias sret %agg.result, %class.Class* %this) - // WIN32: define {{.*}} void @"\01?cdecl_method_small@Class@@QAA?AUSmall@@XZ"(%struct.Small* noalias sret %agg.result, %class.Class* %this) + // WIN32: define {{.*}} void @"\01?cdecl_method_small@Class@@QAA?AUSmall@@XZ"(%class.Class* %this, %struct.Small* noalias sret %agg.result) Big __cdecl cdecl_method_big() { return Big(); } // LINUX: define {{.*}} void @_ZN5Class16cdecl_method_bigEv(%struct.Big* noalias sret %agg.result, %class.Class* %this) - // WIN32: define {{.*}} void @"\01?cdecl_method_big@Class@@QAA?AUBig@@XZ"(%struct.Big* noalias sret %agg.result, %class.Class* %this) + // WIN32: define {{.*}} void @"\01?cdecl_method_big@Class@@QAA?AUBig@@XZ"(%class.Class* %this, %struct.Big* noalias sret %agg.result) void thiscall_method_arg(Empty s) {} // LINUX: define {{.*}} void @_ZN5Class19thiscall_method_argE5Empty(%class.Class* %this) Index: test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp =================================================================== --- test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp +++ test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp @@ -34,14 +34,14 @@ // CHECK32-LABEL: define void @"\01?f@@YAXXZ"() // CHECK32: store i8* bitcast (void (%struct.C*)* @"\01??_9C@@$BA@AE" to i8*), i8** %ptr // CHECK32: store i8* bitcast (i32 (%struct.C*, i32, double)* @"\01??_9C@@$B3AE" to i8*), i8** %ptr2 -// CHECK32: store i8* bitcast (void (%struct.S*, %struct.C*, i32)* @"\01??_9C@@$B7AE" to i8*), i8** %ptr3 +// CHECK32: store i8* bitcast (void (%struct.C*, %struct.S*, i32)* @"\01??_9C@@$B7AE" to i8*), i8** %ptr3 // CHECK32: store i8* bitcast (void (%"struct.(anonymous namespace)::D"*)* @"\01??_9D@?A@@$BA@AE" to i8*), i8** %ptr4 // CHECK32: } // // CHECK64-LABEL: define void @"\01?f@@YAXXZ"() // CHECK64: store i8* bitcast (void (%struct.C*)* @"\01??_9C@@$BA@AA" to i8*), i8** %ptr // CHECK64: store i8* bitcast (i32 (%struct.C*, i32, double)* @"\01??_9C@@$B7AA" to i8*), i8** %ptr2 -// CHECK64: store i8* bitcast (void (%struct.S*, %struct.C*, i32)* @"\01??_9C@@$BBA@AA" to i8*), i8** %ptr3 +// CHECK64: store i8* bitcast (void (%struct.C*, %struct.S*, i32)* @"\01??_9C@@$BBA@AA" to i8*), i8** %ptr3 // CHECK64: store i8* bitcast (void (%"struct.(anonymous namespace)::D"*)* @"\01??_9D@?A@@$BA@AA" to i8*), i8** %ptr // CHECK64: } } @@ -78,17 +78,17 @@ // CHECK64: } // Thunk for calling the 3rd virtual function in C, taking an int parameter, returning a struct. -// CHECK32-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_9C@@$B7AE"(%struct.S* noalias sret %agg.result, %struct.C* %this, i32) unnamed_addr -// CHECK32: [[VPTR:%.*]] = getelementptr inbounds void (%struct.S*, %struct.C*, i32)** %{{.*}}, i64 2 -// CHECK32: [[CALLEE:%.*]] = load void (%struct.S*, %struct.C*, i32)** [[VPTR]] -// CHECK32: call x86_thiscallcc void [[CALLEE]](%struct.S* sret %agg.result, %struct.C* %{{.*}}, i32 %{{.*}}) +// CHECK32-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_9C@@$B7AE"(%struct.C* %this, %struct.S* noalias sret %agg.result, i32) unnamed_addr +// CHECK32: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, %struct.S*, i32)** %{{.*}}, i64 2 +// CHECK32: [[CALLEE:%.*]] = load void (%struct.C*, %struct.S*, i32)** [[VPTR]] +// CHECK32: call x86_thiscallcc void [[CALLEE]](%struct.C* %{{.*}}, %struct.S* sret %agg.result, i32 %{{.*}}) // CHECK32: ret void // CHECK32: } // -// CHECK64-LABEL: define linkonce_odr void @"\01??_9C@@$BBA@AA"(%struct.S* noalias sret %agg.result, %struct.C* %this, i32) unnamed_addr -// CHECK64: [[VPTR:%.*]] = getelementptr inbounds void (%struct.S*, %struct.C*, i32)** %{{.*}}, i64 2 -// CHECK64: [[CALLEE:%.*]] = load void (%struct.S*, %struct.C*, i32)** [[VPTR]] -// CHECK64: call void [[CALLEE]](%struct.S* sret %agg.result, %struct.C* %{{.*}}, i32 %{{.*}}) +// CHECK64-LABEL: define linkonce_odr void @"\01??_9C@@$BBA@AA"(%struct.C* %this, %struct.S* noalias sret %agg.result, i32) unnamed_addr +// CHECK64: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, %struct.S*, i32)** %{{.*}}, i64 2 +// CHECK64: [[CALLEE:%.*]] = load void (%struct.C*, %struct.S*, i32)** [[VPTR]] +// CHECK64: call void [[CALLEE]](%struct.C* %{{.*}}, %struct.S* sret %agg.result, i32 %{{.*}}) // CHECK64: ret void // CHECK64: }