Hi Hans, On Nov 15, 2013, at 9:24 AM, Hans Wennborg <[email protected]> wrote:
> Author: hans > Date: Fri Nov 15 11:24:45 2013 > New Revision: 194827 > > URL: http://llvm.org/viewvc/llvm-project?rev=194827&view=rev > Log: > [-cxx-abi microsoft] Emit thunks for pointers to virtual member functions > > Instead of storing the vtable offset directly in the function pointer and > doing a branch to check for virtualness at each call site, the MS ABI > generates a thunk for calling the function at a specific vtable offset, > and puts that in the function pointer. > > This patch adds support for emitting such thunks. However, it doesn't support > pointers to virtual member functions that are variadic, have an incomplete > aggregate return type or parameter, or are overriding a function in a virtual > base class. > > Differential Revision: http://llvm-reviews.chandlerc.com/D2104 > > Added: > cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp > Modified: > cfe/trunk/include/clang/AST/Mangle.h > cfe/trunk/lib/AST/MicrosoftMangle.cpp > cfe/trunk/lib/CodeGen/CGVTables.cpp > cfe/trunk/lib/CodeGen/CodeGenFunction.h > cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp > > Modified: cfe/trunk/include/clang/AST/Mangle.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Mangle.h?rev=194827&r1=194826&r2=194827&view=diff > ============================================================================== > --- cfe/trunk/include/clang/AST/Mangle.h (original) > +++ cfe/trunk/include/clang/AST/Mangle.h Fri Nov 15 11:24:45 2013 > @@ -193,6 +193,9 @@ public: > ArrayRef<const CXXRecordDecl *> BasePath, > raw_ostream &Out) = 0; > > + virtual void mangleVirtualMemPtrThunk(const CXXMethodDecl *MD, > + int OffsetInVFTable, raw_ostream &) > = 0; > + > static bool classof(const MangleContext *C) { > return C->getKind() == MK_Microsoft; > } > > Modified: cfe/trunk/lib/AST/MicrosoftMangle.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/MicrosoftMangle.cpp?rev=194827&r1=194826&r2=194827&view=diff > ============================================================================== > --- cfe/trunk/lib/AST/MicrosoftMangle.cpp (original) > +++ cfe/trunk/lib/AST/MicrosoftMangle.cpp Fri Nov 15 11:24:45 2013 > @@ -181,6 +181,8 @@ public: > : MicrosoftMangleContext(Context, Diags) {} > virtual bool shouldMangleCXXName(const NamedDecl *D); > virtual void mangleCXXName(const NamedDecl *D, raw_ostream &Out); > + virtual void mangleVirtualMemPtrThunk(const CXXMethodDecl *MD, > + int OffsetInVFTable, raw_ostream &); > virtual void mangleThunk(const CXXMethodDecl *MD, > const ThunkInfo &Thunk, > raw_ostream &); > @@ -1921,6 +1923,19 @@ static void mangleThunkThisAdjustment(co > } > } > > +void MicrosoftMangleContextImpl::mangleVirtualMemPtrThunk( > + const CXXMethodDecl *MD, int OffsetInVFTable, raw_ostream &Out) { > + bool Is64Bit = getASTContext().getTargetInfo().getPointerWidth(0) == 64; > + > + MicrosoftCXXNameMangler Mangler(*this, Out); > + Mangler.getStream() << "\01??_9"; > + Mangler.mangleName(MD->getParent()); > + Mangler.getStream() << "$B"; > + Mangler.mangleNumber(OffsetInVFTable); > + Mangler.getStream() << "A"; > + Mangler.getStream() << (Is64Bit ? "A" : "E"); > +} > + > void MicrosoftMangleContextImpl::mangleThunk(const CXXMethodDecl *MD, > const ThunkInfo &Thunk, > raw_ostream &Out) { > > Modified: cfe/trunk/lib/CodeGen/CGVTables.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGVTables.cpp?rev=194827&r1=194826&r2=194827&view=diff > ============================================================================== > --- cfe/trunk/lib/CodeGen/CGVTables.cpp (original) > +++ cfe/trunk/lib/CodeGen/CGVTables.cpp Fri Nov 15 11:24:45 2013 > @@ -238,92 +238,99 @@ void CodeGenFunction::GenerateVarArgsThu > } > } > > -void CodeGenFunction::GenerateThunk(llvm::Function *Fn, > - const CGFunctionInfo &FnInfo, > - GlobalDecl GD, const ThunkInfo &Thunk) { > +void CodeGenFunction::StartThunk(llvm::Function *Fn, GlobalDecl GD, > + const CGFunctionInfo &FnInfo) { > + assert(!CurGD.getDecl() && "CurGD was already set!"); > + CurGD = GD; > + > + // Build FunctionArgs. > const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl()); > - const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>(); > QualType ThisType = MD->getThisType(getContext()); > + const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>(); > QualType ResultType = > CGM.getCXXABI().HasThisReturn(GD) ? ThisType : FPT->getResultType(); > - > FunctionArgList FunctionArgs; > > - // FIXME: It would be nice if more of this code could be shared with > - // CodeGenFunction::GenerateCode. > - > // Create the implicit 'this' parameter declaration. > - CurGD = GD; > CGM.getCXXABI().BuildInstanceFunctionParams(*this, ResultType, > FunctionArgs); > > // Add the rest of the parameters. > for (FunctionDecl::param_const_iterator I = MD->param_begin(), > - E = MD->param_end(); I != E; ++I) { > - ParmVarDecl *Param = *I; > - > - FunctionArgs.push_back(Param); > - } > + E = MD->param_end(); > + I != E; ++I) > + FunctionArgs.push_back(*I); > > + // Start defining the function. > StartFunction(GlobalDecl(), ResultType, Fn, FnInfo, FunctionArgs, > SourceLocation()); > > + // Since we didn't pass a GlobalDecl to StartFunction, do this ourselves. > CGM.getCXXABI().EmitInstanceFunctionProlog(*this); > CXXThisValue = CXXABIThisValue; > +} > > - // Adjust the 'this' pointer if necessary. > - llvm::Value *AdjustedThisPtr = > - CGM.getCXXABI().performThisAdjustment(*this, LoadCXXThis(), > Thunk.This); > +void CodeGenFunction::EmitCallAndReturnForThunk(GlobalDecl GD, > + llvm::Value *Callee, > + const ThunkInfo *Thunk) { > + assert(isa<CXXMethodDecl>(CurGD.getDecl()) && > + "Please use a new CGF for this thunk"); > + const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl()); > > + // Adjust the 'this' pointer if necessary > + llvm::Value *AdjustedThisPtr = Thunk ? > CGM.getCXXABI().performThisAdjustment( > + *this, LoadCXXThis(), > Thunk->This) > + : LoadCXXThis(); > + > + // Start building CallArgs. > CallArgList CallArgs; > - > - // Add our adjusted 'this' pointer. > + QualType ThisType = MD->getThisType(getContext()); > CallArgs.add(RValue::get(AdjustedThisPtr), ThisType); > > if (isa<CXXDestructorDecl>(MD)) > CGM.getCXXABI().adjustCallArgsForDestructorThunk(*this, GD, CallArgs); > > - // Add the rest of the parameters. > + // Add the rest of the arguments. > for (FunctionDecl::param_const_iterator I = MD->param_begin(), > - E = MD->param_end(); I != E; ++I) { > - ParmVarDecl *param = *I; > - EmitDelegateCallArg(CallArgs, param, param->getLocStart()); > - } > + E = MD->param_end(); I != E; ++I) > + EmitDelegateCallArg(CallArgs, *I, (*I)->getLocStart()); > > - // Get our callee. > - llvm::Type *Ty = > - > CGM.getTypes().GetFunctionType(CGM.getTypes().arrangeGlobalDeclaration(GD)); > - llvm::Value *Callee = CGM.GetAddrOfFunction(GD, Ty, /*ForVTable=*/true); > + const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>(); > > #ifndef NDEBUG > const CGFunctionInfo &CallFnInfo = > CGM.getTypes().arrangeCXXMethodCall(CallArgs, FPT, > RequiredArgs::forPrototypePlus(FPT, > 1)); > - assert(CallFnInfo.getRegParm() == FnInfo.getRegParm() && > - CallFnInfo.isNoReturn() == FnInfo.isNoReturn() && > - CallFnInfo.getCallingConvention() == FnInfo.getCallingConvention()); > + assert(CallFnInfo.getRegParm() == CurFnInfo->getRegParm() && > + CallFnInfo.isNoReturn() == CurFnInfo->isNoReturn() && > + CallFnInfo.getCallingConvention() == > CurFnInfo->getCallingConvention()); > assert(isa<CXXDestructorDecl>(MD) || // ignore dtor return types > similar(CallFnInfo.getReturnInfo(), CallFnInfo.getReturnType(), > - FnInfo.getReturnInfo(), FnInfo.getReturnType())); > - assert(CallFnInfo.arg_size() == FnInfo.arg_size()); > - for (unsigned i = 0, e = FnInfo.arg_size(); i != e; ++i) > + CurFnInfo->getReturnInfo(), CurFnInfo->getReturnType())); > + assert(CallFnInfo.arg_size() == CurFnInfo->arg_size()); > + for (unsigned i = 0, e = CurFnInfo->arg_size(); i != e; ++i) > assert(similar(CallFnInfo.arg_begin()[i].info, > CallFnInfo.arg_begin()[i].type, > - FnInfo.arg_begin()[i].info, FnInfo.arg_begin()[i].type)); > + CurFnInfo->arg_begin()[i].info, > + CurFnInfo->arg_begin()[i].type)); > #endif > - > + > // Determine whether we have a return value slot to use. > + QualType ResultType = > + CGM.getCXXABI().HasThisReturn(GD) ? ThisType : FPT->getResultType(); > ReturnValueSlot Slot; > if (!ResultType->isVoidType() && > - FnInfo.getReturnInfo().getKind() == ABIArgInfo::Indirect && > + CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect && > !hasScalarEvaluationKind(CurFnInfo->getReturnType())) > Slot = ReturnValueSlot(ReturnValue, ResultType.isVolatileQualified()); > > // Now emit our call. > - RValue RV = EmitCall(FnInfo, Callee, Slot, CallArgs, MD); > + RValue RV = EmitCall(*CurFnInfo, Callee, Slot, CallArgs, MD); > > - if (!Thunk.Return.isEmpty()) > - RV = PerformReturnAdjustment(*this, ResultType, RV, Thunk); > + // Consider return adjustment if we have ThunkInfo. > + if (Thunk && !Thunk->Return.isEmpty()) > + RV = PerformReturnAdjustment(*this, ResultType, RV, *Thunk); > > + // Emit return. > if (!ResultType->isVoidType() && Slot.isNull()) > CGM.getCXXABI().EmitReturnFromThunk(*this, RV, ResultType); > > @@ -331,11 +338,26 @@ void CodeGenFunction::GenerateThunk(llvm > AutoreleaseResult = false; > > FinishFunction(); > +} > + > +void CodeGenFunction::GenerateThunk(llvm::Function *Fn, > + const CGFunctionInfo &FnInfo, > + GlobalDecl GD, const ThunkInfo &Thunk) { > + StartThunk(Fn, GD, FnInfo); > + > + // Get our callee. > + llvm::Type *Ty = > + > CGM.getTypes().GetFunctionType(CGM.getTypes().arrangeGlobalDeclaration(GD)); > + llvm::Value *Callee = CGM.GetAddrOfFunction(GD, Ty, /*ForVTable=*/true); > + > + // Make the call and return the result. > + EmitCallAndReturnForThunk(GD, Callee, &Thunk); > > // Set the right linkage. > CGM.setFunctionLinkage(GD, Fn); > > // Set the right visibility. > + const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl()); > setThunkVisibility(CGM, MD, Thunk, Fn); > } > > > Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=194827&r1=194826&r2=194827&view=diff > ============================================================================== > --- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original) > +++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Fri Nov 15 11:24:45 2013 > @@ -1153,6 +1153,11 @@ public: > /// legal to call this function even if there is no current insertion point. > void FinishFunction(SourceLocation EndLoc=SourceLocation()); > > + void StartThunk(llvm::Function *Fn, GlobalDecl GD, const CGFunctionInfo > &FnInfo); > + > + void EmitCallAndReturnForThunk(GlobalDecl GD, llvm::Value *Callee, > + const ThunkInfo *Thunk); > + > /// GenerateThunk - Generate a thunk for the given method. > void GenerateThunk(llvm::Function *Fn, const CGFunctionInfo &FnInfo, > GlobalDecl GD, const ThunkInfo &Thunk); > > Modified: cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp?rev=194827&r1=194826&r2=194827&view=diff > ============================================================================== > --- cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp (original) > +++ cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp Fri Nov 15 11:24:45 2013 > @@ -311,6 +311,10 @@ private: > /// \brief Caching wrapper around VBTableBuilder::enumerateVBTables(). > const VBTableVector &EnumerateVBTables(const CXXRecordDecl *RD); > > + /// \brief Generate a thunk for calling a virtual member function MD. > + llvm::Function *EmitVirtualMemPtrThunk(const CXXMethodDecl *MD, > + StringRef ThunkName); > + > public: > virtual llvm::Type *ConvertMemberPointerType(const MemberPointerType *MPT); > > @@ -970,6 +974,43 @@ MicrosoftCXXABI::EnumerateVBTables(const > return VBTables; > } > > +llvm::Function * > +MicrosoftCXXABI::EmitVirtualMemPtrThunk(const CXXMethodDecl *MD, > + StringRef ThunkName) { > + // If the thunk has been generated previously, just return it. > + if (llvm::GlobalValue *GV = CGM.getModule().getNamedValue(ThunkName)) > + return cast<llvm::Function>(GV); > + > + // Create the llvm::Function. > + const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeGlobalDeclaration(MD); > + llvm::FunctionType *ThunkTy = CGM.getTypes().GetFunctionType(FnInfo); > + llvm::Function *ThunkFn = > + llvm::Function::Create(ThunkTy, llvm::Function::ExternalLinkage, > + ThunkName.str(), &CGM.getModule()); > + assert(ThunkFn->getName() == ThunkName && "name was uniqued!"); > + > + LinkageInfo LV = MD->getLinkageAndVisibility(); LV is unused. Did you intend to use it in the call to setLinkage below? Mark > + ThunkFn->setLinkage(MD->isExternallyVisible() > + ? llvm::GlobalValue::LinkOnceODRLinkage > + : llvm::GlobalValue::InternalLinkage); > + > + CGM.SetLLVMFunctionAttributes(MD, FnInfo, ThunkFn); > + CGM.SetLLVMFunctionAttributesForDefinition(MD, ThunkFn); > + > + // Start codegen. > + CodeGenFunction CGF(CGM); > + CGF.StartThunk(ThunkFn, MD, FnInfo); > + > + // Get to the Callee. > + llvm::Value *This = CGF.LoadCXXThis(); > + llvm::Value *Callee = getVirtualFunctionPointer(CGF, MD, This, ThunkTy); > + > + // Make the call and return the result. > + CGF.EmitCallAndReturnForThunk(MD, Callee, 0); > + > + return ThunkFn; > +} > + > void MicrosoftCXXABI::emitVirtualInheritanceTables(const CXXRecordDecl *RD) { > const VBTableVector &VBTables = EnumerateVBTables(RD); > llvm::GlobalVariable::LinkageTypes Linkage = CGM.getVTableLinkage(RD); > @@ -1370,12 +1411,7 @@ MicrosoftCXXABI::BuildMemberPointer(cons > CodeGenTypes &Types = CGM.getTypes(); > > llvm::Constant *FirstField; > - if (MD->isVirtual()) { > - // FIXME: We have to instantiate a thunk that loads the vftable and > jumps to > - // the right offset. > - CGM.ErrorUnsupported(MD, "pointer to virtual member function"); > - FirstField = llvm::Constant::getNullValue(CGM.VoidPtrTy); > - } else { > + if (!MD->isVirtual()) { > const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>(); > llvm::Type *Ty; > // Check whether the function has a computable LLVM signature. > @@ -1389,6 +1425,33 @@ MicrosoftCXXABI::BuildMemberPointer(cons > } > FirstField = CGM.GetAddrOfFunction(MD, Ty); > FirstField = llvm::ConstantExpr::getBitCast(FirstField, CGM.VoidPtrTy); > + } 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<FunctionType>())) { > + CGM.ErrorUnsupported(MD, "pointer to virtual member function with " > + "incomplete return or parameter type"); > + 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"); > + FirstField = llvm::Constant::getNullValue(CGM.VoidPtrTy); > + } else { > + SmallString<256> ThunkName; > + int OffsetInVFTable = > + ML.Index * > + > getContext().getTypeSizeInChars(getContext().VoidPtrTy).getQuantity(); > + llvm::raw_svector_ostream Out(ThunkName); > + getMangleContext().mangleVirtualMemPtrThunk(MD, OffsetInVFTable, Out); > + Out.flush(); > + > + llvm::Function *Thunk = EmitVirtualMemPtrThunk(MD, ThunkName.str()); > + FirstField = llvm::ConstantExpr::getBitCast(Thunk, CGM.VoidPtrTy); > + } > } > > // The rest of the fields are common with data member pointers. > @@ -1875,4 +1938,3 @@ MicrosoftCXXABI::EmitLoadOfMemberFunctio > CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) { > return new MicrosoftCXXABI(CGM); > } > - > > Added: 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=194827&view=auto > ============================================================================== > --- cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp > (added) > +++ cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp Fri > Nov 15 11:24:45 2013 > @@ -0,0 +1,108 @@ > +// RUN: %clang_cc1 -fno-rtti -emit-llvm -cxx-abi microsoft > -triple=i386-pc-win32 %s -o - | FileCheck %s --check-prefix=CHECK32 > +// RUN: %clang_cc1 -fno-rtti -emit-llvm -cxx-abi microsoft > -triple=x86_64-pc-win32 %s -o - | FileCheck %s --check-prefix=CHECK64 > + > +struct S { > + int x, y, z; > +}; > + > +struct C { > + virtual void foo(); > + virtual int bar(int, double); > + virtual S baz(int); > +}; > + > +namespace { > +struct D { > + virtual void foo(); > +}; > +} > + > +void f() { > + void (C::*ptr)(); > + ptr = &C::foo; > + ptr = &C::foo; // Don't crash trying to define the thunk twice :) > + > + int (C::*ptr2)(int, double); > + ptr2 = &C::bar; > + > + S (C::*ptr3)(int); > + ptr3 = &C::baz; > + > + void (D::*ptr4)(); > + ptr4 = &D::foo; > + > +// 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.S*, %struct.C*, 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: } > +// > +// 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.S*, %struct.C*, 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: } > +} > + > + > +// 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) 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: } > +// > +// 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: 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) 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: 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: ret i32 [[CALL]] > +// 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: ret void > +// CHECK32: } > +// > +// CHECK64-LABEL: define linkonce_odr void @"\01??_9C@@$BBA@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: 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) 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: 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: ret void > +// CHECK64: } > > > _______________________________________________ > cfe-commits mailing list > [email protected] > http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits _______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
