1 /* Copyright (C) 2020 Free Software Foundation, Inc.
2 Contributed by Jakub Jelinek <jakub@redhat.com>.
4 This file is part of the GNU Offloading and Multi Processing Library
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)
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
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.
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/>. */
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. */
34 #define omp_max_predefined_alloc omp_thread_mem_alloc
36 struct omp_allocator_data
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
57 omp_allocator_handle_t allocator;
61 omp_allocator_handle_t
62 omp_init_allocator (omp_memspace_handle_t memspace, int ntraits,
63 const omp_alloctrait_t traits[])
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;
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)
76 case omp_atk_sync_hint:
77 switch (traits[i].value)
80 data.sync_hint = omp_atv_contended;
82 case omp_atv_contended:
83 case omp_atv_uncontended:
84 case omp_atv_sequential:
86 data.sync_hint = traits[i].value;
89 return omp_null_allocator;
92 case omp_atk_alignment:
93 if (traits[i].value == omp_atv_default)
98 if ((traits[i].value & (traits[i].value - 1)) != 0
100 return omp_null_allocator;
101 data.alignment = traits[i].value;
104 switch (traits[i].value)
106 case omp_atv_default:
107 data.access = omp_atv_all;
113 data.access = traits[i].value;
116 return omp_null_allocator;
119 case omp_atk_pool_size:
120 if (traits[i].value == omp_atv_default)
121 data.pool_size = ~(uintptr_t) 0;
123 data.pool_size = traits[i].value;
125 case omp_atk_fallback:
126 switch (traits[i].value)
128 case omp_atv_default:
129 data.fallback = omp_atv_default_mem_fb;
131 case omp_atv_default_mem_fb:
132 case omp_atv_null_fb:
133 case omp_atv_abort_fb:
134 case omp_atv_allocator_fb:
135 data.fallback = traits[i].value;
138 return omp_null_allocator;
141 case omp_atk_fb_data:
142 data.fb_data = traits[i].value;
145 switch (traits[i].value)
147 case omp_atv_default:
149 data.pinned = omp_atv_false;
152 data.pinned = omp_atv_true;
155 return omp_null_allocator;
158 case omp_atk_partition:
159 switch (traits[i].value)
161 case omp_atv_default:
162 data.partition = omp_atv_environment;
164 case omp_atv_environment:
165 case omp_atv_nearest:
166 case omp_atv_blocked:
167 case omp_atv_interleaved:
168 data.partition = traits[i].value;
171 return omp_null_allocator;
175 return omp_null_allocator;
178 if (data.alignment < sizeof (void *))
179 data.alignment = sizeof (void *);
181 /* No support for these so far (for hbw will use memkind). */
182 if (data.pinned || data.memspace == omp_high_bw_mem_space)
183 return omp_null_allocator;
185 ret = gomp_malloc (sizeof (struct omp_allocator_data));
187 #ifndef HAVE_SYNC_BUILTINS
188 gomp_mutex_init (&ret->lock);
190 return (omp_allocator_handle_t) ret;
194 omp_destroy_allocator (omp_allocator_handle_t allocator)
196 if (allocator != omp_null_allocator)
198 #ifndef HAVE_SYNC_BUILTINS
199 gomp_mutex_destroy (&((struct omp_allocator_data *) allocator)->lock);
201 free ((void *) allocator);
206 omp_alloc (size_t size, omp_allocator_handle_t allocator)
208 struct omp_allocator_data *allocator_data;
209 size_t alignment, new_size;
212 if (__builtin_expect (size == 0, 0))
216 if (allocator == omp_null_allocator)
218 struct gomp_thread *thr = gomp_thread ();
219 if (thr->ts.def_allocator == omp_null_allocator)
220 thr->ts.def_allocator = gomp_def_allocator;
221 allocator = (omp_allocator_handle_t) thr->ts.def_allocator;
224 if (allocator > omp_max_predefined_alloc)
226 allocator_data = (struct omp_allocator_data *) allocator;
227 alignment = allocator_data->alignment;
231 allocator_data = NULL;
232 alignment = sizeof (void *);
235 new_size = sizeof (struct omp_mem_header);
236 if (alignment > sizeof (void *))
237 new_size += alignment - sizeof (void *);
238 if (__builtin_add_overflow (size, new_size, &new_size))
241 if (__builtin_expect (allocator_data
242 && allocator_data->pool_size < ~(uintptr_t) 0, 0))
244 uintptr_t used_pool_size;
245 if (new_size > allocator_data->pool_size)
247 #ifdef HAVE_SYNC_BUILTINS
248 used_pool_size = __atomic_load_n (&allocator_data->used_pool_size,
252 uintptr_t new_pool_size;
253 if (__builtin_add_overflow (used_pool_size, new_size,
255 || new_pool_size > allocator_data->pool_size)
257 if (__atomic_compare_exchange_n (&allocator_data->used_pool_size,
258 &used_pool_size, new_pool_size,
259 true, MEMMODEL_RELAXED,
265 gomp_mutex_lock (&allocator_data->lock);
266 if (__builtin_add_overflow (allocator_data->used_pool_size, new_size,
268 || used_pool_size > allocator_data->pool_size)
270 gomp_mutex_unlock (&allocator_data->lock);
273 allocator_data->used_pool_size = used_pool_size;
274 gomp_mutex_unlock (&allocator_data->lock);
276 ptr = malloc (new_size);
279 #ifdef HAVE_SYNC_BUILTINS
280 __atomic_add_fetch (&allocator_data->used_pool_size, -new_size,
283 gomp_mutex_lock (&allocator_data->lock);
284 allocator_data->used_pool_size -= new_size;
285 gomp_mutex_unlock (&allocator_data->lock);
292 ptr = malloc (new_size);
297 if (alignment > sizeof (void *))
298 ret = (void *) (((uintptr_t) ptr
299 + sizeof (struct omp_mem_header)
300 + alignment - sizeof (void *)) & ~(alignment - 1));
302 ret = (char *) ptr + sizeof (struct omp_mem_header);
303 ((struct omp_mem_header *) ret)[-1].ptr = ptr;
304 ((struct omp_mem_header *) ret)[-1].size = new_size;
305 ((struct omp_mem_header *) ret)[-1].allocator = allocator;
311 switch (allocator_data->fallback)
313 case omp_atv_default_mem_fb:
314 if (alignment > sizeof (void *)
316 && allocator_data->pool_size < ~(uintptr_t) 0))
318 allocator = omp_default_mem_alloc;
321 /* Otherwise, we've already performed default mem allocation
322 and if that failed, it won't succeed again (unless it was
323 intermitent. Return NULL then, as that is the fallback. */
325 case omp_atv_null_fb:
328 case omp_atv_abort_fb:
329 gomp_fatal ("Out of memory allocating %lu bytes",
330 (unsigned long) size);
331 case omp_atv_allocator_fb:
332 allocator = allocator_data->fb_data;
340 omp_free (void *ptr, omp_allocator_handle_t allocator)
342 struct omp_mem_header *data;
347 data = &((struct omp_mem_header *) ptr)[-1];
348 if (data->allocator > omp_max_predefined_alloc)
350 struct omp_allocator_data *allocator_data
351 = (struct omp_allocator_data *) (data->allocator);
352 if (allocator_data->pool_size < ~(uintptr_t) 0)
354 #ifdef HAVE_SYNC_BUILTINS
355 __atomic_add_fetch (&allocator_data->used_pool_size, -data->size,
358 gomp_mutex_lock (&allocator_data->lock);
359 allocator_data->used_pool_size -= data->size;
360 gomp_mutex_unlock (&allocator_data->lock);