From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-x334.google.com (mail-wm1-x334.google.com [IPv6:2a00:1450:4864:20::334]) by sourceware.org (Postfix) with ESMTPS id D2F3D3858411 for ; Fri, 5 Nov 2021 13:25:38 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org D2F3D3858411 Received: by mail-wm1-x334.google.com with SMTP id 67-20020a1c1946000000b0030d4c90fa87so6454633wmz.2 for ; Fri, 05 Nov 2021 06:25:38 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:mime-version:subject:from:in-reply-to:date:cc :content-transfer-encoding:message-id:references:to; bh=5zxDZEq+UtrgmXDLVzz74QwxYJIuQ4Gg8xFMZyqe/aQ=; b=1kH/Kf+IcRtYpfLtx98GP3YF0v9rCRXK/B/MmtXzauZnX+1rvi/BikdeYyIMTi5X2L ksmmA5qMMM+gTsHPmcTNiOC7nKFyQDov4AvZAci2KKHU1fCLk4KpD0O+StRjSUzSsl0z pQ3v8SURKlk/OZ/CPrhc1/O3yzvaZHy8dx0uMM5yq063ma2E2U0n+CbJA5WL7V9M92NI Sj+KI1iPZitUuRq36+bFL80DDrm4iPHgbKSq71oDDabC3rQ2559HmKXzfChll3/2WVMH oXZatvho0Fu8LH8ulzS0Z/7RL4LAA3UeuyPqoBa7uxAy4qSnT0rNJWPpLD5d+ag7ojl2 gY+Q== X-Gm-Message-State: AOAM5339/Ifz6CoUST16J74uoWaZUp9q0sLD5lo7yYd/2R0SjwmkfhxL LCoNMoLCiGvyDYJ4C4Gnmug= X-Google-Smtp-Source: ABdhPJy3gWRk5JESe/islHYzzE//6DMgZ4v0lxvO+7TNzK8Dox4cpy1tMaJEsy58kLphGQYFHWx6nA== X-Received: by 2002:a05:600c:1c20:: with SMTP id j32mr29817712wms.1.1636118737706; Fri, 05 Nov 2021 06:25:37 -0700 (PDT) Received: from [192.168.1.214] (host81-138-1-83.in-addr.btopenworld.com. [81.138.1.83]) by smtp.gmail.com with ESMTPSA id u5sm8302425wrg.57.2021.11.05.06.25.37 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Fri, 05 Nov 2021 06:25:37 -0700 (PDT) Content-Type: text/plain; charset=utf-8 Mime-Version: 1.0 (Mac OS X Mail 12.4 \(3445.104.21\)) Subject: Re: [PATCH v2] IPA: Provide a mechanism to register static DTORs via cxa_atexit. From: Iain Sandoe In-Reply-To: <3846F6D3-7701-4162-89A4-FB7C252C802F@gmail.com> Date: Fri, 5 Nov 2021 13:25:36 +0000 Cc: GCC Patches , Richard Biener Content-Transfer-Encoding: quoted-printable Message-Id: <1F3A5D86-7684-4AF7-A26B-81CA96E4468E@gmail.com> References: <20211104204645.25097-1-iain@sandoe.co.uk> <3846F6D3-7701-4162-89A4-FB7C252C802F@gmail.com> To: Jan Hubicka X-Mailer: Apple Mail (2.3445.104.21) X-Spam-Status: No, score=-8.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 05 Nov 2021 13:25:41 -0000 sheesh =E2=80=A6 EWRONGREVISEDPATCH > On 5 Nov 2021, at 13:08, Iain Sandoe wrote: >=20 > I tried enabling this on x86-64-linux (just for interest) and it seems = to work > OK there too - but that testing revealed a thinko that didn=E2=80=99t = show with a > a normal regstrap. =E2=80=A6 now with the correct patch. [PATCH v2] IPA: Provide a mechanism to register static DTORs via cxa_atexit. For at least one target (Darwin) the platform convention is to register static destructors (i.e. __attribute__((destructor))) with __cxa_atexit rather than placing them into a list that is run by some other mechanism. This patch provides a target hook that allows a target to opt into this and handling for the process in ipa_cdtor_merge (). When the mode is enabled (dtors_from_cxa_atexit is set) we: * Generate new CTORs to register static destructors with __cxa_atexit and add them to the existing list of CTORs; we then process the revised CTORs list. * We sort the DTORs into priority and then TU order, this means that they are registered in that order with __cxa_atexit () and therefore will be run in the reverse order. * Likewise, CTORs are sorted into priority and then TU order, which means that they will run in that order. This matches the behavior of using init/fini (or mod_init_func/mod_term_func) sections. Signed-off-by: Iain Sandoe gcc/ChangeLog: * config/darwin.h (TARGET_DTORS_FROM_CXA_ATEXIT): New. * doc/tm.texi: Regenerated. * doc/tm.texi.in: Add TARGET_DTORS_FROM_CXA_ATEXIT hook. * ipa.c (ipa_discover_variable_flags): (cgraph_build_static_cdtor_1): Return the built function decl. (build_cxa_atexit_decl): New. (build_dso_handle_decl): New. (build_cxa_dtor_registrations): New. (compare_cdtor_tu_order): New. (build_cxa_atexit_fns): New. (ipa_cdtor_merge): If dtors_from_cxa_atexit is set, process the DTORs/CTORs accordingly. (pass_ipa_cdtor_merge::gate): Also run if dtors_from_cxa_atexit is set. * target.def (dtors_from_cxa_atexit): New hook. --- gcc/config/darwin.h | 7 +- gcc/doc/tm.texi | 8 ++ gcc/doc/tm.texi.in | 2 + gcc/ipa.c | 200 +++++++++++++++++++++++++++++++++++++++++++- gcc/target.def | 10 +++ 5 files changed, 222 insertions(+), 5 deletions(-) diff --git a/gcc/config/darwin.h b/gcc/config/darwin.h index 27cb3e4bb30..2b19fb7c085 100644 --- a/gcc/config/darwin.h +++ b/gcc/config/darwin.h @@ -54,6 +54,11 @@ see the files COPYING3 and COPYING.RUNTIME = respectively. If not, see =20 #define DO_GLOBAL_DTORS_BODY =20 +/* Register static destructors to run from __cxa_atexit instead of = putting + them into a .mod_term_funcs section. */ + +#define TARGET_DTORS_FROM_CXA_ATEXIT true + /* The string value for __SIZE_TYPE__. */ =20 #ifndef SIZE_TYPE @@ -1160,7 +1165,7 @@ extern void darwin_driver_init (unsigned int = *,struct cl_decoded_option **); =20 /* The Apple assembler and linker do not support constructor = priorities. */ #undef SUPPORTS_INIT_PRIORITY -#define SUPPORTS_INIT_PRIORITY 0 +#define SUPPORTS_INIT_PRIORITY 1 =20 #undef STACK_CHECK_STATIC_BUILTIN #define STACK_CHECK_STATIC_BUILTIN 1 diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 990152f5b15..0a4df18b825 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -9233,6 +9233,14 @@ collecting constructors and destructors to be run = at startup and exit. It is false if we must use @command{collect2}. @end deftypevr =20 +@deftypevr {Target Hook} bool TARGET_DTORS_FROM_CXA_ATEXIT +This value is true if the target wants destructors to be queued to be +run from __cxa_atexit. If this is the case then, for each priority = level, +a new constructor will be entered that registers the destructors for = that +level with __cxa_atexit (and there will be no destructors emitted). +It is false the method implied by @code{have_ctors_dtors} is used. +@end deftypevr + @deftypefn {Target Hook} void TARGET_ASM_CONSTRUCTOR (rtx @var{symbol}, = int @var{priority}) If defined, a function that outputs assembler code to arrange to call the function referenced by @var{symbol} at initialization time. diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index 193c9bdd853..c733f356fe4 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -6021,6 +6021,8 @@ encountering an @code{init_priority} attribute. =20 @hook TARGET_HAVE_CTORS_DTORS =20 +@hook TARGET_DTORS_FROM_CXA_ATEXIT + @hook TARGET_ASM_CONSTRUCTOR =20 @hook TARGET_ASM_DESTRUCTOR diff --git a/gcc/ipa.c b/gcc/ipa.c index 4f62ac183ee..325b658b55e 100644 --- a/gcc/ipa.c +++ b/gcc/ipa.c @@ -837,7 +837,7 @@ ipa_discover_variable_flags (void) FINAL specify whether the externally visible name for collect2 = should be produced. */ =20 -static void +static tree cgraph_build_static_cdtor_1 (char which, tree body, int priority, bool = final, tree optimization, tree target) @@ -916,6 +916,7 @@ cgraph_build_static_cdtor_1 (char which, tree body, = int priority, bool final, =20 set_cfun (NULL); current_function_decl =3D NULL; + return decl; } =20 /* Generate and emit a static constructor or destructor. WHICH must @@ -1022,6 +1023,124 @@ build_cdtor (bool ctor_p, const vec = &cdtors) } } =20 +/* Helper functions for build_cxa_dtor_registrations (). + Build a decl for __cxa_atexit (). */ + +static tree +build_cxa_atexit_decl () +{ + /* The parameter to "__cxa_atexit" is "void (*)(void *)". */ + tree fn_type =3D build_function_type_list (void_type_node, + ptr_type_node, NULL_TREE); + tree fn_ptr_type =3D build_pointer_type (fn_type); + /* The declaration for `__cxa_atexit' is: + int __cxa_atexit (void (*)(void *), void *, void *). */ + const char *name =3D "__cxa_atexit"; + tree cxa_name =3D get_identifier (name); + fn_type =3D build_function_type_list (integer_type_node, fn_ptr_type, + ptr_type_node, ptr_type_node, = NULL_TREE); + tree atexit_fndecl =3D build_decl (BUILTINS_LOCATION, FUNCTION_DECL, + cxa_name, fn_type); + SET_DECL_ASSEMBLER_NAME (atexit_fndecl, cxa_name); + DECL_VISIBILITY (atexit_fndecl) =3D VISIBILITY_DEFAULT; + DECL_VISIBILITY_SPECIFIED (atexit_fndecl) =3D true; + set_call_expr_flags (atexit_fndecl, ECF_LEAF | ECF_NOTHROW); + TREE_PUBLIC (atexit_fndecl) =3D true; + DECL_EXTERNAL (atexit_fndecl) =3D true; + DECL_ARTIFICIAL (atexit_fndecl) =3D true; + return atexit_fndecl; +} + +/* Build a decl for __dso_handle. */ + +static tree +build_dso_handle_decl () +{ + /* Declare the __dso_handle variable. */ + tree dso_handle_decl =3D build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier ("__dso_handle"), + ptr_type_node); + TREE_PUBLIC (dso_handle_decl) =3D true; + DECL_EXTERNAL (dso_handle_decl) =3D true; + DECL_ARTIFICIAL (dso_handle_decl) =3D true; +#ifdef HAVE_GAS_HIDDEN + if (dso_handle_decl !=3D error_mark_node) + { + DECL_VISIBILITY (dso_handle_decl) =3D VISIBILITY_HIDDEN; + DECL_VISIBILITY_SPECIFIED (dso_handle_decl) =3D true; + } +#endif + return dso_handle_decl; +} + +/* This builds one or more constructor functions that register DTORs = with + __cxa_atexit (). Within a priority level, DTORs are registered in = TU + order - which means that they will run in reverse TU order from = cxa_atexit. + This is the same behavior as using a .fini / .mod_term_funcs = section. + As the functions are built, they are appended to the CTORs vector. = */ + +static void +build_cxa_dtor_registrations (const vec &dtors, vec *ctors) +{ + size_t i,j; + size_t len =3D dtors.length (); + + location_t sav_loc =3D input_location; + input_location =3D UNKNOWN_LOCATION; + + tree atexit_fndecl =3D build_cxa_atexit_decl (); + tree dso_handle_decl =3D build_dso_handle_decl (); + + /* We want &__dso_handle. */ + tree dso_ptr =3D build1_loc (UNKNOWN_LOCATION, ADDR_EXPR, + ptr_type_node, dso_handle_decl); + + i =3D 0; + while (i < len) + { + priority_type priority =3D 0; + tree body =3D NULL_TREE; + j =3D i; + do + { + priority_type p; + tree fn =3D dtors[j]; + p =3D DECL_FINI_PRIORITY (fn); + if (j =3D=3D i) + priority =3D p; + else if (p !=3D priority) + break; + j++; + } + while (j < len); + + /* Find the next batch of destructors with the same = initialization + priority. */ + for (;i < j; i++) + { + tree fn =3D dtors[i]; + DECL_STATIC_DESTRUCTOR (fn) =3D 0; + tree dtor_ptr =3D build1_loc (UNKNOWN_LOCATION, ADDR_EXPR, + ptr_type_node, fn); + tree call_cxa_atexit + =3D build_call_expr_loc (UNKNOWN_LOCATION, atexit_fndecl, 3, + dtor_ptr, null_pointer_node, = dso_ptr); + TREE_SIDE_EFFECTS (call_cxa_atexit) =3D 1; + append_to_statement_list (call_cxa_atexit, &body); + } + + gcc_assert (body !=3D NULL_TREE); + /* Generate a function to register the DTORs at this priority. = */ + tree new_ctor + =3D cgraph_build_static_cdtor_1 ('I', body, priority, true, + = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (dtors[0]), + DECL_FUNCTION_SPECIFIC_TARGET = (dtors[0])); + /* Add this to the list of ctors. */ + ctors->safe_push (new_ctor); + } + input_location =3D sav_loc; +} + /* Comparison function for qsort. P1 and P2 are actually of type "tree *" and point to static constructors. DECL_INIT_PRIORITY is used to determine the sort order. */ @@ -1071,7 +1190,46 @@ compare_dtor (const void *p1, const void *p2) else if (priority1 > priority2) return 1; else - /* Ensure a stable sort. */ + /* Ensure a stable sort - into TU order. */ + return DECL_UID (f1) - DECL_UID (f2); +} + +/* Comparison function for qsort. P1 and P2 are of type "tree *" and = point to + a pair of static constructors or destructors. We first sort on the = basis of + priority and then into TU order (on the strict assumption that = DECL_UIDs are + ordered in the same way as the original functions). ???: this seems = quite + fragile. */ + +static int +compare_cdtor_tu_order (const void *p1, const void *p2) +{ + tree f1; + tree f2; + int priority1; + int priority2; + + f1 =3D *(const tree *)p1; + f2 =3D *(const tree *)p2; + /* We process the DTORs first, and then remove their flag, so this = order + allows for functions that are declared as both CTOR and DTOR. */ + if (DECL_STATIC_DESTRUCTOR (f1)) + { + gcc_checking_assert (DECL_STATIC_DESTRUCTOR (f2)); + priority1 =3D DECL_FINI_PRIORITY (f1); + priority2 =3D DECL_FINI_PRIORITY (f2); + } + else + { + priority1 =3D DECL_INIT_PRIORITY (f1); + priority2 =3D DECL_INIT_PRIORITY (f2); + } + + if (priority1 < priority2) + return -1; + else if (priority1 > priority2) + return 1; + else + /* For equal priority, sort into the order of definition in the TU. = */ return DECL_UID (f1) - DECL_UID (f2); } =20 @@ -1097,6 +1255,37 @@ build_cdtor_fns (vec *ctors, vec = *dtors) } } =20 +/* Generate new CTORs to register static destructors with __cxa_atexit = and add + them to the existing list of CTORs; we then process the revised = CTORs list. + + We sort the DTORs into priority and then TU order, this means that = they are + registered in that order with __cxa_atexit () and therefore will be = run in + the reverse order. + + Likewise, CTORs are sorted into priority and then TU order, which = means that + they will run in that order. + + This matches the behavior of using init/fini or = mod_init_func/mod_term_func + sections. */ + +static void +build_cxa_atexit_fns (vec *ctors, vec *dtors) +{ + if (!dtors->is_empty ()) + { + gcc_assert (targetm.dtors_from_cxa_atexit); + dtors->qsort (compare_cdtor_tu_order); + build_cxa_dtor_registrations (*dtors, ctors); + } + + if (!ctors->is_empty ()) + { + gcc_assert (targetm.dtors_from_cxa_atexit); + ctors->qsort (compare_cdtor_tu_order); + build_cdtor (/*ctor_p=3D*/true, *ctors); + } +} + /* Look for constructors and destructors and produce function calling = them. This is needed for targets not supporting ctors or dtors, but we = perform the transformation also at linktime to merge possibly numerous @@ -1115,7 +1304,10 @@ ipa_cdtor_merge (void) if (DECL_STATIC_CONSTRUCTOR (node->decl) || DECL_STATIC_DESTRUCTOR (node->decl)) record_cdtor_fn (node, &ctors, &dtors); - build_cdtor_fns (&ctors, &dtors); + if (targetm.dtors_from_cxa_atexit) + build_cxa_atexit_fns (&ctors, &dtors); + else + build_cdtor_fns (&ctors, &dtors); return 0; } =20 @@ -1162,7 +1354,7 @@ pass_ipa_cdtor_merge::gate (function *) /* Perform the pass when we have no ctors/dtors support or at LTO time to merge multiple constructors into single function. */ - return !targetm.have_ctors_dtors || in_lto_p; + return !targetm.have_ctors_dtors || in_lto_p || = targetm.dtors_from_cxa_atexit; } =20 } // anon namespace diff --git a/gcc/target.def b/gcc/target.def index 87feeec2ea1..6fe1a02a5e8 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -6819,6 +6819,16 @@ collecting constructors and destructors to be run = at startup and exit.\n\ It is false if we must use @command{collect2}.", bool, false) =20 +/* True if the target wants DTORs to be run from cxa_atexit. */ +DEFHOOKPOD +(dtors_from_cxa_atexit, + "This value is true if the target wants destructors to be queued to = be\n\ +run from __cxa_atexit. If this is the case then, for each priority = level,\n\ +a new constructor will be entered that registers the destructors for = that\n\ +level with __cxa_atexit (and there will be no destructors emitted).\n\ +It is false the method implied by @code{have_ctors_dtors} is used.", + bool, false) + /* True if thread-local storage is supported. */ DEFHOOKPOD (have_tls, --=20 2.24.3 (Apple Git-128)