Hi! 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. 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.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 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. --- gcc/acinclude.m4.jj 2025-01-02 11:23:32.759290106 +0100 +++ gcc/acinclude.m4 2025-02-28 11:13:42.246850464 +0100 @@ -442,6 +442,23 @@ AC_DEFINE_UNQUOTED(HAVE_INITFINI_ARRAY_S [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 # --- gcc/configure.ac.jj 2025-02-13 10:20:55.475762996 +0100 +++ gcc/configure.ac 2025-02-28 10:39:54.738638413 +0100 @@ -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. --- gcc/ggc.h.jj 2025-01-02 11:23:22.192437625 +0100 +++ gcc/ggc.h 2025-02-28 12:05:26.920274765 +0100 @@ -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 /* 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_IN 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_INF 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_ 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 * --- gcc/ggc-page.cc.jj 2025-01-02 11:23:31.973301079 +0100 +++ gcc/ggc-page.cc 2025-02-28 12:10:36.257026356 +0100 @@ -1273,9 +1273,15 @@ add_finalizer (void *result, void (*f)(v /* 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 (* 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 --- gcc/ggc-common.cc.jj 2025-01-02 11:23:32.505293652 +0100 +++ gcc/ggc-common.cc 2025-02-28 12:12:20.207598711 +0100 @@ -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, 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) --- gcc/ggc-none.cc.jj 2025-01-02 11:23:28.208353640 +0100 +++ gcc/ggc-none.cc 2025-02-28 12:11:13.904509308 +0100 @@ -40,6 +40,40 @@ ggc_round_alloc_size (size_t requested_s 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) @@ -57,6 +91,21 @@ ggc_internal_cleared_alloc (size_t size, } 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) { return xrealloc (x, size); --- gcc/genmatch.cc.jj 2025-01-15 16:51:49.450087669 +0100 +++ gcc/genmatch.cc 2025-02-28 12:12:37.971354745 +0100 @@ -34,12 +34,22 @@ along with GCC; see the file COPYING3. /* 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 *) { } --- gcc/config.in.jj 2025-02-13 10:20:55.419763773 +0100 +++ gcc/config.in 2025-02-28 10:40:13.463380814 +0100 @@ -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 --- gcc/configure.jj 2025-02-13 10:20:55.459763218 +0100 +++ gcc/configure 2025-02-28 11:14:22.696296991 +0100 @@ -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. Jakub