r284653 - Add optimization to sizeof...(X) handling: if none of parameter pack X's

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 19 15:18:42 PDT 2016


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 {




More information about the cfe-commits mailing list