Simplify tree streaming.
[gcc.git] / libgomp / allocator.c
1 /* Copyright (C) 2020 Free Software Foundation, Inc.
2 Contributed by Jakub Jelinek <jakub@redhat.com>.
3
4 This file is part of the GNU Offloading and Multi Processing Library
5 (libgomp).
6
7 Libgomp is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11
12 Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 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 /* This file contains wrappers for the system allocation routines. Most
27 places in the OpenMP API do not make any provision for failure, so in
28 general we cannot allow memory allocation to fail. */
29
30 #define _GNU_SOURCE
31 #include "libgomp.h"
32 #include <stdlib.h>
33
34 #define omp_max_predefined_alloc omp_thread_mem_alloc
35
36 struct omp_allocator_data
37 {
38 omp_memspace_handle_t memspace;
39 omp_uintptr_t alignment;
40 omp_uintptr_t pool_size;
41 omp_uintptr_t used_pool_size;
42 omp_allocator_handle_t fb_data;
43 unsigned int sync_hint : 8;
44 unsigned int access : 8;
45 unsigned int fallback : 8;
46 unsigned int pinned : 1;
47 unsigned int partition : 7;
48 #ifndef HAVE_SYNC_BUILTINS
49 gomp_mutex_t lock;
50 #endif
51 };
52
53 struct omp_mem_header
54 {
55 void *ptr;
56 size_t size;
57 omp_allocator_handle_t allocator;
58 void *pad;
59 };
60
61 omp_allocator_handle_t
62 omp_init_allocator (omp_memspace_handle_t memspace, int ntraits,
63 const omp_alloctrait_t traits[])
64 {
65 struct omp_allocator_data data
66 = { memspace, 1, ~(uintptr_t) 0, 0, 0, omp_atv_contended, omp_atv_all,
67 omp_atv_default_mem_fb, omp_atv_false, omp_atv_environment };
68 struct omp_allocator_data *ret;
69 int i;
70
71 if (memspace > omp_low_lat_mem_space)
72 return omp_null_allocator;
73 for (i = 0; i < ntraits; i++)
74 switch (traits[i].key)
75 {
76 case omp_atk_sync_hint:
77 switch (traits[i].value)
78 {
79 case omp_atv_default:
80 data.sync_hint = omp_atv_contended;
81 break;
82 case omp_atv_contended:
83 case omp_atv_uncontended:
84 case omp_atv_sequential:
85 case omp_atv_private:
86 data.sync_hint = traits[i].value;
87 break;
88 default:
89 return omp_null_allocator;
90 }
91 break;
92 case omp_atk_alignment:
93 if ((traits[i].value & (traits[i].value - 1)) != 0
94 || !traits[i].value)
95 return omp_null_allocator;
96 data.alignment = traits[i].value;
97 break;
98 case omp_atk_access:
99 switch (traits[i].value)
100 {
101 case omp_atv_default:
102 data.access = omp_atv_all;
103 break;
104 case omp_atv_all:
105 case omp_atv_cgroup:
106 case omp_atv_pteam:
107 case omp_atv_thread:
108 data.access = traits[i].value;
109 break;
110 default:
111 return omp_null_allocator;
112 }
113 break;
114 case omp_atk_pool_size:
115 data.pool_size = traits[i].value;
116 break;
117 case omp_atk_fallback:
118 switch (traits[i].value)
119 {
120 case omp_atv_default:
121 data.fallback = omp_atv_default_mem_fb;
122 break;
123 case omp_atv_default_mem_fb:
124 case omp_atv_null_fb:
125 case omp_atv_abort_fb:
126 case omp_atv_allocator_fb:
127 data.fallback = traits[i].value;
128 break;
129 default:
130 return omp_null_allocator;
131 }
132 break;
133 case omp_atk_fb_data:
134 data.fb_data = traits[i].value;
135 break;
136 case omp_atk_pinned:
137 switch (traits[i].value)
138 {
139 case omp_atv_default:
140 case omp_atv_false:
141 data.pinned = omp_atv_false;
142 break;
143 case omp_atv_true:
144 data.pinned = omp_atv_true;
145 break;
146 default:
147 return omp_null_allocator;
148 }
149 break;
150 case omp_atk_partition:
151 switch (traits[i].value)
152 {
153 case omp_atv_default:
154 data.partition = omp_atv_environment;
155 break;
156 case omp_atv_environment:
157 case omp_atv_nearest:
158 case omp_atv_blocked:
159 case omp_atv_interleaved:
160 data.partition = traits[i].value;
161 break;
162 default:
163 return omp_null_allocator;
164 }
165 break;
166 default:
167 return omp_null_allocator;
168 }
169
170 if (data.alignment < sizeof (void *))
171 data.alignment = sizeof (void *);
172
173 /* No support for these so far (for hbw will use memkind). */
174 if (data.pinned || data.memspace == omp_high_bw_mem_space)
175 return omp_null_allocator;
176
177 ret = gomp_malloc (sizeof (struct omp_allocator_data));
178 *ret = data;
179 #ifndef HAVE_SYNC_BUILTINS
180 gomp_mutex_init (&ret->lock);
181 #endif
182 return (omp_allocator_handle_t) ret;
183 }
184
185 void
186 omp_destroy_allocator (omp_allocator_handle_t allocator)
187 {
188 if (allocator != omp_null_allocator)
189 {
190 #ifndef HAVE_SYNC_BUILTINS
191 gomp_mutex_destroy (&((struct omp_allocator_data *) allocator)->lock);
192 #endif
193 free ((void *) allocator);
194 }
195 }
196
197 void *
198 omp_alloc (size_t size, omp_allocator_handle_t allocator)
199 {
200 struct omp_allocator_data *allocator_data;
201 size_t alignment, new_size;
202 void *ptr, *ret;
203
204 retry:
205 if (allocator == omp_null_allocator)
206 {
207 struct gomp_thread *thr = gomp_thread ();
208 if (thr->ts.def_allocator == omp_null_allocator)
209 thr->ts.def_allocator = gomp_def_allocator;
210 allocator = (omp_allocator_handle_t) thr->ts.def_allocator;
211 }
212
213 if (allocator > omp_max_predefined_alloc)
214 {
215 allocator_data = (struct omp_allocator_data *) allocator;
216 alignment = allocator_data->alignment;
217 }
218 else
219 {
220 allocator_data = NULL;
221 alignment = sizeof (void *);
222 }
223
224 new_size = sizeof (struct omp_mem_header);
225 if (alignment > sizeof (void *))
226 new_size += alignment - sizeof (void *);
227 if (__builtin_add_overflow (size, new_size, &new_size))
228 goto fail;
229
230 if (__builtin_expect (allocator_data
231 && allocator_data->pool_size < ~(uintptr_t) 0, 0))
232 {
233 uintptr_t used_pool_size;
234 if (new_size > allocator_data->pool_size)
235 goto fail;
236 #ifdef HAVE_SYNC_BUILTINS
237 used_pool_size = __atomic_load_n (&allocator_data->used_pool_size,
238 MEMMODEL_RELAXED);
239 do
240 {
241 uintptr_t new_pool_size;
242 if (__builtin_add_overflow (used_pool_size, new_size,
243 &new_pool_size)
244 || new_pool_size > allocator_data->pool_size)
245 goto fail;
246 if (__atomic_compare_exchange_n (&allocator_data->used_pool_size,
247 &used_pool_size, new_pool_size,
248 true, MEMMODEL_RELAXED,
249 MEMMODEL_RELAXED))
250 break;
251 }
252 while (1);
253 #else
254 gomp_mutex_lock (&allocator_data->lock);
255 if (__builtin_add_overflow (allocator_data->used_pool_size, new_size,
256 &used_pool_size)
257 || used_pool_size > allocator_data->pool_size)
258 {
259 gomp_mutex_unlock (&allocator_data->lock);
260 goto fail;
261 }
262 allocator_data->used_pool_size = used_pool_size;
263 gomp_mutex_unlock (&allocator_data->lock);
264 #endif
265 ptr = malloc (new_size);
266 if (ptr == NULL)
267 {
268 #ifdef HAVE_SYNC_BUILTINS
269 __atomic_add_fetch (&allocator_data->used_pool_size, -new_size,
270 MEMMODEL_RELAXED);
271 #else
272 gomp_mutex_lock (&allocator_data->lock);
273 allocator_data->used_pool_size -= new_size;
274 gomp_mutex_unlock (&allocator_data->lock);
275 #endif
276 goto fail;
277 }
278 }
279 else
280 {
281 ptr = malloc (new_size);
282 if (ptr == NULL)
283 goto fail;
284 }
285
286 if (alignment > sizeof (void *))
287 ret = (void *) (((uintptr_t) ptr
288 + sizeof (struct omp_mem_header)
289 + alignment - sizeof (void *)) & ~(alignment - 1));
290 else
291 ret = (char *) ptr + sizeof (struct omp_mem_header);
292 ((struct omp_mem_header *) ret)[-1].ptr = ptr;
293 ((struct omp_mem_header *) ret)[-1].size = new_size;
294 ((struct omp_mem_header *) ret)[-1].allocator = allocator;
295 return ret;
296
297 fail:
298 if (allocator_data)
299 {
300 switch (allocator_data->fallback)
301 {
302 case omp_atv_default_mem_fb:
303 if (alignment > sizeof (void *)
304 || (allocator_data
305 && allocator_data->pool_size < ~(uintptr_t) 0))
306 {
307 allocator = omp_default_mem_alloc;
308 goto retry;
309 }
310 /* Otherwise, we've already performed default mem allocation
311 and if that failed, it won't succeed again (unless it was
312 intermitent. Return NULL then, as that is the fallback. */
313 break;
314 case omp_atv_null_fb:
315 break;
316 default:
317 case omp_atv_abort_fb:
318 gomp_fatal ("Out of memory allocating %lu bytes",
319 (unsigned long) size);
320 case omp_atv_allocator_fb:
321 allocator = allocator_data->fb_data;
322 goto retry;
323 }
324 }
325 return NULL;
326 }
327
328 void
329 omp_free (void *ptr, omp_allocator_handle_t allocator)
330 {
331 struct omp_mem_header *data;
332
333 if (ptr == NULL)
334 return;
335 (void) allocator;
336 data = &((struct omp_mem_header *) ptr)[-1];
337 if (data->allocator > omp_max_predefined_alloc)
338 {
339 struct omp_allocator_data *allocator_data
340 = (struct omp_allocator_data *) (data->allocator);
341 if (allocator_data->pool_size < ~(uintptr_t) 0)
342 {
343 #ifdef HAVE_SYNC_BUILTINS
344 __atomic_add_fetch (&allocator_data->used_pool_size, -data->size,
345 MEMMODEL_RELAXED);
346 #else
347 gomp_mutex_lock (&allocator_data->lock);
348 allocator_data->used_pool_size -= data->new_size;
349 gomp_mutex_unlock (&allocator_data->lock);
350 #endif
351 }
352 }
353 free (data->ptr);
354 }