public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-4177] c++: cache the normal form of a concept-id
@ 2022-11-20 18:04 Patrick Palka
  0 siblings, 0 replies; only message in thread
From: Patrick Palka @ 2022-11-20 18:04 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:1ad735dbfcce07dd913f82308619324171825c58

commit r13-4177-g1ad735dbfcce07dd913f82308619324171825c58
Author: Patrick Palka <ppalka@redhat.com>
Date:   Sun Nov 20 13:02:24 2022 -0500

    c++: cache the normal form of a concept-id
    
    We already cache the overall normal form of a declaration's constraints
    (under the assumption that it can't change over the translation unit).
    But if we have something like
    
      template<class T> concept complicated = /* ... */;
      template<class T> void f() requires complicated<T> && /* ... */;
      template<class T> void g() requires complicated<T> && /* ... */;
    
    then despite this high-level caching we'd still redundantly have to
    expand the concept-id complicated<T> twice, once during normalization of
    f's constraints and again during normalization of g's.  Ideally, we'd
    reuse the previously computed normal form of complicated<T> the second
    time around.
    
    To that end this patch introduces an intermediate layer of caching
    during constraint normalization -- caching of the normal form of a
    concept-id -- that sits between our high-level caching of the overall
    normal form of a declaration's constraints and our low-level caching of
    each individual atomic constraint.
    
    It turns out this caching generalizes normalize_concept_check's caching
    of the normal form of a concept definition (which is equivalent to the
    normal form of the concept-id C<gtargs> where gtargs is C's generic
    arguments) so this patch unifies the caching accordingly.
    
    gcc/cp/ChangeLog:
    
            * constraint.cc (struct norm_entry): Define.
            (struct norm_hasher): Define.
            (norm_cache): Define.
            (normalize_concept_check): Add function comment.  Cache the
            the normal form of the substituted concept-id.  Canonicalize
            generic arguments as NULL_TREE.  Don't coerce arguments unless
            they were substituted.
            (normalize_concept_definition): Simplify.  Use norm_cache
            instead of normalized_map.

Diff:
---
 gcc/cp/constraint.cc | 95 ++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 81 insertions(+), 14 deletions(-)

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index a113d3e269e..ab0f66b3d7e 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -698,6 +698,39 @@ normalize_logical_operation (tree t, tree args, tree_code c, norm_info info)
   return build2 (c, ci, t0, t1);
 }
 
+/* Data types and hash functions for caching the normal form of a concept-id.
+   This essentially memoizes calls to normalize_concept_check.  */
+
+struct GTY((for_user)) norm_entry
+{
+  /* The CONCEPT_DECL of the concept-id.  */
+  tree tmpl;
+  /* The arguments of the concept-id.  */
+  tree args;
+  /* The normal form of the concept-id.  */
+  tree norm;
+};
+
+struct norm_hasher : ggc_ptr_hash<norm_entry>
+{
+  static hashval_t hash (norm_entry *e)
+  {
+    hashval_t hash = iterative_hash_template_arg (e->tmpl, 0);
+    return iterative_hash_template_arg (e->args, hash);
+  }
+
+  static bool equal (norm_entry *e1, norm_entry *e2)
+  {
+    return e1->tmpl == e2->tmpl
+      && template_args_equal (e1->args, e2->args);
+  }
+};
+
+static GTY((deletable)) hash_table<norm_hasher> *norm_cache;
+
+/* Normalize the concept check CHECK where ARGS are the
+   arguments to be substituted into CHECK's arguments.  */
+
 static tree
 normalize_concept_check (tree check, tree args, norm_info info)
 {
@@ -720,24 +753,52 @@ normalize_concept_check (tree check, tree args, norm_info info)
     targs = tsubst_template_args (targs, args, info.complain, info.in_decl);
   if (targs == error_mark_node)
     return error_mark_node;
+  if (template_args_equal (targs, generic_targs_for (tmpl)))
+    /* Canonicalize generic arguments as NULL_TREE, as an optimization.  */
+    targs = NULL_TREE;
 
   /* Build the substitution for the concept definition.  */
   tree parms = TREE_VALUE (DECL_TEMPLATE_PARMS (tmpl));
-  /* Turn on template processing; coercing non-type template arguments
-     will automatically assume they're non-dependent.  */
-  ++processing_template_decl;
-  tree subst = coerce_template_parms (parms, targs, tmpl, tf_none);
-  --processing_template_decl;
-  if (subst == error_mark_node)
+  if (targs && args)
+    /* As an optimization, coerce the arguments only if necessary
+       (i.e. if they were substituted).  */
+    targs = coerce_template_parms (parms, targs, tmpl, tf_none);
+  if (targs == error_mark_node)
     return error_mark_node;
 
+  if (!norm_cache)
+    norm_cache = hash_table<norm_hasher>::create_ggc (31);
+  norm_entry entry = {tmpl, targs, NULL_TREE};
+  norm_entry **slot = nullptr;
+  hashval_t hash = 0;
+  if (!info.generate_diagnostics ())
+    {
+      /* Cache the normal form of the substituted concept-id (when not
+	 diagnosing).  */
+      hash = norm_hasher::hash (&entry);
+      slot = norm_cache->find_slot_with_hash (&entry, hash, INSERT);
+      if (*slot)
+	return (*slot)->norm;
+    }
+
   /* The concept may have been ill-formed.  */
   tree def = get_concept_definition (DECL_TEMPLATE_RESULT (tmpl));
   if (def == error_mark_node)
     return error_mark_node;
 
   info.update_context (check, args);
-  return normalize_expression (def, subst, info);
+  tree norm = normalize_expression (def, targs, info);
+  if (slot)
+    {
+      /* Recompute SLOT since norm_cache may have been expanded during
+	 the recursive call.  */
+      slot = norm_cache->find_slot_with_hash (&entry, hash, INSERT);
+      gcc_checking_assert (!*slot);
+      entry.norm = norm;
+      *slot = ggc_alloc<norm_entry> ();
+      **slot = entry;
+    }
+  return norm;
 }
 
 /* Used by normalize_atom to cache ATOMIC_CONSTRs.  */
@@ -941,15 +1002,16 @@ get_normalized_constraints_from_decl (tree d, bool diag = false)
 /* Returns the normal form of TMPL's definition.  */
 
 static tree
-normalize_concept_definition (tree tmpl, bool diag = false)
+normalize_concept_definition (tree tmpl, bool diag)
 {
+  if (!norm_cache)
+    norm_cache = hash_table<norm_hasher>::create_ggc (31);
+  norm_entry entry = {tmpl, NULL_TREE, NULL_TREE};
+
   if (!diag)
-    if (tree *p = hash_map_safe_get (normalized_map, tmpl))
-      return *p;
+    if (norm_entry *found = norm_cache->find (&entry))
+      return found->norm;
 
-  gcc_assert (concept_definition_p (tmpl));
-  if (OVL_P (tmpl))
-    tmpl = OVL_FIRST (tmpl);
   gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL);
   tree def = get_concept_definition (DECL_TEMPLATE_RESULT (tmpl));
   ++processing_template_decl;
@@ -958,7 +1020,12 @@ normalize_concept_definition (tree tmpl, bool diag = false)
   --processing_template_decl;
 
   if (!diag)
-    hash_map_safe_put<hm_ggc> (normalized_map, tmpl, norm);
+    {
+      norm_entry **slot = norm_cache->find_slot (&entry, INSERT);
+      entry.norm = norm;
+      *slot = ggc_alloc<norm_entry> ();
+      **slot = entry;
+    }
 
   return norm;
 }

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-11-20 18:04 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-20 18:04 [gcc r13-4177] c++: cache the normal form of a concept-id Patrick Palka

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).