From 80ace8a9362c1a62e9bb5ef67d1e6178855ea73b Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Thu, 15 May 2014 23:01:46 +0000 Subject: [PATCH] MS ABI: Use musttail for thunk IR generation This allows us to perfectly forward non-trivial arguments that use inalloca. We still can't forward non-trivial arguments through thunks when we have a covariant return type with a non-trivial adjustment. This would require emitting an extra copy, which is non-conforming anyway. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@208927 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGCall.cpp | 2 +- lib/CodeGen/MicrosoftCXXABI.cpp | 27 +++++++++++- ...crosoft-abi-nontrivial-covariant-thunk.cpp | 24 ++++++++++ ...microsoft-abi-nontrivial-memptr-thunks.cpp | 11 ----- .../microsoft-abi-virtual-member-pointers.cpp | 44 +++++++++++++++---- 5 files changed, 86 insertions(+), 22 deletions(-) create mode 100644 test/CodeGenCXX/microsoft-abi-nontrivial-covariant-thunk.cpp delete mode 100644 test/CodeGenCXX/microsoft-abi-nontrivial-memptr-thunks.cpp diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index cf764d635b..61bf985278 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -2616,7 +2616,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, IP = IP->getNextNode(); AI = new llvm::AllocaInst(ArgStruct, "argmem", IP); } else { - AI = Builder.CreateAlloca(ArgStruct, nullptr, "argmem"); + AI = CreateTempAlloca(ArgStruct, "argmem"); } AI->setUsedWithInAlloca(true); assert(AI->isUsedWithInAlloca() && !AI->isStaticAlloca()); diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 88a0d9ab47..4b8027e838 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -1111,8 +1111,31 @@ llvm::Function *MicrosoftCXXABI::EmitVirtualMemPtrThunk( CGF.Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn"); llvm::Value *Callee = CGF.Builder.CreateLoad(VFuncPtr); - // Make the call and return the result. - CGF.EmitCallAndReturnForThunk(MD, Callee, 0); + unsigned CallingConv; + CodeGen::AttributeListType AttributeList; + CGM.ConstructAttributeList(FnInfo, MD, AttributeList, CallingConv, true); + llvm::AttributeSet Attrs = + llvm::AttributeSet::get(CGF.getLLVMContext(), AttributeList); + + // Do a musttail call with perfect argument forwarding. Any inalloca argument + // will be forwarded in place without any copy. + SmallVector Args; + for (llvm::Argument &A : ThunkFn->args()) + Args.push_back(&A); + llvm::CallInst *Call = CGF.Builder.CreateCall(Callee, Args); + Call->setTailCallKind(llvm::CallInst::TCK_MustTail); + Call->setAttributes(Attrs); + Call->setCallingConv(static_cast(CallingConv)); + + if (Call->getType()->isVoidTy()) + CGF.Builder.CreateRetVoid(); + else + CGF.Builder.CreateRet(Call); + + // Finish the function to maintain CodeGenFunction invariants. + // FIXME: Don't emit unreachable code. + CGF.EmitBlock(CGF.createBasicBlock()); + CGF.FinishFunction(); return ThunkFn; } diff --git a/test/CodeGenCXX/microsoft-abi-nontrivial-covariant-thunk.cpp b/test/CodeGenCXX/microsoft-abi-nontrivial-covariant-thunk.cpp new file mode 100644 index 0000000000..d305dd8327 --- /dev/null +++ b/test/CodeGenCXX/microsoft-abi-nontrivial-covariant-thunk.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 %s -fno-rtti -emit-llvm-only -o - -triple=i386-pc-win32 -verify + +// A is not trivially copyable and must be passed indirectly or with inalloca. +struct A { + A(); + A(const A &o); + virtual ~A(); + int a; +}; + +struct B { + B(); + int b; + virtual B *clone(A); +}; + +// Converting from C* to B* requires a this adjustment. +struct C : A, B { + C(); + int c; + virtual C *clone(A); // expected-error {{cannot compile this non-trivial argument copy for thunk yet}} +}; +B::B() {} // force emission +C::C() {} // force emission diff --git a/test/CodeGenCXX/microsoft-abi-nontrivial-memptr-thunks.cpp b/test/CodeGenCXX/microsoft-abi-nontrivial-memptr-thunks.cpp deleted file mode 100644 index becec2d86f..0000000000 --- a/test/CodeGenCXX/microsoft-abi-nontrivial-memptr-thunks.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -triple=i386-pc-win32 %s -verify - -struct A { - A(); - ~A(); - int a; -}; -struct B { - virtual void f(A); // expected-error {{cannot compile this non-trivial argument copy for thunk yet}} -}; -void (B::*mp)(A) = &B::f; diff --git a/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp b/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp index c069e2db25..974953c014 100644 --- a/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp +++ b/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp @@ -5,10 +5,19 @@ struct S { int x, y, z; }; +// U is not trivially copyable, and requires inalloca to pass by value. +struct U { + int u; + U(); + ~U(); + U(const U &); +}; + struct C { virtual void foo(); virtual int bar(int, double); virtual S baz(int); + virtual S qux(U); }; namespace { @@ -31,6 +40,10 @@ void f() { void (D::*ptr4)(); ptr4 = &D::foo; + S (C::*ptr5)(U); + ptr5 = &C::qux; + + // 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 @@ -51,14 +64,14 @@ void f() { // CHECK32-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_9C@@$BA@AE"(%struct.C* %this) unnamed_addr // CHECK32: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*)** %{{.*}}, i64 0 // CHECK32: [[CALLEE:%.*]] = load void (%struct.C*)** [[VPTR]] -// CHECK32: call x86_thiscallcc void [[CALLEE]](%struct.C* %{{.*}}) +// CHECK32: musttail call x86_thiscallcc void [[CALLEE]](%struct.C* %{{.*}}) // CHECK32: ret void // CHECK32: } // // CHECK64-LABEL: define linkonce_odr void @"\01??_9C@@$BA@AA"(%struct.C* %this) unnamed_addr // CHECK64: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*)** %{{.*}}, i64 0 // CHECK64: [[CALLEE:%.*]] = load void (%struct.C*)** [[VPTR]] -// CHECK64: call void [[CALLEE]](%struct.C* %{{.*}}) +// CHECK64: musttail call void [[CALLEE]](%struct.C* %{{.*}}) // CHECK64: ret void // CHECK64: } @@ -66,14 +79,14 @@ void f() { // CHECK32-LABEL: define linkonce_odr x86_thiscallcc i32 @"\01??_9C@@$B3AE"(%struct.C* %this, i32, double) unnamed_addr // CHECK32: [[VPTR:%.*]] = getelementptr inbounds i32 (%struct.C*, i32, double)** %{{.*}}, i64 1 // CHECK32: [[CALLEE:%.*]] = load i32 (%struct.C*, i32, double)** [[VPTR]] -// CHECK32: [[CALL:%.*]] = call x86_thiscallcc i32 [[CALLEE]](%struct.C* %{{.*}}, i32 %{{.*}}, double %{{.*}}) +// CHECK32: [[CALL:%.*]] = musttail call x86_thiscallcc i32 [[CALLEE]](%struct.C* %{{.*}}, i32 %{{.*}}, double %{{.*}}) // CHECK32: ret i32 [[CALL]] // CHECK32: } // // CHECK64-LABEL: define linkonce_odr i32 @"\01??_9C@@$B7AA"(%struct.C* %this, i32, double) unnamed_addr // CHECK64: [[VPTR:%.*]] = getelementptr inbounds i32 (%struct.C*, i32, double)** %{{.*}}, i64 1 // CHECK64: [[CALLEE:%.*]] = load i32 (%struct.C*, i32, double)** [[VPTR]] -// CHECK64: [[CALL:%.*]] = call i32 [[CALLEE]](%struct.C* %{{.*}}, i32 %{{.*}}, double %{{.*}}) +// CHECK64: [[CALL:%.*]] = musttail call i32 [[CALLEE]](%struct.C* %{{.*}}, i32 %{{.*}}, double %{{.*}}) // CHECK64: ret i32 [[CALL]] // CHECK64: } @@ -81,14 +94,14 @@ void f() { // 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: musttail 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.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: musttail call void [[CALLEE]](%struct.C* %{{.*}}, %struct.S* sret %agg.result, i32 %{{.*}}) // CHECK64: ret void // CHECK64: } @@ -96,13 +109,28 @@ void f() { // CHECK32-LABEL: define internal x86_thiscallcc void @"\01??_9D@?A@@$BA@AE"(%"struct.(anonymous namespace)::D"* %this) unnamed_addr // CHECK32: [[VPTR:%.*]] = getelementptr inbounds void (%"struct.(anonymous namespace)::D"*)** %{{.*}}, i64 0 // CHECK32: [[CALLEE:%.*]] = load void (%"struct.(anonymous namespace)::D"*)** [[VPTR]] -// CHECK32: call x86_thiscallcc void [[CALLEE]](%"struct.(anonymous namespace)::D"* %{{.*}}) +// CHECK32: musttail call x86_thiscallcc void [[CALLEE]](%"struct.(anonymous namespace)::D"* %{{.*}}) // CHECK32: ret void // CHECK32: } // // CHECK64-LABEL: define internal void @"\01??_9D@?A@@$BA@AA"(%"struct.(anonymous namespace)::D"* %this) unnamed_addr // CHECK64: [[VPTR:%.*]] = getelementptr inbounds void (%"struct.(anonymous namespace)::D"*)** %{{.*}}, i64 0 // CHECK64: [[CALLEE:%.*]] = load void (%"struct.(anonymous namespace)::D"*)** [[VPTR]] -// CHECK64: call void [[CALLEE]](%"struct.(anonymous namespace)::D"* %{{.*}}) +// CHECK64: musttail call void [[CALLEE]](%"struct.(anonymous namespace)::D"* %{{.*}}) +// CHECK64: ret void +// CHECK64: } + +// Thunk for calling the fourth virtual function in C, taking a struct parameter and returning a struct. +// CHECK32-LABEL: define linkonce_odr x86_thiscallcc %struct.S* @"\01??_9C@@$BM@AE"(%struct.C* %this, <{ %struct.S*, %struct.U }>* inalloca) unnamed_addr +// CHECK32: [[VPTR:%.*]] = getelementptr inbounds %struct.S* (%struct.C*, <{ %struct.S*, %struct.U }>*)** %{{.*}}, i64 3 +// CHECK32: [[CALLEE:%.*]] = load %struct.S* (%struct.C*, <{ %struct.S*, %struct.U }>*)** [[VPTR]] +// CHECK32: [[CALL:%.*]] = musttail call x86_thiscallcc %struct.S* [[CALLEE]](%struct.C* %this, <{ %struct.S*, %struct.U }>* inalloca %{{.*}}) +// CHECK32: ret %struct.S* [[CALL]] +// CHECK32: } +// +// CHECK64-LABEL: define linkonce_odr void @"\01??_9C@@$BBI@AA"(%struct.C* %this, %struct.S* noalias sret %agg.result, %struct.U*) unnamed_addr +// CHECK64: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, %struct.S*, %struct.U*)** %{{.*}}, i64 3 +// CHECK64: [[CALLEE:%.*]] = load void (%struct.C*, %struct.S*, %struct.U*)** [[VPTR]] +// CHECK64: musttail call void [[CALLEE]](%struct.C* %this, %struct.S* sret %agg.result, %struct.U* %{{.*}}) // CHECK64: ret void // CHECK64: } -- 2.40.0