blob: 5dd8dd15477183cb28de08f29fd7ebd4e3a341db [file] [log] [blame]
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +00001/* ===---------- emutls.c - Implements __emutls_get_address ---------------===
2 *
3 * The LLVM Compiler Infrastructure
4 *
5 * This file is dual licensed under the MIT and the University of Illinois Open
6 * Source Licenses. See LICENSE.TXT for details.
7 *
8 * ===----------------------------------------------------------------------===
9 */
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +000010#include <stdint.h>
11#include <stdlib.h>
12#include <string.h>
13
Saleem Abdulrasoold2eb26c2015-10-06 04:33:08 +000014#include "int_lib.h"
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +000015#include "int_util.h"
16
Frederich Munch922b6022017-04-25 19:04:19 +000017typedef struct emutls_address_array {
18 uintptr_t size; /* number of elements in the 'data' array */
19 void* data[];
20} emutls_address_array;
21
22static void emutls_shutdown(emutls_address_array *array);
23
24#ifndef _WIN32
25
26#include <pthread.h>
27
28static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
29static pthread_key_t emutls_pthread_key;
30
31typedef unsigned int gcc_word __attribute__((mode(word)));
32typedef unsigned int gcc_pointer __attribute__((mode(pointer)));
33
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +000034/* Default is not to use posix_memalign, so systems like Android
35 * can use thread local data without heavier POSIX memory allocators.
36 */
37#ifndef EMUTLS_USE_POSIX_MEMALIGN
38#define EMUTLS_USE_POSIX_MEMALIGN 0
39#endif
40
Saleem Abdulrasool911cfc12015-10-10 21:21:28 +000041static __inline void *emutls_memalign_alloc(size_t align, size_t size) {
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +000042 void *base;
43#if EMUTLS_USE_POSIX_MEMALIGN
44 if (posix_memalign(&base, align, size) != 0)
45 abort();
46#else
47 #define EXTRA_ALIGN_PTR_BYTES (align - 1 + sizeof(void*))
48 char* object;
Frederich Munch922b6022017-04-25 19:04:19 +000049 if ((object = (char*)malloc(EXTRA_ALIGN_PTR_BYTES + size)) == NULL)
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +000050 abort();
51 base = (void*)(((uintptr_t)(object + EXTRA_ALIGN_PTR_BYTES))
52 & ~(uintptr_t)(align - 1));
53
54 ((void**)base)[-1] = object;
55#endif
56 return base;
57}
58
Saleem Abdulrasool911cfc12015-10-10 21:21:28 +000059static __inline void emutls_memalign_free(void *base) {
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +000060#if EMUTLS_USE_POSIX_MEMALIGN
61 free(base);
62#else
63 /* The mallocated address is in ((void**)base)[-1] */
64 free(((void**)base)[-1]);
65#endif
66}
67
Frederich Munch922b6022017-04-25 19:04:19 +000068static void emutls_key_destructor(void* ptr) {
69 emutls_shutdown((emutls_address_array*)ptr);
70 free(ptr);
71}
72
73static __inline void emutls_init(void) {
74 if (pthread_key_create(&emutls_pthread_key, emutls_key_destructor) != 0)
75 abort();
76}
77
78static __inline void emutls_init_once(void) {
79 static pthread_once_t once = PTHREAD_ONCE_INIT;
80 pthread_once(&once, emutls_init);
81}
82
83static __inline void emutls_lock() {
84 pthread_mutex_lock(&emutls_mutex);
85}
86
87static __inline void emutls_unlock() {
88 pthread_mutex_unlock(&emutls_mutex);
89}
90
91static __inline void emutls_setspecific(emutls_address_array *value) {
92 pthread_setspecific(emutls_pthread_key, (void*) value);
93}
94
95static __inline emutls_address_array* emutls_getspecific() {
96 return (emutls_address_array*) pthread_getspecific(emutls_pthread_key);
97}
98
99#else
100
Martell Malonec348a8c2017-05-06 15:13:17 +0000101#include <windows.h>
Frederich Munch922b6022017-04-25 19:04:19 +0000102#include <malloc.h>
103#include <stdio.h>
104#include <assert.h>
Frederich Munch922b6022017-04-25 19:04:19 +0000105
106static LPCRITICAL_SECTION emutls_mutex;
107static DWORD emutls_tls_index = TLS_OUT_OF_INDEXES;
108
109typedef uintptr_t gcc_word;
110typedef void * gcc_pointer;
111
112static void win_error(DWORD last_err, const char *hint) {
113 char *buffer = NULL;
114 if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
115 FORMAT_MESSAGE_FROM_SYSTEM |
116 FORMAT_MESSAGE_MAX_WIDTH_MASK,
117 NULL, last_err, 0, (LPSTR)&buffer, 1, NULL)) {
118 fprintf(stderr, "Windows error: %s\n", buffer);
119 } else {
120 fprintf(stderr, "Unkown Windows error: %s\n", hint);
121 }
122 LocalFree(buffer);
123}
124
125static __inline void win_abort(DWORD last_err, const char *hint) {
126 win_error(last_err, hint);
127 abort();
128}
129
130static __inline void *emutls_memalign_alloc(size_t align, size_t size) {
131 void *base = _aligned_malloc(size, align);
132 if (!base)
133 win_abort(GetLastError(), "_aligned_malloc");
134 return base;
135}
136
137static __inline void emutls_memalign_free(void *base) {
138 _aligned_free(base);
139}
140
141static void emutls_exit(void) {
142 if (emutls_mutex) {
143 DeleteCriticalSection(emutls_mutex);
144 _aligned_free(emutls_mutex);
145 emutls_mutex = NULL;
146 }
147 if (emutls_tls_index != TLS_OUT_OF_INDEXES) {
148 emutls_shutdown((emutls_address_array*)TlsGetValue(emutls_tls_index));
149 TlsFree(emutls_tls_index);
150 emutls_tls_index = TLS_OUT_OF_INDEXES;
151 }
152}
153
154#pragma warning (push)
155#pragma warning (disable : 4100)
156static BOOL CALLBACK emutls_init(PINIT_ONCE p0, PVOID p1, PVOID *p2) {
157 emutls_mutex = (LPCRITICAL_SECTION)_aligned_malloc(sizeof(CRITICAL_SECTION), 16);
158 if (!emutls_mutex) {
159 win_error(GetLastError(), "_aligned_malloc");
160 return FALSE;
161 }
162 InitializeCriticalSection(emutls_mutex);
163
164 emutls_tls_index = TlsAlloc();
165 if (emutls_tls_index == TLS_OUT_OF_INDEXES) {
166 emutls_exit();
167 win_error(GetLastError(), "TlsAlloc");
168 return FALSE;
169 }
170 atexit(&emutls_exit);
171 return TRUE;
172}
173
174static __inline void emutls_init_once(void) {
175 static INIT_ONCE once;
176 InitOnceExecuteOnce(&once, emutls_init, NULL, NULL);
177}
178
179static __inline void emutls_lock() {
180 EnterCriticalSection(emutls_mutex);
181}
182
183static __inline void emutls_unlock() {
184 LeaveCriticalSection(emutls_mutex);
185}
186
187static __inline void emutls_setspecific(emutls_address_array *value) {
188 if (TlsSetValue(emutls_tls_index, (LPVOID) value) == 0)
189 win_abort(GetLastError(), "TlsSetValue");
190}
191
192static __inline emutls_address_array* emutls_getspecific() {
193 LPVOID value = TlsGetValue(emutls_tls_index);
194 if (value == NULL) {
195 const DWORD err = GetLastError();
196 if (err != ERROR_SUCCESS)
197 win_abort(err, "TlsGetValue");
198 }
199 return (emutls_address_array*) value;
200}
201
202/* Provide atomic load/store functions for emutls_get_index if built with MSVC.
203 */
204#if !defined(__ATOMIC_RELEASE)
Martin Storsjobecd2ef2017-08-03 19:04:28 +0000205#include <intrin.h>
Frederich Munch922b6022017-04-25 19:04:19 +0000206
207enum { __ATOMIC_ACQUIRE = 2, __ATOMIC_RELEASE = 3 };
208
209static __inline uintptr_t __atomic_load_n(void *ptr, unsigned type) {
210 assert(type == __ATOMIC_ACQUIRE);
Martin Storsjobecd2ef2017-08-03 19:04:28 +0000211 // These return the previous value - but since we do an OR with 0,
212 // it's equivalent to a plain load.
Frederich Munch922b6022017-04-25 19:04:19 +0000213#ifdef _WIN64
Martin Storsjobecd2ef2017-08-03 19:04:28 +0000214 return InterlockedOr64(ptr, 0);
Frederich Munch922b6022017-04-25 19:04:19 +0000215#else
Martin Storsjobecd2ef2017-08-03 19:04:28 +0000216 return InterlockedOr(ptr, 0);
Frederich Munch922b6022017-04-25 19:04:19 +0000217#endif
218}
219
220static __inline void __atomic_store_n(void *ptr, uintptr_t val, unsigned type) {
221 assert(type == __ATOMIC_RELEASE);
Martin Storsjobecd2ef2017-08-03 19:04:28 +0000222 InterlockedExchangePointer((void *volatile *)ptr, (void *)val);
Frederich Munch922b6022017-04-25 19:04:19 +0000223}
224
225#endif
226
227#pragma warning (pop)
228
229#endif
230
231static size_t emutls_num_object = 0; /* number of allocated TLS objects */
232
233/* Free the allocated TLS data
234 */
235static void emutls_shutdown(emutls_address_array *array) {
236 if (array) {
237 uintptr_t i;
238 for (i = 0; i < array->size; ++i) {
239 if (array->data[i])
240 emutls_memalign_free(array->data[i]);
241 }
242 }
243}
244
245/* For every TLS variable xyz,
246 * there is one __emutls_control variable named __emutls_v.xyz.
247 * If xyz has non-zero initial value, __emutls_v.xyz's "value"
248 * will point to __emutls_t.xyz, which has the initial value.
249 */
250typedef struct __emutls_control {
251 /* Must use gcc_word here, instead of size_t, to match GCC. When
252 gcc_word is larger than size_t, the upper extra bits are all
253 zeros. We can use variables of size_t to operate on size and
254 align. */
255 gcc_word size; /* size of the object in bytes */
256 gcc_word align; /* alignment of the object in bytes */
257 union {
258 uintptr_t index; /* data[index-1] is the object address */
259 void* address; /* object address, when in single thread env */
260 } object;
261 void* value; /* null or non-zero initial value for the object */
262} __emutls_control;
263
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000264/* Emulated TLS objects are always allocated at run-time. */
Saleem Abdulrasool911cfc12015-10-10 21:21:28 +0000265static __inline void *emutls_allocate_object(__emutls_control *control) {
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000266 /* Use standard C types, check with gcc's emutls.o. */
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000267 COMPILE_TIME_ASSERT(sizeof(uintptr_t) == sizeof(gcc_pointer));
268 COMPILE_TIME_ASSERT(sizeof(uintptr_t) == sizeof(void*));
269
270 size_t size = control->size;
271 size_t align = control->align;
Chih-Hung Hsiehc2fab482016-02-04 20:26:00 +0000272 void* base;
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000273 if (align < sizeof(void*))
274 align = sizeof(void*);
275 /* Make sure that align is power of 2. */
276 if ((align & (align - 1)) != 0)
277 abort();
278
Chih-Hung Hsiehc2fab482016-02-04 20:26:00 +0000279 base = emutls_memalign_alloc(align, size);
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000280 if (control->value)
281 memcpy(base, control->value, size);
282 else
283 memset(base, 0, size);
284 return base;
285}
286
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000287
288/* Returns control->object.index; set index if not allocated yet. */
Saleem Abdulrasool911cfc12015-10-10 21:21:28 +0000289static __inline uintptr_t emutls_get_index(__emutls_control *control) {
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000290 uintptr_t index = __atomic_load_n(&control->object.index, __ATOMIC_ACQUIRE);
291 if (!index) {
Frederich Munch922b6022017-04-25 19:04:19 +0000292 emutls_init_once();
293 emutls_lock();
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000294 index = control->object.index;
295 if (!index) {
296 index = ++emutls_num_object;
297 __atomic_store_n(&control->object.index, index, __ATOMIC_RELEASE);
298 }
Frederich Munch922b6022017-04-25 19:04:19 +0000299 emutls_unlock();
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000300 }
301 return index;
302}
303
304/* Updates newly allocated thread local emutls_address_array. */
Saleem Abdulrasool911cfc12015-10-10 21:21:28 +0000305static __inline void emutls_check_array_set_size(emutls_address_array *array,
306 uintptr_t size) {
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000307 if (array == NULL)
308 abort();
309 array->size = size;
Frederich Munch922b6022017-04-25 19:04:19 +0000310 emutls_setspecific(array);
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000311}
312
313/* Returns the new 'data' array size, number of elements,
314 * which must be no smaller than the given index.
315 */
Saleem Abdulrasool911cfc12015-10-10 21:21:28 +0000316static __inline uintptr_t emutls_new_data_array_size(uintptr_t index) {
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000317 /* Need to allocate emutls_address_array with one extra slot
318 * to store the data array size.
319 * Round up the emutls_address_array size to multiple of 16.
320 */
321 return ((index + 1 + 15) & ~((uintptr_t)15)) - 1;
322}
323
Frederich Munch922b6022017-04-25 19:04:19 +0000324/* Returns the size in bytes required for an emutls_address_array with
325 * N number of elements for data field.
326 */
327static __inline uintptr_t emutls_asize(uintptr_t N) {
328 return N * sizeof(void *) + sizeof(emutls_address_array);
329}
330
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000331/* Returns the thread local emutls_address_array.
332 * Extends its size if necessary to hold address at index.
333 */
Saleem Abdulrasool911cfc12015-10-10 21:21:28 +0000334static __inline emutls_address_array *
335emutls_get_address_array(uintptr_t index) {
Frederich Munch922b6022017-04-25 19:04:19 +0000336 emutls_address_array* array = emutls_getspecific();
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000337 if (array == NULL) {
338 uintptr_t new_size = emutls_new_data_array_size(index);
Frederich Munch922b6022017-04-25 19:04:19 +0000339 array = (emutls_address_array*) malloc(emutls_asize(new_size));
George Burgess IV908dacf2016-04-14 23:58:26 +0000340 if (array)
341 memset(array->data, 0, new_size * sizeof(void*));
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000342 emutls_check_array_set_size(array, new_size);
343 } else if (index > array->size) {
344 uintptr_t orig_size = array->size;
345 uintptr_t new_size = emutls_new_data_array_size(index);
Frederich Munch922b6022017-04-25 19:04:19 +0000346 array = (emutls_address_array*) realloc(array, emutls_asize(new_size));
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000347 if (array)
348 memset(array->data + orig_size, 0,
349 (new_size - orig_size) * sizeof(void*));
350 emutls_check_array_set_size(array, new_size);
351 }
352 return array;
353}
354
355void* __emutls_get_address(__emutls_control* control) {
356 uintptr_t index = emutls_get_index(control);
Frederich Munch922b6022017-04-25 19:04:19 +0000357 emutls_address_array* array = emutls_get_address_array(index--);
358 if (array->data[index] == NULL)
359 array->data[index] = emutls_allocate_object(control);
360 return array->data[index];
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000361}