From patchwork Mon Jul 12 20:08:03 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Henderson X-Patchwork-Id: 58668 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 221B71007D1 for ; Tue, 13 Jul 2010 06:08:36 +1000 (EST) Received: (qmail 3620 invoked by alias); 12 Jul 2010 20:08:26 -0000 Received: (qmail 3589 invoked by uid 22791); 12 Jul 2010 20:08:16 -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; Mon, 12 Jul 2010 20:08:06 +0000 Received: from int-mx03.intmail.prod.int.phx2.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.16]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o6CK84Vj006929 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Mon, 12 Jul 2010 16:08:05 -0400 Received: from anchor.twiddle.home (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx03.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o6CK8302020962; Mon, 12 Jul 2010 16:08:03 -0400 Message-ID: <4C3B7623.2020002@redhat.com> Date: Mon, 12 Jul 2010 13:08:03 -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 , Diego Novillo , Jan Hubicka , Jakub Jelinek Subject: Re: [Patch, updated] Make emulated TLS lto-friendly. References: <4C350C25.2030308@redhat.com> <54190C7D-E4F8-4D84-A468-E38B39770D3F@sandoe-acoustics.co.uk> <4C3624B2.9070602@redhat.com> <4C3B321F.8080507@redhat.com> <4C3B4B00.1070300@redhat.com> In-Reply-To: <4C3B4B00.1070300@redhat.com> 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 Test #3. Fixed up a bunch of failures visible in the testsuite. Aliasing isn't supposed to work yet, so ignore failures there. r~ diff --git a/gcc/Makefile.in b/gcc/Makefile.in index a807e8e..9f96925 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1352,6 +1352,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 \ @@ -3135,6 +3136,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) \ @@ -3734,6 +3738,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/expr.c b/gcc/expr.c index 00ebfdc..03b879c 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -6829,20 +6829,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 arglist = build_tree_list (NULL_TREE, arg); - tree call = build_function_call_expr (UNKNOWN_LOCATION, fn, arglist); - 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 @@ -6945,18 +6931,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 @@ -8394,16 +8368,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 8828967..781420c 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -806,6 +806,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/toplev.c b/gcc/toplev.c index 276ae7e..a21a1dc 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -999,11 +999,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..959aa54 --- /dev/null +++ b/gcc/tree-emutls.c @@ -0,0 +1,612 @@ +/* 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(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. */ + +tree +emutls_decl (tree decl) +{ + varpool_node_set_iterator i; + struct varpool_node *var; + + i = varpool_node_set_find (tls_vars, varpool_get_node (decl)); + if (i.index == ~0u) + return NULL; + + var = VEC_index (varpool_node_ptr, control_vars->nodes, i.index); + 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 args, 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 = get_emutls_init_templ_addr (tls_decl); + args = tree_cons (NULL, x, NULL); + x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (tls_decl)); + args = tree_cons (NULL, x, args); + x = fold_convert (word_type_node, DECL_SIZE_UNIT (tls_decl)); + args = tree_cons (NULL, x, args); + x = build_fold_addr_expr (control_decl); + args = tree_cons (NULL, x, args); + + x = built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON]; + x = build_function_call_expr (UNKNOWN_LOCATION, x, args); + + append_to_statement_list (x, pstmts); +} + +struct lower_emutls_data +{ + struct cgraph_node *cfun_node; + struct cgraph_node *builtin_node; + tree builtin_decl; + basic_block bb; + int bb_freq; + gimple_stmt_iterator gsi; + gimple stmt; +}; + +/* Given an operand *PTR within STMT at *GSI, if the operand references + a TLS variable, then lower the reference to a call to the runtime. */ + +static bool +lower_emutls_1 (struct lower_emutls_data *d, tree *ptr) +{ + tree t = *ptr; + tree control, addr; + gimple x; + + /* Look through all components. */ + while (handled_component_p (t)) + { + ptr = &TREE_OPERAND (t, 0); + t = *ptr; + } + + /* Note that MEM_REF is not a "component" per-se, but may contain + ADDR_EXPR of a symbol, which we need to handle. */ + if (TREE_CODE (t) == MEM_REF) + { + ptr = &TREE_OPERAND (t, 0); + t = *ptr; + } + + /* In the case of "&var" we don't want to generate "&*addr", + we'd prefer to simply emit "addr". */ + if (TREE_CODE (t) == ADDR_EXPR) + { + if (lower_emutls_1 (d, &TREE_OPERAND (t, 0))) + { + if (TREE_CODE (TREE_OPERAND (t, 0)) == MEM_REF + && integer_zerop (TREE_OPERAND (TREE_OPERAND (t, 0), 1))) + *ptr = TREE_OPERAND (TREE_OPERAND (t, 0), 0); + return true; + } + return false; + } + + /* If at the end we don't have a TLS variable, nothing to do. */ + if (TREE_CODE (t) != VAR_DECL || !DECL_THREAD_LOCAL_P (t)) + return false; + + /* Compute the address of the TLS variable with help from runtime. */ + control = emutls_decl (t); + TREE_ADDRESSABLE (control) = 1; + addr = create_tmp_var (build_pointer_type (TREE_TYPE (t)), NULL); + x = gimple_build_call (d->builtin_decl, 1, + build_fold_addr_expr (control)); + gsi_insert_before (&d->gsi, x, GSI_SAME_STMT); + gimple_set_location (x, gimple_location (d->stmt)); + + addr = make_ssa_name (addr, x); + gimple_call_set_lhs (x, addr); + + cgraph_create_edge (d->cfun_node, d->builtin_node, x, + d->bb->count, d->bb_freq, d->bb->loop_depth); + + /* Replace "var" with "*addr" in the statement. */ + t = build2 (MEM_REF, TREE_TYPE (t), addr, + build_int_cst (TREE_TYPE (addr), 0)); + *ptr = t; + gimple_set_modified (d->stmt, true); + + return true; +} + +static void +lower_emutls_function_body (struct cgraph_node *node) +{ + struct lower_emutls_data d; + + 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); + + FOR_EACH_BB (d.bb) + { + d.bb_freq = compute_call_stmt_bb_frequency (current_function_decl, d.bb); + + for (d.gsi = gsi_start_bb (d.bb); !gsi_end_p (d.gsi); gsi_next (&d.gsi)) + { + gimple stmt = gsi_stmt (d.gsi); + + d.stmt = stmt; + if (gimple_assign_single_p (stmt)) + { + lower_emutls_1 (&d, gimple_assign_lhs_ptr (stmt)); + lower_emutls_1 (&d, gimple_assign_rhs1_ptr (stmt)); + } + else if (is_gimple_call (stmt)) + { + unsigned i, n = gimple_call_num_args (stmt); + for (i = 0; i < n; ++i) + lower_emutls_1 (&d, gimple_call_arg_ptr (stmt, i)); + if (gimple_call_lhs (stmt)) + lower_emutls_1 (&d, gimple_call_lhs_ptr (stmt)); + } + else + continue; + + update_stmt_if_modified (stmt); + } + } + + 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)); + if (var->alias) + any_aliases = true; + else + 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; + } + + /* If there were any aliases, add them to the set last. In this way + when we create the control variables the control variable for the + alias base will have been created first. */ + if (any_aliases) + for (var = varpool_nodes; var ; var = var->next) + if (DECL_THREAD_LOCAL_P (var->decl) && var->alias) + varpool_node_set_add (tls_vars, var); + + /* 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); + + /* If there was an alias between the TLS variables, + mirror that in the control variables. */ + /* ??? There appears to be an alias_pairs data structure that + holds similar information. */ + if (var->alias) + { + tree cbase = emutls_decl (var->extra_name->decl); + bool ok = varpool_extra_name_alias (cdecl, cbase); + gcc_assert (ok); + } + + cvar = varpool_get_node (cdecl); + varpool_node_set_add (control_vars, cvar); + + 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; + } + + /* 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 a4c97b3..b309be8 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -445,6 +445,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 960ee7d..8cc4167 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5257,9 +5257,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 (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 de78bd0..078efc2 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -186,320 +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 args, 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 = null_pointer_node; - args = tree_cons (NULL, x, NULL); - x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (h->base.from)); - args = tree_cons (NULL, x, args); - x = fold_convert (word_type_node, DECL_SIZE_UNIT (h->base.from)); - args = tree_cons (NULL, x, args); - x = build_fold_addr_expr (h->to); - args = tree_cons (NULL, x, args); - - x = built_in_decls[BUILT_IN_EMUTLS_REGISTER_COMMON]; - x = build_function_call_expr (UNKNOWN_LOCATION, x, args); - - 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 @@ -1213,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; } @@ -2101,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 +1800,6 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, rtx decl_rtl, symbol; section *sect; - 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; - } - last_assemble_variable_decl = 0; /* Normally no need to say anything here for external references, @@ -2204,6 +1822,9 @@ assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED, return; } + /* Emulated TLS had better not get this far. */ + gcc_assert (targetm.have_tls || !DECL_THREAD_LOCAL_P (decl)); + /* If type was incomplete when the variable was declared, see if it is complete now. */ @@ -5691,6 +5312,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; @@ -5705,14 +5331,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); @@ -5731,14 +5349,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. */ @@ -6410,7 +6020,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..dcf3518 100644 --- a/gcc/varpool.c +++ b/gcc/varpool.c @@ -346,17 +346,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)