https://gcc.gnu.org/g:8c15a6cefa0d1f8ec12701af1f528f473c33ff6b

commit r15-7770-g8c15a6cefa0d1f8ec12701af1f528f473c33ff6b
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Sat Mar 1 11:22:27 2025 +0100

    ggc: Avoid using ATTRIBUTE_MALLOC for allocations that need finalization 
[PR117047]
    
    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  <ja...@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.

Diff:
---
 gcc/acinclude.m4  | 17 +++++++++++++++++
 gcc/config.in     |  6 ++++++
 gcc/configure     | 40 ++++++++++++++++++++++++++++++++++++++++
 gcc/configure.ac  |  2 ++
 gcc/genmatch.cc   | 16 +++++++++++++---
 gcc/ggc-common.cc | 30 ++++++++++++++++++++++++++++++
 gcc/ggc-none.cc   | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 gcc/ggc-page.cc   | 27 +++++++++++++++++++++++++++
 gcc/ggc.h         | 41 +++++++++++++++++++++++++++--------------
 9 files changed, 211 insertions(+), 17 deletions(-)

diff --git a/gcc/acinclude.m4 b/gcc/acinclude.m4
index be4033dc6317..ca4820221d57 100644
--- a/gcc/acinclude.m4
+++ b/gcc/acinclude.m4
@@ -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 #
diff --git a/gcc/config.in b/gcc/config.in
index 45ae9fb222eb..2e7e0fe9942c 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -851,6 +851,12 @@
 #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
diff --git a/gcc/configure b/gcc/configure
index 69a9cf7af5f5..86a5c75a146b 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -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.
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 845827a340f7..86f2283a2080 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -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.
diff --git a/gcc/genmatch.cc b/gcc/genmatch.cc
index b9a792e24554..85bd77aa4cfc 100644
--- a/gcc/genmatch.cc
+++ b/gcc/genmatch.cc
@@ -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 *)
 {
 }
 
diff --git a/gcc/ggc-common.cc b/gcc/ggc-common.cc
index d52da5db5578..aece64228ea4 100644
--- a/gcc/ggc-common.cc
+++ b/gcc/ggc-common.cc
@@ -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)
diff --git a/gcc/ggc-none.cc b/gcc/ggc-none.cc
index ade77862185a..3a511e9a46f2 100644
--- a/gcc/ggc-none.cc
+++ b/gcc/ggc-none.cc
@@ -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)
 {
diff --git a/gcc/ggc-page.cc b/gcc/ggc-page.cc
index ba8f37bf7031..971b4334b7c2 100644
--- a/gcc/ggc-page.cc
+++ b/gcc/ggc-page.cc
@@ -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
diff --git a/gcc/ggc.h b/gcc/ggc.h
index 5810aee877d6..d71113d70732 100644
--- 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 *

Reply via email to