Author: ssrivastava Date: Tue Jun 20 17:08:44 2017 New Revision: 305862 URL: http://llvm.org/viewvc/llvm-project?rev=305862&view=rev Log: Prevent devirtualization of calls to un-instantiated functions.
PR 27895 Differential Revision: https://reviews.llvm.org/D22057 Added: cfe/trunk/test/CodeGen/no-devirt.cpp Modified: cfe/trunk/include/clang/AST/Decl.h cfe/trunk/lib/CodeGen/CGClass.cpp cfe/trunk/lib/Sema/Sema.cpp cfe/trunk/lib/Sema/SemaExpr.cpp cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Modified: cfe/trunk/include/clang/AST/Decl.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=305862&r1=305861&r2=305862&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/Decl.h (original) +++ cfe/trunk/include/clang/AST/Decl.h Tue Jun 20 17:08:44 2017 @@ -1656,6 +1656,7 @@ private: unsigned HasImplicitReturnZero : 1; unsigned IsLateTemplateParsed : 1; unsigned IsConstexpr : 1; + unsigned InstantiationIsPending:1; /// \brief Indicates if the function uses __try. unsigned UsesSEHTry : 1; @@ -1751,6 +1752,7 @@ protected: 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()) {} @@ -1943,6 +1945,15 @@ public: 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; } Modified: cfe/trunk/lib/CodeGen/CGClass.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=305862&r1=305861&r2=305862&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CGClass.cpp (original) +++ cfe/trunk/lib/CodeGen/CGClass.cpp Tue Jun 20 17:08:44 2017 @@ -2770,10 +2770,19 @@ CodeGenFunction::CanDevirtualizeMemberFu // 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. Modified: cfe/trunk/lib/Sema/Sema.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=305862&r1=305861&r2=305862&view=diff ============================================================================== --- cfe/trunk/lib/Sema/Sema.cpp (original) +++ cfe/trunk/lib/Sema/Sema.cpp Tue Jun 20 17:08:44 2017 @@ -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()); } Modified: cfe/trunk/lib/Sema/SemaExpr.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=305862&r1=305861&r2=305862&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaExpr.cpp (original) +++ cfe/trunk/lib/Sema/SemaExpr.cpp Tue Jun 20 17:08:44 2017 @@ -13732,6 +13732,7 @@ void Sema::MarkFunctionReferenced(Source // 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. Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp?rev=305862&r1=305861&r2=305862&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp (original) +++ cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Tue Jun 20 17:08:44 2017 @@ -3782,6 +3782,7 @@ void Sema::InstantiateFunctionDefinition // 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) { @@ -3801,6 +3802,7 @@ void Sema::InstantiateFunctionDefinition // Postpone late parsed template instantiations. if (PatternDecl->isLateTemplateParsed() && !LateTemplateParser) { + Function->setInstantiationIsPending(true); PendingInstantiations.push_back( std::make_pair(Function, PointOfInstantiation)); return; @@ -5146,6 +5148,8 @@ void Sema::PerformPendingInstantiations( TSK_ExplicitInstantiationDefinition; InstantiateFunctionDefinition(/*FIXME:*/Inst.second, Function, true, DefinitionRequired, true); + if (Function->isDefined()) + Function->setInstantiationIsPending(false); continue; } Added: cfe/trunk/test/CodeGen/no-devirt.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/no-devirt.cpp?rev=305862&view=auto ============================================================================== --- cfe/trunk/test/CodeGen/no-devirt.cpp (added) +++ cfe/trunk/test/CodeGen/no-devirt.cpp Tue Jun 20 17:08:44 2017 @@ -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 + _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits