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.