From ea01d7661751e062bb670cc1a0bdfee5789cb96f Mon Sep 17 00:00:00 2001 From: Rafael Espindola Date: Thu, 28 Jun 2012 14:28:57 +0000 Subject: [PATCH] Don't devirtualize calls when we don't have the correct type of the this pointer handy. It can be done, but we would have to build a derived-to-base cast during codegen to compute the correct this pointer. I will handle covariant returns next. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159350 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGExprCXX.cpp | 55 ++++++++++++------- ...irtualize-virtual-function-calls-final.cpp | 21 ++++++- 2 files changed, 56 insertions(+), 20 deletions(-) diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index a39e82408d..100ef02af1 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -142,6 +142,14 @@ static bool canDevirtualizeMemberFunctionCalls(ASTContext &Context, return false; } +static CXXRecordDecl *getCXXRecord(const Expr *E) { + QualType T = E->getType(); + if (const PointerType *PTy = T->getAs()) + T = PTy->getPointeeType(); + const RecordType *Ty = T->castAs(); + return cast(Ty->getDecl()); +} + // Note: This function also emit constructor calls to support a MSVC // extensions allowing explicit constructor function call. RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE, @@ -174,18 +182,33 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE, // Compute the object pointer. const Expr *Base = ME->getBase(); bool CanUseVirtualCall = MD->isVirtual() && !ME->hasQualifier(); - bool Devirtualize = CanUseVirtualCall && - canDevirtualizeMemberFunctionCalls(getContext(), Base, MD); - const Expr *Inner = Base; - if (Devirtualize) - Inner = Base->ignoreParenBaseCasts(); + const CXXMethodDecl *DevirtualizedMethod = NULL; + if (CanUseVirtualCall && + canDevirtualizeMemberFunctionCalls(getContext(), Base, MD)) { + const CXXRecordDecl *BestDynamicDecl = Base->getBestDynamicClassType(); + DevirtualizedMethod = MD->getCorrespondingMethodInClass(BestDynamicDecl); + assert(DevirtualizedMethod); + const CXXRecordDecl *DevirtualizedClass = DevirtualizedMethod->getParent(); + const Expr *Inner = Base->ignoreParenBaseCasts(); + if (getCXXRecord(Inner) == DevirtualizedClass) + // If the class of the Inner expression is where the dynamic method + // is defined, build the this pointer from it. + Base = Inner; + else if (getCXXRecord(Base) != DevirtualizedClass) { + // If the method is defined in a class that is not the best dynamic + // one or the one of the full expression, we would have to build + // a derived-to-base cast to compute the correct this pointer, but + // we don't have support for that yet, so do a virtual call. + DevirtualizedMethod = NULL; + } + } llvm::Value *This; if (ME->isArrow()) - This = EmitScalarExpr(Inner); + This = EmitScalarExpr(Base); else - This = EmitLValue(Inner).getAddress(); + This = EmitLValue(Base).getAddress(); if (MD->isTrivial()) { @@ -233,8 +256,7 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE, // // We also don't emit a virtual call if the base expression has a record type // because then we know what the type is. - bool UseVirtualCall = CanUseVirtualCall && !Devirtualize; - const CXXRecordDecl *MostDerivedClassDecl = Inner->getBestDynamicClassType(); + bool UseVirtualCall = CanUseVirtualCall && !DevirtualizedMethod; llvm::Value *Callee; if (const CXXDestructorDecl *Dtor = dyn_cast(MD)) { @@ -245,13 +267,11 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE, MD->isVirtual() && ME->hasQualifier()) Callee = BuildAppleKextVirtualCall(MD, ME->getQualifier(), Ty); - else if (!Devirtualize) + else if (!DevirtualizedMethod) Callee = CGM.GetAddrOfFunction(GlobalDecl(Dtor, Dtor_Complete), Ty); else { - const CXXMethodDecl *DM = - Dtor->getCorrespondingMethodInClass(MostDerivedClassDecl); - assert(DM); - const CXXDestructorDecl *DDtor = cast(DM); + const CXXDestructorDecl *DDtor = + cast(DevirtualizedMethod); Callee = CGM.GetAddrOfFunction(GlobalDecl(DDtor, Dtor_Complete), Ty); } } @@ -265,13 +285,10 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE, MD->isVirtual() && ME->hasQualifier()) Callee = BuildAppleKextVirtualCall(MD, ME->getQualifier(), Ty); - else if (!Devirtualize) + else if (!DevirtualizedMethod) Callee = CGM.GetAddrOfFunction(MD, Ty); else { - const CXXMethodDecl *DerivedMethod = - MD->getCorrespondingMethodInClass(MostDerivedClassDecl); - assert(DerivedMethod); - Callee = CGM.GetAddrOfFunction(DerivedMethod, Ty); + Callee = CGM.GetAddrOfFunction(DevirtualizedMethod, Ty); } } diff --git a/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp b/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp index ba92012bce..d1deb77fa8 100644 --- a/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp +++ b/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp @@ -80,7 +80,11 @@ namespace Test5 { // CHECK: define void @_ZN5Test51fEPNS_1CE void f(C* d) { - // CHECK: call void @_ZN5Test51B1fEv + // FIXME: It should be possible to devirtualize this case, but that is + // not implemented yet. + // CHECK: getelementptr + // CHECK-NEXT: %[[FUNC:.*]] = load + // CHECK-NEXT: call void %[[FUNC]] static_cast(d)->f(); } } @@ -133,3 +137,18 @@ namespace Test7 { return static_cast(z)->f(); } } + +namespace Test8 { + struct A { virtual ~A() {} }; + struct B { + int b; + virtual int foo() { return b; } + }; + struct C final : A, B { }; + // CHECK: define i32 @_ZN5Test84testEPNS_1CE + int test(C *c) { + // CHECK: %[[THIS:.*]] = phi + // CHECK-NEXT: call i32 @_ZN5Test81B3fooEv(%"struct.Test8::B"* %[[THIS]]) + return static_cast(c)->foo(); + } +}