diff options
Diffstat (limited to 'clang')
| -rw-r--r-- | clang/lib/CodeGen/TargetInfo.cpp | 55 | ||||
| -rw-r--r-- | 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 40fc51146aa..1a1ac8bd925 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -1002,6 +1002,26 @@ X86_32ABIInfo::addFieldToArgStruct(SmallVector<llvm::Type *, 6> &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<llvm::Type *, 6> 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 985b1ce62e9..a34a2455c36 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 %{{[^,]*}}) |

