From: Reid Kleckner Date: Fri, 29 Aug 2014 21:43:29 +0000 (+0000) Subject: Make all virtual member pointers use variadic musttail calls X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8060f1653ba0543397ed65b74c7e39d3b61b95d2;p=clang Make all virtual member pointers use variadic musttail calls This avoids encoding information about the function prototype into the thunk at the cost of some function prototype bitcast gymnastics. Fixes PR20653. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@216782 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index aa7de709cb..ce16d3bd64 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -332,6 +332,20 @@ CodeGenTypes::arrangeGlobalDeclaration(GlobalDecl GD) { return arrangeFunctionDeclaration(FD); } +/// Arrange a thunk that takes 'this' as the first parameter followed by +/// varargs. Return a void pointer, regardless of the actual return type. +/// The body of the thunk will end in a musttail call to a function of the +/// correct type, and the caller will bitcast the function to the correct +/// prototype. +const CGFunctionInfo & +CodeGenTypes::arrangeMSMemberPointerThunk(const CXXMethodDecl *MD) { + assert(MD->isVirtual() && "only virtual memptrs have thunks"); + CanQual FTP = GetFormalType(MD); + CanQualType ArgTys[] = { GetThisType(Context, MD->getParent()) }; + return arrangeLLVMFunctionInfo(Context.VoidTy, false, ArgTys, + FTP->getExtInfo(), RequiredArgs(1)); +} + /// Arrange a call as unto a free function, except possibly with an /// additional number of formal parameters considered required. static const CGFunctionInfo & diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h index 51466c6e2c..91727e3d5e 100644 --- a/lib/CodeGen/CodeGenTypes.h +++ b/lib/CodeGen/CodeGenTypes.h @@ -207,6 +207,7 @@ public: const CGFunctionInfo &arrangeCXXMethodCall(const CallArgList &args, const FunctionProtoType *type, RequiredArgs required); + const CGFunctionInfo &arrangeMSMemberPointerThunk(const CXXMethodDecl *MD); const CGFunctionInfo &arrangeFreeFunctionType(CanQual Ty); const CGFunctionInfo &arrangeFreeFunctionType(CanQual Ty); diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index ca1196dbbf..9fa7f339ad 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -1434,6 +1434,9 @@ MicrosoftCXXABI::enumerateVBTables(const CXXRecordDecl *RD) { llvm::Function *MicrosoftCXXABI::EmitVirtualMemPtrThunk( const CXXMethodDecl *MD, const MicrosoftVTableContext::MethodVFTableLocation &ML) { + assert(!isa(MD) && !isa(MD) && + "can't form pointers to ctors or virtual dtors"); + // Calculate the mangled name. SmallString<256> ThunkName; llvm::raw_svector_ostream Out(ThunkName); @@ -1445,7 +1448,7 @@ llvm::Function *MicrosoftCXXABI::EmitVirtualMemPtrThunk( return cast(GV); // Create the llvm::Function. - const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeGlobalDeclaration(MD); + const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeMSMemberPointerThunk(MD); llvm::FunctionType *ThunkTy = CGM.getTypes().GetFunctionType(FnInfo); llvm::Function *ThunkFn = llvm::Function::Create(ThunkTy, llvm::Function::ExternalLinkage, @@ -1464,18 +1467,28 @@ llvm::Function *MicrosoftCXXABI::EmitVirtualMemPtrThunk( // Start codegen. CodeGenFunction CGF(CGM); - CGF.StartThunk(ThunkFn, MD, FnInfo); + CGF.CurGD = GlobalDecl(MD); + CGF.CurFuncIsThunk = true; + + // Build FunctionArgs, but only include the implicit 'this' parameter + // declaration. + FunctionArgList FunctionArgs; + buildThisParam(CGF, FunctionArgs); + + // Start defining the function. + CGF.StartFunction(GlobalDecl(), FnInfo.getReturnType(), ThunkFn, FnInfo, + FunctionArgs, MD->getLocation(), SourceLocation()); + EmitThisParam(CGF); // Load the vfptr and then callee from the vftable. The callee should have // adjusted 'this' so that the vfptr is at offset zero. - llvm::Value *This = CGF.LoadCXXThis(); - llvm::Value *VTable = - CGF.GetVTablePtr(This, ThunkTy->getPointerTo()->getPointerTo()); + llvm::Value *VTable = CGF.GetVTablePtr( + getThisValue(CGF), ThunkTy->getPointerTo()->getPointerTo()); llvm::Value *VFuncPtr = CGF.Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn"); llvm::Value *Callee = CGF.Builder.CreateLoad(VFuncPtr); - CGF.EmitCallAndReturnForThunk(Callee, 0); + CGF.EmitMustTailThunk(MD, getThisValue(CGF), Callee); return ThunkFn; } @@ -1935,8 +1948,8 @@ MicrosoftCXXABI::BuildMemberPointer(const CXXRecordDecl *RD, CodeGenTypes &Types = CGM.getTypes(); llvm::Constant *FirstField; + const FunctionProtoType *FPT = MD->getType()->castAs(); if (!MD->isVirtual()) { - const FunctionProtoType *FPT = MD->getType()->castAs(); llvm::Type *Ty; // Check whether the function has a computable LLVM signature. if (Types.isFuncTypeConvertible(FPT)) { @@ -1952,14 +1965,14 @@ MicrosoftCXXABI::BuildMemberPointer(const CXXRecordDecl *RD, } else { MicrosoftVTableContext::MethodVFTableLocation ML = CGM.getMicrosoftVTableContext().getMethodVFTableLocation(MD); - if (MD->isVariadic()) { - CGM.ErrorUnsupported(MD, "pointer to variadic virtual member function"); - FirstField = llvm::Constant::getNullValue(CGM.VoidPtrTy); - } else if (!CGM.getTypes().isFuncTypeConvertible( - MD->getType()->castAs())) { + if (!CGM.getTypes().isFuncTypeConvertible( + MD->getType()->castAs())) { CGM.ErrorUnsupported(MD, "pointer to virtual member function with " "incomplete return or parameter type"); FirstField = llvm::Constant::getNullValue(CGM.VoidPtrTy); + } else if (FPT->getCallConv() == CC_X86FastCall) { + CGM.ErrorUnsupported(MD, "pointer to fastcall virtual member function"); + FirstField = llvm::Constant::getNullValue(CGM.VoidPtrTy); } else if (ML.VBase) { CGM.ErrorUnsupported(MD, "pointer to virtual member function overriding " "member function in virtual base class"); diff --git a/test/CodeGenCXX/microsoft-abi-member-pointers.cpp b/test/CodeGenCXX/microsoft-abi-member-pointers.cpp index 80d92de3ec..04665c93ce 100644 --- a/test/CodeGenCXX/microsoft-abi-member-pointers.cpp +++ b/test/CodeGenCXX/microsoft-abi-member-pointers.cpp @@ -595,15 +595,15 @@ void (C::*getmp())() { return &C::g; } // CHECK-LABEL: define i64 @"\01?getmp@Test4@@YAP8C@1@AEXXZXZ"() -// CHECK: store { i8*, i32 } { i8* bitcast (void (i8*)* @"\01??_9C@Test4@@$BA@AE" to i8*), i32 4 }, { i8*, i32 }* %{{.*}} +// CHECK: store { i8*, i32 } { i8* bitcast (void (%"struct.Test4::C"*, ...)* @"\01??_9C@Test4@@$BA@AE" to i8*), i32 4 }, { i8*, i32 }* %{{.*}} // -// CHECK-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_9C@Test4@@$BA@AE"(i8*) +// CHECK-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_9C@Test4@@$BA@AE"(%"struct.Test4::C"* %this, ...) // CHECK-NOT: getelementptr -// CHECK: load void (i8*)*** %{{.*}} -// CHECK: getelementptr inbounds void (i8*)** %{{.*}}, i64 0 +// CHECK: load void (%"struct.Test4::C"*, ...)*** %{{.*}} +// CHECK: getelementptr inbounds void (%"struct.Test4::C"*, ...)** %{{.*}}, i64 0 // CHECK-NOT: getelementptr -// CHECK: call x86_thiscallcc void % +// CHECK: musttail call x86_thiscallcc void (%"struct.Test4::C"*, ...)* % } diff --git a/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp b/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp index 5e645aa482..1880f04576 100644 --- a/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp +++ b/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp @@ -49,111 +49,111 @@ 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.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: store i8* bitcast (void (%struct.C*, ...)* @"\01??_9C@@$BA@AE" to i8*), i8** %ptr +// CHECK32: store i8* bitcast (void (%struct.C*, ...)* @"\01??_9C@@$B3AE" to i8*), i8** %ptr2 +// CHECK32: store i8* bitcast (void (%struct.C*, ...)* @"\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.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: store i8* bitcast (void (%struct.C*, ...)* @"\01??_9C@@$BA@AA" to i8*), i8** %ptr +// CHECK64: store i8* bitcast (void (%struct.C*, ...)* @"\01??_9C@@$B7AA" to i8*), i8** %ptr2 +// CHECK64: store i8* bitcast (void (%struct.C*, ...)* @"\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: } } // Thunk for calling the 1st virtual function in C with no parameters. -// CHECK32-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_9C@@$BA@AE"(%struct.C* %this) -// CHECK32-NOT: 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: ret void +// CHECK32-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_9C@@$BA@AE"(%struct.C* %this, ...) +// CHECK32: unnamed_addr +// CHECK32: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, ...)** %{{.*}}, i64 0 +// CHECK32: [[CALLEE:%.*]] = load void (%struct.C*, ...)** [[VPTR]] +// CHECK32: musttail call x86_thiscallcc void (%struct.C*, ...)* [[CALLEE]](%struct.C* %{{.*}}, ...) +// CHECK32-NEXT: ret void // CHECK32: } // -// CHECK64-LABEL: define linkonce_odr void @"\01??_9C@@$BA@AA"(%struct.C* %this) -// CHECK64-NOT: 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: ret void +// CHECK64-LABEL: define linkonce_odr void @"\01??_9C@@$BA@AA"(%struct.C* %this, ...) +// CHECK64: unnamed_addr +// CHECK64: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, ...)** %{{.*}}, i64 0 +// CHECK64: [[CALLEE:%.*]] = load void (%struct.C*, ...)** [[VPTR]] +// CHECK64: musttail call void (%struct.C*, ...)* [[CALLEE]](%struct.C* %{{.*}}, ...) +// CHECK64-NEXT: ret void // CHECK64: } // Thunk for calling the 2nd virtual function in C, taking int and double as parameters, returning int. -// CHECK32-LABEL: define linkonce_odr x86_thiscallcc i32 @"\01??_9C@@$B3AE"(%struct.C* %this, i32, double) -// 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: ret i32 [[CALL]] +// CHECK32-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_9C@@$B3AE"(%struct.C* %this, ...) +// CHECK32: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, ...)** %{{.*}}, i64 1 +// CHECK32: [[CALLEE:%.*]] = load void (%struct.C*, ...)** [[VPTR]] +// CHECK32: musttail call x86_thiscallcc void (%struct.C*, ...)* [[CALLEE]](%struct.C* %{{.*}}, ...) +// CHECK32-NEXT: ret void // CHECK32: } // -// CHECK64-LABEL: define linkonce_odr i32 @"\01??_9C@@$B7AA"(%struct.C* %this, i32, double) -// 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: ret i32 [[CALL]] +// CHECK64-LABEL: define linkonce_odr void @"\01??_9C@@$B7AA"(%struct.C* %this, ...) +// CHECK64: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, ...)** %{{.*}}, i64 1 +// CHECK64: [[CALLEE:%.*]] = load void (%struct.C*, ...)** [[VPTR]] +// CHECK64: musttail call void (%struct.C*, ...)* [[CALLEE]](%struct.C* %{{.*}}, ...) +// CHECK64-NEXT: ret void // 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.C* %this, %struct.S* noalias sret %agg.result, i32) -// 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-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_9C@@$B7AE"(%struct.C* %this, ...) +// CHECK32: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, ...)** %{{.*}}, i64 2 +// CHECK32: [[CALLEE:%.*]] = load void (%struct.C*, ...)** [[VPTR]] +// CHECK32: musttail call x86_thiscallcc void (%struct.C*, ...)* [[CALLEE]](%struct.C* %{{.*}}, ...) +// CHECK32-NEXT: ret void // CHECK32: } // -// CHECK64-LABEL: define linkonce_odr void @"\01??_9C@@$BBA@AA"(%struct.C* %this, %struct.S* noalias sret %agg.result, i32) -// 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-LABEL: define linkonce_odr void @"\01??_9C@@$BBA@AA"(%struct.C* %this, ...) +// CHECK64: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, ...)** %{{.*}}, i64 2 +// CHECK64: [[CALLEE:%.*]] = load void (%struct.C*, ...)** [[VPTR]] +// CHECK64: musttail call void (%struct.C*, ...)* [[CALLEE]](%struct.C* %{{.*}}, ...) +// CHECK64-NEXT: ret void // CHECK64: } // Thunk for calling the virtual function in internal class D. -// CHECK32-LABEL: define internal x86_thiscallcc void @"\01??_9D@?A@@$BA@AE"(%"struct.(anonymous namespace)::D"* %this) -// 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: ret void +// CHECK32-LABEL: define internal x86_thiscallcc void @"\01??_9D@?A@@$BA@AE"(%"struct.(anonymous namespace)::D"* %this, ...) +// CHECK32: [[VPTR:%.*]] = getelementptr inbounds void (%"struct.(anonymous namespace)::D"*, ...)** %{{.*}}, i64 0 +// CHECK32: [[CALLEE:%.*]] = load void (%"struct.(anonymous namespace)::D"*, ...)** [[VPTR]] +// CHECK32: musttail call x86_thiscallcc void (%"struct.(anonymous namespace)::D"*, ...)* [[CALLEE]](%"struct.(anonymous namespace)::D"* %{{.*}}, ...) +// CHECK32-NEXT: ret void // CHECK32: } // -// CHECK64-LABEL: define internal void @"\01??_9D@?A@@$BA@AA"(%"struct.(anonymous namespace)::D"* %this) -// 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: ret void +// CHECK64-LABEL: define internal void @"\01??_9D@?A@@$BA@AA"(%"struct.(anonymous namespace)::D"* %this, ...) +// CHECK64: [[VPTR:%.*]] = getelementptr inbounds void (%"struct.(anonymous namespace)::D"*, ...)** %{{.*}}, i64 0 +// CHECK64: [[CALLEE:%.*]] = load void (%"struct.(anonymous namespace)::D"*, ...)** [[VPTR]] +// CHECK64: musttail call void (%"struct.(anonymous namespace)::D"*, ...)* [[CALLEE]](%"struct.(anonymous namespace)::D"* %{{.*}}, ...) +// CHECK64-NEXT: 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) -// 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* %{{.*}}, <{ %struct.S*, %struct.U }>* inalloca %{{.*}}) -// CHECK32-NEXT: ret %struct.S* [[CALL]] +// CHECK32-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_9C@@$BM@AE"(%struct.C* %this, ...) +// CHECK32: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, ...)** %{{.*}}, i64 3 +// CHECK32: [[CALLEE:%.*]] = load void (%struct.C*, ...)** [[VPTR]] +// CHECK32: musttail call x86_thiscallcc void (%struct.C*, ...)* [[CALLEE]](%struct.C* %{{.*}}, ...) +// CHECK32-NEXT: ret void // CHECK32: } // -// CHECK64-LABEL: define linkonce_odr void @"\01??_9C@@$BBI@AA"(%struct.C* %this, %struct.S* noalias sret %agg.result, %struct.U*) -// 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: call void [[CALLEE]](%struct.C* %{{.*}}, %struct.S* sret %agg.result, %struct.U* %{{.*}}) +// CHECK64-LABEL: define linkonce_odr void @"\01??_9C@@$BBI@AA"(%struct.C* %this, ...) +// CHECK64: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, ...)** %{{.*}}, i64 3 +// CHECK64: [[CALLEE:%.*]] = load void (%struct.C*, ...)** [[VPTR]] +// CHECK64: musttail call void (%struct.C*, ...)* [[CALLEE]](%struct.C* %{{.*}}, ...) // CHECK64: ret void // CHECK64: } // Thunk for calling the fifth virtual function in C, taking a struct parameter // and returning a struct. -// CHECK32-LABEL: define linkonce_odr x86_fastcallcc void @"\01??_9C@@$BBA@AE"(%struct.C* inreg %this, %struct.S* inreg noalias sret %agg.result, <{ %struct.U }>* inalloca) -// CHECK32: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, %struct.S*, <{ %struct.U }>*)** %{{.*}}, i64 4 -// CHECK32: [[CALLEE:%.*]] = load void (%struct.C*, %struct.S*, <{ %struct.U }>*)** [[VPTR]] -// CHECK32: musttail call x86_fastcallcc void [[CALLEE]](%struct.C* inreg %{{.*}}, %struct.S* inreg sret %{{.*}}, <{ %struct.U }>* inalloca %{{.*}}) +// CHECK32-LABEL: define linkonce_odr x86_fastcallcc void @"\01??_9C@@$BBA@AE"(%struct.C* inreg %this, ...) +// CHECK32: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, ...)** %{{.*}}, i64 4 +// CHECK32: [[CALLEE:%.*]] = load void (%struct.C*, ...)** [[VPTR]] +// CHECK32: musttail call x86_fastcallcc void (%struct.C*, ...)* [[CALLEE]](%struct.C* inreg %{{.*}}, ...) // CHECK32-NEXT: ret void // CHECK32: } // -// CHECK64-LABEL: define linkonce_odr void @"\01??_9C@@$BCA@AA"(%struct.C* %this, %struct.S* noalias sret %agg.result, %struct.U*) -// CHECK64: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, %struct.S*, %struct.U*)** %{{.*}}, i64 4 -// CHECK64: [[CALLEE:%.*]] = load void (%struct.C*, %struct.S*, %struct.U*)** [[VPTR]] -// CHECK64: call void [[CALLEE]](%struct.C* %{{.*}}, %struct.S* sret %agg.result, %struct.U* %{{.*}}) +// CHECK64-LABEL: define linkonce_odr void @"\01??_9C@@$BCA@AA"(%struct.C* %this, ...) +// CHECK64: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*, ...)** %{{.*}}, i64 4 +// CHECK64: [[CALLEE:%.*]] = load void (%struct.C*, ...)** [[VPTR]] +// CHECK64: musttail call void (%struct.C*, ...)* [[CALLEE]](%struct.C* %{{.*}}, ...) // CHECK64: ret void // CHECK64: } diff --git a/test/CodeGenCXX/microsoft-abi-vmemptr-conflicts.cpp b/test/CodeGenCXX/microsoft-abi-vmemptr-conflicts.cpp new file mode 100644 index 0000000000..35ff4f3beb --- /dev/null +++ b/test/CodeGenCXX/microsoft-abi-vmemptr-conflicts.cpp @@ -0,0 +1,101 @@ +// RUN: %clang_cc1 -fno-rtti -emit-llvm -triple=i386-pc-win32 %s -o - | FileCheck %s + +// In each test case, we have two member pointers whose thunks have the same +// vtable offset and same mangling, but their prototypes conflict. The +// arguments and return type may differ. Therefore, we have to bitcast the +// function prototype. Unfortunately, if the return types differ, LLVM's +// optimizers can get upset. + +namespace num_params { +struct A { virtual void a(int); }; +struct B { virtual void b(int, int); }; +struct C : A, B { + virtual void a(int); + virtual void b(int, int); +}; +void f(C *c) { + (c->*(&C::a))(0); + (c->*(&C::b))(0, 0); +} +} + +// CHECK-LABEL: define void @"\01?f@num_params@@YAXPAUC@1@@Z"(%"struct.num_params::C"* %c) +// CHECK: call x86_thiscallcc void bitcast (void (%"struct.num_params::C"*, ...)* @"\01??_9C@num_params@@$BA@AE" to void (%"struct.num_params::C"*, i32)*)(%"struct.num_params::C"* %{{.*}}, i32 0) +// CHECK: call x86_thiscallcc void bitcast (void (%"struct.num_params::C"*, ...)* @"\01??_9C@num_params@@$BA@AE" to void (%"struct.num_params::C"*, i32, i32)*)(%"struct.num_params::C"* %{{.*}}, i32 0, i32 0) + +// CHECK-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_9C@num_params@@$BA@AE"(%"struct.num_params::C"* %this, ...) +// CHECK: musttail call x86_thiscallcc void (%"struct.num_params::C"*, ...)* %{{.*}}(%"struct.num_params::C"* %{{.*}}, ...) +// CHECK-NEXT: ret void + +namespace i64_return { +struct A { virtual int a(); }; +struct B { virtual long long b(); }; +struct C : A, B { + virtual int a(); + virtual long long b(); +}; +long long f(C *c) { + int x = (c->*(&C::a))(); + long long y = (c->*(&C::b))(); + return x + y; +} +} + +// CHECK-LABEL: define i64 @"\01?f@i64_return@@YA_JPAUC@1@@Z"(%"struct.i64_return::C"* %c) +// CHECK: call x86_thiscallcc i32 bitcast (void (%"struct.i64_return::C"*, ...)* @"\01??_9C@i64_return@@$BA@AE" to i32 (%"struct.i64_return::C"*)*)(%"struct.i64_return::C"* %{{.*}}) +// CHECK: call x86_thiscallcc i64 bitcast (void (%"struct.i64_return::C"*, ...)* @"\01??_9C@i64_return@@$BA@AE" to i64 (%"struct.i64_return::C"*)*)(%"struct.i64_return::C"* %{{.*}}) + +// CHECK-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_9C@i64_return@@$BA@AE"(%"struct.i64_return::C"* %this, ...) +// CHECK: musttail call x86_thiscallcc void (%"struct.i64_return::C"*, ...)* %{{.*}}(%"struct.i64_return::C"* %{{.*}}, ...) +// CHECK-NEXT: ret void + +namespace sret { +struct Big { int big[32]; }; +struct A { virtual int a(); }; +struct B { virtual Big b(); }; +struct C : A, B { + virtual int a(); + virtual Big b(); +}; +void f(C *c) { + (c->*(&C::a))(); + Big b((c->*(&C::b))()); +} +} + +// CHECK-LABEL: define void @"\01?f@sret@@YAXPAUC@1@@Z"(%"struct.sret::C"* %c) +// CHECK: call x86_thiscallcc i32 bitcast (void (%"struct.sret::C"*, ...)* @"\01??_9C@sret@@$BA@AE" to i32 (%"struct.sret::C"*)*)(%"struct.sret::C"* %{{.*}}) +// CHECK: call x86_thiscallcc void bitcast (void (%"struct.sret::C"*, ...)* @"\01??_9C@sret@@$BA@AE" to void (%"struct.sret::C"*, %"struct.sret::Big"*)*)(%"struct.sret::C"* %{{.*}}, %"struct.sret::Big"* sret %{{.*}}) + +// CHECK-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_9C@sret@@$BA@AE"(%"struct.sret::C"* %this, ...) +// CHECK: musttail call x86_thiscallcc void (%"struct.sret::C"*, ...)* %{{.*}}(%"struct.sret::C"* %{{.*}}, ...) +// CHECK-NEXT: ret void + +namespace cdecl_inalloca { +// Fairly evil, since now we end up doing an inalloca-style call through a +// thunk that doesn't use inalloca. Hopefully the stacks line up? +struct Big { + Big(); + ~Big(); + int big[32]; +}; +struct A { virtual void __cdecl a(); }; +struct B { virtual void __cdecl b(Big); }; +struct C : A, B { + virtual void __cdecl a(); + virtual void __cdecl b(Big); +}; +void f(C *c) { + Big b; + (c->*(&C::a))(); + ((c->*(&C::b))(b)); +} +} + +// CHECK-LABEL: define void @"\01?f@cdecl_inalloca@@YAXPAUC@1@@Z"(%"struct.cdecl_inalloca::C"* %c) +// CHECK: call void bitcast (void (%"struct.cdecl_inalloca::C"*, ...)* @"\01??_9C@cdecl_inalloca@@$BA@AE" to void (%"struct.cdecl_inalloca::C"*)*)(%"struct.cdecl_inalloca::C"* %{{.*}}) +// CHECK: call void bitcast (void (%"struct.cdecl_inalloca::C"*, ...)* @"\01??_9C@cdecl_inalloca@@$BA@AE" to void (<{ %"struct.cdecl_inalloca::C"*, %"struct.cdecl_inalloca::Big" }>*)*)(<{ %"struct.cdecl_inalloca::C"*, %"struct.cdecl_inalloca::Big" }>* inalloca %{{.*}}) + +// CHECK-LABEL: define linkonce_odr void @"\01??_9C@cdecl_inalloca@@$BA@AE"(%"struct.cdecl_inalloca::C"* %this, ...) +// CHECK: musttail call void (%"struct.cdecl_inalloca::C"*, ...)* %{{.*}}(%"struct.cdecl_inalloca::C"* %{{.*}}, ...) +// CHECK-NEXT: ret void diff --git a/test/CodeGenCXX/microsoft-abi-vmemptr-fastcall.cpp b/test/CodeGenCXX/microsoft-abi-vmemptr-fastcall.cpp new file mode 100644 index 0000000000..9d55383d65 --- /dev/null +++ b/test/CodeGenCXX/microsoft-abi-vmemptr-fastcall.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -triple i686-pc-windows-msvc %s -emit-llvm-only -verify + +// We reject this because LLVM doesn't forward the second regparm through the +// thunk. + +struct A { + virtual void __fastcall f(int a, int b); +}; +void (__fastcall A::*doit())(int, int) { + return &A::f; // expected-error {{cannot compile this pointer to fastcall virtual member function yet}} +}