r208458 - MS ABI: Pass 'sret' as the second parameter of instance methods
Reid Kleckner
reid at kleckner.net
Fri May 9 15:46:16 PDT 2014
Author: rnk
Date: Fri May 9 17:46:15 2014
New Revision: 208458
URL: http://llvm.org/viewvc/llvm-project?rev=208458&view=rev
Log:
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
Modified:
cfe/trunk/include/clang/CodeGen/CGFunctionInfo.h
cfe/trunk/lib/CodeGen/CGCXXABI.h
cfe/trunk/lib/CodeGen/CGCall.cpp
cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp
cfe/trunk/lib/CodeGen/TargetInfo.cpp
cfe/trunk/test/CodeGenCXX/microsoft-abi-cdecl-method-sret.cpp
cfe/trunk/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp
cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp
Modified: cfe/trunk/include/clang/CodeGen/CGFunctionInfo.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/CodeGen/CGFunctionInfo.h?rev=208458&r1=208457&r2=208458&view=diff
==============================================================================
--- cfe/trunk/include/clang/CodeGen/CGFunctionInfo.h (original)
+++ cfe/trunk/include/clang/CodeGen/CGFunctionInfo.h Fri May 9 17:46:15 2014
@@ -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;
Modified: cfe/trunk/lib/CodeGen/CGCXXABI.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCXXABI.h?rev=208458&r1=208457&r2=208458&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGCXXABI.h (original)
+++ cfe/trunk/lib/CodeGen/CGCXXABI.h Fri May 9 17:46:15 2014
@@ -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 *
Modified: cfe/trunk/lib/CodeGen/CGCall.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCall.cpp?rev=208458&r1=208457&r2=208458&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGCall.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGCall.cpp Fri May 9 17:46:15 2014
@@ -940,6 +940,7 @@ CodeGenTypes::GetFunctionType(const CGFu
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 CGFu
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 CGFu
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::ConstructAttributeLi
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::ConstructAttributeLi
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::ConstructAttributeLi
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
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
}
++AI;
+
+ if (ArgNo == 1 && SwapThisWithSRet)
+ ++AI; // Skip the sret parameter.
}
if (FI.usesInAlloca())
@@ -1822,13 +1846,15 @@ void CodeGenFunction::EmitFunctionEpilog
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
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 C
// 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 C
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 C
}
}
+ if (SwapThisWithSRet)
+ std::swap(Args[0], Args[1]);
+
if (ArgMemory) {
llvm::Value *Arg = ArgMemory;
llvm::Type *LastParamTy =
Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.cpp?rev=208458&r1=208457&r2=208458&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp Fri May 9 17:46:15 2014
@@ -622,7 +622,10 @@ void CodeGenFunction::StartFunction(Glob
!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.
Modified: cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp?rev=208458&r1=208457&r2=208458&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp (original)
+++ cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp Fri May 9 17:46:15 2014
@@ -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.
Modified: cfe/trunk/lib/CodeGen/TargetInfo.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/TargetInfo.cpp?rev=208458&r1=208457&r2=208458&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/TargetInfo.cpp (original)
+++ cfe/trunk/lib/CodeGen/TargetInfo.cpp Fri May 9 17:46:15 2014
@@ -992,13 +992,13 @@ void X86_32ABIInfo::computeInfo(CGFuncti
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(CGFun
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);
}
Modified: cfe/trunk/test/CodeGenCXX/microsoft-abi-cdecl-method-sret.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-cdecl-method-sret.cpp?rev=208458&r1=208457&r2=208458&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-cdecl-method-sret.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-cdecl-method-sret.cpp Fri May 9 17:46:15 2014
@@ -19,9 +19,9 @@ S C::variadic_sret(const char *f, ...) {
S C::cdecl_sret() { return S(); }
S C::byval_and_sret(S a) { return S(); }
-// CHECK: define x86_cdeclmethodcc void @"\01?variadic_sret at C@@QAA?AUS@@PBDZZ"(%struct.S* noalias sret %agg.result, %struct.C* %this, i8* %f, ...)
-// CHECK: define x86_cdeclmethodcc void @"\01?cdecl_sret at C@@QAA?AUS@@XZ"(%struct.S* noalias sret %agg.result, %struct.C* %this)
-// CHECK: define x86_cdeclmethodcc void @"\01?byval_and_sret at 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 at C@@QAA?AUS@@PBDZZ"(%struct.C* %this, %struct.S* noalias sret %agg.result, i8* %f, ...)
+// CHECK: define void @"\01?cdecl_sret at C@@QAA?AUS@@XZ"(%struct.C* %this, %struct.S* noalias sret %agg.result)
+// CHECK: define void @"\01?byval_and_sret at 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 at C@@QAA?AUS@@PBDZZ"
-// CHECK: call x86_cdeclmethodcc void @"\01?cdecl_sret at C@@QAA?AUS@@XZ"
-// CHECK: call x86_cdeclmethodcc void @"\01?byval_and_sret at C@@QAA?AUS@@U2@@Z"
+// CHECK: call void {{.*}} @"\01?variadic_sret at C@@QAA?AUS@@PBDZZ"
+// CHECK: call void @"\01?cdecl_sret at C@@QAA?AUS@@XZ"
+// CHECK: call void @"\01?byval_and_sret at C@@QAA?AUS@@U2@@Z"
Modified: cfe/trunk/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp?rev=208458&r1=208457&r2=208458&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp Fri May 9 17:46:15 2014
@@ -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 at Class@@QAE?AUSmall@@XZ"(%struct.Small* noalias sret %agg.result, %class.Class* %this)
+ // WIN32: define {{.*}} x86_thiscallcc void @"\01?thiscall_method_small at 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 at Class@@QAE?AUSmallWithCtor@@XZ"(%struct.SmallWithCtor* noalias sret %agg.result, %class.Class* %this)
+ // WIN32: define {{.*}} x86_thiscallcc void @"\01?thiscall_method_small_with_ctor at 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 at Class@@QAA?AUSmall@@XZ"(%struct.Small* noalias sret %agg.result, %class.Class* %this)
+ // WIN32: define {{.*}} void @"\01?cdecl_method_small at 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 at Class@@QAA?AUBig@@XZ"(%struct.Big* noalias sret %agg.result, %class.Class* %this)
+ // WIN32: define {{.*}} void @"\01?cdecl_method_big at 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)
Modified: cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp?rev=208458&r1=208457&r2=208458&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp Fri May 9 17:46:15 2014
@@ -34,14 +34,14 @@ void f() {
// CHECK32-LABEL: define void @"\01?f@@YAXXZ"()
// CHECK32: store i8* bitcast (void (%struct.C*)* @"\01??_9C@@$BA at 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 at AE" to i8*), i8** %ptr4
// CHECK32: }
//
// CHECK64-LABEL: define void @"\01?f@@YAXXZ"()
// CHECK64: store i8* bitcast (void (%struct.C*)* @"\01??_9C@@$BA at 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 at AA" to i8*), i8** %ptr3
+// CHECK64: store i8* bitcast (void (%struct.C*, %struct.S*, i32)* @"\01??_9C@@$BBA at AA" to i8*), i8** %ptr3
// CHECK64: store i8* bitcast (void (%"struct.(anonymous namespace)::D"*)* @"\01??_9D@?A@@$BA at 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 at 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 at 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: }
More information about the cfe-commits
mailing list