Hi rnk, timurrrr,
This patch adds support for emitting thunks for pointers to virtual member
functions.
Unfortunately it doesn't work for variadic functions or functions where the
return type or one of the paramters is incomplete, but I figure we should
support this broad case first and then figure out what to do for the others.
This is my first step into ABI land, so comments very welcome :)
http://llvm-reviews.chandlerc.com/D2104
Files:
include/clang/AST/Mangle.h
lib/AST/ItaniumMangle.cpp
lib/AST/MicrosoftMangle.cpp
lib/CodeGen/CGVTables.cpp
lib/CodeGen/CodeGenFunction.h
lib/CodeGen/MicrosoftCXXABI.cpp
test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp
Index: include/clang/AST/Mangle.h
===================================================================
--- include/clang/AST/Mangle.h
+++ include/clang/AST/Mangle.h
@@ -114,6 +114,8 @@
// FIXME: consider replacing raw_ostream & with something like SmallString &.
void mangleName(const NamedDecl *D, raw_ostream &);
virtual void mangleCXXName(const NamedDecl *D, raw_ostream &) = 0;
+ virtual void mangleVirtualMemPtrThunk(const CXXMethodDecl *MD,
+ int VTableIndex, raw_ostream &) = 0;
virtual void mangleThunk(const CXXMethodDecl *MD,
const ThunkInfo &Thunk,
raw_ostream &) = 0;
Index: lib/AST/ItaniumMangle.cpp
===================================================================
--- lib/AST/ItaniumMangle.cpp
+++ lib/AST/ItaniumMangle.cpp
@@ -127,6 +127,10 @@
bool shouldMangleCXXName(const NamedDecl *D);
void mangleCXXName(const NamedDecl *D, raw_ostream &);
+ void mangleVirtualMemPtrThunk(const CXXMethodDecl *MD, int VTableIndex,
+ raw_ostream &) {
+ llvm_unreachable("Cannot mangle these for Itanium.");
+ }
void mangleThunk(const CXXMethodDecl *MD,
const ThunkInfo &Thunk,
raw_ostream &);
Index: lib/AST/MicrosoftMangle.cpp
===================================================================
--- lib/AST/MicrosoftMangle.cpp
+++ lib/AST/MicrosoftMangle.cpp
@@ -181,6 +181,8 @@
: 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 VTableIndex, raw_ostream &);
virtual void mangleThunk(const CXXMethodDecl *MD,
const ThunkInfo &Thunk,
raw_ostream &);
@@ -1880,6 +1882,19 @@
}
}
+void MicrosoftMangleContextImpl::mangleVirtualMemPtrThunk(
+ const CXXMethodDecl *MD, int VTableIndex, 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(VTableIndex * (Is64Bit ? 8 : 4));
+ Mangler.getStream() << "A";
+ Mangler.getStream() << (Is64Bit ? "A" : "E");
+}
+
void MicrosoftMangleContextImpl::mangleThunk(const CXXMethodDecl *MD,
const ThunkInfo &Thunk,
raw_ostream &Out) {
Index: lib/CodeGen/CGVTables.cpp
===================================================================
--- lib/CodeGen/CGVTables.cpp
+++ lib/CodeGen/CGVTables.cpp
@@ -339,6 +339,93 @@
setThunkVisibility(CGM, MD, Thunk, Fn);
}
+llvm::Function *
+CodeGenFunction::GenerateVirtualMemPtrThunk(const CXXMethodDecl *MD,
+ int VTableIndex) {
+ SmallString<256> ThunkName;
+ llvm::raw_svector_ostream Out(ThunkName);
+ CGM.getCXXABI().getMangleContext().mangleVirtualMemPtrThunk(MD, VTableIndex,
+ Out);
+ Out.flush();
+
+ // 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!");
+ CGM.SetLLVMFunctionAttributesForDefinition(MD, ThunkFn);
+ ThunkFn->setLinkage(llvm::GlobalValue::LinkOnceODRLinkage);
+ CGM.SetLLVMFunctionAttributes(MD, FnInfo, ThunkFn);
+ CGM.SetLLVMFunctionAttributesForDefinition(MD, ThunkFn);
+
+ CurGD = MD;
+
+ // Build FunctionArgs.
+ FunctionArgList FunctionArgs;
+ const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>();
+ QualType ResultType = FPT->getResultType();
+ CGM.getCXXABI().BuildInstanceFunctionParams(*this, ResultType, FunctionArgs);
+ for (FunctionDecl::param_const_iterator I = MD->param_begin(),
+ E = MD->param_end();
+ I != E; ++I)
+ FunctionArgs.push_back(*I);
+
+ // Start defining the function.
+ StartFunction(GlobalDecl(), ResultType, ThunkFn, FnInfo, FunctionArgs,
+ SourceLocation());
+ CGM.getCXXABI().EmitInstanceFunctionProlog(*this);
+
+ // Get the 'this' pointer.
+ CXXThisValue = CXXABIThisValue;
+ llvm::Value *This = LoadCXXThis();
+
+ // Get to the vtable from 'this'.
+ llvm::Value *VTable =
+ GetVTablePtr(This, ThunkTy->getPointerTo()->getPointerTo());
+
+ // Load function pointer from vtable.
+ llvm::Value *VFuncPtr =
+ Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfuncptr");
+ llvm::Value *Callee = Builder.CreateLoad(VFuncPtr, "callee");
+
+ // Build call arguments.
+ CallArgList CallArgs;
+ QualType ThisType = MD->getThisType(getContext());
+ CallArgs.add(RValue::get(This), ThisType);
+ for (FunctionDecl::param_const_iterator I = MD->param_begin(),
+ E = MD->param_end();
+ I != E; ++I)
+ EmitDelegateCallArg(CallArgs, *I, (*I)->getLocStart());
+
+ // Determine whether we have a return value slot to use.
+ ReturnValueSlot Slot;
+ if (!ResultType->isVoidType() &&
+ FnInfo.getReturnInfo().getKind() == ABIArgInfo::Indirect &&
+ !hasScalarEvaluationKind(CurFnInfo->getReturnType()))
+ Slot = ReturnValueSlot(ReturnValue, ResultType.isVolatileQualified());
+
+ // Emit the call.
+ RValue RV = EmitCall(FnInfo, Callee, Slot, CallArgs, MD);
+
+ // Note: we don't do any return adjustment. The callee should be doing that if
+ // necessary.
+
+ // Emit return.
+ if (!ResultType->isVoidType() && Slot.isNull())
+ CGM.getCXXABI().EmitReturnFromThunk(*this, RV, ResultType);
+
+ FinishFunction();
+ CGM.setFunctionLinkage(MD, ThunkFn); // XXX: Do we need this?
+
+ return ThunkFn;
+}
+
void CodeGenVTables::emitThunk(GlobalDecl GD, const ThunkInfo &Thunk,
bool ForVTable) {
const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeGlobalDeclaration(GD);
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -1157,6 +1157,11 @@
void GenerateThunk(llvm::Function *Fn, const CGFunctionInfo &FnInfo,
GlobalDecl GD, const ThunkInfo &Thunk);
+ /// \brief Generate a thunk for calling virtual member function MD, which
+ /// is at position VTableIndex in the vtable.
+ llvm::Function *GenerateVirtualMemPtrThunk(const CXXMethodDecl *MD,
+ int VTableIndex);
+
void GenerateVarArgsThunk(llvm::Function *Fn, const CGFunctionInfo &FnInfo,
GlobalDecl GD, const ThunkInfo &Thunk);
Index: lib/CodeGen/MicrosoftCXXABI.cpp
===================================================================
--- lib/CodeGen/MicrosoftCXXABI.cpp
+++ lib/CodeGen/MicrosoftCXXABI.cpp
@@ -1349,11 +1349,20 @@
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");
+ if (MD->isVirtual() && MD->isVariadic()) {
+ CGM.ErrorUnsupported(MD, "pointer to variadic virtual member function");
FirstField = llvm::Constant::getNullValue(CGM.VoidPtrTy);
+ } else if (MD->isVirtual() && !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 (MD->isVirtual()) {
+ int VTableIndex =
+ CGM.getMicrosoftVTableContext().getMethodVFTableLocation(MD).Index;
+ CodeGenFunction CGF(CGM);
+ llvm::Function *Thunk = CGF.GenerateVirtualMemPtrThunk(MD, VTableIndex);
+ FirstField = llvm::ConstantExpr::getBitCast(Thunk, CGM.VoidPtrTy);
} else {
const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>();
llvm::Type *Ty;
@@ -1854,4 +1863,3 @@
CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) {
return new MicrosoftCXXABI(CGM);
}
-
Index: test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp
@@ -0,0 +1,78 @@
+// 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);
+};
+
+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;
+
+// CHECK32: 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: }
+
+// CHECK64: 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: }
+}
+
+// Thunk for calling the 1st virtual function in C with no parameters.
+// CHECK32: define x86_thiscallcc void @"\01??_9C@@$BA@AE"(%struct.C* %this) unnamed_addr {{.*}} {
+// CHECK32: %vfuncptr = getelementptr inbounds void (%struct.C*)** %vtable, i64 0
+// CHECK32: %callee = load void (%struct.C*)** %vfuncptr
+// CHECK32: call x86_thiscallcc void %callee(%struct.C* %this{{.*}})
+// CHECK32: ret void
+// CHECK32: }
+// CHECK64: define void @"\01??_9C@@$BA@AA"(%struct.C* %this) unnamed_addr {{.*}} {
+// CHECK64: %vfuncptr = getelementptr inbounds void (%struct.C*)** %vtable, i64 0
+// CHECK64: %callee = load void (%struct.C*)** %vfuncptr
+// CHECK64: call void %callee(%struct.C* %this{{.*}})
+// CHECK64: ret void
+// CHECK64: }
+
+// Thunk for calling the 2nd virtual function in C, taking int and double as parameters, returning int.
+// CHECK32: define x86_thiscallcc i32 @"\01??_9C@@$B3AE"(%struct.C* %this, i32, double) unnamed_addr {{.*}} {
+// CHECK32: %vfuncptr = getelementptr inbounds i32 (%struct.C*, i32, double)** %vtable, i64 1
+// CHECK32: %callee = load i32 (%struct.C*, i32, double)** %vfuncptr
+// CHECK32: %call = call x86_thiscallcc i32 %callee(%struct.C* %this{{.*}}, i32 %{{.*}}, double %{{.*}})
+// CHECK32: ret i32 %call
+// CHECK32: }
+// CHECK64: define i32 @"\01??_9C@@$B7AA"(%struct.C* %this, i32, double) unnamed_addr {{.*}} {
+// CHECK64: %vfuncptr = getelementptr inbounds i32 (%struct.C*, i32, double)** %vtable, i64 1
+// CHECK64: %callee = load i32 (%struct.C*, i32, double)** %vfuncptr
+// CHECK64: %call = call i32 %callee(%struct.C* %this{{.*}}, i32 %{{.*}}, double %{{.*}})
+// CHECK64: ret i32 %call
+// CHECK64: }
+
+// Thunk for calling the 3rd virtual function in C, taking an int parameter, returning a struct.
+// CHECK32: define x86_thiscallcc void @"\01??_9C@@$B7AE"(%struct.S* noalias sret %agg.result, %struct.C* %this, i32) unnamed_addr {{.*}} {
+// CHECK32: %vfuncptr = getelementptr inbounds void (%struct.S*, %struct.C*, i32)** %vtable, i64 2
+// CHECK32: %callee = load void (%struct.S*, %struct.C*, i32)** %vfuncptr
+// CHECK32: call x86_thiscallcc void %callee(%struct.S* sret %agg.result, %struct.C* %this{{.*}}, i32 %{{.*}})
+// CHECK32: ret void
+// CHECK32: }
+// CHECK64: define void @"\01??_9C@@$BBA@AA"(%struct.S* noalias sret %agg.result, %struct.C* %this, i32) unnamed_addr {{.*}} {
+// CHECK64: %vfuncptr = getelementptr inbounds void (%struct.S*, %struct.C*, i32)** %vtable, i64 2
+// CHECK64: %callee = load void (%struct.S*, %struct.C*, i32)** %vfuncptr
+// CHECK64: call void %callee(%struct.S* sret %agg.result, %struct.C* %this{{.*}}, i32 %{{.*}})
+// CHECK64: ret void
+// CHECK64: }
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits