From 852361d2171f7b5689c5aaaccfa2583018a284e8 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Sat, 26 Jul 2014 00:12:26 +0000 Subject: [PATCH] MS ABI: Ensure 'this' is first for byval+sret methods Previously we were building up the inalloca struct in the usual pattern of return type followed by arguments. However, on Windows, 'this' always precedes the 'sret' parameter, so we need to insert it into the struct first as a special case. llvm-svn: 213990 --- clang/lib/CodeGen/TargetInfo.cpp | 55 +++++++++++-------- clang/test/CodeGenCXX/microsoft-abi-byval-sret.cpp | 61 ++++++++++++++++++---- 2 files changed, 85 insertions(+), 31 deletions(-) diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index 40fc511..1a1ac8b 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -1002,6 +1002,26 @@ X86_32ABIInfo::addFieldToArgStruct(SmallVector &FrameFields, } } +static bool isArgInAlloca(const ABIArgInfo &Info) { + // Leave ignored and inreg arguments alone. + switch (Info.getKind()) { + case ABIArgInfo::InAlloca: + return true; + case ABIArgInfo::Indirect: + assert(Info.getIndirectByVal()); + return true; + case ABIArgInfo::Ignore: + return false; + case ABIArgInfo::Direct: + case ABIArgInfo::Extend: + case ABIArgInfo::Expand: + if (Info.getInReg()) + return false; + return true; + } + llvm_unreachable("invalid enum"); +} + void X86_32ABIInfo::rewriteWithInAlloca(CGFunctionInfo &FI) const { assert(IsWin32StructABI && "inalloca only supported on win32"); @@ -1009,9 +1029,19 @@ void X86_32ABIInfo::rewriteWithInAlloca(CGFunctionInfo &FI) const { SmallVector FrameFields; unsigned StackOffset = 0; + CGFunctionInfo::arg_iterator I = FI.arg_begin(), E = FI.arg_end(); - // Put the sret parameter into the inalloca struct if it's in memory. + // Put 'this' into the struct before 'sret', if necessary. + bool IsThisCall = + FI.getCallingConvention() == llvm::CallingConv::X86_ThisCall; ABIArgInfo &Ret = FI.getReturnInfo(); + if (Ret.isIndirect() && Ret.isSRetAfterThis() && !IsThisCall && + isArgInAlloca(I->info)) { + addFieldToArgStruct(FrameFields, StackOffset, I->info, I->type); + ++I; + } + + // Put the sret parameter into the inalloca struct if it's in memory. if (Ret.isIndirect() && !Ret.getInReg()) { CanQualType PtrTy = getContext().getPointerType(FI.getReturnType()); addFieldToArgStruct(FrameFields, StackOffset, Ret, PtrTy); @@ -1020,30 +1050,13 @@ void X86_32ABIInfo::rewriteWithInAlloca(CGFunctionInfo &FI) const { } // Skip the 'this' parameter in ecx. - CGFunctionInfo::arg_iterator I = FI.arg_begin(), E = FI.arg_end(); - if (FI.getCallingConvention() == llvm::CallingConv::X86_ThisCall) + if (IsThisCall) ++I; // Put arguments passed in memory into the struct. for (; I != E; ++I) { - - // Leave ignored and inreg arguments alone. - switch (I->info.getKind()) { - case ABIArgInfo::Indirect: - assert(I->info.getIndirectByVal()); - break; - case ABIArgInfo::Ignore: - continue; - case ABIArgInfo::Direct: - case ABIArgInfo::Extend: - if (I->info.getInReg()) - continue; - break; - default: - break; - } - - addFieldToArgStruct(FrameFields, StackOffset, I->info, I->type); + if (isArgInAlloca(I->info)) + addFieldToArgStruct(FrameFields, StackOffset, I->info, I->type); } FI.setArgStruct(llvm::StructType::get(getVMContext(), FrameFields, diff --git a/clang/test/CodeGenCXX/microsoft-abi-byval-sret.cpp b/clang/test/CodeGenCXX/microsoft-abi-byval-sret.cpp index 985b1ce..a34a245 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-byval-sret.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-byval-sret.cpp @@ -5,25 +5,66 @@ struct A { A(const A &o) : a(o.a) {} ~A() {} int a; +}; + +struct B { A foo(A o); + A __cdecl bar(A o); + A __stdcall baz(A o); + A __fastcall qux(A o); }; -A A::foo(A x) { - A y(*this); - y.a += x.a; - return y; +A B::foo(A x) { + return x; } -// CHECK-LABEL: define x86_thiscallcc %struct.A* @"\01?foo@A@@QAE?AU1@U1@@Z" -// CHECK: (%struct.A* %this, <{ %struct.A*, %struct.A }>* inalloca) +// CHECK-LABEL: define x86_thiscallcc %struct.A* @"\01?foo@B@@QAE?AUA@@U2@@Z" +// CHECK: (%struct.B* %this, <{ %struct.A*, %struct.A }>* inalloca) // CHECK: getelementptr inbounds <{ %struct.A*, %struct.A }>* %{{.*}}, i32 0, i32 0 // CHECK: load %struct.A** // CHECK: ret %struct.A* +A B::bar(A x) { + return x; +} + +// CHECK-LABEL: define %struct.A* @"\01?bar@B@@QAA?AUA@@U2@@Z" +// CHECK: (<{ %struct.B*, %struct.A*, %struct.A }>* inalloca) +// CHECK: getelementptr inbounds <{ %struct.B*, %struct.A*, %struct.A }>* %{{.*}}, i32 0, i32 1 +// CHECK: load %struct.A** +// CHECK: ret %struct.A* + +A B::baz(A x) { + return x; +} + +// CHECK-LABEL: define x86_stdcallcc %struct.A* @"\01?baz@B@@QAG?AUA@@U2@@Z" +// CHECK: (<{ %struct.B*, %struct.A*, %struct.A }>* inalloca) +// CHECK: getelementptr inbounds <{ %struct.B*, %struct.A*, %struct.A }>* %{{.*}}, i32 0, i32 1 +// CHECK: load %struct.A** +// CHECK: ret %struct.A* + +A B::qux(A x) { + return x; +} + +// CHECK-LABEL: define x86_fastcallcc void @"\01?qux@B@@QAI?AUA@@U2@@Z" +// CHECK: (%struct.B* inreg %this, %struct.A* inreg noalias sret %agg.result, <{ %struct.A }>* inalloca) +// CHECK: ret void + int main() { - A x; - A y = x.foo(x); + B b; + A a = b.foo(A()); + a = b.bar(a); + a = b.baz(a); + a = b.qux(a); } -// CHECK: call x86_thiscallcc %struct.A* @"\01?foo@A@@QAE?AU1@U1@@Z" -// CHECK: (%struct.A* %{{[^,]*}}, <{ %struct.A*, %struct.A }>* inalloca %{{[^,]*}}) +// CHECK: call x86_thiscallcc %struct.A* @"\01?foo@B@@QAE?AUA@@U2@@Z" +// CHECK: (%struct.B* %{{[^,]*}}, <{ %struct.A*, %struct.A }>* inalloca %{{[^,]*}}) +// CHECK: call %struct.A* @"\01?bar@B@@QAA?AUA@@U2@@Z" +// CHECK: (<{ %struct.B*, %struct.A*, %struct.A }>* inalloca %{{[^,]*}}) +// CHECK: call x86_stdcallcc %struct.A* @"\01?baz@B@@QAG?AUA@@U2@@Z" +// CHECK: (<{ %struct.B*, %struct.A*, %struct.A }>* inalloca %{{[^,]*}}) +// CHECK: call x86_fastcallcc void @"\01?qux@B@@QAI?AUA@@U2@@Z" +// CHECK: (%struct.B* inreg %{{[^,]*}}, %struct.A* inreg sret %{{.*}}, <{ %struct.A }>* inalloca %{{[^,]*}}) -- 2.7.4