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.

Reply via email to