Merge branch 'vendor/GCC47'
[dragonfly.git] / contrib / gcc-4.7 / libgcc / emutls.c
1 /* TLS emulation.
2    Copyright (C) 2006, 2008, 2009, 2011 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 "libgcc_tm.h"
31 #include "gthr.h"
32
33 typedef unsigned int word __attribute__((mode(word)));
34 typedef unsigned int pointer __attribute__((mode(pointer)));
35
36 struct __emutls_object
37 {
38   word size;
39   word align;
40   union {
41     pointer offset;
42     void *ptr;
43   } loc;
44   void *templ;
45 };
46
47 struct __emutls_array
48 {
49   pointer size;
50   void **data[];
51 };
52
53 void *__emutls_get_address (struct __emutls_object *);
54 void __emutls_register_common (struct __emutls_object *, word, word, void *);
55
56 #ifdef __GTHREADS
57 #ifdef __GTHREAD_MUTEX_INIT
58 static __gthread_mutex_t emutls_mutex = __GTHREAD_MUTEX_INIT;
59 #else
60 static __gthread_mutex_t emutls_mutex;
61 #endif
62 static __gthread_key_t emutls_key;
63 static pointer emutls_size;
64
65 static void
66 emutls_destroy (void *ptr)
67 {
68   struct __emutls_array *arr = ptr;
69   pointer size = arr->size;
70   pointer i;
71
72   for (i = 0; i < size; ++i)
73     {
74       if (arr->data[i])
75         free (arr->data[i][-1]);
76     }
77
78   free (ptr);
79 }
80
81 static void
82 emutls_init (void)
83 {
84 #ifndef __GTHREAD_MUTEX_INIT
85   __GTHREAD_MUTEX_INIT_FUNCTION (&emutls_mutex);
86 #endif
87   if (__gthread_key_create (&emutls_key, emutls_destroy) != 0)
88     abort ();
89 }
90 #endif
91
92 static void *
93 emutls_alloc (struct __emutls_object *obj)
94 {
95   void *ptr;
96   void *ret;
97
98   /* We could use here posix_memalign if available and adjust
99      emutls_destroy accordingly.  */
100   if (obj->align <= sizeof (void *))
101     {
102       ptr = malloc (obj->size + sizeof (void *));
103       if (ptr == NULL)
104         abort ();
105       ((void **) ptr)[0] = ptr;
106       ret = ptr + sizeof (void *);
107     }
108   else
109     {
110       ptr = malloc (obj->size + sizeof (void *) + obj->align - 1);
111       if (ptr == NULL)
112         abort ();
113       ret = (void *) (((pointer) (ptr + sizeof (void *) + obj->align - 1))
114                       & ~(pointer)(obj->align - 1));
115       ((void **) ret)[-1] = ptr;
116     }
117
118   if (obj->templ)
119     memcpy (ret, obj->templ, obj->size);
120   else
121     memset (ret, 0, obj->size);
122
123   return ret;
124 }
125
126 void *
127 __emutls_get_address (struct __emutls_object *obj)
128 {
129   if (! __gthread_active_p ())
130     {
131       if (__builtin_expect (obj->loc.ptr == NULL, 0))
132         obj->loc.ptr = emutls_alloc (obj);
133       return obj->loc.ptr;
134     }
135
136 #ifndef __GTHREADS
137   abort ();
138 #else
139   pointer offset = __atomic_load_n (&obj->loc.offset, __ATOMIC_ACQUIRE);
140
141   if (__builtin_expect (offset == 0, 0))
142     {
143       static __gthread_once_t once = __GTHREAD_ONCE_INIT;
144       __gthread_once (&once, emutls_init);
145       __gthread_mutex_lock (&emutls_mutex);
146       offset = obj->loc.offset;
147       if (offset == 0)
148         {
149           offset = ++emutls_size;
150           __atomic_store_n (&obj->loc.offset, offset, __ATOMIC_RELEASE);
151         }
152       __gthread_mutex_unlock (&emutls_mutex);
153     }
154
155   struct __emutls_array *arr = __gthread_getspecific (emutls_key);
156   if (__builtin_expect (arr == NULL, 0))
157     {
158       pointer size = offset + 32;
159       arr = calloc (size + 1, sizeof (void *));
160       if (arr == NULL)
161         abort ();
162       arr->size = size;
163       __gthread_setspecific (emutls_key, (void *) arr);
164     }
165   else if (__builtin_expect (offset > arr->size, 0))
166     {
167       pointer orig_size = arr->size;
168       pointer size = orig_size * 2;
169       if (offset > size)
170         size = offset + 32;
171       arr = realloc (arr, (size + 1) * sizeof (void *));
172       if (arr == NULL)
173         abort ();
174       arr->size = size;
175       memset (arr->data + orig_size, 0,
176               (size - orig_size) * sizeof (void *));
177       __gthread_setspecific (emutls_key, (void *) arr);
178     }
179
180   void *ret = arr->data[offset - 1];
181   if (__builtin_expect (ret == NULL, 0))
182     {
183       ret = emutls_alloc (obj);
184       arr->data[offset - 1] = ret;
185     }
186   return ret;
187 #endif
188 }
189
190 void
191 __emutls_register_common (struct __emutls_object *obj,
192                           word size, word align, void *templ)
193 {
194   if (obj->size < size)
195     {
196       obj->size = size;
197       obj->templ = NULL;
198     }
199   if (obj->align < align)
200     obj->align = align;
201   if (templ && size == obj->size)
202     obj->templ = templ;
203 }