[-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
llvm-svn: 194827
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 8617482..a2469f0 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -311,6 +311,10 @@
/// \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 @@
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();
+ 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 @@
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 @@
}
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 @@
CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) {
return new MicrosoftCXXABI(CGM);
}
-