summaryrefslogtreecommitdiff
path: root/gcc/varasm.c
diff options
context:
space:
mode:
authorrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>2006-10-04 21:09:20 +0000
committerrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>2006-10-04 21:09:20 +0000
commit2551f8e0be582a9e3fa959425842978d4c6772dc (patch)
treec4b6efa5601a2114eda9c1a31ae3155935113ab0 /gcc/varasm.c
parent227bbb0869285c1d34fc4e4e7b1ed064875692a4 (diff)
downloadgcc-2551f8e0be582a9e3fa959425842978d4c6772dc.tar.gz
gcc/
* Makefile.in (libgcc.mk, LIBGCC_DEPS): Add emutls.c. * builtin-types.def (BT_WORD): Make unsigned. (BT_FN_VOID_PTR_WORD_WORD_PTR): New. * builtins.def (BUILT_IN_EMUTLS_GET_ADDRESS): New. (BUILT_IN_EMUTLS_REGISTER_COMMON): New. * c-decl.c (grokdeclarator): Don't error if !have_tls. * c-parser.c (c_parser_omp_threadprivate): Likewise. * cgraph.c (decide_is_variable_needed): Look at force_output. Recurse for emulated tls. * cgraphunit.c (cgraph_varpool_remove_unreferenced_decls): Remove checks redundant with decide_is_variable_needed. (cgraph_build_static_cdtor): Do cgraph_varpool_assemble_pending_decls. * dwarf2out.c (loc_descriptor_from_tree_1): Don't do anything for emulated tls. * expr.c (emutls_var_address): New. (expand_expr_real_1): Expand emulated tls. (expand_expr_addr_expr_1): Likewise. * libgcc-std.ver: Add __emutls_get_address, __emutls_register_common. * output.h (emutls_finish): Declare. * toplev.c (compile_file): Call it. * tree-ssa-address.c (gen_addr_rtx): Check for const-ness of the address before wrapping in CONST. * varasm.c (emutls_htab, emutls_object_type): New. (EMUTLS_VAR_PREFIX, EMUTLS_TMPL_PREFIX): New. (get_emutls_object_name, get_emutls_object_type): New. (get_emutls_init_templ_addr, emutls_decl): New. (emutls_common_1, emutls_finish): New. (assemble_variable): When emulating tls, swap decls; generate constructor for the emutls objects. (do_assemble_alias): When emulating tls, swap decl and target name. (default_encode_section_info): Don't add SYMBOL_FLAG_TLS_SHIFT for emulated tls. * emutls.c: New file. * config/sparc/sol2.h (ASM_DECLARE_OBJECT_NAME): Only emit tls_object for real tls. gcc/cp/ * decl.c (grokvardecl): Don't error if !have_tls. (grokdeclarator): Likewise. * parser.c (cp_parser_omp_threadprivate): Likewise. gcc/fortran/ * f95-lang.c (gfc_init_builtin_functions): Add __emutls_get_address and __emutls_register_common. * openmp.c (gfc_match_omp_threadprivate): Don't error if !have_tls. * trans-common.c (build_common_decl): Don't check have_tls. * trans-decl.c (gfc_finish_var_decl): Likewise. * types.def (BT_WORD, BT_FN_PTR_PTR): New. (BT_FN_VOID_PTR_WORD_WORD_PTR): New. gcc/testsuite/ * lib/target-supports.exp (check_effective_target_tls): Redefine to mean non-emulated tls. * gcc.dg/tls/alias-1.c: Remove tls requirement. * gcc.dg/tls/asm-1.c, gcc.dg/tls/debug-1.c, gcc.dg/tls/diag-1.c, gcc.dg/tls/diag-2.c, gcc.dg/tls/diag-3.c, gcc.dg/tls/diag-4.c, gcc.dg/tls/diag-5.c, gcc.dg/tls/init-1.c, gcc.dg/tls/nonpic-1.c, gcc.dg/tls/opt-10.c, gcc.dg/tls/opt-5.c, gcc.dg/tls/opt-6.c, gcc.dg/tls/opt-8.c, gcc.dg/tls/opt-9.c, gcc.dg/tls/pic-1.c, gcc.dg/tls/struct-1.c, gcc.dg/tls/trivial.c: Likewise. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@117440 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/varasm.c')
-rw-r--r--gcc/varasm.c294
1 files changed, 293 insertions, 1 deletions
diff --git a/gcc/varasm.c b/gcc/varasm.c
index c45a0f74b7e..ff3e443ef2c 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -53,6 +53,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
#include "cgraph.h"
#include "cfglayout.h"
#include "basic-block.h"
+#include "tree-iterator.h"
#ifdef XCOFF_DEBUGGING_INFO
#include "xcoffout.h" /* Needed for external data
@@ -200,6 +201,236 @@ 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;
+
+#ifndef NO_DOT_IN_LABEL
+# define EMUTLS_VAR_PREFIX "__emutls_v."
+# define EMUTLS_TMPL_PREFIX "__emutls_t."
+#elif !defined NO_DOLLAR_IN_LABEL
+# define EMUTLS_VAR_PREFIX "__emutls_v$"
+# define EMUTLS_TMPL_PREFIX "__emutls_t$"
+#else
+# define EMUTLS_VAR_PREFIX "__emutls_v_"
+# define EMUTLS_TMPL_PREFIX "__emutls_t_"
+#endif
+
+/* 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)
+{
+ char *toname = alloca (strlen (IDENTIFIER_POINTER (name))
+ + sizeof (EMUTLS_VAR_PREFIX));
+ strcpy (toname, EMUTLS_VAR_PREFIX);
+ strcpy (toname + sizeof (EMUTLS_VAR_PREFIX) - 1, IDENTIFIER_POINTER (name));
+
+ return get_identifier (toname);
+}
+
+/* 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, next_field, word_type_node;
+
+ type = emutls_object_type;
+ if (type)
+ return type;
+
+ emutls_object_type = type = lang_hooks.types.make_type (RECORD_TYPE);
+ type_name = get_identifier ("__emutls_object");
+ type_name = build_decl (TYPE_DECL, type_name, type);
+ TYPE_NAME (type) = type_name;
+
+ field = build_decl (FIELD_DECL, get_identifier ("__templ"), ptr_type_node);
+ DECL_CONTEXT (field) = type;
+ next_field = field;
+
+ field = build_decl (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 (FIELD_DECL, get_identifier ("__align"), word_type_node);
+ DECL_CONTEXT (field) = type;
+ TREE_CHAIN (field) = next_field;
+ next_field = field;
+
+ field = build_decl (FIELD_DECL, get_identifier ("__size"), word_type_node);
+ DECL_CONTEXT (field) = type;
+ TREE_CHAIN (field) = next_field;
+
+ 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;
+ char *toname;
+
+ if (!DECL_INITIAL (decl))
+ return null_pointer_node;
+
+ name = DECL_ASSEMBLER_NAME (decl);
+ toname = alloca (strlen (IDENTIFIER_POINTER (name))
+ + sizeof (EMUTLS_TMPL_PREFIX));
+ strcpy (toname, EMUTLS_TMPL_PREFIX);
+ strcpy (toname + sizeof (EMUTLS_TMPL_PREFIX) - 1, IDENTIFIER_POINTER (name));
+ name = get_identifier (toname);
+
+ to = build_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_WEAK (to) = DECL_WEAK (decl);
+ if (DECL_ONE_ONLY (decl))
+ {
+ make_decl_one_only (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_INITIAL (to) = DECL_INITIAL (decl);
+ DECL_INITIAL (decl) = NULL;
+
+ cgraph_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 = htab_hash_string (IDENTIFIER_POINTER (name));
+ in.from = decl;
+ loc = htab_find_slot_with_hash (emutls_htab, &in, in.hash, INSERT);
+ h = *loc;
+ if (h != NULL)
+ to = h->to;
+ else
+ {
+ to = build_decl (VAR_DECL, get_emutls_object_name (name),
+ get_emutls_object_type ());
+
+ h = ggc_alloc (sizeof (struct tree_map));
+ h->hash = in.hash;
+ h->from = decl;
+ h->to = to;
+ *(struct tree_map **) loc = h;
+
+ DECL_ARTIFICIAL (to) = 1;
+ DECL_IGNORED_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_CONTEXT (to) = DECL_CONTEXT (decl);
+ }
+
+ /* 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. */
+
+ 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);
+
+ 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->from))
+ return 1;
+
+ word_type_node = lang_hooks.types.type_for_mode (word_mode, 1);
+
+ x = get_emutls_init_templ_addr (h->from);
+ args = tree_cons (NULL, x, NULL);
+ x = build_int_cst (word_type_node, DECL_ALIGN_UNIT (h->from));
+ args = tree_cons (NULL, x, args);
+ x = fold_convert (word_type_node, DECL_SIZE_UNIT (h->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 (x, args);
+
+ append_to_statement_list (x, pstmts);
+ return 1;
+}
+
+void
+emutls_finish (void)
+{
+ tree body = NULL_TREE;
+
+ if (emutls_htab == NULL)
+ return;
+
+ 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
@@ -1733,6 +1964,50 @@ 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))
+ {
+ 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 = get_emutls_init_templ_addr (decl);
+
+ DECL_INITIAL (to) = build_constructor (type, v);
+ }
+
+ decl = to;
+ }
+
if (lang_hooks.decls.prepare_assemble_variable)
lang_hooks.decls.prepare_assemble_variable (decl);
@@ -4856,6 +5131,14 @@ 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);
@@ -4873,6 +5156,14 @@ 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. */
@@ -5755,7 +6046,8 @@ default_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED)
flags |= SYMBOL_FLAG_FUNCTION;
if (targetm.binds_local_p (decl))
flags |= SYMBOL_FLAG_LOCAL;
- if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl))
+ if (targetm.have_tls && TREE_CODE (decl) == VAR_DECL
+ && DECL_THREAD_LOCAL_P (decl))
flags |= DECL_TLS_MODEL (decl) << SYMBOL_FLAG_TLS_SHIFT;
else if (targetm.in_small_data_p (decl))
flags |= SYMBOL_FLAG_SMALL;