From patchwork Wed Dec 7 15:39:39 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Sandoe X-Patchwork-Id: 61663 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 026E0382E47B for ; Wed, 7 Dec 2022 15:40:17 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 026E0382E47B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1670427617; bh=ke6PoGPqXL2DUGbe3l70mgyV0dipsAfnFWS54xXlF6c=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=DwR4MsvJKW/QMXWGID7D7brcWXVvfGxQV+ijDJscNzNh4/LmbLTtcKAxhEeuiDdyl LeNshm0b858NxneZZpi9Hvvt64iEG16dhY/pTbUyAl19VJbam2YCBLLYe+RSsziA6H 56TjbCm+72KapPlS31vwfYKf7345bw4BWyvlBF9c= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wr1-x430.google.com (mail-wr1-x430.google.com [IPv6:2a00:1450:4864:20::430]) by sourceware.org (Postfix) with ESMTPS id AAB5B3834C03 for ; Wed, 7 Dec 2022 15:39:46 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org AAB5B3834C03 Received: by mail-wr1-x430.google.com with SMTP id co23so1179886wrb.4 for ; Wed, 07 Dec 2022 07:39:46 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:reply-to:message-id:date :subject:cc:to:from:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=ke6PoGPqXL2DUGbe3l70mgyV0dipsAfnFWS54xXlF6c=; b=Z6gZnOeXoVsB2I2xUoDnA1R9qKd2nA7LDF3X5pxyEODsQgIh2Ufw7aBRJ/MmLtLpnI OUVTWeHS9jdy35GOLHw2FFyptnniVTwFl2H0+Zi7gIF5GtYSI5VDg/8sKYf4elZFcXKK EsRZHUyljf2r8aoyS8soXEtC2sKVgEbQnZrRvlZm5QvW2Y1edLSj3dESzzBYxIv479JF IL97biKSN695Sn9ftJgQKb6hYs1MEshF2CqzIH+9wlZA2G+1VIvrXi2NZaqHRMdyaPbJ gbIWJ36XUMmgxnXPiA72wcfRvlM8xJGd1vnkoudllok6SOYjhax/Ta9503kxJsYKGYfU L4rg== X-Gm-Message-State: ANoB5plKwfOq7b5Ayep9ZQwn2j1/mDo6hkE0D1YACSqWDlL3YmTpCD2I a8FBbzfhxVeKoCKPPLumuAnGFKOkw4I= X-Google-Smtp-Source: AA0mqf6WUCQMf4QJWtr8Qk2ulrmmdVDD3mPmaVi99Fqw9iGxzjUpulXgErDWz3/ttEedHJ5a0JD4og== X-Received: by 2002:a5d:6f02:0:b0:242:1720:d52d with SMTP id ay2-20020a5d6f02000000b002421720d52dmr27401020wrb.704.1670427585204; Wed, 07 Dec 2022 07:39:45 -0800 (PST) Received: from localhost.localdomain (host81-138-1-83.in-addr.btopenworld.com. [81.138.1.83]) by smtp.gmail.com with ESMTPSA id o30-20020a05600c511e00b003a3442f1229sm2460319wms.29.2022.12.07.07.39.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 07 Dec 2022 07:39:44 -0800 (PST) X-Google-Original-From: Iain Sandoe To: gcc-patches@gcc.gnu.org Subject: [PATCH] c++, TLS: Support cross-tu static initialization for targets without alias support [PR106435]. Date: Wed, 7 Dec 2022 15:39:39 +0000 Message-Id: <20221207153939.49157-1-iain@sandoe.co.uk> X-Mailer: git-send-email 2.37.1 (Apple Git-137.1) MIME-Version: 1.0 X-Spam-Status: No, score=-8.3 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.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) 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-Patchwork-Original-From: Iain Sandoe via Gcc-patches From: Iain Sandoe Reply-To: iain@sandoe.co.uk Cc: Iain Sandoe , jason@redhat.com Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" This has been tested on x86_64 and arm64 Darwin and on x86_64 linux gnu. The basic patch is live in the homebrew macOS support and so has had quite wide coverage on non-trivial codebases. OK for master? Iain Since this actually fixes wrong code, I wonder if we should also consider back-porting. --- >8 --- The description below relates to the code path when TARGET_SUPPORTS_ALIASES is false; current operation is maintained for targets with alias support and any new support code should be DCEd in that case. --- Currently, cross-tu static initialisation is not supported for targets without alias support. The patch adds support by building a shim function in place of the alias for these targets; the shim simply calls the generic initialiser. Although this is slightly less efficient than the alias, in practice (for targets that allow sibcalls) the penalty is a single jump when code is optimised. From the perspective of a TU referencing an extern TLS variable, there is no way to determine if it requires a guarded dynamic init. So, in the referencing TU, we build a weak reference to the potential init and check at runtime if the init is present before calling it. This strategy is fine for targets that have ELF semantics, but fails at link time for Mach-O (which does not permit the reference to be undefined in the static link). The actual initialiser call is contained in a wrapper function, and to resolve the Mach-O linker issue, in the TU that is referencing the var, we now generate both the wrapper _and_ a weak definition of a dummy init function. In the case that there _is_ a dynamic init (in a different TU), that version will be non-weak and will be override the weak dummy one. In the case that we have a trivial static init (so no init in any other TU) the weak-defined dummy init will be called (a single return insn for optimised code). We mitigate the call to the dummy init by reworking the wrapper code-gen path to remove the test for the weak reference function (as it will always be true) since the static linker will now determine the function to be called. Signed-off-by: Iain Sandoe PR c++/106435 gcc/c-family/ChangeLog: * c-opts.cc (c_common_post_options): Allow fextern-tls-init for targets without alias support. gcc/cp/ChangeLog: * decl2.cc (get_tls_init_fn): Allow targets without alias support. (handle_tls_init): Emit aliases for single init functions where the target supporst this, otherwise emit a stub function that calls the main tls init function. (generate_tls_dummy_init): New. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/pr106435-b.cc: New file. * g++.dg/cpp0x/pr106435.C: New test. * g++.dg/cpp0x/pr106435.h: New file. --- gcc/c-family/c-opts.cc | 2 +- gcc/cp/decl2.cc | 80 ++++++++++++++++++++---- gcc/testsuite/g++.dg/cpp0x/pr106435-b.cc | 22 +++++++ gcc/testsuite/g++.dg/cpp0x/pr106435.C | 24 +++++++ gcc/testsuite/g++.dg/cpp0x/pr106435.h | 27 ++++++++ 5 files changed, 142 insertions(+), 13 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr106435-b.cc create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr106435.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr106435.h diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc index 70745aa4e7c..064645f980d 100644 --- a/gcc/c-family/c-opts.cc +++ b/gcc/c-family/c-opts.cc @@ -1070,7 +1070,7 @@ c_common_post_options (const char **pfilename) if (flag_extern_tls_init) { - if (!TARGET_SUPPORTS_ALIASES || !SUPPORTS_WEAK) + if (!SUPPORTS_WEAK) { /* Lazy TLS initialization for a variable in another TU requires alias and weak reference support. */ diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index f95529a5c9a..c6550c0c2fc 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -3672,9 +3672,8 @@ get_tls_init_fn (tree var) if (!flag_extern_tls_init && DECL_EXTERNAL (var)) return NULL_TREE; - /* If the variable is internal, or if we can't generate aliases, - call the local init function directly. */ - if (!TREE_PUBLIC (var) || !TARGET_SUPPORTS_ALIASES) + /* If the variable is internal call the local init function directly. */ + if (!TREE_PUBLIC (var)) return get_local_tls_init_fn (DECL_SOURCE_LOCATION (var)); tree sname = mangle_tls_init_fn (var); @@ -3811,8 +3810,12 @@ generate_tls_wrapper (tree fn) if (tree init_fn = get_tls_init_fn (var)) { tree if_stmt = NULL_TREE; - /* If init_fn is a weakref, make sure it exists before calling. */ - if (lookup_attribute ("weak", DECL_ATTRIBUTES (init_fn))) + + /* If init_fn is a weakref, make sure it exists before calling. + If the target does not support aliases, then we will have generated + a dummy weak function, so there is no need to test its existence. */ + if (TARGET_SUPPORTS_ALIASES && + lookup_attribute ("weak", DECL_ATTRIBUTES (init_fn))) { if_stmt = begin_if_stmt (); tree addr = cp_build_addr_expr (init_fn, tf_warning_or_error); @@ -3837,6 +3840,25 @@ generate_tls_wrapper (tree fn) expand_or_defer_fn (finish_function (/*inline_p=*/false)); } +/* A dummy init function to act as a weak placeholder for a (possibly non- + existent) dynamic init. */ +static void +generate_tls_dummy_init (tree fn) +{ + tree var = DECL_BEFRIENDING_CLASSES (fn); + tree init_fn = get_tls_init_fn (var); + /* If have no init fn, or it is non-weak, then we do not need to make a + dummy. */ + if (!init_fn || !lookup_attribute ("weak", DECL_ATTRIBUTES (init_fn))) + return; + start_preparsed_function (init_fn, NULL_TREE, SF_DEFAULT | SF_PRE_PARSED); + tree body = begin_function_body (); + declare_weak (init_fn); + finish_return_stmt (NULL_TREE); + finish_function_body (body); + expand_or_defer_fn (finish_function (/*inline_p=*/false)); +} + /* Start a global constructor or destructor function. */ static tree @@ -4626,22 +4648,24 @@ handle_tls_init (void) finish_expr_stmt (cp_build_modify_expr (loc, guard, NOP_EXPR, boolean_true_node, tf_warning_or_error)); + auto_vec direct_calls; for (; vars; vars = TREE_CHAIN (vars)) { tree var = TREE_VALUE (vars); tree init = TREE_PURPOSE (vars); one_static_initialization_or_destruction (/*initp=*/true, var, init); - /* Output init aliases even with -fno-extern-tls-init. */ - if (TARGET_SUPPORTS_ALIASES && TREE_PUBLIC (var)) + /* Output inits even with -fno-extern-tls-init. + We save the list here and output either an alias or a stub function + below. */ + if (TREE_PUBLIC (var)) { tree single_init_fn = get_tls_init_fn (var); if (single_init_fn == NULL_TREE) continue; - cgraph_node *alias - = cgraph_node::get_create (fn)->create_same_body_alias - (single_init_fn, fn); - gcc_assert (alias != NULL); + if (single_init_fn == fn) + continue; + direct_calls.safe_push (single_init_fn); } } @@ -4649,6 +4673,30 @@ handle_tls_init (void) finish_if_stmt (if_stmt); finish_function_body (body); expand_or_defer_fn (finish_function (/*inline_p=*/false)); + + /* For each TLS var that we have an init function, we either emit an alias + between that and the tls_init, or a stub function that just calls the + tls_init. */ + while (!direct_calls.is_empty()) + { + tree single_init_fn = direct_calls.pop (); + if (TARGET_SUPPORTS_ALIASES) + { + cgraph_node *alias + = cgraph_node::get_create (fn)->create_same_body_alias + (single_init_fn, fn); + gcc_assert (alias != NULL); + } + else + { + start_preparsed_function (single_init_fn, NULL_TREE, SF_PRE_PARSED); + tree body = begin_function_body (); + tree r = build_call_expr (fn, 0); + finish_expr_stmt (r); + finish_function_body (body); + expand_or_defer_fn (finish_function (/*inline_p=*/false)); + } + } } /* We're at the end of compilation, so generate any mangling aliases that @@ -5058,7 +5106,15 @@ c_parse_final_cleanups (void) } if (!DECL_INITIAL (decl) && decl_tls_wrapper_p (decl)) - generate_tls_wrapper (decl); + { + generate_tls_wrapper (decl); + if (!TARGET_SUPPORTS_ALIASES) + /* The wrapper might have a weak reference to an init, we provide + a dummy function to satisfy that here. The linker/dynamic + loader will override this with the actual init, if one is + required. */ + generate_tls_dummy_init (decl); + } if (!DECL_SAVED_TREE (decl)) continue; diff --git a/gcc/testsuite/g++.dg/cpp0x/pr106435-b.cc b/gcc/testsuite/g++.dg/cpp0x/pr106435-b.cc new file mode 100644 index 00000000000..bd586286647 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/pr106435-b.cc @@ -0,0 +1,22 @@ +// PR c++/106435 +#include "pr106435.h" + +//#include + +Foo::Foo() { + ++num_calls; +// std::cout << "Foo::Foo(this=" << this << ")\n"; +} + +int Foo::func() { +// std::cout << "Foo::func(this=" << this << ")\n"; + return num_calls; +} + +thread_local Foo Bar::foo; +thread_local Foo Bar::baz; + +thread_local Foo F = Foo{5}; + +thread_local void* tl = (void*)&F; +thread_local void* tl1 = nullptr; diff --git a/gcc/testsuite/g++.dg/cpp0x/pr106435.C b/gcc/testsuite/g++.dg/cpp0x/pr106435.C new file mode 100644 index 00000000000..9071e032697 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/pr106435.C @@ -0,0 +1,24 @@ +// PR c++/106435 +// { dg-do run { target c++11 } } +// { dg-additional-sources "pr106435-b.cc" } + +#include "pr106435.h" + +int num_calls = 0; + +thread_local Foo Bar::bat; + +int main() { + int v = Bar::foo.func(); + if (v != 2) + __builtin_abort (); + v = Bar::bat.func(); + if (v != 3) + __builtin_abort (); + if (F.getX() != 5) + __builtin_abort(); + if (gt_tl () != (void*)&F) + __builtin_abort(); + if (gt_tl1 ()) + __builtin_abort(); +} diff --git a/gcc/testsuite/g++.dg/cpp0x/pr106435.h b/gcc/testsuite/g++.dg/cpp0x/pr106435.h new file mode 100644 index 00000000000..06d3e60484f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/pr106435.h @@ -0,0 +1,27 @@ +// PR c++/106435 +#pragma once + +extern int num_calls; +struct Foo { + Foo(); + Foo(int _x) : x(_x) {} + int func(); + int getX () { return x; } + int x; +}; + +struct Bar { + thread_local static Foo foo; + thread_local static Foo baz; + thread_local static Foo bat; +}; + +extern thread_local void* tl; + +inline void* gt_tl () {return tl;} + +extern thread_local Foo F; + +extern thread_local void* tl1; + +inline void* gt_tl1 () {return tl1;}