]> granicus.if.org Git - clang/commitdiff
Implement code generation of member function pointer calls. Fixes PR5121.
authorAnders Carlsson <andersca@mac.com>
Sat, 3 Oct 2009 19:43:08 +0000 (19:43 +0000)
committerAnders Carlsson <andersca@mac.com>
Sat, 3 Oct 2009 19:43:08 +0000 (19:43 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@83271 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGCXX.cpp
lib/CodeGen/CGCall.cpp
lib/CodeGen/CodeGenFunction.h
lib/CodeGen/CodeGenTypes.h
test/CodeGenCXX/member-function-pointers.cpp

index 1ea60eff56d0d00274dc810c8bb34c57b2fb9a5d..2d5c62e3e230218749ffb31fee72ba351868efea 100644 (file)
@@ -199,6 +199,9 @@ RValue CodeGenFunction::EmitCXXMemberCall(const CXXMethodDecl *MD,
 }
 
 RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE) {
+  if (isa<BinaryOperator>(CE->getCallee())) 
+    return EmitCXXMemberPointerCallExpr(CE);
+      
   const MemberExpr *ME = cast<MemberExpr>(CE->getCallee());
   const CXXMethodDecl *MD = cast<CXXMethodDecl>(ME->getMemberDecl());
 
@@ -241,6 +244,112 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE) {
                            CE->arg_begin(), CE->arg_end());
 }
 
+RValue
+CodeGenFunction::EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E) {
+  const BinaryOperator *BO = cast<BinaryOperator>(E->getCallee());
+  const DeclRefExpr *BaseExpr = cast<DeclRefExpr>(BO->getLHS());
+  const DeclRefExpr *MemFn = cast<DeclRefExpr>(BO->getRHS());
+  
+  const MemberPointerType *MPT = MemFn->getType()->getAs<MemberPointerType>();
+  const FunctionProtoType *FPT = 
+    MPT->getPointeeType()->getAs<FunctionProtoType>();
+  const CXXRecordDecl *RD = 
+    cast<CXXRecordDecl>(cast<RecordType>(MPT->getClass())->getDecl());
+
+  const llvm::FunctionType *FTy = 
+    CGM.getTypes().GetFunctionType(CGM.getTypes().getFunctionInfo(RD, FPT),
+                                   FPT->isVariadic());
+
+  const llvm::Type *Int8PtrTy = 
+    llvm::Type::getInt8Ty(VMContext)->getPointerTo();
+
+  // Get the member function pointer.
+  llvm::Value *MemFnPtr = 
+    CreateTempAlloca(ConvertType(MemFn->getType()), "mem.fn");
+  EmitAggExpr(MemFn, MemFnPtr, /*VolatileDest=*/false);
+
+  // Emit the 'this' pointer.
+  llvm::Value *This;
+  
+  if (BO->getOpcode() == BinaryOperator::PtrMemI)
+    This = EmitScalarExpr(BaseExpr);
+  else 
+    This = EmitLValue(BaseExpr).getAddress();
+  
+  // Adjust it.
+  llvm::Value *Adj = Builder.CreateStructGEP(MemFnPtr, 1);
+  Adj = Builder.CreateLoad(Adj, "mem.fn.adj");
+  
+  llvm::Value *Ptr = Builder.CreateBitCast(This, Int8PtrTy, "ptr");
+  Ptr = Builder.CreateGEP(Ptr, Adj, "adj");
+  
+  This = Builder.CreateBitCast(Ptr, This->getType(), "this");
+  
+  llvm::Value *FnPtr = Builder.CreateStructGEP(MemFnPtr, 0, "mem.fn.ptr");
+  
+  const llvm::Type *PtrDiffTy = ConvertType(getContext().getPointerDiffType());
+
+  llvm::Value *FnAsInt = Builder.CreateLoad(FnPtr, "fn");
+  
+  // If the LSB in the function pointer is 1, the function pointer points to
+  // a virtual function.
+  llvm::Value *IsVirtual 
+    = Builder.CreateAnd(FnAsInt, llvm::ConstantInt::get(PtrDiffTy, 1),
+                        "and");
+  
+  IsVirtual = Builder.CreateTrunc(IsVirtual,
+                                  llvm::Type::getInt1Ty(VMContext));
+  
+  llvm::BasicBlock *FnVirtual = createBasicBlock("fn.virtual");
+  llvm::BasicBlock *FnNonVirtual = createBasicBlock("fn.nonvirtual");
+  llvm::BasicBlock *FnEnd = createBasicBlock("fn.end");
+  
+  Builder.CreateCondBr(IsVirtual, FnVirtual, FnNonVirtual);
+  EmitBlock(FnVirtual);
+  
+  const llvm::Type *VTableTy = 
+    FTy->getPointerTo()->getPointerTo()->getPointerTo();
+
+  llvm::Value *VTable = Builder.CreateBitCast(This, VTableTy);
+  VTable = Builder.CreateLoad(VTable);
+  
+  VTable = Builder.CreateGEP(VTable, FnAsInt, "fn");
+  
+  // Since the function pointer is 1 plus the virtual table offset, we
+  // subtract 1 by using a GEP.
+  VTable = Builder.CreateConstGEP1_64(VTable, -1);
+  
+  llvm::Value *VirtualFn = Builder.CreateLoad(VTable, "virtualfn");
+  
+  EmitBranch(FnEnd);
+  EmitBlock(FnNonVirtual);
+  
+  // If the function is not virtual, just load the pointer.
+  llvm::Value *NonVirtualFn = Builder.CreateLoad(FnPtr, "fn");
+  NonVirtualFn = Builder.CreateIntToPtr(NonVirtualFn, FTy->getPointerTo());
+  
+  EmitBlock(FnEnd);
+
+  llvm::PHINode *Callee = Builder.CreatePHI(FTy->getPointerTo());
+  Callee->reserveOperandSpace(2);
+  Callee->addIncoming(VirtualFn, FnVirtual);
+  Callee->addIncoming(NonVirtualFn, FnNonVirtual);
+
+  CallArgList Args;
+
+  QualType ThisType = 
+    getContext().getPointerType(getContext().getTagDeclType(RD));
+
+  // Push the this ptr.
+  Args.push_back(std::make_pair(RValue::get(This), ThisType));
+  
+  // And the rest of the call args
+  EmitCallArgs(Args, FPT, E->arg_begin(), E->arg_end());
+  QualType ResultType = BO->getType()->getAs<FunctionType>()->getResultType();
+  return EmitCall(CGM.getTypes().getFunctionInfo(ResultType, Args),
+                  Callee, Args, 0);
+}
+
 RValue
 CodeGenFunction::EmitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *E,
                                                const CXXMethodDecl *MD) {
index 0b9b3fc2708708371f732ad869b9add9ed2c1853..2a1843eb6c6e980367508df67a3e49a047d2729a 100644 (file)
@@ -63,6 +63,21 @@ static unsigned getCallingConventionForDecl(const Decl *D) {
   return llvm::CallingConv::C;
 }
 
+const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const CXXRecordDecl *RD,
+                                                 const FunctionProtoType *FTP) {
+  llvm::SmallVector<QualType, 16> ArgTys;
+  
+  // Add the 'this' pointer.
+  ArgTys.push_back(Context.getPointerType(Context.getTagDeclType(RD)));
+  
+  for (unsigned i = 0, e = FTP->getNumArgs(); i != e; ++i)
+    ArgTys.push_back(FTP->getArgType(i));
+  
+  // FIXME: Set calling convention correctly, it needs to be associated with the
+  // type somehow.
+  return getFunctionInfo(FTP->getResultType(), ArgTys, 0);
+}
+
 const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const CXXMethodDecl *MD) {
   llvm::SmallVector<QualType, 16> ArgTys;
   // Add the 'this' pointer unless this is a static method.
index 09a20e22279beb68cdd0f2fe4e498e08839cabfa..34b3860a48f8d3428b0330598164b579be19bdfd 100644 (file)
@@ -864,10 +864,12 @@ public:
                            CallExpr::const_arg_iterator ArgBeg,
                            CallExpr::const_arg_iterator ArgEnd);
   RValue EmitCXXMemberCallExpr(const CXXMemberCallExpr *E);
+  RValue EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E);
 
   RValue EmitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *E,
                                        const CXXMethodDecl *MD);
 
+  
   RValue EmitBuiltinExpr(const FunctionDecl *FD,
                          unsigned BuiltinID, const CallExpr *E);
 
index 53df1068209d2043a66efeb04013bb48c167430a..ad71e0ad053ce417425229fd0eb68a5a4307f7fb 100644 (file)
@@ -181,7 +181,11 @@ public:
   const CGFunctionInfo &getFunctionInfo(const FunctionDecl *FD);
   const CGFunctionInfo &getFunctionInfo(const CXXMethodDecl *MD);
   const CGFunctionInfo &getFunctionInfo(const ObjCMethodDecl *MD);
-
+  
+  // getFunctionInfo - Get the function info for a member function.
+  const CGFunctionInfo &getFunctionInfo(const CXXRecordDecl *RD,
+                                        const FunctionProtoType *FTP);
+  
   /// getFunctionInfo - Get the function info for a function described by a
   /// return type and argument types. If the calling convention is not
   /// specified, the "C" calling convention will be used.
index 57e2e7f2d6c6b7750379d9716012ca35765bac1d..9727a9dabdad72f713c1408bb6fa22b00a87629f 100644 (file)
@@ -49,3 +49,8 @@ void f2() {
   // CHECK: store i64 0, i64* [[pa2adj]]
   void (A::*pa3)() = &A::vf;
 }
+
+void f3(A *a, A &ar) {
+  (a->*pa)();
+  (ar.*pa)();
+}