From: Anders Carlsson Date: Sun, 8 May 2011 20:32:23 +0000 (+0000) Subject: Move code to emit the callee of an CXXOperatorCallExpr out into a separate function... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a2447e0d1e28669cd637204a871f15b1215277fd;p=clang Move code to emit the callee of an CXXOperatorCallExpr out into a separate function in CGClass.cpp git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@131075 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index cb41008713..4172948b60 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -1521,3 +1521,136 @@ llvm::Value *CodeGenFunction::GetVTablePtr(llvm::Value *This, llvm::Value *VTablePtrSrc = Builder.CreateBitCast(This, Ty->getPointerTo()); return Builder.CreateLoad(VTablePtrSrc, "vtable"); } + +static const CXXRecordDecl *getMostDerivedClassDecl(const Expr *Base) { + const Expr *E = Base; + + while (true) { + E = E->IgnoreParens(); + if (const CastExpr *CE = dyn_cast(E)) { + if (CE->getCastKind() == CK_DerivedToBase || + CE->getCastKind() == CK_UncheckedDerivedToBase || + CE->getCastKind() == CK_NoOp) { + E = CE->getSubExpr(); + continue; + } + } + + break; + } + + QualType DerivedType = E->getType(); + if (const PointerType *PTy = DerivedType->getAs()) + DerivedType = PTy->getPointeeType(); + + return cast(DerivedType->castAs()->getDecl()); +} + +// FIXME: Ideally Expr::IgnoreParenNoopCasts should do this, but it doesn't do +// quite what we want. +static const Expr *skipNoOpCastsAndParens(const Expr *E) { + while (true) { + if (const ParenExpr *PE = dyn_cast(E)) { + E = PE->getSubExpr(); + continue; + } + + if (const CastExpr *CE = dyn_cast(E)) { + if (CE->getCastKind() == CK_NoOp) { + E = CE->getSubExpr(); + continue; + } + } + if (const UnaryOperator *UO = dyn_cast(E)) { + if (UO->getOpcode() == UO_Extension) { + E = UO->getSubExpr(); + continue; + } + } + return E; + } +} + +/// canDevirtualizeMemberFunctionCall - Checks whether the given virtual member +/// function call on the given expr can be devirtualized. +/// expr can be devirtualized. +static bool canDevirtualizeMemberFunctionCall(const Expr *Base, + const CXXMethodDecl *MD) { + // If the most derived class is marked final, we know that no subclass can + // override this member function and so we can devirtualize it. For example: + // + // struct A { virtual void f(); } + // struct B final : A { }; + // + // void f(B *b) { + // b->f(); + // } + // + const CXXRecordDecl *MostDerivedClassDecl = getMostDerivedClassDecl(Base); + if (MostDerivedClassDecl->hasAttr()) + return true; + + // If the member function is marked 'final', we know that it can't be + // overridden and can therefore devirtualize it. + if (MD->hasAttr()) + return true; + + // Similarly, if the class itself is marked 'final' it can't be overridden + // and we can therefore devirtualize the member function call. + if (MD->getParent()->hasAttr()) + return true; + + Base = skipNoOpCastsAndParens(Base); + if (const DeclRefExpr *DRE = dyn_cast(Base)) { + if (const VarDecl *VD = dyn_cast(DRE->getDecl())) { + // This is a record decl. We know the type and can devirtualize it. + return VD->getType()->isRecordType(); + } + + return false; + } + + // We can always devirtualize calls on temporary object expressions. + if (isa(Base)) + return true; + + // And calls on bound temporaries. + if (isa(Base)) + return true; + + // Check if this is a call expr that returns a record type. + if (const CallExpr *CE = dyn_cast(Base)) + return CE->getCallReturnType()->isRecordType(); + + // We can't devirtualize the call. + return false; +} + +static bool UseVirtualCall(ASTContext &Context, + const CXXOperatorCallExpr *CE, + const CXXMethodDecl *MD) { + if (!MD->isVirtual()) + return false; + + // When building with -fapple-kext, all calls must go through the vtable since + // the kernel linker can do runtime patching of vtables. + if (Context.getLangOptions().AppleKext) + return true; + + return !canDevirtualizeMemberFunctionCall(CE->getArg(0), MD); +} + +llvm::Value * +CodeGenFunction::EmitCXXOperatorMemberCallee(const CXXOperatorCallExpr *E, + const CXXMethodDecl *MD, + llvm::Value *This) { + const FunctionProtoType *FPT = MD->getType()->castAs(); + const llvm::Type *Ty = + CGM.getTypes().GetFunctionType(CGM.getTypes().getFunctionInfo(MD), + FPT->isVariadic()); + + if (UseVirtualCall(getContext(), E, MD)) + return BuildVirtualCall(MD, This, Ty); + + return CGM.GetAddrOfFunction(MD, Ty); +} diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index f6103b6c7a..6ee9bc1033 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -345,18 +345,7 @@ CodeGenFunction::EmitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *E, } } - const FunctionProtoType *FPT = MD->getType()->getAs(); - const llvm::Type *Ty = - CGM.getTypes().GetFunctionType(CGM.getTypes().getFunctionInfo(MD), - FPT->isVariadic()); - llvm::Value *Callee; - if (MD->isVirtual() && - !canDevirtualizeMemberFunctionCalls(getContext(), - E->getArg(0), MD)) - Callee = BuildVirtualCall(MD, This, Ty); - else - Callee = CGM.GetAddrOfFunction(MD, Ty); - + llvm::Value *Callee = EmitCXXOperatorMemberCallee(E, MD, This); return EmitCXXMemberCall(MD, Callee, ReturnValue, This, /*VTT=*/0, E->arg_begin() + 1, E->arg_end()); } diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 0745647b5e..34532de80c 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1918,6 +1918,9 @@ public: RValue EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E, ReturnValueSlot ReturnValue); + llvm::Value *EmitCXXOperatorMemberCallee(const CXXOperatorCallExpr *E, + const CXXMethodDecl *MD, + llvm::Value *This); RValue EmitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *E, const CXXMethodDecl *MD, ReturnValueSlot ReturnValue);