https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118790
--- Comment #33 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
So, for the GTY fix I was thinking about something like:
--- gcc/tree.cc.jj 2025-01-20 10:26:42.216048422 +0100
+++ gcc/tree.cc 2025-02-12 13:31:07.912605405 +0100
@@ -217,7 +217,7 @@ static GTY ((cache)) hash_table<cl_optio
static GTY ((cache))
hash_table<tree_decl_map_cache_hasher> *debug_expr_for_decl;
-static GTY ((cache))
+static GTY ((cache ("2pass")))
hash_table<tree_decl_map_cache_hasher> *value_expr_for_decl;
static GTY ((cache))
--- gcc/hash-map.h.jj 2025-01-02 11:23:37.818219480 +0100
+++ gcc/hash-map.h 2025-02-12 13:33:08.003941869 +0100
@@ -308,6 +308,7 @@ private:
template<typename T, typename U, typename V> friend void gt_pch_nx
(hash_map<T, U, V> *);
template<typename T, typename U, typename V> friend void gt_pch_nx
(hash_map<T, U, V> *, gt_pointer_operator, void *);
template<typename T, typename U, typename V> friend void gt_cleare_cache
(hash_map<T, U, V> *);
+ template<typename T, typename U, typename V> friend void
gt_cleare_cache_2pass (hash_map<T, U, V> *);
hash_table<hash_entry> m_table;
};
@@ -337,6 +338,14 @@ gt_cleare_cache (hash_map<K, V, H> *h)
}
template<typename K, typename V, typename H>
+inline void
+gt_cleare_cache_2pass (hash_map<K, V, H> *h)
+{
+ if (h)
+ gt_cleare_cache_2pass (&h->m_table);
+}
+
+template<typename K, typename V, typename H>
inline void
gt_pch_nx (hash_map<K, V, H> *h, gt_pointer_operator op, void *cookie)
{
--- gcc/gengtype.cc.jj 2025-01-02 11:23:02.613710956 +0100
+++ gcc/gengtype.cc 2025-02-12 13:25:02.306650140 +0100
@@ -4656,13 +4656,12 @@ write_roots (pair_p variables, bool emit
outf_p f = get_output_file_with_visibility (CONST_CAST (input_file*,
v->line.file));
struct flist *fli;
- bool cache = false;
options_p o;
for (o = v->opt; o; o = o->next)
if (strcmp (o->name, "cache") == 0)
- cache = true;
- if (!cache)
+ break;
+ if (!o)
continue;
for (fli = flp; fli; fli = fli->next)
@@ -4677,7 +4676,10 @@ write_roots (pair_p variables, bool emit
oprintf (f, " ()\n{\n");
}
- oprintf (f, " gt_cleare_cache (%s);\n", v->name);
+ if (o->kind == OPTION_STRING && strcmp (o->info.string, "2pass") == 0)
+ oprintf (f, " gt_cleare_cache_2pass (%s);\n", v->name);
+ else
+ oprintf (f, " gt_cleare_cache (%s);\n", v->name);
}
finish_cache_funcs (flp);
--- gcc/hash-table.h.jj 2025-01-02 11:23:18.432490116 +0100
+++ gcc/hash-table.h 2025-02-12 14:05:06.137219383 +0100
@@ -524,6 +524,7 @@ private:
gt_pointer_operator, void *);
template<typename T> friend void gt_cleare_cache (hash_table<T> *);
+ template<typename T> friend void gt_cleare_cache_2pass (hash_table<T> *);
void empty_slow ();
@@ -1318,4 +1319,26 @@ gt_cleare_cache (hash_table<H> *h)
}
}
+/* Variant of the above for cache ("2pass"). This version first marks
+ all the cache entries which should be kept and only afterwards deletes
+ those that shouldn't, which allows some keys in the cache to be marked
+ only when referenced in values of other entries in the cache. */
+
+template<typename H>
+inline void
+gt_cleare_cache_2pass (hash_table<H> *h)
+{
+ typedef hash_table<H> table;
+ if (!h)
+ return;
+
+ for (typename table::iterator iter = h->begin (); iter != h->end (); ++iter)
+ if (!table::is_empty (*iter) && !table::is_deleted (*iter))
+ {
+ int res = H::keep_cache_entry (*iter);
+ if (res != 0 && res != -1)
+ gt_ggc_mx ((*iter)->to);
+ }
+ gt_cleare_cache (h);
+}
#endif /* TYPED_HASHTAB_H */
This fixes the ICE, but now that I think about that, 2 passes aren't enough the
way it is written, one pass will handle just one extra indirection through
DECL_VALUE_EXPR (like this testcase, id_string ggc_marked_p, which has
*id_string.55 has DECL_VALUE_EXPR and id_string.55 not initially marked, and
id_string.55 having DECL_VALUE_EXPR to some tree referencing just VAR_DECLs
already marked. But if there is an extra indirection and one is unlucky,
ggc_marked_p x could have DECL_VALUE_EXPR *y ad y could have DECL_VALUE_EXPR *z
and z could have DECL_VALUE_EXPR *w, then if
tree_decl_map entry for z comes first, then for y and then for x, this would
mark also y but not already z.
So, probably instead of gt_ggc_mx ((*iter)->to); it should call some function
(perhaps specified in cache's operand) on *iter and that function should
actually walk_tree &e.to with callback that would for any VAR_DECLs
!ggc_marked_p with DECL_HAS_VALUE_EXPR_P first walk their DECL_VALUE_EXPR and
then gt_ggc_mx mark them.