Author: rsmith Date: Wed Oct 19 17:18:42 2016 New Revision: 284653 URL: http://llvm.org/viewvc/llvm-project?rev=284653&view=rev Log: Add optimization to sizeof...(X) handling: if none of parameter pack X's corresponding arguments are unexpanded pack expansions, we can compute the result without substituting them. This significantly improves the memory usage and performance of make_integer_sequence implementations that do this kind of thing:
using result = integer_sequence<T, Ns ..., sizeof...(Ns) + Ns ...>; ... but note that such an implementation will still perform O(sizeof...(Ns)^2) work while building the second pack expansion (we just have a somewhat lower constant now). In principle we could get this down to linear time by caching whether the number of expansions of a pack is constant, or checking whether we're within an alias template before scanning the pack for pack expansions (since that's the only case in which we do substitutions within a dependent context at the moment), but this patch doesn't attempt that. Modified: cfe/trunk/include/clang/Sema/Sema.h cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp cfe/trunk/lib/Sema/TreeTransform.h cfe/trunk/test/SemaTemplate/alias-templates.cpp Modified: cfe/trunk/include/clang/Sema/Sema.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=284653&r1=284652&r2=284653&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/Sema.h (original) +++ cfe/trunk/include/clang/Sema/Sema.h Wed Oct 19 17:18:42 2016 @@ -6499,6 +6499,14 @@ public: SourceLocation &Ellipsis, Optional<unsigned> &NumExpansions) const; + /// Given a template argument that contains an unexpanded parameter pack, but + /// which has already been substituted, attempt to determine the number of + /// elements that will be produced once this argument is fully-expanded. + /// + /// This is intended for use when transforming 'sizeof...(Arg)' in order to + /// avoid actually expanding the pack where possible. + Optional<unsigned> getFullyPackExpandedSize(TemplateArgument Arg); + //===--------------------------------------------------------------------===// // C++ Template Argument Deduction (C++ [temp.deduct]) //===--------------------------------------------------------------------===// Modified: cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp?rev=284653&r1=284652&r2=284653&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp (original) +++ cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp Wed Oct 19 17:18:42 2016 @@ -639,7 +639,7 @@ bool Sema::CheckParameterPacksForExpansi return true; } } - + return false; } @@ -936,6 +936,63 @@ Sema::getTemplateArgumentPackExpansionPa llvm_unreachable("Invalid TemplateArgument Kind!"); } +Optional<unsigned> Sema::getFullyPackExpandedSize(TemplateArgument Arg) { + assert(Arg.containsUnexpandedParameterPack()); + + // If this is a substituted pack, grab that pack. If not, we don't know + // the size yet. + // FIXME: We could find a size in more cases by looking for a substituted + // pack anywhere within this argument, but that's not necessary in the common + // case for 'sizeof...(A)' handling. + TemplateArgument Pack; + switch (Arg.getKind()) { + case TemplateArgument::Type: + if (auto *Subst = Arg.getAsType()->getAs<SubstTemplateTypeParmPackType>()) + Pack = Subst->getArgumentPack(); + else + return None; + break; + + case TemplateArgument::Expression: + if (auto *Subst = + dyn_cast<SubstNonTypeTemplateParmPackExpr>(Arg.getAsExpr())) + Pack = Subst->getArgumentPack(); + else if (auto *Subst = dyn_cast<FunctionParmPackExpr>(Arg.getAsExpr())) { + for (ParmVarDecl *PD : *Subst) + if (PD->isParameterPack()) + return None; + return Subst->getNumExpansions(); + } else + return None; + break; + + case TemplateArgument::Template: + if (SubstTemplateTemplateParmPackStorage *Subst = + Arg.getAsTemplate().getAsSubstTemplateTemplateParmPack()) + Pack = Subst->getArgumentPack(); + else + return None; + break; + + case TemplateArgument::Declaration: + case TemplateArgument::NullPtr: + case TemplateArgument::TemplateExpansion: + case TemplateArgument::Integral: + case TemplateArgument::Pack: + case TemplateArgument::Null: + return None; + } + + // Check that no argument in the pack is itself a pack expansion. + for (TemplateArgument Elem : Pack.pack_elements()) { + // There's no point recursing in this case; we would have already + // expanded this pack expansion into the enclosing pack if we could. + if (Elem.isPackExpansion()) + return None; + } + return Pack.pack_size(); +} + static void CheckFoldOperand(Sema &S, Expr *E) { if (!E) return; Modified: cfe/trunk/lib/Sema/TreeTransform.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=284653&r1=284652&r2=284653&view=diff ============================================================================== --- cfe/trunk/lib/Sema/TreeTransform.h (original) +++ cfe/trunk/lib/Sema/TreeTransform.h Wed Oct 19 17:18:42 2016 @@ -10763,6 +10763,51 @@ TreeTransform<Derived>::TransformSizeOfP E->getRParenLoc(), None, None); } + // Try to compute the result without performing a partial substitution. + Optional<unsigned> Result = 0; + for (const TemplateArgument &Arg : PackArgs) { + if (!Arg.isPackExpansion()) { + Result = *Result + 1; + continue; + } + + TemplateArgumentLoc ArgLoc; + InventTemplateArgumentLoc(Arg, ArgLoc); + + // Find the pattern of the pack expansion. + SourceLocation Ellipsis; + Optional<unsigned> OrigNumExpansions; + TemplateArgumentLoc Pattern = + getSema().getTemplateArgumentPackExpansionPattern(ArgLoc, Ellipsis, + OrigNumExpansions); + + // Substitute under the pack expansion. Do not expand the pack (yet). + TemplateArgumentLoc OutPattern; + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1); + if (getDerived().TransformTemplateArgument(Pattern, OutPattern, + /*Uneval*/ true)) + return true; + + // See if we can determine the number of arguments from the result. + Optional<unsigned> NumExpansions = + getSema().getFullyPackExpandedSize(OutPattern.getArgument()); + if (!NumExpansions) { + // No: we must be in an alias template expansion, and we're going to need + // to actually expand the packs. + Result = None; + break; + } + + Result = *Result + *NumExpansions; + } + + // Common case: we could determine the number of expansions without + // substituting. + if (Result) + return getDerived().RebuildSizeOfPackExpr(E->getOperatorLoc(), E->getPack(), + E->getPackLoc(), + E->getRParenLoc(), *Result, None); + TemplateArgumentListInfo TransformedPackArgs(E->getPackLoc(), E->getPackLoc()); { @@ -10775,6 +10820,8 @@ TreeTransform<Derived>::TransformSizeOfP return ExprError(); } + // Check whether we managed to fully-expand the pack. + // FIXME: Is it possible for us to do so and not hit the early exit path? SmallVector<TemplateArgument, 8> Args; bool PartialSubstitution = false; for (auto &Loc : TransformedPackArgs.arguments()) { Modified: cfe/trunk/test/SemaTemplate/alias-templates.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/alias-templates.cpp?rev=284653&r1=284652&r2=284653&view=diff ============================================================================== --- cfe/trunk/test/SemaTemplate/alias-templates.cpp (original) +++ cfe/trunk/test/SemaTemplate/alias-templates.cpp Wed Oct 19 17:18:42 2016 @@ -220,6 +220,23 @@ namespace PR14858 { template<typename ...T, typename ...U> void h(X<T...> &) {} template<typename ...T, typename ...U> void h(X<U...> &) {} // ok, different + + template<typename ...T> void i(auto (T ...t) -> int(&)[sizeof...(t)]); + auto mk_arr(int, int) -> int(&)[2]; + void test_i() { i<int, int>(mk_arr); } + +#if 0 // FIXME: This causes clang to assert. + template<typename ...T> using Z = auto (T ...p) -> int (&)[sizeof...(p)]; + template<typename ...T, typename ...U> void j(Z<T..., U...> &) {} + void test_j() { j<int, int>(mk_arr); } +#endif + + template<typename ...T> struct Q { + template<typename ...U> using V = int[sizeof...(U)]; + template<typename ...U> void f(V<typename U::type..., typename T::type...> *); + }; + struct B { typedef int type; }; + void test_q(int (&a)[5]) { Q<B, B, B>().f<B, B>(&a); } } namespace redecl { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits