]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
ggc: Avoid using ATTRIBUTE_MALLOC for allocations that need finalization [PR117047]
authorJakub Jelinek <jakub@redhat.com>
Sat, 1 Mar 2025 10:22:27 +0000 (11:22 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Sat, 1 Mar 2025 10:22:27 +0000 (11:22 +0100)
As analyzed by Andrew/David/Richi/Sam in the PR, the reason for the
libgccjit ICE is that there are GC allocations with finalizers and we
still mark ggc_internal_{,cleared_}alloc with ATTRIBUTE_MALLOC, which
to the optimizers hints that nothing will actually read the state
of the objects when they get out of lifetime.  The finalizer actually
inspects those though.  What actually happens in the testcases is that on
  tree expr_size = TYPE_SIZE (expr->get_type ()->as_tree ());
we see that expr->get_type () was allocated using something with malloc
attribute but it doesn't escape and only the type size from it is queried,
so there is no need to store other members of it.  Except that it does escape
in the GC internals.  Normal GC allocations are fine, they don't look at the
data in the allocated objects on "free", but the ones with finalizers actually
call a function on that object and expect the data to be in there.
So that we don't lose ATTRIBUTE_MALLOC for the common case when no
finalization is needed, the following patch uses the approach used e.g.
for glibc error function which can sometimes be noreturn but at other
times just return normally.
If possible, it uses __attribute__((alias ("..."))) to add an alias
to the function, where one is without ATTRIBUTE_MALLOC and one
(with _no_dtor suffix) is with ATTRIBUTE_MALLOC (note, as this is
C++ and I didn't want to hardcode particular mangling I used an
extern "C" function with 2 aliases to it), and otherwise adds a wrapper
(for the ggc-page/ggc-common case with noinline attribute if possible,
for ggc-none that doesn't matter because ggc-none doesn't support
finalizers).
The *_no_dtor aliases/wrappers are then used in inline functions which
pass unconditional NULL, 0 as the f/s pair.

2025-03-01  Jakub Jelinek  <jakub@redhat.com>

PR jit/117047
* acinclude.m4 (gcc_CHECK_ATTRIBUTE_ALIAS): New.
* configure.ac: Add gcc_CHECK_ATTRIBUTE_ALIAS.
* ggc.h (ggc_internal_alloc): Remove ATTRIBUTE_MALLOC from
overload with finalizer pointer.  Call ggc_internal_alloc_no_dtor
in inline overload without finalizer pointer.
(ggc_internal_alloc_no_dtor): Declare.
(ggc_internal_cleared_alloc): Remove ATTRIBUTE_MALLOC from
overload with finalizer pointer.  Call
ggc_internal_cleared_alloc_no_dtor in inline overload without
finalizer pointer.
(ggc_internal_cleared_alloc_no_dtor): Declare.
(ggc_alloc): Call ggc_internal_alloc_no_dtor if no finalization
is needed.
(ggc_alloc_no_dtor): Call ggc_internal_alloc_no_dtor.
(ggc_cleared_alloc): Call ggc_internal_cleared_alloc_no_dtor if no
finalization is needed.
(ggc_vec_alloc): Call ggc_internal_alloc_no_dtor if no finalization
is needed.
(ggc_cleared_vec_alloc): Call ggc_internal_cleared_alloc_no_dtor if no
finalization is needed.
* ggc-page.cc (ggc_internal_alloc): If HAVE_ATTRIBUTE_ALIAS, turn
overload with finalizer into alias to ggc_internal_alloc_ and
rename it to ...
(ggc_internal_alloc_): ... this, make it extern "C".
(ggc_internal_alloc_no_dtor): New alias if HAVE_ATTRIBUTE_ALIAS,
otherwise new noinline wrapper.
* ggc-common.cc (ggc_internal_cleared_alloc): If HAVE_ATTRIBUTE_ALIAS,
turn overload with finalizer into alias to ggc_internal_alloc_ and
rename it to ...
(ggc_internal_cleared_alloc_): ... this, make it extern "C".
(ggc_internal_cleared_alloc_no_dtor): New alias if
HAVE_ATTRIBUTE_ALIAS, otherwise new noinline wrapper.
* ggc-none.cc (ggc_internal_alloc): If HAVE_ATTRIBUTE_ALIAS, turn
overload with finalizer into alias to ggc_internal_alloc_ and
rename it to ...
(ggc_internal_alloc_): ... this, make it extern "C".
(ggc_internal_alloc_no_dtor): New alias if HAVE_ATTRIBUTE_ALIAS,
otherwise new wrapper.
(ggc_internal_cleared_alloc): If HAVE_ATTRIBUTE_ALIAS, turn overload
with finalizer into alias to ggc_internal_alloc_ and rename it to ...
(ggc_internal_cleared_alloc_): ... this, make it extern "C".
(ggc_internal_cleared_alloc_no_dtor): New alias if
HAVE_ATTRIBUTE_ALIAS, otherwise new wrapper.
* genmatch.cc (ggc_internal_cleared_alloc, ggc_free): Formatting fix.
(ggc_internal_cleared_alloc_no_dtor): Define.
* config.in: Regenerate.
* configure: Regenerate.

gcc/acinclude.m4
gcc/config.in
gcc/configure
gcc/configure.ac
gcc/genmatch.cc
gcc/ggc-common.cc
gcc/ggc-none.cc
gcc/ggc-page.cc
gcc/ggc.h

index be4033dc6317454aa9d246d8f7b31e27a852aecc..ca4820221d5769f03af24b97fff4539cec582a91 100644 (file)
@@ -442,6 +442,23 @@ AC_DEFINE_UNQUOTED(HAVE_INITFINI_ARRAY_SUPPORT,
   [Define 0/1 if .init_array/.fini_array sections are available and working.])
 ])
 
+dnl Check whether the host supports symbol aliases.
+AC_DEFUN([gcc_CHECK_ATTRIBUTE_ALIAS], [
+  AC_CACHE_CHECK([whether the host/build supports symbol aliases],
+                 gcc_cv_have_attribute_alias, [
+  if test "x${build}" = "x${host}"; then
+    AC_TRY_LINK([
+extern "C" void foo(void) { }
+extern void bar(void) __attribute__((alias("foo")));],
+    [bar();], gcc_cv_have_attribute_alias=yes, gcc_cv_have_attribute_alias=no)
+  else
+    gcc_cv_have_attribute_alias=no
+  fi])
+  if test $gcc_cv_have_attribute_alias = yes; then
+    AC_DEFINE(HAVE_ATTRIBUTE_ALIAS, 1,
+      [Define to 1 if the host/build supports __attribute__((alias(...))).])
+  fi])
+
 dnl # gcc_GAS_FLAGS
 dnl # Used by gcc_GAS_CHECK_FEATURE 
 dnl #
index 45ae9fb222eb93e63941bc3048ddc1cf69be6f07..2e7e0fe9942c84713ccca8cb157b2b04ac59420c 100644 (file)
 #endif
 
 
+/* Define to 1 if the host/build supports __attribute__((alias(...))). */
+#ifndef USED_FOR_TARGET
+#undef HAVE_ATTRIBUTE_ALIAS
+#endif
+
+
 /* Define to 1 if you have the Mac OS X function
    CFLocaleCopyPreferredLanguages in the CoreFoundation framework. */
 #ifndef USED_FOR_TARGET
index 69a9cf7af5f552b2405b7c27abbfeb39a26aebbc..86a5c75a146b1389ccbbaf15ba1a82b8d08d2310 100755 (executable)
@@ -26412,6 +26412,46 @@ _ACEOF
 
 
 
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the host/build supports symbol aliases" >&5
+$as_echo_n "checking whether the host/build supports symbol aliases... " >&6; }
+if ${gcc_cv_have_attribute_alias+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+  if test "x${build}" = "x${host}"; then
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+extern "C" void foo(void) { }
+extern void bar(void) __attribute__((alias("foo")));
+int
+main ()
+{
+bar();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  gcc_cv_have_attribute_alias=yes
+else
+  gcc_cv_have_attribute_alias=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  else
+    gcc_cv_have_attribute_alias=no
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_have_attribute_alias" >&5
+$as_echo "$gcc_cv_have_attribute_alias" >&6; }
+  if test $gcc_cv_have_attribute_alias = yes; then
+
+$as_echo "#define HAVE_ATTRIBUTE_ALIAS 1" >>confdefs.h
+
+  fi
+
 # Some assemblers (GNU as for LoongArch) generates relocations for
 # leb128 symbol arithmetic for relaxation, we need to disable relaxation
 # probing leb128 support then.
index 845827a340f7a3c36ef21b59a516af265c04ffe2..86f2283a2080a2c60a07a5908727f29dbce5cf04 100644 (file)
@@ -3296,6 +3296,8 @@ AC_MSG_RESULT($gcc_cv_ld_ro_rw_mix)
 
 gcc_AC_INITFINI_ARRAY
 
+gcc_CHECK_ATTRIBUTE_ALIAS
+
 # Some assemblers (GNU as for LoongArch) generates relocations for
 # leb128 symbol arithmetic for relaxation, we need to disable relaxation
 # probing leb128 support then.
index b9a792e245542ab1d785c188cf0733f6e29ea2b5..85bd77aa4cfce2de0935896d738bece7b8e64d81 100644 (file)
@@ -34,12 +34,22 @@ along with GCC; see the file COPYING3.  If not see
 
 
 /* Stubs for GGC referenced through instantiations triggered by hash-map.  */
-void *ggc_internal_cleared_alloc (size_t, void (*)(void *),
-                                 size_t, size_t MEM_STAT_DECL)
+void *
+ggc_internal_cleared_alloc (size_t, void (*)(void *),
+                           size_t, size_t MEM_STAT_DECL)
 {
   return NULL;
 }
-void ggc_free (void *)
+
+void *
+ggc_internal_cleared_alloc_no_dtor (size_t, void (*)(void *),
+                                   size_t, size_t MEM_STAT_DECL)
+{
+  return NULL;
+}
+
+void
+ggc_free (void *)
 {
 }
 
index d52da5db5578d8c23ad3b9ef34d4b7ef5f016bee..aece64228ea4340e7eda0347aa58464b90a0b907 100644 (file)
@@ -119,6 +119,25 @@ ggc_mark_roots (void)
 }
 
 /* Allocate a block of memory, then clear it.  */
+#ifdef HAVE_ATTRIBUTE_ALIAS
+extern "C" void *
+ggc_internal_cleared_alloc_ (size_t size, void (*f)(void *), size_t s, size_t n
+                            MEM_STAT_DECL)
+{
+  void *buf = ggc_internal_alloc (size, f, s, n PASS_MEM_STAT);
+  memset (buf, 0, size);
+  return buf;
+}
+
+extern void *
+ggc_internal_cleared_alloc (size_t size, void (*f)(void *), size_t s,
+                           size_t n MEM_STAT_DECL)
+     __attribute__((__alias__ ("ggc_internal_cleared_alloc_")));
+extern void *
+ggc_internal_cleared_alloc_no_dtor (size_t size, void (*f)(void *),
+                                   size_t s, size_t n MEM_STAT_DECL)
+     __attribute__((__alias__ ("ggc_internal_cleared_alloc_")));
+#else
 void *
 ggc_internal_cleared_alloc (size_t size, void (*f)(void *), size_t s, size_t n
                            MEM_STAT_DECL)
@@ -128,6 +147,17 @@ ggc_internal_cleared_alloc (size_t size, void (*f)(void *), size_t s, size_t n
   return buf;
 }
 
+#ifdef __GNUC__
+__attribute__ ((__noinline__))
+#endif
+void *
+ggc_internal_cleared_alloc_no_dtor (size_t size, void (*f)(void *),
+                                   size_t s, size_t n MEM_STAT_DECL)
+{
+  return ggc_internal_cleared_alloc (s, f, s, n PASS_MEM_STAT);
+}
+#endif
+
 /* Resize a block of memory, possibly re-allocating it.  */
 void *
 ggc_realloc (void *x, size_t size MEM_STAT_DECL)
index ade77862185a6606858ab980bfb5d02c9f04c190..3a511e9a46f2148cf71572e6d13a2dccf73fa077 100644 (file)
@@ -40,6 +40,40 @@ ggc_round_alloc_size (size_t requested_size)
   return requested_size;
 }
 
+#ifdef HAVE_ATTRIBUTE_ALIAS
+extern "C" void *
+ggc_internal_alloc_ (size_t size, void (*f)(void *), size_t, size_t
+                    MEM_STAT_DECL)
+{
+  gcc_assert (!f); // ggc-none doesn't support finalizers
+  return xmalloc (size);
+}
+
+extern "C" void *
+ggc_internal_cleared_alloc_ (size_t size, void (*f)(void *), size_t, size_t
+                            MEM_STAT_DECL)
+{
+  gcc_assert (!f); // ggc-none doesn't support finalizers
+  return xcalloc (size, 1);
+}
+
+extern void *
+ggc_internal_alloc (size_t size, void (*f)(void *), size_t s,
+                               size_t n MEM_STAT_DECL)
+     __attribute__((__alias__ ("ggc_internal_alloc_")));
+extern void *
+ggc_internal_alloc_no_dtor (size_t size, void (*f)(void *), size_t s,
+                           size_t n MEM_STAT_DECL)
+     __attribute__((__alias__ ("ggc_internal_alloc_")));
+extern void *
+ggc_internal_cleared_alloc (size_t size, void (*f)(void *),
+                           size_t s, size_t n MEM_STAT_DECL)
+     __attribute__((__alias__ ("ggc_internal_cleared_alloc_")));
+extern void *
+ggc_internal_cleared_alloc_no_dtor (size_t size, void (*f)(void *),
+                                   size_t s, size_t n MEM_STAT_DECL)
+     __attribute__((__alias__ ("ggc_internal_cleared_alloc_")));
+#else
 void *
 ggc_internal_alloc (size_t size, void (*f)(void *), size_t, size_t
                    MEM_STAT_DECL)
@@ -56,6 +90,21 @@ ggc_internal_cleared_alloc (size_t size, void (*f)(void *), size_t, size_t
   return xcalloc (size, 1);
 }
 
+void *
+ggc_internal_alloc_no_dtor (size_t size, void (*f)(void *), size_t s,
+                           size_t n MEM_STAT_DECL)
+{
+  return ggc_internal_alloc (size, f, s, n PASS_MEM_STAT);
+}
+
+void *
+ggc_internal_cleared_alloc_no_dtor (size_t size, void (*f)(void *),
+                                   size_t s, size_t n MEM_STAT_DECL)
+{
+  return ggc_internal_cleared_alloc (size, f, s, n PASS_MEM_STAT);
+}
+#endif
+
 void *
 ggc_realloc_stat (void *x, size_t size MEM_STAT_DECL)
 {
index ba8f37bf7031b5fcaed2370047cedec719f20889..971b4334b7c2f45c386dbb206f8c0e24db3c1851 100644 (file)
@@ -1273,9 +1273,15 @@ add_finalizer (void *result, void (*f)(void *), size_t s, size_t n)
 
 /* Allocate a chunk of memory of SIZE bytes.  Its contents are undefined.  */
 
+#ifdef HAVE_ATTRIBUTE_ALIAS
+extern "C" void *
+ggc_internal_alloc_ (size_t size, void (*f)(void *), size_t s, size_t n
+                    MEM_STAT_DECL)
+#else
 void *
 ggc_internal_alloc (size_t size, void (*f)(void *), size_t s, size_t n
                    MEM_STAT_DECL)
+#endif
 {
   size_t order, word, bit, object_offset, object_size;
   struct page_entry *entry;
@@ -1458,6 +1464,27 @@ ggc_internal_alloc (size_t size, void (*f)(void *), size_t s, size_t n
   return result;
 }
 
+#ifdef HAVE_ATTRIBUTE_ALIAS
+extern void *
+ggc_internal_alloc (size_t size, void (*f)(void *), size_t s,
+                   size_t n MEM_STAT_DECL)
+     __attribute__((__alias__ ("ggc_internal_alloc_")));
+extern void *
+ggc_internal_alloc_no_dtor (size_t size, void (*f)(void *), size_t s,
+                           size_t n MEM_STAT_DECL)
+     __attribute__((__alias__ ("ggc_internal_alloc_")));
+#else
+#ifdef __GNUC__
+__attribute__ ((__noinline__))
+#endif
+void *
+ggc_internal_alloc_no_dtor (size_t size, void (*f)(void *), size_t s,
+                           size_t n MEM_STAT_DECL)
+{
+  return ggc_internal_alloc (size, f, s, n PASS_MEM_STAT);
+}
+#endif
+
 /* Mark function for strings.  */
 
 void
index 5810aee877d661aff9857a5222393efa4779b033..d71113d707320a744e17b964dad3f6d5a22ddaf2 100644 (file)
--- a/gcc/ggc.h
+++ b/gcc/ggc.h
@@ -127,13 +127,18 @@ extern void gt_pch_save (FILE *f);
 
 /* The internal primitive.  */
 extern void *ggc_internal_alloc (size_t, void (*)(void *), size_t,
-                                size_t CXX_MEM_STAT_INFO)
+                                size_t CXX_MEM_STAT_INFO);
+/* If the second argument is non-NULL, it can't be marked ATTRIBUTE_MALLOC,
+   because ggc_free performs finalization.  Add an alias or wrapper used just
+   for the NULL finalizer which can be marked with ATTRIBUTE_MALLOC.  */
+extern void *ggc_internal_alloc_no_dtor (size_t, void (*)(void *), size_t,
+                                        size_t CXX_MEM_STAT_INFO)
      ATTRIBUTE_MALLOC;
 
 inline void *
 ggc_internal_alloc (size_t s CXX_MEM_STAT_INFO)
 {
-  return ggc_internal_alloc (s, NULL, 0, 1 PASS_MEM_STAT);
+  return ggc_internal_alloc_no_dtor (s, NULL, 0, 1 PASS_MEM_STAT);
 }
 
 extern size_t ggc_round_alloc_size (size_t requested_size);
@@ -141,12 +146,16 @@ extern size_t ggc_round_alloc_size (size_t requested_size);
 /* Allocates cleared memory.  */
 extern void *ggc_internal_cleared_alloc (size_t, void (*)(void *),
                                         size_t, size_t
-                                        CXX_MEM_STAT_INFO) ATTRIBUTE_MALLOC;
+                                        CXX_MEM_STAT_INFO);
+extern void *ggc_internal_cleared_alloc_no_dtor (size_t, void (*)(void *),
+                                                size_t, size_t
+                                                CXX_MEM_STAT_INFO)
+     ATTRIBUTE_MALLOC;
 
 inline void *
 ggc_internal_cleared_alloc (size_t s CXX_MEM_STAT_INFO)
 {
-  return ggc_internal_cleared_alloc (s, NULL, 0, 1 PASS_MEM_STAT);
+  return ggc_internal_cleared_alloc_no_dtor (s, NULL, 0, 1 PASS_MEM_STAT);
 }
 
 /* Resize a block.  */
@@ -187,8 +196,8 @@ ggc_alloc (ALONE_CXX_MEM_STAT_INFO)
     return static_cast<T *> (ggc_internal_alloc (sizeof (T), finalize<T>, 0, 1
                                                 PASS_MEM_STAT));
   else
-    return static_cast<T *> (ggc_internal_alloc (sizeof (T), NULL, 0, 1
-                                                PASS_MEM_STAT));
+    return static_cast<T *> (ggc_internal_alloc_no_dtor (sizeof (T), NULL,
+                                                        0, 1 PASS_MEM_STAT));
 }
 
 /* GGC allocation function that does not call finalizer for type
@@ -199,8 +208,8 @@ template<typename T>
 inline T *
 ggc_alloc_no_dtor (ALONE_CXX_MEM_STAT_INFO)
 {
-  return static_cast<T *> (ggc_internal_alloc (sizeof (T), NULL, 0, 1
-                                              PASS_MEM_STAT));
+  return static_cast<T *> (ggc_internal_alloc_no_dtor (sizeof (T), NULL, 0, 1
+                                                      PASS_MEM_STAT));
 }
 
 template<typename T>
@@ -212,8 +221,9 @@ ggc_cleared_alloc (ALONE_CXX_MEM_STAT_INFO)
                                                         finalize<T>, 0, 1
                                                         PASS_MEM_STAT));
   else
-    return static_cast<T *> (ggc_internal_cleared_alloc (sizeof (T), NULL, 0, 1
-                                                        PASS_MEM_STAT));
+    return static_cast<T *> (ggc_internal_cleared_alloc_no_dtor (sizeof (T),
+                                                                NULL, 0, 1
+                                                                PASS_MEM_STAT));
 }
 
 template<typename T>
@@ -224,8 +234,9 @@ ggc_vec_alloc (size_t c CXX_MEM_STAT_INFO)
     return static_cast<T *> (ggc_internal_alloc (c * sizeof (T), finalize<T>,
                                                 sizeof (T), c PASS_MEM_STAT));
   else
-    return static_cast<T *> (ggc_internal_alloc (c * sizeof (T), NULL, 0, 0
-                                                PASS_MEM_STAT));
+    return static_cast<T *> (ggc_internal_alloc_no_dtor (c * sizeof (T),
+                                                        NULL, 0, 0
+                                                        PASS_MEM_STAT));
 }
 
 template<typename T>
@@ -238,8 +249,10 @@ ggc_cleared_vec_alloc (size_t c CXX_MEM_STAT_INFO)
                                                         sizeof (T), c
                                                         PASS_MEM_STAT));
   else
-    return static_cast<T *> (ggc_internal_cleared_alloc (c * sizeof (T), NULL,
-                                                        0, 0 PASS_MEM_STAT));
+    return static_cast<T *> (ggc_internal_cleared_alloc_no_dtor (c
+                                                                * sizeof (T),
+                                                                NULL, 0, 0
+                                                                PASS_MEM_STAT));
 }
 
 inline void *