Hi rjmccall, rsmith,
The idea is as follows:
1. After CodeGenModule::Release is called we know that the AST is complete and
we
can compute which classes with internal linkage have no children.
2. Do a recursive AST visitation to find those classes. We do this lazily on the
first attempt to emit a virtual call.
3. Now emit direct calls when the class whose member is called is in this list.
This is common in code bases with many anonymous namespaces and virtual calls,
such as LLVM. PR16984.
http://llvm-reviews.chandlerc.com/D1511
Files:
lib/CodeGen/CGExprCXX.cpp
lib/CodeGen/CodeGenModule.cpp
lib/CodeGen/CodeGenModule.h
test/CodeGenCXX/devirtualize-virtual-function-calls-internal-linkage.cpp
Index: lib/CodeGen/CGExprCXX.cpp
===================================================================
--- lib/CodeGen/CGExprCXX.cpp
+++ lib/CodeGen/CGExprCXX.cpp
@@ -16,6 +16,7 @@
#include "CGCXXABI.h"
#include "CGDebugInfo.h"
#include "CGObjCRuntime.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/CodeGenOptions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/Support/CallSite.h"
@@ -62,6 +63,57 @@
Callee, ReturnValue, Args, MD);
}
+namespace {
+/// \brief Visitor to find final classes through limited linkage.
+class FinalClassFinder : public RecursiveASTVisitor<FinalClassFinder> {
+ CodeGenModule::FinalClassesSet InternalClasses;
+ CodeGenModule::FinalClassesSet FoundBases;
+
+public:
+ void finish(CodeGenModule::FinalClassesSet &FinalClasses) {
+ // As a post pass add all classes to the set that are neither externally
+ // visible nor have any subclasses. We can consider those as final.
+ for (CodeGenModule::FinalClassesSet::iterator I = InternalClasses.begin(),
+ E = InternalClasses.end();
+ I != E; ++I)
+ if (!FoundBases.count(*I))
+ FinalClasses.insert(*I);
+ }
+
+ bool VisitCXXRecordDecl(CXXRecordDecl *D) {
+ // We can't check the bases of a class without definition.
+ if (!D->hasDefinition())
+ return true;
+
+ // Remember all bases. A base class cannot be final.
+ for (CXXRecordDecl::base_class_const_iterator I = D->bases_begin(),
+ E = D->bases_end();
+ I != E; ++I)
+ if (const CXXRecordDecl *RD = I->getType()->getAsCXXRecordDecl())
+ if (!RD->isExternallyVisible())
+ FoundBases.insert(RD);
+
+ // We only care about classes that aren't externally visible. Remember this
+ // internal class, we might figure out that it's final.
+ if (!D->isExternallyVisible())
+ InternalClasses.insert(D);
+
+ return true;
+ }
+};
+}
+
+const CodeGenModule::FinalClassesSet &CodeGenModule::GetFinalClassesSet() {
+ // Lazily build the set when we need it.
+ if (CanCheckForFinalClasses) {
+ FinalClassFinder FCF;
+ FCF.TraverseDecl(Context.getTranslationUnitDecl());
+ FCF.finish(FinalClasses);
+ CanCheckForFinalClasses = false;
+ }
+ return FinalClasses;
+}
+
// FIXME: Ideally Expr::IgnoreParenNoopCasts should do this, but it doesn't do
// quite what we want.
static const Expr *skipNoOpCastsAndParens(const Expr *E) {
@@ -89,10 +141,10 @@
/// canDevirtualizeMemberFunctionCalls - Checks whether virtual calls on given
/// expr can be devirtualized.
-static bool canDevirtualizeMemberFunctionCalls(ASTContext &Context,
- const Expr *Base,
- const CXXMethodDecl *MD) {
-
+static bool canDevirtualizeMemberFunctionCalls(
+ ASTContext &Context, const Expr *Base, const CXXMethodDecl *MD,
+ const CodeGenModule::FinalClassesSet &FinalClasses) {
+
// When building with -fapple-kext, all calls must go through the vtable since
// the kernel linker can do runtime patching of vtables.
if (Context.getLangOpts().AppleKext)
@@ -109,7 +161,8 @@
// }
//
const CXXRecordDecl *MostDerivedClassDecl = Base->getBestDynamicClassType();
- if (MostDerivedClassDecl->hasAttr<FinalAttr>())
+ if (FinalClasses.count(MostDerivedClassDecl) ||
+ MostDerivedClassDecl->hasAttr<FinalAttr>())
return true;
// If the member function is marked 'final', we know that it can't be
@@ -119,7 +172,8 @@
// 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<FinalAttr>())
+ if (FinalClasses.count(MD->getParent()) ||
+ MD->getParent()->hasAttr<FinalAttr>())
return true;
Base = skipNoOpCastsAndParens(Base);
@@ -188,7 +242,8 @@
const CXXMethodDecl *DevirtualizedMethod = NULL;
if (CanUseVirtualCall &&
- canDevirtualizeMemberFunctionCalls(getContext(), Base, MD)) {
+ canDevirtualizeMemberFunctionCalls(getContext(), Base, MD,
+ CGM.GetFinalClassesSet())) {
const CXXRecordDecl *BestDynamicDecl = Base->getBestDynamicClassType();
DevirtualizedMethod = MD->getCorrespondingMethodInClass(BestDynamicDecl);
assert(DevirtualizedMethod);
Index: lib/CodeGen/CodeGenModule.cpp
===================================================================
--- lib/CodeGen/CodeGenModule.cpp
+++ lib/CodeGen/CodeGenModule.cpp
@@ -83,7 +83,8 @@
SanitizerBlacklist(
llvm::SpecialCaseList::createOrDie(CGO.SanitizerBlacklistFile)),
SanOpts(SanitizerBlacklist->isIn(M) ? SanitizerOptions::Disabled
- : LangOpts.Sanitize) {
+ : LangOpts.Sanitize),
+ CanCheckForFinalClasses(false) {
// Initialize the type cache.
llvm::LLVMContext &LLVMContext = M.getContext();
@@ -171,6 +172,10 @@
}
void CodeGenModule::Release() {
+ // We can now see all classes and infer if a class is final.
+ CanCheckForFinalClasses = true;
+ assert(FinalClasses.empty());
+
EmitDeferred();
EmitCXXGlobalInitFunc();
EmitCXXGlobalDtorFunc();
Index: lib/CodeGen/CodeGenModule.h
===================================================================
--- lib/CodeGen/CodeGenModule.h
+++ lib/CodeGen/CodeGenModule.h
@@ -416,6 +416,11 @@
const SanitizerOptions &SanOpts;
+ /// \brief Lazily computed list of final classes due to limited linkage in the
+ /// context of devirtualization.
+ llvm::SmallPtrSet<const CXXRecordDecl *, 8> FinalClasses;
+ bool CanCheckForFinalClasses;
+
/// @}
public:
CodeGenModule(ASTContext &C, const CodeGenOptions &CodeGenOpts,
@@ -984,6 +989,12 @@
/// declarations are emitted lazily.
void EmitGlobal(GlobalDecl D);
+ typedef llvm::SmallPtrSet<const CXXRecordDecl *, 8> FinalClassesSet;
+ /// \brief Get a list of all classes that can be considered final due to
+ /// limited linkage in the context of devirtualization. This is only available
+ /// when Release has been called.
+ const FinalClassesSet &GetFinalClassesSet();
+
private:
llvm::GlobalValue *GetGlobalValue(StringRef Ref);
Index: test/CodeGenCXX/devirtualize-virtual-function-calls-internal-linkage.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/devirtualize-virtual-function-calls-internal-linkage.cpp
@@ -0,0 +1,76 @@
+// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+
+class C1 {
+ virtual void bar();
+ virtual void baz();
+};
+
+namespace {
+class C2 : public C1 {
+ void bar();
+ void baz() {}
+};
+
+void C2::bar() { baz(); }
+
+// CHECK-LABEL: define{{.*}}@_ZN12_GLOBAL__N_12C23barEv
+// CHECK: call{{.*}}@_ZN12_GLOBAL__N_12C23bazEv
+}
+
+namespace {
+class D2 : public C1 {
+ void bar() { baz(); }
+ void baz() {}
+};
+
+class D3 : public D2 {
+ void bar() { baz(); }
+ void baz() {}
+};
+}
+// CHECK-LABEL: define{{.*}}@_ZN12_GLOBAL__N_12D33barEv
+// CHECK: call{{.*}}@_ZN12_GLOBAL__N_12D33bazEv
+
+// CHECK-LABEL: define{{.*}}@_ZN12_GLOBAL__N_12D23barEv
+// CHECK-NOT: call{{.*}}baz
+// CHECK: ret
+
+namespace {
+class E2 : public C1 {
+ void bar() { baz(); }
+ void baz() {}
+};
+}
+
+class E3 : public E2 {
+ void bar() { baz(); }
+ void baz() {}
+};
+// CHECK-LABEL: define{{.*}}@_ZN2E33barEv
+// CHECK-NOT: call{{.*}}baz
+// CHECK: ret
+
+// CHECK-LABEL: define{{.*}}@_ZN12_GLOBAL__N_12E23barEv
+// CHECK-NOT: call{{.*}}baz
+// CHECK: ret
+
+template <typename T> class F1 {
+ virtual T bar();
+ virtual T baz();
+};
+
+template <> class F1<void> {
+ virtual int bar();
+ virtual int baz();
+};
+
+namespace {
+template <typename T> class F2 : public F1<T> {
+ int bar() { return baz(); }
+ int baz() { return 0;}
+};
+}
+
+C1 *test3() { return new E3(); }
+C1 *test2() { return new D3(); }
+C1 *test1() { return new C2(); }
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits