OSDN Git Service

2008-11-03 Sebastian Pop <sebastian.pop@amd.com>
[pf3gnuchains/gcc-fork.git] / gcc / emutls.c
1 /* TLS emulation.
2    Copyright (C) 2006 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 2, or (at your option) any later
10 version.
11
12 In addition to the permissions in the GNU General Public License, the
13 Free Software Foundation gives you unlimited permission to link the
14 compiled version of this file into combinations with other programs,
15 and to distribute those combinations without any restriction coming
16 from the use of this file.  (The General Public License restrictions
17 do apply in other respects; for example, they cover modification of
18 the file, and distribution when not linked into a combine
19 executable.)
20
21 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
22 WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
24 for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with GCC; see the file COPYING.  If not, write to the Free
28 Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
29 02110-1301, USA.  */
30
31 #include "tconfig.h"
32 #include "tsystem.h"
33 #include "coretypes.h"
34 #include "tm.h"
35 #include "gthr.h"
36
37 typedef unsigned int word __attribute__((mode(word)));
38 typedef unsigned int pointer __attribute__((mode(pointer)));
39
40 struct __emutls_object
41 {
42   word size;
43   word align;
44   union {
45     pointer offset;
46     void *ptr;
47   } loc;
48   void *templ;
49 };
50
51 struct __emutls_array
52 {
53   pointer size;
54   void **data[];
55 };
56
57 #ifdef __GTHREADS
58 #ifdef __GTHREAD_MUTEX_INIT
59 static __gthread_mutex_t emutls_mutex = __GTHREAD_MUTEX_INIT;
60 #else
61 static __gthread_mutex_t emutls_mutex;
62 #endif
63 static __gthread_key_t emutls_key;
64 static pointer emutls_size;
65
66 static void
67 emutls_destroy (void *ptr)
68 {
69   struct __emutls_array *arr = ptr;
70   pointer size = arr->size;
71   pointer i;
72
73   for (i = 0; i < size; ++i)
74     {
75       if (arr->data[i])
76         free (arr->data[i][-1]);
77     }
78
79   free (ptr);
80 }
81
82 static void
83 emutls_init (void)
84 {
85 #ifndef __GTHREAD_MUTEX_INIT
86   __GTHREAD_MUTEX_INIT_FUNCTION (&emutls_mutex);
87 #endif
88   if (__gthread_key_create (&emutls_key, emutls_destroy) != 0)
89     abort ();
90 }
91 #endif
92
93 static void *
94 emutls_alloc (struct __emutls_object *obj)
95 {
96   void *ptr;
97   void *ret;
98
99   /* We could use here posix_memalign if available and adjust
100      emutls_destroy accordingly.  */
101   if (obj->align <= sizeof (void *))
102     {
103       ptr = malloc (obj->size + sizeof (void *));
104       if (ptr == NULL)
105         abort ();
106       ((void **) ptr)[0] = ptr;
107       ret = ptr + sizeof (void *);
108     }
109   else
110     {
111       ptr = malloc (obj->size + sizeof (void *) + obj->align - 1);
112       if (ptr == NULL)
113         abort ();
114       ret = (void *) (((pointer) (ptr + sizeof (void *) + obj->align - 1))
115                       & ~(pointer)(obj->align - 1));
116       ((void **) ret)[-1] = ptr;
117     }
118
119   if (obj->templ)
120     memcpy (ret, obj->templ, obj->size);
121   else
122     memset (ret, 0, obj->size);
123
124   return ret;
125 }
126
127 void *
128 __emutls_get_address (struct __emutls_object *obj)
129 {
130   if (! __gthread_active_p ())
131     {
132       if (__builtin_expect (obj->loc.ptr == NULL, 0))
133         obj->loc.ptr = emutls_alloc (obj);
134       return obj->loc.ptr;
135     }
136
137 #ifndef __GTHREADS
138   abort ();
139 #else
140   pointer offset = obj->loc.offset;
141
142   if (__builtin_expect (offset == 0, 0))
143     {
144       static __gthread_once_t once = __GTHREAD_ONCE_INIT;
145       __gthread_once (&once, emutls_init);
146       __gthread_mutex_lock (&emutls_mutex);
147       offset = ++emutls_size;
148       obj->loc.offset = offset;
149       __gthread_mutex_unlock (&emutls_mutex);
150     }
151
152   struct __emutls_array *arr = __gthread_getspecific (emutls_key);
153   if (__builtin_expect (arr == NULL, 0))
154     {
155       pointer size = offset + 32;
156       arr = calloc (size, sizeof (void *));
157       if (arr == NULL)
158         abort ();
159       arr->size = size;
160       __gthread_setspecific (emutls_key, (void *) arr);
161     }
162   else if (__builtin_expect (offset >= arr->size, 0))
163     {
164       pointer orig_size = arr->size;
165       pointer size = orig_size * 2;
166       if (offset >= size)
167         size = offset + 32;
168       arr = realloc (arr, size * sizeof (void *));
169       if (arr == NULL)
170         abort ();
171       arr->size = size;
172       memset (arr->data + orig_size - 1, 0,
173               (size - orig_size) * sizeof (void *));
174       __gthread_setspecific (emutls_key, (void *) arr);
175     }
176
177   void *ret = arr->data[offset - 1];
178   if (__builtin_expect (ret == NULL, 0))
179     {
180       ret = emutls_alloc (obj);
181       arr->data[offset - 1] = ret;
182     }
183   return ret;
184 #endif
185 }
186
187 void
188 __emutls_register_common (struct __emutls_object *obj,
189                           word size, word align, void *templ)
190 {
191   if (obj->size < size)
192     {
193       obj->size = size;
194       obj->templ = NULL;
195     }
196   if (obj->align < align)
197     obj->align = align;
198   if (templ && size == obj->size)
199     obj->templ = templ;
200 }