Skip to content

Commit

Permalink
Prevent devirtualization of calls to un-instantiated functions.
Browse files Browse the repository at this point in the history
PR 27895

Differential Revision: https://reviews.llvm.org/D22057

llvm-svn: 305862
  • Loading branch information
Sunil Srivastava committed Jun 20, 2017
1 parent 0d93185 commit 15ed292
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 2 deletions.
11 changes: 11 additions & 0 deletions clang/include/clang/AST/Decl.h
Expand Up @@ -1656,6 +1656,7 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
unsigned HasImplicitReturnZero : 1;
unsigned IsLateTemplateParsed : 1;
unsigned IsConstexpr : 1;
unsigned InstantiationIsPending:1;

/// \brief Indicates if the function uses __try.
unsigned UsesSEHTry : 1;
Expand Down Expand Up @@ -1751,6 +1752,7 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
IsDeleted(false), IsTrivial(false), IsDefaulted(false),
IsExplicitlyDefaulted(false), HasImplicitReturnZero(false),
IsLateTemplateParsed(false), IsConstexpr(isConstexprSpecified),
InstantiationIsPending(false),
UsesSEHTry(false), HasSkippedBody(false), WillHaveBody(false),
EndRangeLoc(NameInfo.getEndLoc()), TemplateOrSpecialization(),
DNLoc(NameInfo.getInfo()) {}
Expand Down Expand Up @@ -1943,6 +1945,15 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
bool isConstexpr() const { return IsConstexpr; }
void setConstexpr(bool IC) { IsConstexpr = IC; }

/// \brief Whether the instantiation of this function is pending.
/// This bit is set when the decision to instantiate this function is made
/// and unset if and when the function body is created. That leaves out
/// cases where instantiation did not happen because the template definition
/// was not seen in this TU. This bit remains set in those cases, under the
/// assumption that the instantiation will happen in some other TU.
bool instantiationIsPending() const { return InstantiationIsPending; }
void setInstantiationIsPending(bool IC) { InstantiationIsPending = IC; }

/// \brief Indicates the function uses __try.
bool usesSEHTry() const { return UsesSEHTry; }
void setUsesSEHTry(bool UST) { UsesSEHTry = UST; }
Expand Down
13 changes: 11 additions & 2 deletions clang/lib/CodeGen/CGClass.cpp
Expand Up @@ -2770,10 +2770,19 @@ CodeGenFunction::CanDevirtualizeMemberFunctionCall(const Expr *Base,

// We can devirtualize calls on an object accessed by a class member access
// expression, since by C++11 [basic.life]p6 we know that it can't refer to
// a derived class object constructed in the same location.
// a derived class object constructed in the same location. However, we avoid
// devirtualizing a call to a template function that we could instantiate
// implicitly, but have not decided to do so. This is needed because if this
// function does not get instantiated, the devirtualization will create a
// direct call to a function whose body may not exist. In contrast, calls to
// template functions that are not defined in this TU are allowed to be
// devirtualized under assumption that it is user responsibility to
// instantiate them in some other TU.
if (const MemberExpr *ME = dyn_cast<MemberExpr>(Base))
if (const ValueDecl *VD = dyn_cast<ValueDecl>(ME->getMemberDecl()))
return VD->getType()->isRecordType();
return VD->getType()->isRecordType() &&
(MD->instantiationIsPending() || MD->isDefined() ||
!MD->isImplicitlyInstantiable());

// Likewise for calls on an object accessed by a (non-reference) pointer to
// member access.
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/Sema.cpp
Expand Up @@ -740,6 +740,9 @@ void Sema::ActOnEndOfTranslationUnit() {
// Load pending instantiations from the external source.
SmallVector<PendingImplicitInstantiation, 4> Pending;
ExternalSource->ReadPendingInstantiations(Pending);
for (auto PII : Pending)
if (auto Func = dyn_cast<FunctionDecl>(PII.first))
Func->setInstantiationIsPending(true);
PendingInstantiations.insert(PendingInstantiations.begin(),
Pending.begin(), Pending.end());
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/SemaExpr.cpp
Expand Up @@ -13732,6 +13732,7 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
// call to such a function.
InstantiateFunctionDefinition(PointOfInstantiation, Func);
else {
Func->setInstantiationIsPending(true);
PendingInstantiations.push_back(std::make_pair(Func,
PointOfInstantiation));
// Notify the consumer that a function was implicitly instantiated.
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Expand Up @@ -3782,6 +3782,7 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
// Try again at the end of the translation unit (at which point a
// definition will be required).
assert(!Recursive);
Function->setInstantiationIsPending(true);
PendingInstantiations.push_back(
std::make_pair(Function, PointOfInstantiation));
} else if (TSK == TSK_ImplicitInstantiation) {
Expand All @@ -3801,6 +3802,7 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
// Postpone late parsed template instantiations.
if (PatternDecl->isLateTemplateParsed() &&
!LateTemplateParser) {
Function->setInstantiationIsPending(true);
PendingInstantiations.push_back(
std::make_pair(Function, PointOfInstantiation));
return;
Expand Down Expand Up @@ -5146,6 +5148,8 @@ void Sema::PerformPendingInstantiations(bool LocalOnly) {
TSK_ExplicitInstantiationDefinition;
InstantiateFunctionDefinition(/*FIXME:*/Inst.second, Function, true,
DefinitionRequired, true);
if (Function->isDefined())
Function->setInstantiationIsPending(false);
continue;
}

Expand Down
59 changes: 59 additions & 0 deletions clang/test/CodeGen/no-devirt.cpp
@@ -0,0 +1,59 @@
// RUN: %clang_cc1 %s -DUSEIT -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s

// Test with decls and template defs in pch, and just use in .cpp
// RUN: %clang_cc1 %s -DTMPL_DEF_IN_HEADER -triple %itanium_abi_triple -emit-pch -o %t
// RUN: %clang_cc1 %s -DTMPL_DEF_IN_HEADER -DUSEIT -triple %itanium_abi_triple -include-pch %t -emit-llvm -o - | FileCheck %s

// Test with A in pch, and B and C in main
// Test with just decls in pch, and template defs and use in .cpp
// RUN: %clang_cc1 %s -triple %itanium_abi_triple -emit-pch -o %t
// RUN: %clang_cc1 %s -DUSEIT -triple %itanium_abi_triple -include-pch %t -emit-llvm -o - | FileCheck %s

#ifndef HEADER
#define HEADER
template < typename T, int N = 0 > class TmplWithArray {
public:
virtual T& operator [] (int idx);
virtual T& func1 (int idx);
virtual T& func2 (int idx);
T ar[N+1];
};
struct Wrapper {
TmplWithArray<bool, 10> data;
bool indexIt(int a) {
if (a > 6) return data[a] ; // Should not devirtualize
if (a > 4) return data.func1(a); // Should devirtualize
return data.func2(a); // Should devirtualize
}
};

#ifdef TMPL_DEF_IN_HEADER
template <typename T, int N> T& TmplWithArray<T, N >::operator[](int idx) {
return ar[idx];
}
template <typename T, int N> T& TmplWithArray<T, N >::func1(int idx) {
return ar[idx];
}
#endif // TMPL_DEF_IN_HEADER
#endif // HEADER

#ifdef USEIT
#ifndef TMPL_DEF_IN_HEADER
template <typename T, int N> T& TmplWithArray<T, N >::operator[](int idx) {
return ar[idx];
}
template <typename T, int N> T& TmplWithArray<T, N >::func1(int idx) {
return ar[idx];
}
#endif // !TMPL_DEF_IN_HEADER
extern Wrapper ew;
bool stuff(int p)
{
return ew.indexIt(p);
}
#endif

// CHECK-NOT: call {{.*}} @_ZN13TmplWithArrayIbLi10EEixEi
// CHECK-DAG: call {{.*}} @_ZN13TmplWithArrayIbLi10EE5func1Ei
// CHECK-DAG: call {{.*}} @_ZN13TmplWithArrayIbLi10EE5func2Ei

0 comments on commit 15ed292

Please sign in to comment.