blob: 12aad3a42b76dc5566a8e5cb2cd94a0b9939c048 [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>
105#include <immintrin.h>
106
107static LPCRITICAL_SECTION emutls_mutex;
108static DWORD emutls_tls_index = TLS_OUT_OF_INDEXES;
109
110typedef uintptr_t gcc_word;
111typedef void * gcc_pointer;
112
113static void win_error(DWORD last_err, const char *hint) {
114 char *buffer = NULL;
115 if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
116 FORMAT_MESSAGE_FROM_SYSTEM |
117 FORMAT_MESSAGE_MAX_WIDTH_MASK,
118 NULL, last_err, 0, (LPSTR)&buffer, 1, NULL)) {
119 fprintf(stderr, "Windows error: %s\n", buffer);
120 } else {
121 fprintf(stderr, "Unkown Windows error: %s\n", hint);
122 }
123 LocalFree(buffer);
124}
125
126static __inline void win_abort(DWORD last_err, const char *hint) {
127 win_error(last_err, hint);
128 abort();
129}
130
131static __inline void *emutls_memalign_alloc(size_t align, size_t size) {
132 void *base = _aligned_malloc(size, align);
133 if (!base)
134 win_abort(GetLastError(), "_aligned_malloc");
135 return base;
136}
137
138static __inline void emutls_memalign_free(void *base) {
139 _aligned_free(base);
140}
141
142static void emutls_exit(void) {
143 if (emutls_mutex) {
144 DeleteCriticalSection(emutls_mutex);
145 _aligned_free(emutls_mutex);
146 emutls_mutex = NULL;
147 }
148 if (emutls_tls_index != TLS_OUT_OF_INDEXES) {
149 emutls_shutdown((emutls_address_array*)TlsGetValue(emutls_tls_index));
150 TlsFree(emutls_tls_index);
151 emutls_tls_index = TLS_OUT_OF_INDEXES;
152 }
153}
154
155#pragma warning (push)
156#pragma warning (disable : 4100)
157static BOOL CALLBACK emutls_init(PINIT_ONCE p0, PVOID p1, PVOID *p2) {
158 emutls_mutex = (LPCRITICAL_SECTION)_aligned_malloc(sizeof(CRITICAL_SECTION), 16);
159 if (!emutls_mutex) {
160 win_error(GetLastError(), "_aligned_malloc");
161 return FALSE;
162 }
163 InitializeCriticalSection(emutls_mutex);
164
165 emutls_tls_index = TlsAlloc();
166 if (emutls_tls_index == TLS_OUT_OF_INDEXES) {
167 emutls_exit();
168 win_error(GetLastError(), "TlsAlloc");
169 return FALSE;
170 }
171 atexit(&emutls_exit);
172 return TRUE;
173}
174
175static __inline void emutls_init_once(void) {
176 static INIT_ONCE once;
177 InitOnceExecuteOnce(&once, emutls_init, NULL, NULL);
178}
179
180static __inline void emutls_lock() {
181 EnterCriticalSection(emutls_mutex);
182}
183
184static __inline void emutls_unlock() {
185 LeaveCriticalSection(emutls_mutex);
186}
187
188static __inline void emutls_setspecific(emutls_address_array *value) {
189 if (TlsSetValue(emutls_tls_index, (LPVOID) value) == 0)
190 win_abort(GetLastError(), "TlsSetValue");
191}
192
193static __inline emutls_address_array* emutls_getspecific() {
194 LPVOID value = TlsGetValue(emutls_tls_index);
195 if (value == NULL) {
196 const DWORD err = GetLastError();
197 if (err != ERROR_SUCCESS)
198 win_abort(err, "TlsGetValue");
199 }
200 return (emutls_address_array*) value;
201}
202
203/* Provide atomic load/store functions for emutls_get_index if built with MSVC.
204 */
205#if !defined(__ATOMIC_RELEASE)
206
207enum { __ATOMIC_ACQUIRE = 2, __ATOMIC_RELEASE = 3 };
208
209static __inline uintptr_t __atomic_load_n(void *ptr, unsigned type) {
210 assert(type == __ATOMIC_ACQUIRE);
211#ifdef _WIN64
212 return (uintptr_t) _load_be_u64(ptr);
213#else
214 return (uintptr_t) _load_be_u32(ptr);
215#endif
216}
217
218static __inline void __atomic_store_n(void *ptr, uintptr_t val, unsigned type) {
219 assert(type == __ATOMIC_RELEASE);
220#ifdef _WIN64
221 _store_be_u64(ptr, val);
222#else
223 _store_be_u32(ptr, val);
224#endif
225}
226
227#endif
228
229#pragma warning (pop)
230
231#endif
232
233static size_t emutls_num_object = 0; /* number of allocated TLS objects */
234
235/* Free the allocated TLS data
236 */
237static void emutls_shutdown(emutls_address_array *array) {
238 if (array) {
239 uintptr_t i;
240 for (i = 0; i < array->size; ++i) {
241 if (array->data[i])
242 emutls_memalign_free(array->data[i]);
243 }
244 }
245}
246
247/* For every TLS variable xyz,
248 * there is one __emutls_control variable named __emutls_v.xyz.
249 * If xyz has non-zero initial value, __emutls_v.xyz's "value"
250 * will point to __emutls_t.xyz, which has the initial value.
251 */
252typedef struct __emutls_control {
253 /* Must use gcc_word here, instead of size_t, to match GCC. When
254 gcc_word is larger than size_t, the upper extra bits are all
255 zeros. We can use variables of size_t to operate on size and
256 align. */
257 gcc_word size; /* size of the object in bytes */
258 gcc_word align; /* alignment of the object in bytes */
259 union {
260 uintptr_t index; /* data[index-1] is the object address */
261 void* address; /* object address, when in single thread env */
262 } object;
263 void* value; /* null or non-zero initial value for the object */
264} __emutls_control;
265
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000266/* Emulated TLS objects are always allocated at run-time. */
Saleem Abdulrasool911cfc12015-10-10 21:21:28 +0000267static __inline void *emutls_allocate_object(__emutls_control *control) {
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000268 /* Use standard C types, check with gcc's emutls.o. */
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000269 COMPILE_TIME_ASSERT(sizeof(uintptr_t) == sizeof(gcc_pointer));
270 COMPILE_TIME_ASSERT(sizeof(uintptr_t) == sizeof(void*));
271
272 size_t size = control->size;
273 size_t align = control->align;
Chih-Hung Hsiehc2fab482016-02-04 20:26:00 +0000274 void* base;
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000275 if (align < sizeof(void*))
276 align = sizeof(void*);
277 /* Make sure that align is power of 2. */
278 if ((align & (align - 1)) != 0)
279 abort();
280
Chih-Hung Hsiehc2fab482016-02-04 20:26:00 +0000281 base = emutls_memalign_alloc(align, size);
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000282 if (control->value)
283 memcpy(base, control->value, size);
284 else
285 memset(base, 0, size);
286 return base;
287}
288
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000289
290/* Returns control->object.index; set index if not allocated yet. */
Saleem Abdulrasool911cfc12015-10-10 21:21:28 +0000291static __inline uintptr_t emutls_get_index(__emutls_control *control) {
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000292 uintptr_t index = __atomic_load_n(&control->object.index, __ATOMIC_ACQUIRE);
293 if (!index) {
Frederich Munch922b6022017-04-25 19:04:19 +0000294 emutls_init_once();
295 emutls_lock();
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000296 index = control->object.index;
297 if (!index) {
298 index = ++emutls_num_object;
299 __atomic_store_n(&control->object.index, index, __ATOMIC_RELEASE);
300 }
Frederich Munch922b6022017-04-25 19:04:19 +0000301 emutls_unlock();
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000302 }
303 return index;
304}
305
306/* Updates newly allocated thread local emutls_address_array. */
Saleem Abdulrasool911cfc12015-10-10 21:21:28 +0000307static __inline void emutls_check_array_set_size(emutls_address_array *array,
308 uintptr_t size) {
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000309 if (array == NULL)
310 abort();
311 array->size = size;
Frederich Munch922b6022017-04-25 19:04:19 +0000312 emutls_setspecific(array);
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000313}
314
315/* Returns the new 'data' array size, number of elements,
316 * which must be no smaller than the given index.
317 */
Saleem Abdulrasool911cfc12015-10-10 21:21:28 +0000318static __inline uintptr_t emutls_new_data_array_size(uintptr_t index) {
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000319 /* Need to allocate emutls_address_array with one extra slot
320 * to store the data array size.
321 * Round up the emutls_address_array size to multiple of 16.
322 */
323 return ((index + 1 + 15) & ~((uintptr_t)15)) - 1;
324}
325
Frederich Munch922b6022017-04-25 19:04:19 +0000326/* Returns the size in bytes required for an emutls_address_array with
327 * N number of elements for data field.
328 */
329static __inline uintptr_t emutls_asize(uintptr_t N) {
330 return N * sizeof(void *) + sizeof(emutls_address_array);
331}
332
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000333/* Returns the thread local emutls_address_array.
334 * Extends its size if necessary to hold address at index.
335 */
Saleem Abdulrasool911cfc12015-10-10 21:21:28 +0000336static __inline emutls_address_array *
337emutls_get_address_array(uintptr_t index) {
Frederich Munch922b6022017-04-25 19:04:19 +0000338 emutls_address_array* array = emutls_getspecific();
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000339 if (array == NULL) {
340 uintptr_t new_size = emutls_new_data_array_size(index);
Frederich Munch922b6022017-04-25 19:04:19 +0000341 array = (emutls_address_array*) malloc(emutls_asize(new_size));
George Burgess IV908dacf2016-04-14 23:58:26 +0000342 if (array)
343 memset(array->data, 0, new_size * sizeof(void*));
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000344 emutls_check_array_set_size(array, new_size);
345 } else if (index > array->size) {
346 uintptr_t orig_size = array->size;
347 uintptr_t new_size = emutls_new_data_array_size(index);
Frederich Munch922b6022017-04-25 19:04:19 +0000348 array = (emutls_address_array*) realloc(array, emutls_asize(new_size));
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000349 if (array)
350 memset(array->data + orig_size, 0,
351 (new_size - orig_size) * sizeof(void*));
352 emutls_check_array_set_size(array, new_size);
353 }
354 return array;
355}
356
357void* __emutls_get_address(__emutls_control* control) {
358 uintptr_t index = emutls_get_index(control);
Frederich Munch922b6022017-04-25 19:04:19 +0000359 emutls_address_array* array = emutls_get_address_array(index--);
360 if (array->data[index] == NULL)
361 array->data[index] = emutls_allocate_object(control);
362 return array->data[index];
Chih-Hung Hsieh4814b9c2015-08-31 17:14:07 +0000363}