0
|
1 /* TLS emulation.
|
|
2 Copyright (C) 2006, 2008, 2009 Free Software Foundation, Inc.
|
|
3 Contributed by Jakub Jelinek <jakub@redhat.com>.
|
|
4
|
|
5 This file is part of GCC.
|
|
6
|
|
7 GCC is free software; you can redistribute it and/or modify it under
|
|
8 the terms of the GNU General Public License as published by the Free
|
|
9 Software Foundation; either version 3, or (at your option) any later
|
|
10 version.
|
|
11
|
|
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
15 for more details.
|
|
16
|
|
17 Under Section 7 of GPL version 3, you are granted additional
|
|
18 permissions described in the GCC Runtime Library Exception, version
|
|
19 3.1, as published by the Free Software Foundation.
|
|
20
|
|
21 You should have received a copy of the GNU General Public License and
|
|
22 a copy of the GCC Runtime Library Exception along with this program;
|
|
23 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
24 <http://www.gnu.org/licenses/>. */
|
|
25
|
|
26 #include "tconfig.h"
|
|
27 #include "tsystem.h"
|
|
28 #include "coretypes.h"
|
|
29 #include "tm.h"
|
|
30 #include "gthr.h"
|
|
31
|
|
32 typedef unsigned int word __attribute__((mode(word)));
|
|
33 typedef unsigned int pointer __attribute__((mode(pointer)));
|
|
34
|
|
35 struct __emutls_object
|
|
36 {
|
|
37 word size;
|
|
38 word align;
|
|
39 union {
|
|
40 pointer offset;
|
|
41 void *ptr;
|
|
42 } loc;
|
|
43 void *templ;
|
|
44 };
|
|
45
|
|
46 struct __emutls_array
|
|
47 {
|
|
48 pointer size;
|
|
49 void **data[];
|
|
50 };
|
|
51
|
|
52 void *__emutls_get_address (struct __emutls_object *);
|
|
53 void __emutls_register_common (struct __emutls_object *, word, word, void *);
|
|
54
|
|
55 #ifdef __GTHREADS
|
|
56 #ifdef __GTHREAD_MUTEX_INIT
|
|
57 static __gthread_mutex_t emutls_mutex = __GTHREAD_MUTEX_INIT;
|
|
58 #else
|
|
59 static __gthread_mutex_t emutls_mutex;
|
|
60 #endif
|
|
61 static __gthread_key_t emutls_key;
|
|
62 static pointer emutls_size;
|
|
63
|
|
64 static void
|
|
65 emutls_destroy (void *ptr)
|
|
66 {
|
|
67 struct __emutls_array *arr = ptr;
|
|
68 pointer size = arr->size;
|
|
69 pointer i;
|
|
70
|
|
71 for (i = 0; i < size; ++i)
|
|
72 {
|
|
73 if (arr->data[i])
|
|
74 free (arr->data[i][-1]);
|
|
75 }
|
|
76
|
|
77 free (ptr);
|
|
78 }
|
|
79
|
|
80 static void
|
|
81 emutls_init (void)
|
|
82 {
|
|
83 #ifndef __GTHREAD_MUTEX_INIT
|
|
84 __GTHREAD_MUTEX_INIT_FUNCTION (&emutls_mutex);
|
|
85 #endif
|
|
86 if (__gthread_key_create (&emutls_key, emutls_destroy) != 0)
|
|
87 abort ();
|
|
88 }
|
|
89 #endif
|
|
90
|
|
91 static void *
|
|
92 emutls_alloc (struct __emutls_object *obj)
|
|
93 {
|
|
94 void *ptr;
|
|
95 void *ret;
|
|
96
|
|
97 /* We could use here posix_memalign if available and adjust
|
|
98 emutls_destroy accordingly. */
|
|
99 if (obj->align <= sizeof (void *))
|
|
100 {
|
|
101 ptr = malloc (obj->size + sizeof (void *));
|
|
102 if (ptr == NULL)
|
|
103 abort ();
|
|
104 ((void **) ptr)[0] = ptr;
|
|
105 ret = ptr + sizeof (void *);
|
|
106 }
|
|
107 else
|
|
108 {
|
|
109 ptr = malloc (obj->size + sizeof (void *) + obj->align - 1);
|
|
110 if (ptr == NULL)
|
|
111 abort ();
|
|
112 ret = (void *) (((pointer) (ptr + sizeof (void *) + obj->align - 1))
|
|
113 & ~(pointer)(obj->align - 1));
|
|
114 ((void **) ret)[-1] = ptr;
|
|
115 }
|
|
116
|
|
117 if (obj->templ)
|
|
118 memcpy (ret, obj->templ, obj->size);
|
|
119 else
|
|
120 memset (ret, 0, obj->size);
|
|
121
|
|
122 return ret;
|
|
123 }
|
|
124
|
|
125 void *
|
|
126 __emutls_get_address (struct __emutls_object *obj)
|
|
127 {
|
|
128 if (! __gthread_active_p ())
|
|
129 {
|
|
130 if (__builtin_expect (obj->loc.ptr == NULL, 0))
|
|
131 obj->loc.ptr = emutls_alloc (obj);
|
|
132 return obj->loc.ptr;
|
|
133 }
|
|
134
|
|
135 #ifndef __GTHREADS
|
|
136 abort ();
|
|
137 #else
|
|
138 pointer offset = obj->loc.offset;
|
|
139
|
|
140 if (__builtin_expect (offset == 0, 0))
|
|
141 {
|
|
142 static __gthread_once_t once = __GTHREAD_ONCE_INIT;
|
|
143 __gthread_once (&once, emutls_init);
|
|
144 __gthread_mutex_lock (&emutls_mutex);
|
|
145 offset = obj->loc.offset;
|
|
146 if (offset == 0)
|
|
147 {
|
|
148 offset = ++emutls_size;
|
|
149 obj->loc.offset = offset;
|
|
150 }
|
|
151 __gthread_mutex_unlock (&emutls_mutex);
|
|
152 }
|
|
153
|
|
154 struct __emutls_array *arr = __gthread_getspecific (emutls_key);
|
|
155 if (__builtin_expect (arr == NULL, 0))
|
|
156 {
|
|
157 pointer size = offset + 32;
|
|
158 arr = calloc (size, sizeof (void *));
|
|
159 if (arr == NULL)
|
|
160 abort ();
|
|
161 arr->size = size;
|
|
162 __gthread_setspecific (emutls_key, (void *) arr);
|
|
163 }
|
|
164 else if (__builtin_expect (offset >= arr->size, 0))
|
|
165 {
|
|
166 pointer orig_size = arr->size;
|
|
167 pointer size = orig_size * 2;
|
|
168 if (offset >= size)
|
|
169 size = offset + 32;
|
|
170 arr = realloc (arr, size * sizeof (void *));
|
|
171 if (arr == NULL)
|
|
172 abort ();
|
|
173 arr->size = size;
|
|
174 memset (arr->data + orig_size - 1, 0,
|
|
175 (size - orig_size) * sizeof (void *));
|
|
176 __gthread_setspecific (emutls_key, (void *) arr);
|
|
177 }
|
|
178
|
|
179 void *ret = arr->data[offset - 1];
|
|
180 if (__builtin_expect (ret == NULL, 0))
|
|
181 {
|
|
182 ret = emutls_alloc (obj);
|
|
183 arr->data[offset - 1] = ret;
|
|
184 }
|
|
185 return ret;
|
|
186 #endif
|
|
187 }
|
|
188
|
|
189 void
|
|
190 __emutls_register_common (struct __emutls_object *obj,
|
|
191 word size, word align, void *templ)
|
|
192 {
|
|
193 if (obj->size < size)
|
|
194 {
|
|
195 obj->size = size;
|
|
196 obj->templ = NULL;
|
|
197 }
|
|
198 if (obj->align < align)
|
|
199 obj->align = align;
|
|
200 if (templ && size == obj->size)
|
|
201 obj->templ = templ;
|
|
202 }
|