From patchwork Wed Jul 14 23:50:20 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Henderson X-Patchwork-Id: 58941 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 69EBFB6F0E for ; Thu, 15 Jul 2010 09:50:46 +1000 (EST) Received: (qmail 24047 invoked by alias); 14 Jul 2010 23:50:44 -0000 Received: (qmail 24034 invoked by uid 22791); 14 Jul 2010 23:50:37 -0000 X-SWARE-Spam-Status: No, hits=-4.4 required=5.0 tests=AWL, BAYES_00, KAM_STOCKGEN, RCVD_IN_DNSWL_HI, SPF_HELO_PASS, TW_CP, TW_TM, T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 14 Jul 2010 23:50:24 +0000 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o6ENoMpn017290 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 14 Jul 2010 19:50:22 -0400 Received: from anchor.twiddle.home (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o6ENoLnP028679; Wed, 14 Jul 2010 19:50:21 -0400 Message-ID: <4C3E4D3C.1030205@redhat.com> Date: Wed, 14 Jul 2010 16:50:20 -0700 From: Richard Henderson User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.10) Gecko/20100621 Fedora/3.0.5-1.fc13 Thunderbird/3.0.5 MIME-Version: 1.0 To: IainS CC: GCC Patches , richard.guenther@gmail.com Subject: [CFT, try 7] Emulated tls rewrite X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org This go-around handles phi nodes, as requested by Richi. This was much more irritating than it should have been, due to immediate use maintainence. It also gets re-arranged to use walk_gimple_ops, which I should have been using in the first place. The testsuite is still running here, amd64-linux --disable-tls, but I'm quitting for today. I never got around to looking at those non-tls.exp testsuite failures, Iain. Hopefully they went away magically. ;-) r~ diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 5a45701..893d30d 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1354,6 +1354,7 @@ OBJS-common = \ tree-diagnostic.o \ tree-dump.o \ tree-eh.o \ + tree-emutls.o \ tree-if-conv.o \ tree-into-ssa.o \ tree-iterator.o \ @@ -3142,6 +3143,9 @@ tree-switch-conversion.o : tree-switch-conversion.c $(CONFIG_H) $(SYSTEM_H) \ tree-complex.o : tree-complex.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \ $(TM_H) $(FLAGS_H) $(TREE_FLOW_H) $(GIMPLE_H) \ tree-iterator.h $(TREE_PASS_H) tree-ssa-propagate.h $(DIAGNOSTIC_H) +tree-emutls.o : tree-emutls.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \ + $(GIMPLE_H) $(TREE_PASS_H) $(TREE_FLOW_H) $(CGRAPH_H) langhooks.h \ + $(TARGET_H) targhooks.h tree-iterator.h output.h gt-tree-emutls.h tree-vect-generic.o : tree-vect-generic.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) \ $(TM_H) $(TREE_FLOW_H) $(GIMPLE_H) tree-iterator.h $(TREE_PASS_H) \ $(FLAGS_H) $(OPTABS_H) $(MACHMODE_H) $(EXPR_H) \ @@ -3746,6 +3750,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \ $(srcdir)/tree-ssanames.c $(srcdir)/tree-eh.c $(srcdir)/tree-ssa-address.c \ $(srcdir)/tree-cfg.c \ $(srcdir)/tree-dfa.c \ + $(srcdir)/tree-emutls.c \ $(srcdir)/tree-iterator.c $(srcdir)/gimplify.c \ $(srcdir)/tree-chrec.h \ $(srcdir)/tree-scalar-evolution.c \ diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index f0d0a97..f9d45ec 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -418,12 +418,17 @@ cgraph_process_new_functions (void) push_cfun (DECL_STRUCT_FUNCTION (fndecl)); current_function_decl = fndecl; compute_inline_parameters (node); - if ((cgraph_state == CGRAPH_STATE_IPA_SSA - && !gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl))) - /* When not optimizing, be sure we run early local passes anyway - to expand OMP. */ - || !optimize) + + /* ??? Honza: what's the real difference between IPA and IPA_SSA? + We seem to be assuming that the "real" ipa passes require SSA + but that the "small" ipa passes do not. This is false. Any + new function created by a "small" ipa pass *must* have the + early local passes run so that (at least) init_datastructures + gets executed. Failure to do so results in an immediate crash + once we get to pass_all_optimizations. */ + if (!gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl))) execute_pass_list (pass_early_local_passes.pass.sub); + free_dominance_info (CDI_POST_DOMINATORS); free_dominance_info (CDI_DOMINATORS); pop_cfun (); @@ -2064,7 +2069,9 @@ cgraph_build_static_cdtor (char which, tree body, int priority) cgraph_add_new_function (decl, false); cgraph_mark_needed_node (cgraph_node (decl)); + set_cfun (NULL); + current_function_decl = NULL; } void diff --git a/gcc/expr.c b/gcc/expr.c index 899d5b8..07e2d7e 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -6819,19 +6819,6 @@ highest_pow2_factor_for_target (const_tree target, const_tree exp) return MAX (factor, talign); } -/* Return &VAR expression for emulated thread local VAR. */ - -static tree -emutls_var_address (tree var) -{ - tree emuvar = emutls_decl (var); - tree fn = built_in_decls [BUILT_IN_EMUTLS_GET_ADDRESS]; - tree arg = build_fold_addr_expr_with_type (emuvar, ptr_type_node); - tree call = build_call_expr (fn, 1, arg); - return fold_convert (build_pointer_type (TREE_TYPE (var)), call); -} - - /* Subroutine of expand_expr. Expand the two operands of a binary expression EXP0 and EXP1 placing the results in OP0 and OP1. The value may be stored in TARGET if TARGET is nonzero. The @@ -6934,18 +6921,6 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode, inner = TREE_OPERAND (exp, 0); break; - case VAR_DECL: - /* TLS emulation hook - replace __thread VAR's &VAR with - __emutls_get_address (&_emutls.VAR). */ - if (! targetm.have_tls - && TREE_CODE (exp) == VAR_DECL - && DECL_THREAD_LOCAL_P (exp)) - { - exp = emutls_var_address (exp); - return expand_expr (exp, target, tmode, modifier); - } - /* Fall through. */ - default: /* If the object is a DECL, then expand it for its rtl. Don't bypass expand_expr, as that can have various side effects; LABEL_DECLs for @@ -8383,16 +8358,6 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode, && (TREE_STATIC (exp) || DECL_EXTERNAL (exp))) layout_decl (exp, 0); - /* TLS emulation hook - replace __thread vars with - *__emutls_get_address (&_emutls.var). */ - if (! targetm.have_tls - && TREE_CODE (exp) == VAR_DECL - && DECL_THREAD_LOCAL_P (exp)) - { - exp = build_fold_indirect_ref_loc (loc, emutls_var_address (exp)); - return expand_expr_real_1 (exp, target, tmode, modifier, NULL); - } - /* ... fall through ... */ case FUNCTION_DECL: diff --git a/gcc/output.h b/gcc/output.h index d1e5f24..1756efc 100644 --- a/gcc/output.h +++ b/gcc/output.h @@ -165,9 +165,6 @@ extern void merge_weak (tree, tree); /* Emit any pending weak declarations. */ extern void weak_finish (void); -/* Emit any pending emutls declarations and initializations. */ -extern void emutls_finish (void); - /* Return the default TLS model for a given variable. */ extern enum tls_model decl_default_tls_model (const_tree); diff --git a/gcc/passes.c b/gcc/passes.c index 72e9b5a..5a4cdc8 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -804,6 +804,7 @@ init_optimization_passes (void) } NEXT_PASS (pass_ipa_increase_alignment); NEXT_PASS (pass_ipa_matrix_reorg); + NEXT_PASS (pass_ipa_lower_emutls); *p = NULL; p = &all_regular_ipa_passes; diff --git a/gcc/testsuite/gcc.dg/tls/thr-init-1.c b/gcc/testsuite/gcc.dg/tls/thr-init-1.c new file mode 100644 index 0000000..de273d9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tls/thr-init-1.c @@ -0,0 +1,8 @@ +/* { dg-require-effective-target tls } */ +/* { dg-do compile } */ + +static __thread int fstat ; +static __thread int fstat = 1 ; +static __thread int fstat ; +static __thread int fstat = 2; /* { dg-error "redefinition of 'fstat'" } */ + /* { dg-message "note: previous definition of 'fstat' was here" "" { target *-*-* } 5 } */ diff --git a/gcc/testsuite/gcc.dg/tls/thr-init-2.c b/gcc/testsuite/gcc.dg/tls/thr-init-2.c new file mode 100644 index 0000000..6d00d8c --- /dev/null +++ b/gcc/testsuite/gcc.dg/tls/thr-init-2.c @@ -0,0 +1,23 @@ +/* { dg-require-effective-target tls } */ +/* { dg-do run } */ + +extern void abort() ; + +static __thread int fstat ; +static __thread int fstat = 1; + +int test_code(int b) +{ + fstat += b ; + return fstat; +} + +int main (int ac, char *av[]) +{ + int a = test_code(1); + + if ((a != 2) || (fstat != 2)) + abort () ; + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c b/gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c new file mode 100644 index 0000000..89725c3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/tls/thr-init-1.c @@ -0,0 +1,25 @@ +/* { dg-do run } */ +/* { dg-require-effective-target tls } */ + +extern int printf (char *,...); +extern void abort() ; + +int test_code(int b) +{ +static __thread int fstat = 1; + fstat += b ; + return fstat; +} + +int main (int ac, char *av[]) +{ + int a = test_code(1); + + if ( a != 2 ) + { + printf ("a=%d\n", a) ; + abort (); + } + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c b/gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c new file mode 100644 index 0000000..9d09319 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/tls/thr-init-2.c @@ -0,0 +1,28 @@ +/* { dg-do run } */ +/* { dg-require-effective-target tls } */ + +extern int printf (char *,...); +extern void abort() ; + +static __thread int fstat ; +static __thread int fstat = 1; +static __thread int fstat ; + +int test_code(int b) +{ + fstat += b ; + return fstat; +} + +int main (int ac, char *av[]) +{ + int a = test_code(1); + + if ( a != 2 || fstat != 2 ) + { + printf ("a=%d fstat=%d\n", a, fstat) ; + abort (); + } + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/torture/tls/tls-test.c b/gcc/testsuite/gcc.dg/torture/tls/tls-test.c new file mode 100644 index 0000000..a40e15e --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/tls/tls-test.c @@ -0,0 +1,51 @@ +/* { dg-do run } */ +/* { dg-require-effective-target tls } */ +/* { dg-require-effective-target pthread } */ + +#include +extern int printf (char *,...); +__thread int a = 5; +int *volatile a_in_other_thread = (int *) 12345; + +static void * +thread_func (void *arg) +{ + a_in_other_thread = &a; + a+=5; + *((int *) arg) = a; + return (void *)0; +} + +int +main () +{ + pthread_t thread; + void *thread_retval; + int *volatile a_in_main_thread; + int *volatile again ; + int thr_a; + + a_in_main_thread = &a; + + if (pthread_create (&thread, (pthread_attr_t *)0, thread_func, &thr_a)) + return 0; + + if (pthread_join (thread, &thread_retval)) + return 0; + + again = &a; + if (again != a_in_main_thread) + { + printf ("FAIL: main thread addy changed from 0x%0x to 0x%0x\n", + a_in_other_thread, again); + return 1; + } + + if (a != 5 || thr_a != 10 || (a_in_other_thread == a_in_main_thread)) + { + printf ("FAIL: a= %d, thr_a = %d Addr = 0x%0x\n", + a, thr_a, a_in_other_thread); + return 1; + } + return 0; +} diff --git a/gcc/testsuite/gcc.dg/torture/tls/tls.exp b/gcc/testsuite/gcc.dg/torture/tls/tls.exp new file mode 100644 index 0000000..91c8843 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/tls/tls.exp @@ -0,0 +1,36 @@ +# Copyright (C) 2010 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# . + +# GCC testsuite that uses the `dg.exp' driver. + +# Load support procs. +load_lib gcc-dg.exp + +# If a testcase doesn't have special options, use these. +global DEFAULT_CFLAGS +if ![info exists DEFAULT_CFLAGS] then { + set DEFAULT_CFLAGS " -ansi -pedantic-errors" +} + +# Initialize `dg'. +dg-init + +# Main loop. +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \ + $DEFAULT_CFLAGS + +# All done. +dg-finish diff --git a/gcc/toplev.c b/gcc/toplev.c index 964669f..60ca5dc 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -992,11 +992,6 @@ compile_file (void) if (seen_error ()) return; - /* Ensure that emulated TLS control vars are finalized and build - a static constructor for them, when it is required. */ - if (!targetm.have_tls) - emutls_finish (); - varpool_assemble_pending_decls (); finish_aliases_2 (); diff --git a/gcc/tree-emutls.c b/gcc/tree-emutls.c new file mode 100644 index 0000000..f7eb4cd --- /dev/null +++ b/gcc/tree-emutls.c @@ -0,0 +1,784 @@ +/* Lower TLS operations to emulation functions. + Copyright (C) 2006, 2007, 2008, 2009, 2010 + Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "gimple.h" +#include "tree-pass.h" +#include "tree-flow.h" +#include "cgraph.h" +#include "langhooks.h" +#include "target.h" + +/* ??? Should go. */ +#include "targhooks.h" +#include "tree-iterator.h" +#include "output.h" + +/* TODO: Get rid of the EMUTLS hooks that no one uses. */ + +/* Whenever a target does not support thread-local storage (TLS) natively, + we can emulate it with some run-time support in libgcc. This will in + turn rely on "keyed storage" a-la pthread_key_create; essentially all + thread libraries provide such functionality. + + In order to coordinate with the libgcc runtime, each TLS variable is + described by a "control variable". This control variable records the + required size, alignment, and initial value of the TLS variable for + instantiation at runtime. It also stores an integer token to be used + by the runtime to find the address of the variable within each thread. + + On the compiler side, this means that we need to replace all instances + of "tls_var" in the code with "*__emutls_get_addr(&control_var)". We + also need to eliminate "tls_var" from the symbol table and introduce + "control_var". + + We used to perform all of the transformations during conversion to rtl, + and the variable substitutions magically within assemble_variable. + However, this late fiddling of the symbol table conflicts with LTO and + whole-program compilation. Therefore we must now make all the changes + to the symbol table early in the GIMPLE optimization path, before we + write things out to LTO intermediate files. */ + +/* These two vectors, once fully populated, are kept in lock-step so that + the index of a TLS variable equals the index of its control variable in + the other vector. */ +static GTY (()) varpool_node_set tls_vars; +static GTY (()) varpool_node_set control_vars; + +/* The type of the control structure, shared with the emutls.c runtime. */ +/* ??? See if we can eliminate the one query via decl_emutls_var_p from + varasm.c. With that gone, this need not be live outside the ipa pass. */ +static GTY (()) tree emutls_object_type; + +/* Emulated TLS objects have the TLS model TLS_MODEL_EMULATED. This + macro can be used on them to distinguish the control variable from + the initialization template. */ + +bool +decl_emutls_var_p(const_tree decl) +{ + return TREE_TYPE (decl) == emutls_object_type; +} + +#if !defined (NO_DOT_IN_LABEL) +# define EMUTLS_SEPARATOR "." +#elif !defined (NO_DOLLAR_IN_LABEL) +# define EMUTLS_SEPARATOR "$" +#else +# define EMUTLS_SEPARATOR "_" +#endif + +/* Create an IDENTIFIER_NODE by prefixing PREFIX to the + IDENTIFIER_NODE NAME's name. */ + +static tree +prefix_name (const char *prefix, tree name) +{ + unsigned plen = strlen (prefix); + unsigned nlen = strlen (IDENTIFIER_POINTER (name)); + char *toname = (char *) alloca (plen + nlen + 1); + + memcpy (toname, prefix, plen); + memcpy (toname + plen, IDENTIFIER_POINTER (name), nlen + 1); + + return get_identifier (toname); +} + +/* Create an identifier for the struct __emutls_object, given an identifier + of the DECL_ASSEMBLY_NAME of the original object. */ + +static tree +get_emutls_object_name (tree name) +{ + const char *prefix = (targetm.emutls.var_prefix + ? targetm.emutls.var_prefix + : "__emutls_v" EMUTLS_SEPARATOR); + return prefix_name (prefix, name); +} + +tree +default_emutls_var_fields (tree type, tree *name ATTRIBUTE_UNUSED) +{ + tree word_type_node, field, next_field; + + field = build_decl (UNKNOWN_LOCATION, + FIELD_DECL, get_identifier ("__templ"), ptr_type_node); + DECL_CONTEXT (field) = type; + next_field = field; + + field = build_decl (UNKNOWN_LOCATION, + FIELD_DECL, get_identifier ("__offset"), + ptr_type_node); + DECL_CONTEXT (field) = type; + TREE_CHAIN (field) = next_field; + next_field = field; + + word_type_node = lang_hooks.types.type_for_mode (word_mode, 1); + field = build_decl (UNKNOWN_LOCATION, + FIELD_DECL, get_identifier ("__align"), + word_type_node); + DECL_CONTEXT (field) = type; + TREE_CHAIN (field) = next_field; + next_field = field; + + field = build_decl (UNKNOWN_LOCATION, + FIELD_DECL, get_identifier ("__size"), word_type_node); + DECL_CONTEXT (field) = type; + TREE_CHAIN (field) = next_field; + + return field; +} + +/* Initialize emulated tls object TO, which refers to TLS variable + DECL and is initialized by PROXY. */ + +tree +default_emutls_var_init (tree to, tree decl, tree proxy) +{ + VEC(constructor_elt,gc) *v = VEC_alloc (constructor_elt, gc, 4); + constructor_elt *elt; + tree type = TREE_TYPE (to); + tree field = TYPE_FIELDS (type); + + elt = VEC_quick_push (constructor_elt, v, NULL); + elt->index = field; + elt->value = fold_convert (TREE_TYPE (field), DECL_SIZE_UNIT (decl)); + + elt = VEC_quick_push (constructor_elt, v, NULL); + field = TREE_CHAIN (field); + elt->index = field; + elt->value = build_int_cst (TREE_TYPE (field), + DECL_ALIGN_UNIT (decl)); + + elt = VEC_quick_push (constructor_elt, v, NULL); + field = TREE_CHAIN (field); + elt->index = field; + elt->value = null_pointer_node; + + elt = VEC_quick_push (constructor_elt, v, NULL); + field = TREE_CHAIN (field); + elt->index = field; + elt->value = proxy; + + return build_constructor (type, v); +} + +/* Create the structure for struct __emutls_object. This should match the + structure at the top of emutls.c, modulo the union there. */ + +static tree +get_emutls_object_type (void) +{ + tree type, type_name, field; + + type = emutls_object_type; + if (type) + return type; + + emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE); + type_name = NULL; + field = targetm.emutls.var_fields (type, &type_name); + if (!type_name) + type_name = get_identifier ("__emutls_object"); + type_name = build_decl (UNKNOWN_LOCATION, + TYPE_DECL, type_name, type); + TYPE_NAME (type) = type_name; + TYPE_FIELDS (type) = field; + layout_type (type); + + return type; +} + +/* Create a read-only variable like DECL, with the same DECL_INITIAL. + This will be used for initializing the emulated tls data area. */ + +static tree +get_emutls_init_templ_addr (tree decl) +{ + tree name, to; + + if (targetm.emutls.register_common && !DECL_INITIAL (decl) + && !DECL_SECTION_NAME (decl)) + return null_pointer_node; + + name = DECL_ASSEMBLER_NAME (decl); + if (!targetm.emutls.tmpl_prefix || targetm.emutls.tmpl_prefix[0]) + { + const char *prefix = (targetm.emutls.tmpl_prefix + ? targetm.emutls.tmpl_prefix + : "__emutls_t" EMUTLS_SEPARATOR); + name = prefix_name (prefix, name); + } + + to = build_decl (DECL_SOURCE_LOCATION (decl), + VAR_DECL, name, TREE_TYPE (decl)); + SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to)); + + DECL_ARTIFICIAL (to) = 1; + TREE_USED (to) = TREE_USED (decl); + TREE_READONLY (to) = 1; + DECL_IGNORED_P (to) = 1; + DECL_CONTEXT (to) = DECL_CONTEXT (decl); + DECL_SECTION_NAME (to) = DECL_SECTION_NAME (decl); + DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl); + + DECL_WEAK (to) = DECL_WEAK (decl); + if (DECL_ONE_ONLY (decl)) + { + make_decl_one_only (to, DECL_ASSEMBLER_NAME (to)); + TREE_STATIC (to) = TREE_STATIC (decl); + TREE_PUBLIC (to) = TREE_PUBLIC (decl); + DECL_VISIBILITY (to) = DECL_VISIBILITY (decl); + } + else + TREE_STATIC (to) = 1; + + DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl); + DECL_INITIAL (to) = DECL_INITIAL (decl); + DECL_INITIAL (decl) = NULL; + + varpool_finalize_decl (to); + return build_fold_addr_expr (to); +} + +/* Create and return the control variable for the TLS variable DECL. */ + +static tree +new_emutls_decl (tree decl) +{ + tree name, to; + + name = DECL_ASSEMBLER_NAME (decl); + to = build_decl (DECL_SOURCE_LOCATION (decl), VAR_DECL, + get_emutls_object_name (name), + get_emutls_object_type ()); + + SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to)); + + DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED; + DECL_ARTIFICIAL (to) = 1; + DECL_IGNORED_P (to) = 1; + TREE_READONLY (to) = 0; + TREE_STATIC (to) = 1; + + DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl); + DECL_CONTEXT (to) = DECL_CONTEXT (decl); + TREE_USED (to) = TREE_USED (decl); + TREE_PUBLIC (to) = TREE_PUBLIC (decl); + DECL_EXTERNAL (to) = DECL_EXTERNAL (decl); + DECL_COMMON (to) = DECL_COMMON (decl); + DECL_WEAK (to) = DECL_WEAK (decl); + DECL_VISIBILITY (to) = DECL_VISIBILITY (decl); + DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl); + DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (decl); + DECL_DLLIMPORT_P (to) = DECL_DLLIMPORT_P (decl); + + DECL_ATTRIBUTES (to) = targetm.merge_decl_attributes (decl, to); + + if (DECL_ONE_ONLY (decl)) + make_decl_one_only (to, DECL_ASSEMBLER_NAME (to)); + + /* ??? What in the world is this for? */ + if (targetm.emutls.var_align_fixed) + /* If we're not allowed to change the proxy object's + alignment, pretend it's been set by the user. */ + DECL_USER_ALIGN (to) = 1; + + /* If this variable is defined locally, then we need to initialize the + control structure with size and alignment information. Initialization + of COMMON block variables happens elsewhere via a constructor. */ + if (!DECL_EXTERNAL (to) + && (!DECL_COMMON (to) + || (DECL_INITIAL (decl) + && DECL_INITIAL (decl) != error_mark_node))) + { + tree tmpl = get_emutls_init_templ_addr (decl); + DECL_INITIAL (to) = targetm.emutls.var_init (to, decl, tmpl); + record_references_in_initializer (to, false); + } + + varpool_finalize_decl (to); + return to; +} + +/* Look up the control variable for the TLS variable DECL. */ + +static unsigned int +emutls_index (tree decl) +{ + varpool_node_set_iterator i; + + i = varpool_node_set_find (tls_vars, varpool_get_node (decl)); + gcc_assert (i.index != ~0u); + + return i.index; +} + +/* ??? The only remaining user is in dwarf2out.c. Figure out how to + eliminate that too. */ + +tree +emutls_decl (tree decl) +{ + struct varpool_node *var; + unsigned int i; + + i = emutls_index (decl); + var = VEC_index (varpool_node_ptr, control_vars->nodes, i); + return var->decl; +} + +/* Generate a call statement to initialize CONTROL_DECL for TLS_DECL. + This only needs to happen for TLS COMMON variables; non-COMMON + variables can be initialized statically. Insert the generated + call statement at the end of PSTMTS. */ + +static void +emutls_common_1 (tree tls_decl, tree control_decl, tree *pstmts) +{ + tree x; + tree word_type_node; + + if (! DECL_COMMON (tls_decl) + || (DECL_INITIAL (tls_decl) + && DECL_INITIAL (tls_decl) != error_mark_node)) + return; + + word_type_node = lang_hooks.types.type_for_mode (word_mode, 1); + + x = build_call_expr (built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON], 4, + build_fold_addr_expr (control_decl), + fold_convert (word_type_node, + DECL_SIZE_UNIT (tls_decl)), + build_int_cst (word_type_node, + DECL_ALIGN_UNIT (tls_decl)), + get_emutls_init_templ_addr (tls_decl)); + + append_to_statement_list (x, pstmts); +} + +struct lower_emutls_data +{ + VEC(tree,heap) *accessed; + struct cgraph_node *cfun_node; + struct cgraph_node *builtin_node; + tree builtin_decl; + basic_block bb; + int bb_freq; + location_t loc; + gimple_seq seq; +}; + +/* Given a TLS variable DECL, return an SSA_NAME holding its address. */ + +static tree +gen_emutls_addr (tree decl, struct lower_emutls_data *d) +{ + unsigned int index; + tree addr; + + /* Compute the address of the TLS variable with help from runtime. */ + index = emutls_index (decl); + addr = VEC_index (tree, d->accessed, index); + if (addr == NULL) + { + struct varpool_node *cvar; + tree cdecl; + gimple x; + + cvar = VEC_index (varpool_node_ptr, control_vars->nodes, index); + cdecl = cvar->decl; + TREE_ADDRESSABLE (cdecl) = 1; + + addr = create_tmp_var (build_pointer_type (TREE_TYPE (decl)), NULL); + x = gimple_build_call (d->builtin_decl, 1, build_fold_addr_expr (cdecl)); + gimple_set_location (x, d->loc); + + addr = make_ssa_name (addr, x); + gimple_call_set_lhs (x, addr); + + gimple_seq_add_stmt (&d->seq, x); + + cgraph_create_edge (d->cfun_node, d->builtin_node, x, + d->bb->count, d->bb_freq, d->bb->loop_depth); + + /* We may be adding a new reference to a new variable to the function. + This means we have to play with the ipa-reference web. */ + ipa_record_reference (d->cfun_node, NULL, NULL, cvar, IPA_REF_ADDR, x); + + /* Record this ssa_name for possible use later in the basic block. */ + VEC_replace (tree, d->accessed, index, addr); + } + + return addr; +} + +/* Callback for walk_gimple_op. D = WI->INFO is a struct lower_emutls_data. + Given an operand *PTR within D->STMT, if the operand references a TLS + variable, then lower the reference to a call to the runtime. Insert + any new statements required into D->SEQ; the caller is responsible for + placing those appropriately. */ + +static tree +lower_emutls_1 (tree *ptr, int *walk_subtrees, void *cb_data) +{ + struct walk_stmt_info *wi = (struct walk_stmt_info *) cb_data; + struct lower_emutls_data *d = (struct lower_emutls_data *) wi->info; + tree t = *ptr; + bool is_addr = false; + tree addr; + + *walk_subtrees = 0; + + switch (TREE_CODE (t)) + { + case ADDR_EXPR: + /* If this is not a straight-forward "&var", but rather something + like "&var.a", then we may need special handling. */ + if (TREE_CODE (TREE_OPERAND (t, 0)) != VAR_DECL) + { + bool save_changed; + + /* If we're allowed more than just is_gimple_val, continue. */ + if (!wi->val_only) + { + *walk_subtrees = 1; + return NULL_TREE; + } + + /* See if any substitution would be made. */ + save_changed = wi->changed; + wi->changed = false; + wi->val_only = false; + walk_tree (&TREE_OPERAND (t, 0), lower_emutls_1, wi, NULL); + wi->val_only = true; + + /* If so, then extract this entire sub-expression "&p->a" into a + new assignment statement, and substitute yet another SSA_NAME. */ + if (wi->changed) + { + gimple x; + + addr = create_tmp_var (TREE_TYPE (t), NULL); + x = gimple_build_assign (addr, t); + gimple_set_location (x, d->loc); + + addr = make_ssa_name (addr, x); + gimple_assign_set_lhs (x, addr); + + gimple_seq_add_stmt (&d->seq, x); + + *ptr = addr; + } + else + wi->changed = save_changed; + + return NULL_TREE; + } + + t = TREE_OPERAND (t, 0); + is_addr = true; + /* FALLTHRU */ + + case VAR_DECL: + if (!DECL_THREAD_LOCAL_P (t)) + return NULL_TREE; + break; + + default: + /* We're not interested in other decls or types, only subexpressions. */ + if (EXPR_P (t)) + *walk_subtrees = 1; + /* FALLTHRU */ + + case SSA_NAME: + /* Special-case the return of SSA_NAME, since it's so common. */ + return NULL_TREE; + } + + addr = gen_emutls_addr (t, d); + if (is_addr) + { + /* Replace "&var" with "addr" in the statement. */ + *ptr = addr; + } + else + { + /* Replace "var" with "*addr" in the statement. */ + t = build2 (MEM_REF, TREE_TYPE (t), addr, + build_int_cst (TREE_TYPE (addr), 0)); + *ptr = t; + } + + wi->changed = true; + return NULL_TREE; +} + +/* Lower all of the operands of STMT. */ + +static void +lower_emutls_stmt (gimple stmt, struct lower_emutls_data *d) +{ + struct walk_stmt_info wi; + + d->loc = gimple_location (stmt); + + memset (&wi, 0, sizeof (wi)); + wi.info = d; + wi.val_only = true; + walk_gimple_op (stmt, lower_emutls_1, &wi); + + if (wi.changed) + update_stmt (stmt); +} + +/* Lower the I'th operand of PHI. */ + +static void +lower_emutls_phi_arg (gimple phi, unsigned int i, struct lower_emutls_data *d) +{ + struct walk_stmt_info wi; + struct phi_arg_d *pd = gimple_phi_arg (phi, i); + + /* Early out for a very common case we don't care about. */ + if (TREE_CODE (pd->def) == SSA_NAME) + return; + + d->loc = pd->locus; + + memset (&wi, 0, sizeof (wi)); + wi.info = d; + wi.val_only = true; + walk_tree (&pd->def, lower_emutls_1, &wi, NULL); + + /* For normal statements, we let update_stmt do its job. But for phi + nodes, we have to manipulate the immediate use list by hand. */ + if (wi.changed) + { + gcc_assert (TREE_CODE (pd->def) == SSA_NAME); + link_imm_use_stmt (&pd->imm_use, pd->def, phi); + } +} + +/* Lower the entire function NODE. */ + +static void +lower_emutls_function_body (struct cgraph_node *node) +{ + struct lower_emutls_data d; + bool any_edge_inserts = false; + unsigned int n_tls; + + current_function_decl = node->decl; + push_cfun (DECL_STRUCT_FUNCTION (node->decl)); + + d.cfun_node = node; + d.builtin_decl = built_in_decls[BUILT_IN_EMUTLS_GET_ADDRESS]; + d.builtin_node = cgraph_node (d.builtin_decl); + + n_tls = VEC_length (varpool_node_ptr, tls_vars->nodes); + d.accessed = VEC_alloc (tree, heap, n_tls); + VEC_safe_grow (tree, heap, d.accessed, n_tls); + + FOR_EACH_BB (d.bb) + { + gimple_stmt_iterator gsi; + unsigned int i, nedge; + + /* ??? This is the frequency of the new block that might be created by + split_edge. One might have thought EDGE_FREQUENCY. However, this + *must* match split_edge, or we'll fail verify_cgraph. */ + d.bb_freq = CGRAPH_FREQ_BASE; + + /* Lower each of the PHI nodes of the block, as we may have + propagated &tlsvar into a PHI argument. These loops are + arranged so that we process each edge at once, and each + PHI argument for that edge. */ + + nedge = EDGE_COUNT (d.bb->preds); + for (i = 0; i < nedge; ++i) + { + edge e = EDGE_PRED (d.bb, i); + + /* We can re-use any SSA_NAME created on this edge. */ + memset (VEC_address (tree, d.accessed), 0, n_tls * sizeof(tree)); + d.seq = NULL; + + for (gsi = gsi_start_phis (d.bb); !gsi_end_p (gsi); gsi_next (&gsi)) + lower_emutls_phi_arg (gsi_stmt (gsi), i, &d); + + /* Insert all statements generated by all phi nodes for this + particular edge all at once. */ + if (d.seq) + { + gsi_insert_seq_on_edge (e, d.seq); + any_edge_inserts = true; + } + } + + d.bb_freq = compute_call_stmt_bb_frequency (current_function_decl, d.bb); + + /* We can re-use any SSA_NAME created during this basic block. */ + memset (VEC_address (tree, d.accessed), 0, n_tls * sizeof(tree)); + + /* Lower each of the statements of the block. */ + for (gsi = gsi_start_bb (d.bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + d.seq = NULL; + lower_emutls_stmt (gsi_stmt (gsi), &d); + + /* If any new statements were created, insert them immediately + before the first use. This prevents variable lifetimes from + becoming unnecessarily long. */ + if (d.seq) + gsi_insert_seq_before (&gsi, d.seq, GSI_SAME_STMT); + } + } + + if (any_edge_inserts) + gsi_commit_edge_inserts (); + + VEC_free (tree, heap, d.accessed); + + pop_cfun (); + current_function_decl = NULL; +} + +static unsigned int +ipa_lower_emutls (void) +{ + struct varpool_node *var; + struct cgraph_node *func; + bool any_aliases = false; + tree ctor_body = NULL; + unsigned int i; + + tls_vars = varpool_node_set_new (); + + /* Examine all global variables for TLS variables. */ + for (var = varpool_nodes; var ; var = var->next) + if (DECL_THREAD_LOCAL_P (var->decl)) + { + /* ??? We really should be more consistent about setting these + sorts of flags. TREE_STATIC != C "static" keyword, and thus + it should be set *with* DECL_EXTERNAL. */ + gcc_checking_assert (TREE_STATIC (var->decl) + || DECL_EXTERNAL (var->decl)); + varpool_node_set_add (tls_vars, var); + } + + /* If we found no TLS variables, then there is no further work to do. */ + if (tls_vars->nodes == NULL) + { + tls_vars = NULL; + if (dump_file) + fprintf (dump_file, "No TLS variables found.\n"); + return 0; + } + + /* Create the control variables for each TLS variable. */ + control_vars = varpool_node_set_new (); + for (i = 0; VEC_iterate (varpool_node_ptr, tls_vars->nodes, i, var); ++i) + { + tree cdecl; + struct varpool_node *cvar; + + var = VEC_index (varpool_node_ptr, tls_vars->nodes, i); + cdecl = new_emutls_decl (var->decl); + + cvar = varpool_get_node (cdecl); + varpool_node_set_add (control_vars, cvar); + + if (var->alias) + { + any_aliases = true; + cvar->alias = true; + } + else + { + /* Make sure the COMMON block control variable gets initialized. + Note that there's no point in doing this for aliases; we only + need to do this once for the main variable. */ + emutls_common_1 (var->decl, cdecl, &ctor_body); + } + + /* Indicate that the value of the TLS variable may be found elsewhere. + This also prevents the variable from re-appearing in the GIMPLE. */ + /* ??? Unfortuantely, there's no decent actual value to put here; + there's nothing we can emit for the debugger at the moment. */ + SET_DECL_VALUE_EXPR (var->decl, error_mark_node); + DECL_HAS_VALUE_EXPR_P (var->decl) = 1; + } + + /* If there were any aliases, then frob the alias_pairs vector. */ + if (any_aliases) + { + alias_pair *p; + for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p); ++i) + if (DECL_THREAD_LOCAL_P (p->decl)) + { + p->decl = emutls_decl (p->decl); + p->target = get_emutls_object_name (p->target); + } + } + + /* Adjust all uses of TLS variables within the function bodies. */ + for (func = cgraph_nodes; func; func = func->next) + if (func->reachable && func->lowered) + lower_emutls_function_body (func); + + /* Generate the constructor for any COMMON control variables created. */ + if (ctor_body) + cgraph_build_static_cdtor ('I', ctor_body, DEFAULT_INIT_PRIORITY); + + return TODO_dump_func | TODO_ggc_collect | TODO_verify_stmts; +} + +/* If the target supports TLS natively, we need do nothing here. */ + +static bool +gate_emutls (void) +{ + return !targetm.have_tls; +} + +struct simple_ipa_opt_pass pass_ipa_lower_emutls = +{ + { + SIMPLE_IPA_PASS, + "emutls", /* name */ + gate_emutls, /* gate */ + ipa_lower_emutls, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_NONE, /* tv_id */ + PROP_cfg, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + } +}; + +#include "gt-tree-emutls.h" diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index c72d7cf..33c898e 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -446,6 +446,7 @@ extern struct gimple_opt_pass pass_warn_unused_result; extern struct gimple_opt_pass pass_split_functions; /* IPA Passes */ +extern struct simple_ipa_opt_pass pass_ipa_lower_emutls; extern struct simple_ipa_opt_pass pass_ipa_function_and_variable_visibility; extern struct simple_ipa_opt_pass pass_ipa_early_inline; diff --git a/gcc/tree.h b/gcc/tree.h index be70d2b..865c47a 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5258,9 +5258,12 @@ extern void set_user_assembler_name (tree, const char *); extern void process_pending_assemble_externals (void); extern void finish_aliases_1 (void); extern void finish_aliases_2 (void); -extern tree emutls_decl (tree); extern void remove_unreachable_alias_pairs (void); +/* tree-emutls.c */ +extern tree emutls_decl (tree); +extern bool decl_emutls_var_p (const_tree); + /* In stmt.c */ extern void expand_computed_goto (tree); extern bool parse_output_constraint (const char **, int, int, int, diff --git a/gcc/varasm.c b/gcc/varasm.c index 55218c4..1f32903 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -186,317 +186,6 @@ static GTY(()) int anchor_labelno; /* A pool of constants that can be shared between functions. */ static GTY(()) struct rtx_constant_pool *shared_constant_pool; -/* TLS emulation. */ - -static GTY ((if_marked ("tree_map_marked_p"), param_is (struct tree_map))) - htab_t emutls_htab; -static GTY (()) tree emutls_object_type; -/* Emulated TLS objects have the TLS model TLS_MODEL_EMULATED. This - macro can be used on them to distinguish the control variable from - the initialization template. */ -#define DECL_EMUTLS_VAR_P(D) (TREE_TYPE (D) == emutls_object_type) - -#if !defined (NO_DOT_IN_LABEL) -# define EMUTLS_SEPARATOR "." -#elif !defined (NO_DOLLAR_IN_LABEL) -# define EMUTLS_SEPARATOR "$" -#else -# define EMUTLS_SEPARATOR "_" -#endif - -/* Create an IDENTIFIER_NODE by prefixing PREFIX to the - IDENTIFIER_NODE NAME's name. */ - -static tree -prefix_name (const char *prefix, tree name) -{ - unsigned plen = strlen (prefix); - unsigned nlen = strlen (IDENTIFIER_POINTER (name)); - char *toname = (char *) alloca (plen + nlen + 1); - - memcpy (toname, prefix, plen); - memcpy (toname + plen, IDENTIFIER_POINTER (name), nlen + 1); - - return get_identifier (toname); -} - -/* Create an identifier for the struct __emutls_object, given an identifier - of the DECL_ASSEMBLY_NAME of the original object. */ - -static tree -get_emutls_object_name (tree name) -{ - const char *prefix = (targetm.emutls.var_prefix - ? targetm.emutls.var_prefix - : "__emutls_v" EMUTLS_SEPARATOR); - return prefix_name (prefix, name); -} - -tree -default_emutls_var_fields (tree type, tree *name ATTRIBUTE_UNUSED) -{ - tree word_type_node, field, next_field; - - field = build_decl (UNKNOWN_LOCATION, - FIELD_DECL, get_identifier ("__templ"), ptr_type_node); - DECL_CONTEXT (field) = type; - next_field = field; - - field = build_decl (UNKNOWN_LOCATION, - FIELD_DECL, get_identifier ("__offset"), - ptr_type_node); - DECL_CONTEXT (field) = type; - TREE_CHAIN (field) = next_field; - next_field = field; - - word_type_node = lang_hooks.types.type_for_mode (word_mode, 1); - field = build_decl (UNKNOWN_LOCATION, - FIELD_DECL, get_identifier ("__align"), - word_type_node); - DECL_CONTEXT (field) = type; - TREE_CHAIN (field) = next_field; - next_field = field; - - field = build_decl (UNKNOWN_LOCATION, - FIELD_DECL, get_identifier ("__size"), word_type_node); - DECL_CONTEXT (field) = type; - TREE_CHAIN (field) = next_field; - - return field; -} - -/* Create the structure for struct __emutls_object. This should match the - structure at the top of emutls.c, modulo the union there. */ - -static tree -get_emutls_object_type (void) -{ - tree type, type_name, field; - - type = emutls_object_type; - if (type) - return type; - - emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE); - type_name = NULL; - field = targetm.emutls.var_fields (type, &type_name); - if (!type_name) - type_name = get_identifier ("__emutls_object"); - type_name = build_decl (UNKNOWN_LOCATION, - TYPE_DECL, type_name, type); - TYPE_NAME (type) = type_name; - TYPE_FIELDS (type) = field; - layout_type (type); - - return type; -} - -/* Create a read-only variable like DECL, with the same DECL_INITIAL. - This will be used for initializing the emulated tls data area. */ - -static tree -get_emutls_init_templ_addr (tree decl) -{ - tree name, to; - - if (targetm.emutls.register_common && !DECL_INITIAL (decl) - && !DECL_SECTION_NAME (decl)) - return null_pointer_node; - - name = DECL_ASSEMBLER_NAME (decl); - if (!targetm.emutls.tmpl_prefix || targetm.emutls.tmpl_prefix[0]) - { - const char *prefix = (targetm.emutls.tmpl_prefix - ? targetm.emutls.tmpl_prefix - : "__emutls_t" EMUTLS_SEPARATOR); - name = prefix_name (prefix, name); - } - - to = build_decl (DECL_SOURCE_LOCATION (decl), - VAR_DECL, name, TREE_TYPE (decl)); - SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to)); - - DECL_ARTIFICIAL (to) = 1; - TREE_USED (to) = TREE_USED (decl); - TREE_READONLY (to) = 1; - DECL_IGNORED_P (to) = 1; - DECL_CONTEXT (to) = DECL_CONTEXT (decl); - DECL_SECTION_NAME (to) = DECL_SECTION_NAME (decl); - DECL_PRESERVE_P (to) = DECL_PRESERVE_P (decl); - - DECL_WEAK (to) = DECL_WEAK (decl); - if (DECL_ONE_ONLY (decl)) - { - make_decl_one_only (to, DECL_ASSEMBLER_NAME (to)); - TREE_STATIC (to) = TREE_STATIC (decl); - TREE_PUBLIC (to) = TREE_PUBLIC (decl); - DECL_VISIBILITY (to) = DECL_VISIBILITY (decl); - } - else - TREE_STATIC (to) = 1; - - DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl); - DECL_INITIAL (to) = DECL_INITIAL (decl); - DECL_INITIAL (decl) = NULL; - - varpool_finalize_decl (to); - return build_fold_addr_expr (to); -} - -/* When emulating tls, we use a control structure for use by the runtime. - Create and return this structure. */ - -tree -emutls_decl (tree decl) -{ - tree name, to; - struct tree_map *h, in; - void **loc; - - if (targetm.have_tls || decl == NULL || decl == error_mark_node - || TREE_CODE (decl) != VAR_DECL || ! DECL_THREAD_LOCAL_P (decl)) - return decl; - - /* Look up the object in the hash; return the control structure if - it has already been created. */ - if (! emutls_htab) - emutls_htab = htab_create_ggc (512, tree_map_hash, tree_map_eq, 0); - - name = DECL_ASSEMBLER_NAME (decl); - - /* Note that we use the hash of the decl's name, rather than a hash - of the decl's pointer. In emutls_finish we iterate through the - hash table, and we want this traversal to be predictable. */ - in.hash = IDENTIFIER_HASH_VALUE (name); - in.base.from = decl; - loc = htab_find_slot_with_hash (emutls_htab, &in, in.hash, INSERT); - h = (struct tree_map *) *loc; - if (h != NULL) - to = h->to; - else - { - to = build_decl (DECL_SOURCE_LOCATION (decl), - VAR_DECL, get_emutls_object_name (name), - get_emutls_object_type ()); - - h = ggc_alloc_tree_map (); - h->hash = in.hash; - h->base.from = decl; - h->to = to; - *(struct tree_map **) loc = h; - - DECL_TLS_MODEL (to) = TLS_MODEL_EMULATED; - DECL_ARTIFICIAL (to) = 1; - DECL_IGNORED_P (to) = 1; - /* FIXME: work around PR44132. */ - DECL_PRESERVE_P (to) = 1; - TREE_READONLY (to) = 0; - SET_DECL_ASSEMBLER_NAME (to, DECL_NAME (to)); - if (DECL_ONE_ONLY (decl)) - make_decl_one_only (to, DECL_ASSEMBLER_NAME (to)); - DECL_CONTEXT (to) = DECL_CONTEXT (decl); - if (targetm.emutls.var_align_fixed) - /* If we're not allowed to change the proxy object's - alignment, pretend it's been set by the user. */ - DECL_USER_ALIGN (to) = 1; - } - - /* Note that these fields may need to be updated from time to time from - the original decl. Consider: - extern __thread int i; - int foo() { return i; } - __thread int i = 1; - in which I goes from external to locally defined and initialized. */ - DECL_DLLIMPORT_P (to) = DECL_DLLIMPORT_P (decl); - DECL_ATTRIBUTES (to) = targetm.merge_decl_attributes (decl, to); - - TREE_STATIC (to) = TREE_STATIC (decl); - TREE_USED (to) = TREE_USED (decl); - TREE_PUBLIC (to) = TREE_PUBLIC (decl); - DECL_EXTERNAL (to) = DECL_EXTERNAL (decl); - DECL_COMMON (to) = DECL_COMMON (decl); - DECL_WEAK (to) = DECL_WEAK (decl); - DECL_VISIBILITY (to) = DECL_VISIBILITY (decl); - DECL_VISIBILITY_SPECIFIED (to) = DECL_VISIBILITY_SPECIFIED (decl); - - /* Fortran might pass this to us. */ - DECL_RESTRICTED_P (to) = DECL_RESTRICTED_P (decl); - - return to; -} - -static int -emutls_common_1 (void **loc, void *xstmts) -{ - struct tree_map *h = *(struct tree_map **) loc; - tree x, *pstmts = (tree *) xstmts; - tree word_type_node; - - if (! DECL_COMMON (h->base.from) - || (DECL_INITIAL (h->base.from) - && DECL_INITIAL (h->base.from) != error_mark_node)) - return 1; - - word_type_node = lang_hooks.types.type_for_mode (word_mode, 1); - - /* The idea was to call get_emutls_init_templ_addr here, but if we - do this and there is an initializer, -fanchor_section loses, - because it would be too late to ensure the template is - output. */ - x = built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON]; - x = build_call_expr (x, 4, - build_fold_addr_expr (h->to), - fold_convert (word_type_node, - DECL_SIZE_UNIT (h->base.from)), - build_int_cst (word_type_node, - DECL_ALIGN_UNIT (h->base.from)), - null_pointer_node); - - append_to_statement_list (x, pstmts); - return 1; -} - -/* Callback to finalize one emutls control variable. */ - -static int -emutls_finalize_control_var (void **loc, - void *unused ATTRIBUTE_UNUSED) -{ - struct tree_map *h = *(struct tree_map **) loc; - if (h != NULL) - { - struct varpool_node *node = varpool_node (h->to); - /* Because varpool_finalize_decl () has side-effects, - only apply to un-finalized vars. */ - if (node && !node->finalized) - varpool_finalize_decl (h->to); - } - return 1; -} - -/* Finalize emutls control vars and add a static constructor if - required. */ - -void -emutls_finish (void) -{ - if (emutls_htab == NULL) - return; - htab_traverse_noresize (emutls_htab, - emutls_finalize_control_var, NULL); - - if (targetm.emutls.register_common) - { - tree body = NULL_TREE; - - htab_traverse_noresize (emutls_htab, emutls_common_1, &body); - if (body == NULL_TREE) - return; - - cgraph_build_static_cdtor ('I', body, DEFAULT_INIT_PRIORITY); - } -} - /* Helper routines for maintaining section_htab. */ static int @@ -1210,11 +899,6 @@ get_variable_section (tree decl, bool prefer_noswitch_p) && ADDR_SPACE_GENERIC_P (as)); if (DECL_THREAD_LOCAL_P (decl)) return tls_comm_section; - /* This cannot be common bss for an emulated TLS object without - a register_common hook. */ - else if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED - && !targetm.emutls.register_common) - ; else if (TREE_PUBLIC (decl) && bss_initializer_p (decl)) return comm_section; } @@ -2098,40 +1782,6 @@ assemble_variable_contents (tree decl, const char *name, } } -/* Initialize emulated tls object TO, which refers to TLS variable - DECL and is initialized by PROXY. */ - -tree -default_emutls_var_init (tree to, tree decl, tree proxy) -{ - VEC(constructor_elt,gc) *v = VEC_alloc (constructor_elt, gc, 4); - constructor_elt *elt; - tree type = TREE_TYPE (to); - tree field = TYPE_FIELDS (type); - - elt = VEC_quick_push (constructor_elt, v, NULL); - elt->index = field; - elt->value = fold_convert (TREE_TYPE (field), DECL_SIZE_UNIT (decl)); - - elt = VEC_quick_push (constructor_elt, v, NULL); - field = TREE_CHAIN (field); - elt->index = field; - elt->value = build_int_cst (TREE_TYPE (field), - DECL_ALIGN_UNIT (decl)); - - elt = VEC_quick_push (constructor_elt, v, NULL); - field = TREE_CHAIN (field); - elt->index = field; - elt->value = null_pointer_node; - - elt = VEC_quick_push (constructor_elt, v, NULL); - field = TREE_CHAIN (field); - elt->index = field; - elt->value = proxy; - - return build_constructor (type, v); -} - /* Assemble everything that is needed for a variable or function declaration. Not used for automatic variables, and not used for function definitions. Should not be called for variables of incomplete structure type. @@ -2153,35 +1803,9 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, /* This function is supposed to handle VARIABLES. Ensure we have one. */ gcc_assert (TREE_CODE (decl) == VAR_DECL); - if (! targetm.have_tls - && TREE_CODE (decl) == VAR_DECL - && DECL_THREAD_LOCAL_P (decl)) - { - tree to = emutls_decl (decl); - - /* If this variable is defined locally, then we need to initialize the - control structure with size and alignment information. We do this - at the last moment because tentative definitions can take a locally - defined but uninitialized variable and initialize it later, which - would result in incorrect contents. */ - if (! DECL_EXTERNAL (to) - && (! DECL_COMMON (to) - || (DECL_INITIAL (decl) - && DECL_INITIAL (decl) != error_mark_node))) - { - DECL_INITIAL (to) = targetm.emutls.var_init - (to, decl, get_emutls_init_templ_addr (decl)); - - /* Make sure the template is marked as needed early enough. - Without this, if the variable is placed in a - section-anchored block, the template will only be marked - when it's too late. */ - record_references_in_initializer (to, false); - } - - decl = to; - } - + /* Emulated TLS had better not get this far. */ + gcc_checking_assert (targetm.have_tls || !DECL_THREAD_LOCAL_P (decl)); + last_assemble_variable_decl = 0; /* Normally no need to say anything here for external references, @@ -5685,6 +5309,11 @@ find_decl_and_mark_needed (tree decl, tree target) static void do_assemble_alias (tree decl, tree target) { + /* Emulated TLS had better not get this var. */ + gcc_assert(!(!targetm.have_tls + && TREE_CODE (decl) == VAR_DECL + && DECL_THREAD_LOCAL_P (decl))); + if (TREE_ASM_WRITTEN (decl)) return; @@ -5699,14 +5328,6 @@ do_assemble_alias (tree decl, tree target) { ultimate_transparent_alias_target (&target); - if (!targetm.have_tls - && TREE_CODE (decl) == VAR_DECL - && DECL_THREAD_LOCAL_P (decl)) - { - decl = emutls_decl (decl); - target = get_emutls_object_name (target); - } - if (!TREE_SYMBOL_REFERENCED (target)) weakref_targets = tree_cons (decl, target, weakref_targets); @@ -5725,14 +5346,6 @@ do_assemble_alias (tree decl, tree target) return; } - if (!targetm.have_tls - && TREE_CODE (decl) == VAR_DECL - && DECL_THREAD_LOCAL_P (decl)) - { - decl = emutls_decl (decl); - target = get_emutls_object_name (target); - } - #ifdef ASM_OUTPUT_DEF /* Make name accessible from other files, if appropriate. */ @@ -6404,7 +6017,7 @@ categorize_decl_for_section (const_tree decl, int reloc) { if (DECL_TLS_MODEL (decl) == TLS_MODEL_EMULATED) { - if (DECL_EMUTLS_VAR_P (decl)) + if (decl_emutls_var_p (decl)) { if (targetm.emutls.var_section) ret = SECCAT_EMUTLS_VAR; diff --git a/gcc/varpool.c b/gcc/varpool.c index 94c949e..3843d9c 100644 --- a/gcc/varpool.c +++ b/gcc/varpool.c @@ -211,7 +211,9 @@ varpool_remove_node (struct varpool_node *node) } ipa_remove_all_references (&node->ref_list); ipa_remove_all_refering (&node->ref_list); - ggc_free (node); + /* ??? We need to remove the reference in emutls data structures. Perhaps + it would be better to simply add the xref to the varpool node. */ + /* ggc_free (node); */ } /* Dump given cgraph node. */ @@ -346,17 +348,6 @@ decide_is_variable_needed (struct varpool_node *node, tree decl) && !DECL_EXTERNAL (decl)) return true; - /* When emulating tls, we actually see references to the control - variable, rather than the user-level variable. */ - if (!targetm.have_tls - && TREE_CODE (decl) == VAR_DECL - && DECL_THREAD_LOCAL_P (decl)) - { - tree control = emutls_decl (decl); - if (decide_is_variable_needed (varpool_node (control), control)) - return true; - } - /* When not reordering top level variables, we have to assume that we are going to keep everything. */ if (flag_toplevel_reorder)