summaryrefslogtreecommitdiffstats
path: root/clang/lib/CodeGen
diff options
context:
space:
mode:
authorHans Wennborg <hans@hanshq.net>2013-11-15 17:24:45 +0000
committerHans Wennborg <hans@hanshq.net>2013-11-15 17:24:45 +0000
commit88497d61573c35719119abf62fedffb181882445 (patch)
treebc35c1ffe63868c5bce0dbda0974de3717c1fb18 /clang/lib/CodeGen
parent6747c640689b3f1cb44e29c386ba7fa9ab6adfb6 (diff)
downloadbcm5719-llvm-88497d61573c35719119abf62fedffb181882445.tar.gz
bcm5719-llvm-88497d61573c35719119abf62fedffb181882445.zip
[-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
Diffstat (limited to 'clang/lib/CodeGen')
-rw-r--r--clang/lib/CodeGen/CGVTables.cpp102
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.h5
-rw-r--r--clang/lib/CodeGen/MicrosoftCXXABI.cpp76
3 files changed, 136 insertions, 47 deletions
diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index 5d657b064a4..42e22f0d74b 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -238,92 +238,99 @@ void CodeGenFunction::GenerateVarArgsThunk(
}
}
-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::Function *Fn,
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);
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 3e623eab485..db291e3b1dd 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -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);
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 86174826bd8..a2469f0906f 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -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 CXXRecordDecl *RD) {
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 @@ MicrosoftCXXABI::BuildMemberPointer(const CXXRecordDecl *RD,
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(const CXXRecordDecl *RD,
}
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::EmitLoadOfMemberFunctionPointer(CodeGenFunction &CGF,
CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) {
return new MicrosoftCXXABI(CGM);
}
-
OpenPOWER on IntegriCloud