MS ABI: Pass 'sret' as the second parameter of instance methods

Summary:
MSVC always passes 'sret' after 'this', unlike GCC.  This required
changing a number of places in Clang that assumed the sret parameter was
always first in LLVM IR.

This fixes win64 MSVC ABI compatibility for methods returning structs.

Reviewers: rsmith, majnemer

Subscribers: cfe-commits

Differential Revision: http://reviews.llvm.org/D3618

llvm-svn: 208458
This commit is contained in:
Reid Kleckner 2014-05-09 22:46:15 +00:00
parent 46e1ecdecc
commit 37abaca3c2
9 changed files with 108 additions and 41 deletions

View File

@ -82,9 +82,10 @@ private:
};
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 @@ public:
AI.setIndirectAlign(Alignment);
AI.setIndirectByVal(ByVal);
AI.setIndirectRealign(Realign);
AI.setSRetAfterThis(false);
AI.setPaddingType(Padding);
return AI;
}
@ -233,6 +235,15 @@ public:
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;

View File

@ -114,6 +114,10 @@ public:
/// 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 *

View File

@ -940,6 +940,7 @@ CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI) {
bool Inserted = FunctionsBeingProcessed.insert(&FI); (void)Inserted;
assert(Inserted && "Recursively being processed?");
bool SwapThisWithSRet = false;
SmallVector<llvm::Type*, 8> argTypes;
llvm::Type *resultType = 0;
@ -973,6 +974,8 @@ CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI) {
llvm::Type *ty = ConvertType(ret);
unsigned addressSpace = Context.getTargetAddressSpace(ret);
argTypes.push_back(llvm::PointerType::get(ty, addressSpace));
SwapThisWithSRet = retAI.isSRetAfterThis();
break;
}
@ -1035,6 +1038,9 @@ CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI) {
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?");
@ -1149,6 +1155,7 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI,
QualType RetTy = FI.getReturnType();
unsigned Index = 1;
bool SwapThisWithSRet = false;
const ABIArgInfo &RetAI = FI.getReturnInfo();
switch (RetAI.getKind()) {
case ABIArgInfo::Extend:
@ -1176,10 +1183,12 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI,
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);
@ -1201,6 +1210,11 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI,
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,
@ -1344,13 +1358,20 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI,
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
@ -1580,6 +1601,9 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI,
}
++AI;
if (ArgNo == 1 && SwapThisWithSRet)
++AI; // Skip the sret parameter.
}
if (FI.usesInAlloca())
@ -1822,13 +1846,15 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI,
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;
}
@ -1837,7 +1863,7 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI,
break;
case TEK_Scalar:
EmitStoreOfScalar(Builder.CreateLoad(ReturnValue),
MakeNaturalAlignAddrLValue(CurFn->arg_begin(), RetTy),
MakeNaturalAlignAddrLValue(AI, RetTy),
/*isInit*/ true);
break;
}
@ -2600,13 +2626,19 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
// 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());
@ -2622,6 +2654,10 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
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.
@ -2811,6 +2847,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
}
}
if (SwapThisWithSRet)
std::swap(Args[0], Args[1]);
if (ArgMemory) {
llvm::Value *Arg = ArgMemory;
llvm::Type *LastParamTy =

View File

@ -622,7 +622,10 @@ void CodeGenFunction::StartFunction(GlobalDecl GD,
!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.

View File

@ -46,6 +46,8 @@ public:
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.

View File

@ -992,13 +992,13 @@ void X86_32ABIInfo::computeInfo(CGFunctionInfo &FI) const {
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 @@ void WinX86_64ABIInfo::computeInfo(CGFunctionInfo &FI) const {
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);
}

View File

@ -19,9 +19,9 @@ S C::variadic_sret(const char *f, ...) { return S(); }
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 @@ int main() {
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"

View File

@ -201,19 +201,19 @@ class Class {
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)

View File

@ -34,14 +34,14 @@ void f() {
// 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 @@ void f() {
// 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: }