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 *