OSDN Git Service

f26d21772e3907607352891f604ca8b6fe13dfc7
[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 #ifdef __GTHREADS
52 #ifdef __GTHREAD_MUTEX_INIT
53 static __gthread_mutex_t emutls_mutex = __GTHREAD_MUTEX_INIT;
54 #else
55 static __gthread_mutex_t emutls_mutex;
56 #endif
57 static __gthread_key_t emutls_key;
58 static pointer emutls_size;
59
60 static void
61 emutls_destroy (void *ptr)
62 {
63   void ***arr = (void ***) ptr;
64   unsigned long int size = (unsigned long int) arr[0];
65   ++arr;
66   while (--size)
67     {
68       if (*arr)
69         free ((*arr)[-1]);
70       ++arr;
71     }
72   free (ptr);
73 }
74
75 static void
76 emutls_init (void)
77 {
78 #ifndef __GTHREAD_MUTEX_INIT
79   __GTHREAD_MUTEX_INIT_FUNCTION (&emutls_mutex);
80 #endif
81   if (__gthread_key_create (&emutls_key, emutls_destroy) != 0)
82     abort ();
83 }
84 #endif
85
86 static void *
87 emutls_alloc (struct __emutls_object *obj)
88 {
89   void *ptr;
90   void *ret;
91
92   /* We could use here posix_memalign if available and adjust
93      emutls_destroy accordingly.  */
94   if (obj->align <= sizeof (void *))
95     {
96       ptr = malloc (obj->size + sizeof (void *));
97       if (ptr == NULL)
98         abort ();
99       ((void **) ptr)[0] = ptr;
100       ret = ptr + sizeof (void *);
101     }
102   else
103     {
104       ptr = malloc (obj->size + sizeof (void *) + obj->align - 1);
105       if (ptr == NULL)
106         abort ();
107       ret = (void *) (((pointer) (ptr + sizeof (void *) + obj->align - 1))
108                       & ~(pointer)(obj->align - 1));
109       ((void **) ret)[-1] = ptr;
110     }
111
112   if (obj->templ)
113     memcpy (ret, obj->templ, obj->size);
114   else
115     memset (ret, 0, obj->size);
116
117   return ret;
118 }
119
120 void *
121 __emutls_get_address (struct __emutls_object *obj)
122 {
123   if (! __gthread_active_p ())
124     {
125       if (__builtin_expect (obj->loc.ptr == NULL, 0))
126         obj->loc.ptr = emutls_alloc (obj);
127       return obj->loc.ptr;
128     }
129
130 #ifndef __GTHREADS
131   abort ();
132 #else
133   pointer offset;
134
135   if (__builtin_expect (obj->loc.offset == 0, 0))
136     {
137       static __gthread_once_t once = __GTHREAD_ONCE_INIT;
138       __gthread_once (&once, emutls_init);
139       __gthread_mutex_lock (&emutls_mutex);
140       offset = ++emutls_size;
141       obj->loc.offset = offset;
142       __gthread_mutex_unlock (&emutls_mutex);
143     }
144   else
145     offset = obj->loc.offset;
146
147   void **arr = (void **) __gthread_getspecific (emutls_key);
148   if (__builtin_expect (arr == NULL, 0))
149     {
150       pointer size = offset + 32;
151       arr = calloc (size, sizeof (void *));
152       if (arr == NULL)
153         abort ();
154       arr[0] = (void *) size;
155       __gthread_setspecific (emutls_key, (void *) arr);
156     }
157   else if (__builtin_expect (offset >= (pointer) arr[0], 0))
158     {
159       pointer orig_size = (pointer) arr[0];
160       pointer size = orig_size * 2;
161       if (offset >= size)
162         size = offset + 32;
163       arr = realloc (arr, size * sizeof (void *));
164       if (arr == NULL)
165         abort ();
166       memset (arr + orig_size, 0, (size - orig_size) * sizeof (void *));
167       __gthread_setspecific (emutls_key, (void *) arr);
168     }
169
170   void *ret = arr[offset];
171   if (__builtin_expect (ret == NULL, 0))
172     {
173       ret = emutls_alloc (obj);
174       arr[offset] = ret;
175     }
176   return ret;
177 #endif
178 }
179
180 void
181 __emutls_register_common (struct __emutls_object *obj,
182                           word size, word align, void *templ)
183 {
184   if (obj->size < size)
185     {
186       obj->size = size;
187       obj->templ = NULL;
188     }
189   if (obj->align < align)
190     obj->align = align;
191   if (templ && size == obj->size)
192     obj->templ = templ;
193 }