> Am 01.03.2025 um 09:41 schrieb Jakub Jelinek <ja...@redhat.com>:
>
> 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?
Ok
Thanks,
Richard
> 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
>