Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look
OK for trunk/16?
-- >8 --
The TU in this PR exhibits a 40x compile-time slowdown with -freflection
vs without, all due to consteval_only_p which happens to be quite slow
on large intertwined classes due to its recursive walking of
TYPE_FIELDS.
This patch firstly rewrites the predicate to use direct recursion
instead of cp_walk_tree so that it's easier to reason about and more
closely mirrors the standard definition ([basic.types.general]/2).
This patch also makes the predicate partially tristate, where
the unknown state tracks whether we've seen an incomplete class type
and therefore don't know if it's consteval-only.
Finally this patch caches the result of the predicate for class types
when the result is known (and the class type is complete). When the
result is unknown then we must not cache so that a subsequent call to
the predicate can try again. We also need to be able to cope with a
class input that's not been laid out yet.
With this patch compile time for the TU is now 1.15x with -freflection
instead of 40x.
PR c++/125179
gcc/cp/ChangeLog:
* reflect.cc (consteval_only_type_r): Remove this cp_walk_tree
callback and replace with ...
(consteval_only_class_cache, consteval_only_p_1): ... this
recursive memoized implementation.
(consteval_only_p): Define in terms of consteval_only_p_1.
---
gcc/cp/reflect.cc | 122 +++++++++++++++++++++++++++++-----------------
1 file changed, 78 insertions(+), 44 deletions(-)
diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index b36c4be42736..10b5ed2b109f 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -8561,47 +8561,10 @@ splice (tree refl)
return refl;
}
-/* A walker for consteval_only_p. It cannot be a lambda, because we
- have to call this recursively, sigh. */
+static tristate consteval_only_p_1 (tree, hash_set<tree>&);
-static tree
-consteval_only_type_r (tree *tp, int *walk_subtrees, void *data)
-{
- tree t = *tp;
- /* Types can contain themselves recursively, hence this. */
- auto visited = static_cast<hash_set<tree> *>(data);
-
- if (!TYPE_P (t))
- return NULL_TREE;
-
- if (REFLECTION_TYPE_P (t))
- return t;
-
- if (typedef_variant_p (t))
- /* Tell cp_walk_subtrees to look through typedefs. */
- *walk_subtrees = 2;
-
- if (RECORD_OR_UNION_TYPE_P (t))
- {
- /* Don't walk template arguments; A<info>::type isn't a consteval-only
- type. */
- *walk_subtrees = 0;
- /* So we have to walk the fields manually. */
- for (tree member = TYPE_FIELDS (t);
- member; member = DECL_CHAIN (member))
- if (TREE_CODE (member) == FIELD_DECL)
- if (tree r = cp_walk_tree (&TREE_TYPE (member),
- consteval_only_type_r, visited, visited))
- return r;
- }
-
- return NULL_TREE;
-}
-
-/* True if T is a consteval-only type as per [basic.types.general]:
- "A type is consteval-only if it is either std::meta::info or a type
- compounded from a consteval-only type", or something that has
- a consteval-only type. */
+/* True if T is a consteval-only type as per [basic.types.general], or
+ is a declaration with such a type, or a TREE_VEC thereof. */
bool
consteval_only_p (tree t)
@@ -8612,7 +8575,7 @@ consteval_only_p (tree t)
if (!TYPE_P (t))
t = TREE_TYPE (t);
- if (!t)
+ if (!t || t == error_mark_node)
return false;
if (TREE_CODE (t) == TREE_VEC)
@@ -8634,9 +8597,80 @@ consteval_only_p (tree t)
which could be consteval-only, depending on T. */
t = complete_type (t);
- /* Classes with std::meta::info members are also consteval-only. */
- hash_set<tree> visited;
- return !!cp_walk_tree (&t, consteval_only_type_r, &visited, &visited);
+ hash_set<tree> class_set;
+ return consteval_only_p_1 (t, class_set).is_true ();
+}
+
+/* A cache of the boolean result of consteval_only_p_1 for class types, when
+ the result is known. */
+
+static GTY((cache)) type_tree_cache_map *consteval_only_class_cache;
+
+/* Recursive workhorse of consteval_only_p. Returns true if T is definitely
+ consteval-only, false if it's definitely not, and unknown if we saw an
+ incomplete type and therefore don't know. CLASS_SET is the set of class
types
+ we're recursively inside. */
+
+static tristate
+consteval_only_p_1 (tree t, hash_set<tree>& class_set)
+{
+ t = TYPE_MAIN_VARIANT (t);
+
+ if (REFLECTION_TYPE_P (t))
+ return true;
+ else if (INDIRECT_TYPE_P (t))
+ return consteval_only_p_1 (TREE_TYPE (t), class_set);
+ else if (TREE_CODE (t) == ARRAY_TYPE)
+ return consteval_only_p_1 (TREE_TYPE (t), class_set);
+ else if (FUNC_OR_METHOD_TYPE_P (t))
+ {
+ tristate r = consteval_only_p_1 (TREE_TYPE (t), class_set);
+ for (tree parm = TYPE_ARG_TYPES (t);
+ parm != NULL_TREE && parm != void_list_node;
+ parm = TREE_CHAIN (parm))
+ {
+ r = r || consteval_only_p_1 (TREE_VALUE (parm), class_set);
+ if (r.is_true ())
+ break;
+ }
+ return r;
+ }
+ else if (RECORD_OR_UNION_TYPE_P (t))
+ {
+ if (tree *slot = hash_map_safe_get (consteval_only_class_cache, t))
+ return *slot == boolean_true_node;
+
+ if (class_set.add (t))
+ /* Handle struct A { A* p; } etc. */
+ return false;
+
+ tristate r = COMPLETE_TYPE_P (t) ? false : tristate::unknown ();
+ for (tree member = TYPE_FIELDS (t); member; member = DECL_CHAIN (member))
+ if (TREE_CODE (member) == FIELD_DECL)
+ {
+ r = r || consteval_only_p_1 (TREE_TYPE (member), class_set);
+ if (r.is_true ())
+ break;
+ }
+
+ if (COMPLETE_TYPE_P (t))
+ {
+ if (r.is_true ())
+ hash_map_safe_put<hm_ggc> (consteval_only_class_cache,
+ t, boolean_true_node);
+ else if (r.is_false ())
+ hash_map_safe_put<hm_ggc> (consteval_only_class_cache,
+ t, boolean_false_node);
+ }
+
+ class_set.remove (t);
+ return r;
+ }
+ else if (TYPE_PTRMEM_P (t))
+ return (consteval_only_p_1 (TYPE_PTRMEM_CLASS_TYPE (t), class_set)
+ || consteval_only_p_1 (TYPE_PTRMEM_POINTED_TO_TYPE (t), class_set));
+ else
+ return false;
}
/* A walker for check_out_of_consteval_use_r. It cannot be a lambda, because
--
2.54.0.rc1.54.g60f07c4f5c