1 /* Copyright (C) 2020-2021 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. */
35 #define omp_max_predefined_alloc omp_thread_mem_alloc
37 struct omp_allocator_data
39 omp_memspace_handle_t memspace
;
40 omp_uintptr_t alignment
;
41 omp_uintptr_t pool_size
;
42 omp_uintptr_t used_pool_size
;
43 omp_allocator_handle_t fb_data
;
44 unsigned int sync_hint
: 8;
45 unsigned int access
: 8;
46 unsigned int fallback
: 8;
47 unsigned int pinned
: 1;
48 unsigned int partition
: 7;
49 #ifndef HAVE_SYNC_BUILTINS
58 omp_allocator_handle_t allocator
;
62 omp_allocator_handle_t
63 omp_init_allocator (omp_memspace_handle_t memspace
, int ntraits
,
64 const omp_alloctrait_t traits
[])
66 struct omp_allocator_data data
67 = { memspace
, 1, ~(uintptr_t) 0, 0, 0, omp_atv_contended
, omp_atv_all
,
68 omp_atv_default_mem_fb
, omp_atv_false
, omp_atv_environment
};
69 struct omp_allocator_data
*ret
;
72 if (memspace
> omp_low_lat_mem_space
)
73 return omp_null_allocator
;
74 for (i
= 0; i
< ntraits
; i
++)
75 switch (traits
[i
].key
)
77 case omp_atk_sync_hint
:
78 switch (traits
[i
].value
)
81 data
.sync_hint
= omp_atv_contended
;
83 case omp_atv_contended
:
84 case omp_atv_uncontended
:
85 case omp_atv_serialized
:
87 data
.sync_hint
= traits
[i
].value
;
90 return omp_null_allocator
;
93 case omp_atk_alignment
:
94 if (traits
[i
].value
== omp_atv_default
)
99 if ((traits
[i
].value
& (traits
[i
].value
- 1)) != 0
101 return omp_null_allocator
;
102 data
.alignment
= traits
[i
].value
;
105 switch (traits
[i
].value
)
107 case omp_atv_default
:
108 data
.access
= omp_atv_all
;
114 data
.access
= traits
[i
].value
;
117 return omp_null_allocator
;
120 case omp_atk_pool_size
:
121 if (traits
[i
].value
== omp_atv_default
)
122 data
.pool_size
= ~(uintptr_t) 0;
124 data
.pool_size
= traits
[i
].value
;
126 case omp_atk_fallback
:
127 switch (traits
[i
].value
)
129 case omp_atv_default
:
130 data
.fallback
= omp_atv_default_mem_fb
;
132 case omp_atv_default_mem_fb
:
133 case omp_atv_null_fb
:
134 case omp_atv_abort_fb
:
135 case omp_atv_allocator_fb
:
136 data
.fallback
= traits
[i
].value
;
139 return omp_null_allocator
;
142 case omp_atk_fb_data
:
143 data
.fb_data
= traits
[i
].value
;
146 switch (traits
[i
].value
)
148 case omp_atv_default
:
150 data
.pinned
= omp_atv_false
;
153 data
.pinned
= omp_atv_true
;
156 return omp_null_allocator
;
159 case omp_atk_partition
:
160 switch (traits
[i
].value
)
162 case omp_atv_default
:
163 data
.partition
= omp_atv_environment
;
165 case omp_atv_environment
:
166 case omp_atv_nearest
:
167 case omp_atv_blocked
:
168 case omp_atv_interleaved
:
169 data
.partition
= traits
[i
].value
;
172 return omp_null_allocator
;
176 return omp_null_allocator
;
179 if (data
.alignment
< sizeof (void *))
180 data
.alignment
= sizeof (void *);
182 /* No support for these so far (for hbw will use memkind). */
183 if (data
.pinned
|| data
.memspace
== omp_high_bw_mem_space
)
184 return omp_null_allocator
;
186 ret
= gomp_malloc (sizeof (struct omp_allocator_data
));
188 #ifndef HAVE_SYNC_BUILTINS
189 gomp_mutex_init (&ret
->lock
);
191 return (omp_allocator_handle_t
) ret
;
195 omp_destroy_allocator (omp_allocator_handle_t allocator
)
197 if (allocator
!= omp_null_allocator
)
199 #ifndef HAVE_SYNC_BUILTINS
200 gomp_mutex_destroy (&((struct omp_allocator_data
*) allocator
)->lock
);
202 free ((void *) allocator
);
206 ialias (omp_init_allocator
)
207 ialias (omp_destroy_allocator
)
210 omp_aligned_alloc (size_t alignment
, size_t size
,
211 omp_allocator_handle_t allocator
)
213 struct omp_allocator_data
*allocator_data
;
214 size_t new_size
, new_alignment
;
217 if (__builtin_expect (size
== 0, 0))
221 new_alignment
= alignment
;
222 if (allocator
== omp_null_allocator
)
224 struct gomp_thread
*thr
= gomp_thread ();
225 if (thr
->ts
.def_allocator
== omp_null_allocator
)
226 thr
->ts
.def_allocator
= gomp_def_allocator
;
227 allocator
= (omp_allocator_handle_t
) thr
->ts
.def_allocator
;
230 if (allocator
> omp_max_predefined_alloc
)
232 allocator_data
= (struct omp_allocator_data
*) allocator
;
233 if (new_alignment
< allocator_data
->alignment
)
234 new_alignment
= allocator_data
->alignment
;
238 allocator_data
= NULL
;
239 if (new_alignment
< sizeof (void *))
240 new_alignment
= sizeof (void *);
243 new_size
= sizeof (struct omp_mem_header
);
244 if (new_alignment
> sizeof (void *))
245 new_size
+= new_alignment
- sizeof (void *);
246 if (__builtin_add_overflow (size
, new_size
, &new_size
))
249 if (__builtin_expect (allocator_data
250 && allocator_data
->pool_size
< ~(uintptr_t) 0, 0))
252 uintptr_t used_pool_size
;
253 if (new_size
> allocator_data
->pool_size
)
255 #ifdef HAVE_SYNC_BUILTINS
256 used_pool_size
= __atomic_load_n (&allocator_data
->used_pool_size
,
260 uintptr_t new_pool_size
;
261 if (__builtin_add_overflow (used_pool_size
, new_size
,
263 || new_pool_size
> allocator_data
->pool_size
)
265 if (__atomic_compare_exchange_n (&allocator_data
->used_pool_size
,
266 &used_pool_size
, new_pool_size
,
267 true, MEMMODEL_RELAXED
,
273 gomp_mutex_lock (&allocator_data
->lock
);
274 if (__builtin_add_overflow (allocator_data
->used_pool_size
, new_size
,
276 || used_pool_size
> allocator_data
->pool_size
)
278 gomp_mutex_unlock (&allocator_data
->lock
);
281 allocator_data
->used_pool_size
= used_pool_size
;
282 gomp_mutex_unlock (&allocator_data
->lock
);
284 ptr
= malloc (new_size
);
287 #ifdef HAVE_SYNC_BUILTINS
288 __atomic_add_fetch (&allocator_data
->used_pool_size
, -new_size
,
291 gomp_mutex_lock (&allocator_data
->lock
);
292 allocator_data
->used_pool_size
-= new_size
;
293 gomp_mutex_unlock (&allocator_data
->lock
);
300 ptr
= malloc (new_size
);
305 if (new_alignment
> sizeof (void *))
306 ret
= (void *) (((uintptr_t) ptr
307 + sizeof (struct omp_mem_header
)
308 + new_alignment
- sizeof (void *))
309 & ~(new_alignment
- 1));
311 ret
= (char *) ptr
+ sizeof (struct omp_mem_header
);
312 ((struct omp_mem_header
*) ret
)[-1].ptr
= ptr
;
313 ((struct omp_mem_header
*) ret
)[-1].size
= new_size
;
314 ((struct omp_mem_header
*) ret
)[-1].allocator
= allocator
;
320 switch (allocator_data
->fallback
)
322 case omp_atv_default_mem_fb
:
323 if ((new_alignment
> sizeof (void *) && new_alignment
> alignment
)
325 && allocator_data
->pool_size
< ~(uintptr_t) 0))
327 allocator
= omp_default_mem_alloc
;
330 /* Otherwise, we've already performed default mem allocation
331 and if that failed, it won't succeed again (unless it was
332 intermittent. Return NULL then, as that is the fallback. */
334 case omp_atv_null_fb
:
337 case omp_atv_abort_fb
:
338 gomp_fatal ("Out of memory allocating %lu bytes",
339 (unsigned long) size
);
340 case omp_atv_allocator_fb
:
341 allocator
= allocator_data
->fb_data
;
348 ialias (omp_aligned_alloc
)
351 omp_alloc (size_t size
, omp_allocator_handle_t allocator
)
353 return ialias_call (omp_aligned_alloc
) (1, size
, allocator
);
356 /* Like omp_aligned_alloc, but apply on top of that:
357 "For allocations that arise from this ... the null_fb value of the
358 fallback allocator trait behaves as if the abort_fb had been specified." */
361 GOMP_alloc (size_t alignment
, size_t size
, uintptr_t allocator
)
364 = ialias_call (omp_aligned_alloc
) (alignment
, size
,
365 (omp_allocator_handle_t
) allocator
);
366 if (__builtin_expect (ret
== NULL
, 0) && size
)
367 gomp_fatal ("Out of memory allocating %lu bytes",
368 (unsigned long) size
);
373 omp_free (void *ptr
, omp_allocator_handle_t allocator
)
375 struct omp_mem_header
*data
;
380 data
= &((struct omp_mem_header
*) ptr
)[-1];
381 if (data
->allocator
> omp_max_predefined_alloc
)
383 struct omp_allocator_data
*allocator_data
384 = (struct omp_allocator_data
*) (data
->allocator
);
385 if (allocator_data
->pool_size
< ~(uintptr_t) 0)
387 #ifdef HAVE_SYNC_BUILTINS
388 __atomic_add_fetch (&allocator_data
->used_pool_size
, -data
->size
,
391 gomp_mutex_lock (&allocator_data
->lock
);
392 allocator_data
->used_pool_size
-= data
->size
;
393 gomp_mutex_unlock (&allocator_data
->lock
);
403 GOMP_free (void *ptr
, uintptr_t allocator
)
405 return ialias_call (omp_free
) (ptr
, (omp_allocator_handle_t
) allocator
);
409 omp_aligned_calloc (size_t alignment
, size_t nmemb
, size_t size
,
410 omp_allocator_handle_t allocator
)
412 struct omp_allocator_data
*allocator_data
;
413 size_t new_size
, size_temp
, new_alignment
;
416 if (__builtin_expect (size
== 0 || nmemb
== 0, 0))
420 new_alignment
= alignment
;
421 if (allocator
== omp_null_allocator
)
423 struct gomp_thread
*thr
= gomp_thread ();
424 if (thr
->ts
.def_allocator
== omp_null_allocator
)
425 thr
->ts
.def_allocator
= gomp_def_allocator
;
426 allocator
= (omp_allocator_handle_t
) thr
->ts
.def_allocator
;
429 if (allocator
> omp_max_predefined_alloc
)
431 allocator_data
= (struct omp_allocator_data
*) allocator
;
432 if (new_alignment
< allocator_data
->alignment
)
433 new_alignment
= allocator_data
->alignment
;
437 allocator_data
= NULL
;
438 if (new_alignment
< sizeof (void *))
439 new_alignment
= sizeof (void *);
442 new_size
= sizeof (struct omp_mem_header
);
443 if (new_alignment
> sizeof (void *))
444 new_size
+= new_alignment
- sizeof (void *);
445 if (__builtin_mul_overflow (size
, nmemb
, &size_temp
))
447 if (__builtin_add_overflow (size_temp
, new_size
, &new_size
))
450 if (__builtin_expect (allocator_data
451 && allocator_data
->pool_size
< ~(uintptr_t) 0, 0))
453 uintptr_t used_pool_size
;
454 if (new_size
> allocator_data
->pool_size
)
456 #ifdef HAVE_SYNC_BUILTINS
457 used_pool_size
= __atomic_load_n (&allocator_data
->used_pool_size
,
461 uintptr_t new_pool_size
;
462 if (__builtin_add_overflow (used_pool_size
, new_size
,
464 || new_pool_size
> allocator_data
->pool_size
)
466 if (__atomic_compare_exchange_n (&allocator_data
->used_pool_size
,
467 &used_pool_size
, new_pool_size
,
468 true, MEMMODEL_RELAXED
,
474 gomp_mutex_lock (&allocator_data
->lock
);
475 if (__builtin_add_overflow (allocator_data
->used_pool_size
, new_size
,
477 || used_pool_size
> allocator_data
->pool_size
)
479 gomp_mutex_unlock (&allocator_data
->lock
);
482 allocator_data
->used_pool_size
= used_pool_size
;
483 gomp_mutex_unlock (&allocator_data
->lock
);
485 ptr
= calloc (1, new_size
);
488 #ifdef HAVE_SYNC_BUILTINS
489 __atomic_add_fetch (&allocator_data
->used_pool_size
, -new_size
,
492 gomp_mutex_lock (&allocator_data
->lock
);
493 allocator_data
->used_pool_size
-= new_size
;
494 gomp_mutex_unlock (&allocator_data
->lock
);
501 ptr
= calloc (1, new_size
);
506 if (new_alignment
> sizeof (void *))
507 ret
= (void *) (((uintptr_t) ptr
508 + sizeof (struct omp_mem_header
)
509 + new_alignment
- sizeof (void *))
510 & ~(new_alignment
- 1));
512 ret
= (char *) ptr
+ sizeof (struct omp_mem_header
);
513 ((struct omp_mem_header
*) ret
)[-1].ptr
= ptr
;
514 ((struct omp_mem_header
*) ret
)[-1].size
= new_size
;
515 ((struct omp_mem_header
*) ret
)[-1].allocator
= allocator
;
521 switch (allocator_data
->fallback
)
523 case omp_atv_default_mem_fb
:
524 if ((new_alignment
> sizeof (void *) && new_alignment
> alignment
)
526 && allocator_data
->pool_size
< ~(uintptr_t) 0))
528 allocator
= omp_default_mem_alloc
;
531 /* Otherwise, we've already performed default mem allocation
532 and if that failed, it won't succeed again (unless it was
533 intermittent. Return NULL then, as that is the fallback. */
535 case omp_atv_null_fb
:
538 case omp_atv_abort_fb
:
539 gomp_fatal ("Out of memory allocating %lu bytes",
540 (unsigned long) (size
* nmemb
));
541 case omp_atv_allocator_fb
:
542 allocator
= allocator_data
->fb_data
;
549 ialias (omp_aligned_calloc
)
552 omp_calloc (size_t nmemb
, size_t size
, omp_allocator_handle_t allocator
)
554 return ialias_call (omp_aligned_calloc
) (1, nmemb
, size
, allocator
);
558 omp_realloc (void *ptr
, size_t size
, omp_allocator_handle_t allocator
,
559 omp_allocator_handle_t free_allocator
)
561 struct omp_allocator_data
*allocator_data
, *free_allocator_data
;
562 size_t new_size
, old_size
, new_alignment
, old_alignment
;
564 struct omp_mem_header
*data
;
566 if (__builtin_expect (ptr
== NULL
, 0))
567 return ialias_call (omp_aligned_alloc
) (1, size
, allocator
);
569 if (__builtin_expect (size
== 0, 0))
571 ialias_call (omp_free
) (ptr
, free_allocator
);
575 data
= &((struct omp_mem_header
*) ptr
)[-1];
576 free_allocator
= data
->allocator
;
579 new_alignment
= sizeof (void *);
580 if (allocator
== omp_null_allocator
)
581 allocator
= free_allocator
;
583 if (allocator
> omp_max_predefined_alloc
)
585 allocator_data
= (struct omp_allocator_data
*) allocator
;
586 if (new_alignment
< allocator_data
->alignment
)
587 new_alignment
= allocator_data
->alignment
;
590 allocator_data
= NULL
;
591 if (free_allocator
> omp_max_predefined_alloc
)
592 free_allocator_data
= (struct omp_allocator_data
*) free_allocator
;
594 free_allocator_data
= NULL
;
595 old_alignment
= (uintptr_t) ptr
- (uintptr_t) (data
->ptr
);
597 new_size
= sizeof (struct omp_mem_header
);
598 if (new_alignment
> sizeof (void *))
599 new_size
+= new_alignment
- sizeof (void *);
600 if (__builtin_add_overflow (size
, new_size
, &new_size
))
602 old_size
= data
->size
;
604 if (__builtin_expect (allocator_data
605 && allocator_data
->pool_size
< ~(uintptr_t) 0, 0))
607 uintptr_t used_pool_size
;
608 size_t prev_size
= 0;
609 /* Check if we can use realloc. Don't use it if extra alignment
610 was used previously or newly, because realloc might return a pointer
611 with different alignment and then we'd need to memmove the data
613 if (free_allocator_data
614 && free_allocator_data
== allocator_data
615 && new_alignment
== sizeof (void *)
616 && old_alignment
== sizeof (struct omp_mem_header
))
617 prev_size
= old_size
;
618 if (new_size
> prev_size
619 && new_size
- prev_size
> allocator_data
->pool_size
)
621 #ifdef HAVE_SYNC_BUILTINS
622 used_pool_size
= __atomic_load_n (&allocator_data
->used_pool_size
,
626 uintptr_t new_pool_size
;
627 if (new_size
> prev_size
)
629 if (__builtin_add_overflow (used_pool_size
, new_size
- prev_size
,
631 || new_pool_size
> allocator_data
->pool_size
)
635 new_pool_size
= used_pool_size
+ new_size
- prev_size
;
636 if (__atomic_compare_exchange_n (&allocator_data
->used_pool_size
,
637 &used_pool_size
, new_pool_size
,
638 true, MEMMODEL_RELAXED
,
644 gomp_mutex_lock (&allocator_data
->lock
);
645 if (new_size
> prev_size
)
647 if (__builtin_add_overflow (allocator_data
->used_pool_size
,
648 new_size
- prev_size
,
650 || used_pool_size
> allocator_data
->pool_size
)
652 gomp_mutex_unlock (&allocator_data
->lock
);
657 used_pool_size
= (allocator_data
->used_pool_size
658 + new_size
- prev_size
);
659 allocator_data
->used_pool_size
= used_pool_size
;
660 gomp_mutex_unlock (&allocator_data
->lock
);
663 new_ptr
= realloc (data
->ptr
, new_size
);
665 new_ptr
= malloc (new_size
);
668 #ifdef HAVE_SYNC_BUILTINS
669 __atomic_add_fetch (&allocator_data
->used_pool_size
,
670 prev_size
- new_size
,
673 gomp_mutex_lock (&allocator_data
->lock
);
674 allocator_data
->used_pool_size
-= new_size
- prev_size
;
675 gomp_mutex_unlock (&allocator_data
->lock
);
681 ret
= (char *) new_ptr
+ sizeof (struct omp_mem_header
);
682 ((struct omp_mem_header
*) ret
)[-1].ptr
= new_ptr
;
683 ((struct omp_mem_header
*) ret
)[-1].size
= new_size
;
684 ((struct omp_mem_header
*) ret
)[-1].allocator
= allocator
;
688 else if (new_alignment
== sizeof (void *)
689 && old_alignment
== sizeof (struct omp_mem_header
)
690 && (free_allocator_data
== NULL
691 || free_allocator_data
->pool_size
== ~(uintptr_t) 0))
693 new_ptr
= realloc (data
->ptr
, new_size
);
696 ret
= (char *) new_ptr
+ sizeof (struct omp_mem_header
);
697 ((struct omp_mem_header
*) ret
)[-1].ptr
= new_ptr
;
698 ((struct omp_mem_header
*) ret
)[-1].size
= new_size
;
699 ((struct omp_mem_header
*) ret
)[-1].allocator
= allocator
;
704 new_ptr
= malloc (new_size
);
709 if (new_alignment
> sizeof (void *))
710 ret
= (void *) (((uintptr_t) new_ptr
711 + sizeof (struct omp_mem_header
)
712 + new_alignment
- sizeof (void *))
713 & ~(new_alignment
- 1));
715 ret
= (char *) new_ptr
+ sizeof (struct omp_mem_header
);
716 ((struct omp_mem_header
*) ret
)[-1].ptr
= new_ptr
;
717 ((struct omp_mem_header
*) ret
)[-1].size
= new_size
;
718 ((struct omp_mem_header
*) ret
)[-1].allocator
= allocator
;
719 if (old_size
- old_alignment
< size
)
720 size
= old_size
- old_alignment
;
721 memcpy (ret
, ptr
, size
);
722 if (__builtin_expect (free_allocator_data
723 && free_allocator_data
->pool_size
< ~(uintptr_t) 0, 0))
725 #ifdef HAVE_SYNC_BUILTINS
726 __atomic_add_fetch (&free_allocator_data
->used_pool_size
, -data
->size
,
729 gomp_mutex_lock (&free_allocator_data
->lock
);
730 free_allocator_data
->used_pool_size
-= data
->size
;
731 gomp_mutex_unlock (&free_allocator_data
->lock
);
740 switch (allocator_data
->fallback
)
742 case omp_atv_default_mem_fb
:
743 if (new_alignment
> sizeof (void *)
745 && allocator_data
->pool_size
< ~(uintptr_t) 0))
747 allocator
= omp_default_mem_alloc
;
750 /* Otherwise, we've already performed default mem allocation
751 and if that failed, it won't succeed again (unless it was
752 intermittent. Return NULL then, as that is the fallback. */
754 case omp_atv_null_fb
:
757 case omp_atv_abort_fb
:
758 gomp_fatal ("Out of memory allocating %lu bytes",
759 (unsigned long) size
);
760 case omp_atv_allocator_fb
:
761 allocator
= allocator_data
->fb_data
;