1 /* Copyright (C) 2020-2023 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 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
38 #define omp_max_predefined_alloc omp_thread_mem_alloc
40 /* These macros may be overridden in config/<target>/allocator.c.
41 The following definitions (ab)use comma operators to avoid unused
43 #ifndef MEMSPACE_ALLOC
44 #define MEMSPACE_ALLOC(MEMSPACE, SIZE) \
45 malloc (((void)(MEMSPACE), (SIZE)))
47 #ifndef MEMSPACE_CALLOC
48 #define MEMSPACE_CALLOC(MEMSPACE, SIZE) \
49 calloc (1, (((void)(MEMSPACE), (SIZE))))
51 #ifndef MEMSPACE_REALLOC
52 #define MEMSPACE_REALLOC(MEMSPACE, ADDR, OLDSIZE, SIZE) \
53 realloc (ADDR, (((void)(MEMSPACE), (void)(OLDSIZE), (SIZE))))
56 #define MEMSPACE_FREE(MEMSPACE, ADDR, SIZE) \
57 free (((void)(MEMSPACE), (void)(SIZE), (ADDR)))
59 #ifndef MEMSPACE_VALIDATE
60 #define MEMSPACE_VALIDATE(MEMSPACE, ACCESS) \
61 (((void)(MEMSPACE), (void)(ACCESS), 1))
64 /* Map the predefined allocators to the correct memory space.
65 The index to this table is the omp_allocator_handle_t enum value.
66 When the user calls omp_alloc with a predefined allocator this
67 table determines what memory they get. */
68 static const omp_memspace_handle_t predefined_alloc_mapping
[] = {
69 omp_default_mem_space
, /* omp_null_allocator doesn't actually use this. */
70 omp_default_mem_space
, /* omp_default_mem_alloc. */
71 omp_large_cap_mem_space
, /* omp_large_cap_mem_alloc. */
72 omp_const_mem_space
, /* omp_const_mem_alloc. */
73 omp_high_bw_mem_space
, /* omp_high_bw_mem_alloc. */
74 omp_low_lat_mem_space
, /* omp_low_lat_mem_alloc. */
75 omp_low_lat_mem_space
, /* omp_cgroup_mem_alloc (implementation defined). */
76 omp_low_lat_mem_space
, /* omp_pteam_mem_alloc (implementation defined). */
77 omp_low_lat_mem_space
, /* omp_thread_mem_alloc (implementation defined). */
80 #define ARRAY_SIZE(A) (sizeof (A) / sizeof ((A)[0]))
81 _Static_assert (ARRAY_SIZE (predefined_alloc_mapping
)
82 == omp_max_predefined_alloc
+ 1,
83 "predefined_alloc_mapping must match omp_memspace_handle_t");
85 enum gomp_numa_memkind_kind
87 GOMP_MEMKIND_NONE
= 0,
88 #define GOMP_MEMKIND_KINDS \
89 GOMP_MEMKIND_KIND (HBW_INTERLEAVE), \
90 GOMP_MEMKIND_KIND (HBW_PREFERRED), \
91 GOMP_MEMKIND_KIND (DAX_KMEM_ALL), \
92 GOMP_MEMKIND_KIND (DAX_KMEM), \
93 GOMP_MEMKIND_KIND (INTERLEAVE), \
94 GOMP_MEMKIND_KIND (DEFAULT)
95 #define GOMP_MEMKIND_KIND(kind) GOMP_MEMKIND_##kind
97 #undef GOMP_MEMKIND_KIND
99 GOMP_MEMKIND_LIBNUMA
= GOMP_MEMKIND_COUNT
102 struct omp_allocator_data
104 omp_memspace_handle_t memspace
;
105 omp_uintptr_t alignment
;
106 omp_uintptr_t pool_size
;
107 omp_uintptr_t used_pool_size
;
108 omp_allocator_handle_t fb_data
;
109 unsigned int sync_hint
: 8;
110 unsigned int access
: 8;
111 unsigned int fallback
: 8;
112 unsigned int pinned
: 1;
113 unsigned int partition
: 7;
114 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
115 unsigned int memkind
: 8;
117 #ifndef HAVE_SYNC_BUILTINS
122 struct omp_mem_header
126 omp_allocator_handle_t allocator
;
130 struct gomp_libnuma_data
133 void *(*numa_alloc_local
) (size_t);
134 void *(*numa_realloc
) (void *, size_t, size_t);
135 void (*numa_free
) (void *, size_t);
138 struct gomp_memkind_data
140 void *memkind_handle
;
141 void *(*memkind_malloc
) (void *, size_t);
142 void *(*memkind_calloc
) (void *, size_t, size_t);
143 void *(*memkind_realloc
) (void *, void *, size_t);
144 void (*memkind_free
) (void *, void *);
145 int (*memkind_check_available
) (void *);
146 void **kinds
[GOMP_MEMKIND_COUNT
];
149 #ifdef LIBGOMP_USE_LIBNUMA
150 static struct gomp_libnuma_data
*libnuma_data
;
151 static pthread_once_t libnuma_data_once
= PTHREAD_ONCE_INIT
;
154 gomp_init_libnuma (void)
156 void *handle
= dlopen ("libnuma.so.1", RTLD_LAZY
);
157 struct gomp_libnuma_data
*data
;
159 data
= calloc (1, sizeof (struct gomp_libnuma_data
));
168 int (*numa_available
) (void);
170 = (__typeof (numa_available
)) dlsym (handle
, "numa_available");
171 if (!numa_available
|| numa_available () != 0)
179 __atomic_store_n (&libnuma_data
, data
, MEMMODEL_RELEASE
);
182 data
->numa_handle
= handle
;
183 data
->numa_alloc_local
184 = (__typeof (data
->numa_alloc_local
)) dlsym (handle
, "numa_alloc_local");
186 = (__typeof (data
->numa_realloc
)) dlsym (handle
, "numa_realloc");
188 = (__typeof (data
->numa_free
)) dlsym (handle
, "numa_free");
189 __atomic_store_n (&libnuma_data
, data
, MEMMODEL_RELEASE
);
192 static struct gomp_libnuma_data
*
193 gomp_get_libnuma (void)
195 struct gomp_libnuma_data
*data
196 = __atomic_load_n (&libnuma_data
, MEMMODEL_ACQUIRE
);
199 pthread_once (&libnuma_data_once
, gomp_init_libnuma
);
200 return __atomic_load_n (&libnuma_data
, MEMMODEL_ACQUIRE
);
204 #ifdef LIBGOMP_USE_MEMKIND
205 static struct gomp_memkind_data
*memkind_data
;
206 static pthread_once_t memkind_data_once
= PTHREAD_ONCE_INIT
;
209 gomp_init_memkind (void)
211 void *handle
= dlopen ("libmemkind.so.0", RTLD_LAZY
);
212 struct gomp_memkind_data
*data
;
214 static const char *kinds
[] = {
216 #define GOMP_MEMKIND_KIND(kind) "MEMKIND_" #kind
218 #undef GOMP_MEMKIND_KIND
221 data
= calloc (1, sizeof (struct gomp_memkind_data
));
230 __atomic_store_n (&memkind_data
, data
, MEMMODEL_RELEASE
);
233 data
->memkind_handle
= handle
;
235 = (__typeof (data
->memkind_malloc
)) dlsym (handle
, "memkind_malloc");
237 = (__typeof (data
->memkind_calloc
)) dlsym (handle
, "memkind_calloc");
238 data
->memkind_realloc
239 = (__typeof (data
->memkind_realloc
)) dlsym (handle
, "memkind_realloc");
241 = (__typeof (data
->memkind_free
)) dlsym (handle
, "memkind_free");
242 data
->memkind_check_available
243 = (__typeof (data
->memkind_check_available
))
244 dlsym (handle
, "memkind_check_available");
245 if (data
->memkind_malloc
246 && data
->memkind_calloc
247 && data
->memkind_realloc
248 && data
->memkind_free
249 && data
->memkind_check_available
)
250 for (i
= 1; i
< GOMP_MEMKIND_COUNT
; ++i
)
252 data
->kinds
[i
] = (void **) dlsym (handle
, kinds
[i
]);
253 if (data
->kinds
[i
] && data
->memkind_check_available (*data
->kinds
[i
]))
254 data
->kinds
[i
] = NULL
;
256 __atomic_store_n (&memkind_data
, data
, MEMMODEL_RELEASE
);
259 static struct gomp_memkind_data
*
260 gomp_get_memkind (void)
262 struct gomp_memkind_data
*data
263 = __atomic_load_n (&memkind_data
, MEMMODEL_ACQUIRE
);
266 pthread_once (&memkind_data_once
, gomp_init_memkind
);
267 return __atomic_load_n (&memkind_data
, MEMMODEL_ACQUIRE
);
271 omp_allocator_handle_t
272 omp_init_allocator (omp_memspace_handle_t memspace
, int ntraits
,
273 const omp_alloctrait_t traits
[])
275 struct omp_allocator_data data
276 = { memspace
, 1, ~(uintptr_t) 0, 0, 0, omp_atv_contended
, omp_atv_all
,
277 omp_atv_default_mem_fb
, omp_atv_false
, omp_atv_environment
,
278 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
282 struct omp_allocator_data
*ret
;
285 if (memspace
> omp_low_lat_mem_space
)
286 return omp_null_allocator
;
287 for (i
= 0; i
< ntraits
; i
++)
288 switch (traits
[i
].key
)
290 case omp_atk_sync_hint
:
291 switch (traits
[i
].value
)
293 case omp_atv_default
:
294 data
.sync_hint
= omp_atv_contended
;
296 case omp_atv_contended
:
297 case omp_atv_uncontended
:
298 case omp_atv_serialized
:
299 case omp_atv_private
:
300 data
.sync_hint
= traits
[i
].value
;
303 return omp_null_allocator
;
306 case omp_atk_alignment
:
307 if (traits
[i
].value
== omp_atv_default
)
312 if ((traits
[i
].value
& (traits
[i
].value
- 1)) != 0
314 return omp_null_allocator
;
315 data
.alignment
= traits
[i
].value
;
318 switch (traits
[i
].value
)
320 case omp_atv_default
:
321 data
.access
= omp_atv_all
;
327 data
.access
= traits
[i
].value
;
330 return omp_null_allocator
;
333 case omp_atk_pool_size
:
334 if (traits
[i
].value
== omp_atv_default
)
335 data
.pool_size
= ~(uintptr_t) 0;
337 data
.pool_size
= traits
[i
].value
;
339 case omp_atk_fallback
:
340 switch (traits
[i
].value
)
342 case omp_atv_default
:
343 data
.fallback
= omp_atv_default_mem_fb
;
345 case omp_atv_default_mem_fb
:
346 case omp_atv_null_fb
:
347 case omp_atv_abort_fb
:
348 case omp_atv_allocator_fb
:
349 data
.fallback
= traits
[i
].value
;
352 return omp_null_allocator
;
355 case omp_atk_fb_data
:
356 data
.fb_data
= traits
[i
].value
;
359 switch (traits
[i
].value
)
361 case omp_atv_default
:
363 data
.pinned
= omp_atv_false
;
366 data
.pinned
= omp_atv_true
;
369 return omp_null_allocator
;
372 case omp_atk_partition
:
373 switch (traits
[i
].value
)
375 case omp_atv_default
:
376 data
.partition
= omp_atv_environment
;
378 case omp_atv_environment
:
379 case omp_atv_nearest
:
380 case omp_atv_blocked
:
381 case omp_atv_interleaved
:
382 data
.partition
= traits
[i
].value
;
385 return omp_null_allocator
;
389 return omp_null_allocator
;
392 if (data
.alignment
< sizeof (void *))
393 data
.alignment
= sizeof (void *);
397 #ifdef LIBGOMP_USE_MEMKIND
398 case omp_high_bw_mem_space
:
399 struct gomp_memkind_data
*memkind_data
;
400 memkind_data
= gomp_get_memkind ();
401 if (data
.partition
== omp_atv_interleaved
402 && memkind_data
->kinds
[GOMP_MEMKIND_HBW_INTERLEAVE
])
404 data
.memkind
= GOMP_MEMKIND_HBW_INTERLEAVE
;
407 else if (memkind_data
->kinds
[GOMP_MEMKIND_HBW_PREFERRED
])
409 data
.memkind
= GOMP_MEMKIND_HBW_PREFERRED
;
413 case omp_large_cap_mem_space
:
414 memkind_data
= gomp_get_memkind ();
415 if (memkind_data
->kinds
[GOMP_MEMKIND_DAX_KMEM_ALL
])
416 data
.memkind
= GOMP_MEMKIND_DAX_KMEM_ALL
;
417 else if (memkind_data
->kinds
[GOMP_MEMKIND_DAX_KMEM
])
418 data
.memkind
= GOMP_MEMKIND_DAX_KMEM
;
422 #ifdef LIBGOMP_USE_MEMKIND
423 if (data
.partition
== omp_atv_interleaved
)
425 memkind_data
= gomp_get_memkind ();
426 if (memkind_data
->kinds
[GOMP_MEMKIND_INTERLEAVE
])
427 data
.memkind
= GOMP_MEMKIND_INTERLEAVE
;
433 #ifdef LIBGOMP_USE_LIBNUMA
434 if (data
.memkind
== GOMP_MEMKIND_NONE
&& data
.partition
== omp_atv_nearest
)
436 libnuma_data
= gomp_get_libnuma ();
437 if (libnuma_data
->numa_alloc_local
!= NULL
)
438 data
.memkind
= GOMP_MEMKIND_LIBNUMA
;
442 /* No support for this so far. */
444 return omp_null_allocator
;
446 /* Reject unsupported memory spaces. */
447 if (!MEMSPACE_VALIDATE (data
.memspace
, data
.access
))
448 return omp_null_allocator
;
450 ret
= gomp_malloc (sizeof (struct omp_allocator_data
));
452 #ifndef HAVE_SYNC_BUILTINS
453 gomp_mutex_init (&ret
->lock
);
455 return (omp_allocator_handle_t
) ret
;
459 omp_destroy_allocator (omp_allocator_handle_t allocator
)
461 if (allocator
!= omp_null_allocator
)
463 #ifndef HAVE_SYNC_BUILTINS
464 gomp_mutex_destroy (&((struct omp_allocator_data
*) allocator
)->lock
);
466 free ((void *) allocator
);
470 ialias (omp_init_allocator
)
471 ialias (omp_destroy_allocator
)
474 omp_aligned_alloc (size_t alignment
, size_t size
,
475 omp_allocator_handle_t allocator
)
477 struct omp_allocator_data
*allocator_data
;
478 size_t new_size
, new_alignment
;
480 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
481 enum gomp_numa_memkind_kind memkind
;
484 if (__builtin_expect (size
== 0, 0))
488 new_alignment
= alignment
;
489 if (allocator
== omp_null_allocator
)
491 struct gomp_thread
*thr
= gomp_thread ();
492 if (thr
->ts
.def_allocator
== omp_null_allocator
)
493 thr
->ts
.def_allocator
= gomp_def_allocator
;
494 allocator
= (omp_allocator_handle_t
) thr
->ts
.def_allocator
;
497 if (allocator
> omp_max_predefined_alloc
)
499 allocator_data
= (struct omp_allocator_data
*) allocator
;
500 if (new_alignment
< allocator_data
->alignment
)
501 new_alignment
= allocator_data
->alignment
;
502 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
503 memkind
= allocator_data
->memkind
;
508 allocator_data
= NULL
;
509 if (new_alignment
< sizeof (void *))
510 new_alignment
= sizeof (void *);
511 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
512 memkind
= GOMP_MEMKIND_NONE
;
514 #ifdef LIBGOMP_USE_MEMKIND
515 if (allocator
== omp_high_bw_mem_alloc
)
516 memkind
= GOMP_MEMKIND_HBW_PREFERRED
;
517 else if (allocator
== omp_large_cap_mem_alloc
)
518 memkind
= GOMP_MEMKIND_DAX_KMEM_ALL
;
521 struct gomp_memkind_data
*memkind_data
= gomp_get_memkind ();
522 if (!memkind_data
->kinds
[memkind
])
523 memkind
= GOMP_MEMKIND_NONE
;
528 new_size
= sizeof (struct omp_mem_header
);
529 if (new_alignment
> sizeof (void *))
530 new_size
+= new_alignment
- sizeof (void *);
531 if (__builtin_add_overflow (size
, new_size
, &new_size
))
533 #ifdef OMP_LOW_LAT_MEM_ALLOC_INVALID
534 if (allocator
== omp_low_lat_mem_alloc
)
538 if (__builtin_expect (allocator_data
539 && allocator_data
->pool_size
< ~(uintptr_t) 0, 0))
541 uintptr_t used_pool_size
;
542 if (new_size
> allocator_data
->pool_size
)
544 #ifdef HAVE_SYNC_BUILTINS
545 used_pool_size
= __atomic_load_n (&allocator_data
->used_pool_size
,
549 uintptr_t new_pool_size
;
550 if (__builtin_add_overflow (used_pool_size
, new_size
,
552 || new_pool_size
> allocator_data
->pool_size
)
554 if (__atomic_compare_exchange_n (&allocator_data
->used_pool_size
,
555 &used_pool_size
, new_pool_size
,
556 true, MEMMODEL_RELAXED
,
562 gomp_mutex_lock (&allocator_data
->lock
);
563 if (__builtin_add_overflow (allocator_data
->used_pool_size
, new_size
,
565 || used_pool_size
> allocator_data
->pool_size
)
567 gomp_mutex_unlock (&allocator_data
->lock
);
570 allocator_data
->used_pool_size
= used_pool_size
;
571 gomp_mutex_unlock (&allocator_data
->lock
);
573 #ifdef LIBGOMP_USE_LIBNUMA
574 if (memkind
== GOMP_MEMKIND_LIBNUMA
)
575 ptr
= libnuma_data
->numa_alloc_local (new_size
);
576 # ifdef LIBGOMP_USE_MEMKIND
580 #ifdef LIBGOMP_USE_MEMKIND
583 struct gomp_memkind_data
*memkind_data
= gomp_get_memkind ();
584 void *kind
= *memkind_data
->kinds
[memkind
];
585 ptr
= memkind_data
->memkind_malloc (kind
, new_size
);
589 ptr
= MEMSPACE_ALLOC (allocator_data
->memspace
, new_size
);
592 #ifdef HAVE_SYNC_BUILTINS
593 __atomic_add_fetch (&allocator_data
->used_pool_size
, -new_size
,
596 gomp_mutex_lock (&allocator_data
->lock
);
597 allocator_data
->used_pool_size
-= new_size
;
598 gomp_mutex_unlock (&allocator_data
->lock
);
605 #ifdef LIBGOMP_USE_LIBNUMA
606 if (memkind
== GOMP_MEMKIND_LIBNUMA
)
607 ptr
= libnuma_data
->numa_alloc_local (new_size
);
608 # ifdef LIBGOMP_USE_MEMKIND
612 #ifdef LIBGOMP_USE_MEMKIND
615 struct gomp_memkind_data
*memkind_data
= gomp_get_memkind ();
616 void *kind
= *memkind_data
->kinds
[memkind
];
617 ptr
= memkind_data
->memkind_malloc (kind
, new_size
);
622 omp_memspace_handle_t memspace
;
623 memspace
= (allocator_data
624 ? allocator_data
->memspace
625 : predefined_alloc_mapping
[allocator
]);
626 ptr
= MEMSPACE_ALLOC (memspace
, new_size
);
632 if (new_alignment
> sizeof (void *))
633 ret
= (void *) (((uintptr_t) ptr
634 + sizeof (struct omp_mem_header
)
635 + new_alignment
- sizeof (void *))
636 & ~(new_alignment
- 1));
638 ret
= (char *) ptr
+ sizeof (struct omp_mem_header
);
639 ((struct omp_mem_header
*) ret
)[-1].ptr
= ptr
;
640 ((struct omp_mem_header
*) ret
)[-1].size
= new_size
;
641 ((struct omp_mem_header
*) ret
)[-1].allocator
= allocator
;
645 int fallback
= (allocator_data
646 ? allocator_data
->fallback
647 : allocator
== omp_default_mem_alloc
649 : omp_atv_default_mem_fb
);
652 case omp_atv_default_mem_fb
:
653 allocator
= omp_default_mem_alloc
;
655 case omp_atv_null_fb
:
658 case omp_atv_abort_fb
:
659 gomp_fatal ("Out of memory allocating %lu bytes",
660 (unsigned long) size
);
661 case omp_atv_allocator_fb
:
662 allocator
= allocator_data
->fb_data
;
668 ialias (omp_aligned_alloc
)
671 omp_alloc (size_t size
, omp_allocator_handle_t allocator
)
673 return ialias_call (omp_aligned_alloc
) (1, size
, allocator
);
676 /* Like omp_aligned_alloc, but apply on top of that:
677 "For allocations that arise from this ... the null_fb value of the
678 fallback allocator trait behaves as if the abort_fb had been specified." */
681 GOMP_alloc (size_t alignment
, size_t size
, uintptr_t allocator
)
684 = ialias_call (omp_aligned_alloc
) (alignment
, size
,
685 (omp_allocator_handle_t
) allocator
);
686 if (__builtin_expect (ret
== NULL
, 0) && size
)
687 gomp_fatal ("Out of memory allocating %lu bytes",
688 (unsigned long) size
);
693 omp_free (void *ptr
, omp_allocator_handle_t allocator
)
695 struct omp_mem_header
*data
;
696 omp_memspace_handle_t memspace
= omp_default_mem_space
;
701 data
= &((struct omp_mem_header
*) ptr
)[-1];
702 if (data
->allocator
> omp_max_predefined_alloc
)
704 struct omp_allocator_data
*allocator_data
705 = (struct omp_allocator_data
*) (data
->allocator
);
706 if (allocator_data
->pool_size
< ~(uintptr_t) 0)
708 #ifdef HAVE_SYNC_BUILTINS
709 __atomic_add_fetch (&allocator_data
->used_pool_size
, -data
->size
,
712 gomp_mutex_lock (&allocator_data
->lock
);
713 allocator_data
->used_pool_size
-= data
->size
;
714 gomp_mutex_unlock (&allocator_data
->lock
);
717 #ifdef LIBGOMP_USE_LIBNUMA
718 if (allocator_data
->memkind
== GOMP_MEMKIND_LIBNUMA
)
720 libnuma_data
->numa_free (data
->ptr
, data
->size
);
723 # ifdef LIBGOMP_USE_MEMKIND
727 #ifdef LIBGOMP_USE_MEMKIND
728 if (allocator_data
->memkind
)
730 struct gomp_memkind_data
*memkind_data
= gomp_get_memkind ();
731 void *kind
= *memkind_data
->kinds
[allocator_data
->memkind
];
732 memkind_data
->memkind_free (kind
, data
->ptr
);
737 memspace
= allocator_data
->memspace
;
741 #ifdef LIBGOMP_USE_MEMKIND
742 enum gomp_numa_memkind_kind memkind
= GOMP_MEMKIND_NONE
;
743 if (data
->allocator
== omp_high_bw_mem_alloc
)
744 memkind
= GOMP_MEMKIND_HBW_PREFERRED
;
745 else if (data
->allocator
== omp_large_cap_mem_alloc
)
746 memkind
= GOMP_MEMKIND_DAX_KMEM_ALL
;
749 struct gomp_memkind_data
*memkind_data
= gomp_get_memkind ();
750 if (memkind_data
->kinds
[memkind
])
752 void *kind
= *memkind_data
->kinds
[memkind
];
753 memkind_data
->memkind_free (kind
, data
->ptr
);
759 memspace
= predefined_alloc_mapping
[data
->allocator
];
762 MEMSPACE_FREE (memspace
, data
->ptr
, data
->size
);
768 GOMP_free (void *ptr
, uintptr_t allocator
)
770 return ialias_call (omp_free
) (ptr
, (omp_allocator_handle_t
) allocator
);
774 omp_aligned_calloc (size_t alignment
, size_t nmemb
, size_t size
,
775 omp_allocator_handle_t allocator
)
777 struct omp_allocator_data
*allocator_data
;
778 size_t new_size
, size_temp
, new_alignment
;
780 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
781 enum gomp_numa_memkind_kind memkind
;
784 if (__builtin_expect (size
== 0 || nmemb
== 0, 0))
788 new_alignment
= alignment
;
789 if (allocator
== omp_null_allocator
)
791 struct gomp_thread
*thr
= gomp_thread ();
792 if (thr
->ts
.def_allocator
== omp_null_allocator
)
793 thr
->ts
.def_allocator
= gomp_def_allocator
;
794 allocator
= (omp_allocator_handle_t
) thr
->ts
.def_allocator
;
797 if (allocator
> omp_max_predefined_alloc
)
799 allocator_data
= (struct omp_allocator_data
*) allocator
;
800 if (new_alignment
< allocator_data
->alignment
)
801 new_alignment
= allocator_data
->alignment
;
802 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
803 memkind
= allocator_data
->memkind
;
808 allocator_data
= NULL
;
809 if (new_alignment
< sizeof (void *))
810 new_alignment
= sizeof (void *);
811 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
812 memkind
= GOMP_MEMKIND_NONE
;
814 #ifdef LIBGOMP_USE_MEMKIND
815 if (allocator
== omp_high_bw_mem_alloc
)
816 memkind
= GOMP_MEMKIND_HBW_PREFERRED
;
817 else if (allocator
== omp_large_cap_mem_alloc
)
818 memkind
= GOMP_MEMKIND_DAX_KMEM_ALL
;
821 struct gomp_memkind_data
*memkind_data
= gomp_get_memkind ();
822 if (!memkind_data
->kinds
[memkind
])
823 memkind
= GOMP_MEMKIND_NONE
;
828 new_size
= sizeof (struct omp_mem_header
);
829 if (new_alignment
> sizeof (void *))
830 new_size
+= new_alignment
- sizeof (void *);
831 if (__builtin_mul_overflow (size
, nmemb
, &size_temp
))
833 if (__builtin_add_overflow (size_temp
, new_size
, &new_size
))
835 #ifdef OMP_LOW_LAT_MEM_ALLOC_INVALID
836 if (allocator
== omp_low_lat_mem_alloc
)
840 if (__builtin_expect (allocator_data
841 && allocator_data
->pool_size
< ~(uintptr_t) 0, 0))
843 uintptr_t used_pool_size
;
844 if (new_size
> allocator_data
->pool_size
)
846 #ifdef HAVE_SYNC_BUILTINS
847 used_pool_size
= __atomic_load_n (&allocator_data
->used_pool_size
,
851 uintptr_t new_pool_size
;
852 if (__builtin_add_overflow (used_pool_size
, new_size
,
854 || new_pool_size
> allocator_data
->pool_size
)
856 if (__atomic_compare_exchange_n (&allocator_data
->used_pool_size
,
857 &used_pool_size
, new_pool_size
,
858 true, MEMMODEL_RELAXED
,
864 gomp_mutex_lock (&allocator_data
->lock
);
865 if (__builtin_add_overflow (allocator_data
->used_pool_size
, new_size
,
867 || used_pool_size
> allocator_data
->pool_size
)
869 gomp_mutex_unlock (&allocator_data
->lock
);
872 allocator_data
->used_pool_size
= used_pool_size
;
873 gomp_mutex_unlock (&allocator_data
->lock
);
875 #ifdef LIBGOMP_USE_LIBNUMA
876 if (memkind
== GOMP_MEMKIND_LIBNUMA
)
877 /* numa_alloc_local uses mmap with MAP_ANONYMOUS, returning
878 memory that is initialized to zero. */
879 ptr
= libnuma_data
->numa_alloc_local (new_size
);
880 # ifdef LIBGOMP_USE_MEMKIND
884 #ifdef LIBGOMP_USE_MEMKIND
887 struct gomp_memkind_data
*memkind_data
= gomp_get_memkind ();
888 void *kind
= *memkind_data
->kinds
[memkind
];
889 ptr
= memkind_data
->memkind_calloc (kind
, 1, new_size
);
893 ptr
= MEMSPACE_CALLOC (allocator_data
->memspace
, new_size
);
896 #ifdef HAVE_SYNC_BUILTINS
897 __atomic_add_fetch (&allocator_data
->used_pool_size
, -new_size
,
900 gomp_mutex_lock (&allocator_data
->lock
);
901 allocator_data
->used_pool_size
-= new_size
;
902 gomp_mutex_unlock (&allocator_data
->lock
);
909 #ifdef LIBGOMP_USE_LIBNUMA
910 if (memkind
== GOMP_MEMKIND_LIBNUMA
)
911 /* numa_alloc_local uses mmap with MAP_ANONYMOUS, returning
912 memory that is initialized to zero. */
913 ptr
= libnuma_data
->numa_alloc_local (new_size
);
914 # ifdef LIBGOMP_USE_MEMKIND
918 #ifdef LIBGOMP_USE_MEMKIND
921 struct gomp_memkind_data
*memkind_data
= gomp_get_memkind ();
922 void *kind
= *memkind_data
->kinds
[memkind
];
923 ptr
= memkind_data
->memkind_calloc (kind
, 1, new_size
);
928 omp_memspace_handle_t memspace
;
929 memspace
= (allocator_data
930 ? allocator_data
->memspace
931 : predefined_alloc_mapping
[allocator
]);
932 ptr
= MEMSPACE_CALLOC (memspace
, new_size
);
938 if (new_alignment
> sizeof (void *))
939 ret
= (void *) (((uintptr_t) ptr
940 + sizeof (struct omp_mem_header
)
941 + new_alignment
- sizeof (void *))
942 & ~(new_alignment
- 1));
944 ret
= (char *) ptr
+ sizeof (struct omp_mem_header
);
945 ((struct omp_mem_header
*) ret
)[-1].ptr
= ptr
;
946 ((struct omp_mem_header
*) ret
)[-1].size
= new_size
;
947 ((struct omp_mem_header
*) ret
)[-1].allocator
= allocator
;
951 int fallback
= (allocator_data
952 ? allocator_data
->fallback
953 : allocator
== omp_default_mem_alloc
955 : omp_atv_default_mem_fb
);
958 case omp_atv_default_mem_fb
:
959 allocator
= omp_default_mem_alloc
;
961 case omp_atv_null_fb
:
964 case omp_atv_abort_fb
:
965 gomp_fatal ("Out of memory allocating %lu bytes",
966 (unsigned long) (size
* nmemb
));
967 case omp_atv_allocator_fb
:
968 allocator
= allocator_data
->fb_data
;
974 ialias (omp_aligned_calloc
)
977 omp_calloc (size_t nmemb
, size_t size
, omp_allocator_handle_t allocator
)
979 return ialias_call (omp_aligned_calloc
) (1, nmemb
, size
, allocator
);
983 omp_realloc (void *ptr
, size_t size
, omp_allocator_handle_t allocator
,
984 omp_allocator_handle_t free_allocator
)
986 struct omp_allocator_data
*allocator_data
, *free_allocator_data
;
987 size_t new_size
, old_size
, new_alignment
, old_alignment
;
989 struct omp_mem_header
*data
;
990 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
991 enum gomp_numa_memkind_kind memkind
, free_memkind
;
994 if (__builtin_expect (ptr
== NULL
, 0))
995 return ialias_call (omp_aligned_alloc
) (1, size
, allocator
);
997 if (__builtin_expect (size
== 0, 0))
999 ialias_call (omp_free
) (ptr
, free_allocator
);
1003 data
= &((struct omp_mem_header
*) ptr
)[-1];
1004 free_allocator
= data
->allocator
;
1007 new_alignment
= sizeof (void *);
1008 if (allocator
== omp_null_allocator
)
1009 allocator
= free_allocator
;
1011 if (allocator
> omp_max_predefined_alloc
)
1013 allocator_data
= (struct omp_allocator_data
*) allocator
;
1014 if (new_alignment
< allocator_data
->alignment
)
1015 new_alignment
= allocator_data
->alignment
;
1016 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
1017 memkind
= allocator_data
->memkind
;
1022 allocator_data
= NULL
;
1023 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
1024 memkind
= GOMP_MEMKIND_NONE
;
1026 #ifdef LIBGOMP_USE_MEMKIND
1027 if (allocator
== omp_high_bw_mem_alloc
)
1028 memkind
= GOMP_MEMKIND_HBW_PREFERRED
;
1029 else if (allocator
== omp_large_cap_mem_alloc
)
1030 memkind
= GOMP_MEMKIND_DAX_KMEM_ALL
;
1033 struct gomp_memkind_data
*memkind_data
= gomp_get_memkind ();
1034 if (!memkind_data
->kinds
[memkind
])
1035 memkind
= GOMP_MEMKIND_NONE
;
1039 if (free_allocator
> omp_max_predefined_alloc
)
1041 free_allocator_data
= (struct omp_allocator_data
*) free_allocator
;
1042 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
1043 free_memkind
= free_allocator_data
->memkind
;
1048 free_allocator_data
= NULL
;
1049 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
1050 free_memkind
= GOMP_MEMKIND_NONE
;
1052 #ifdef LIBGOMP_USE_MEMKIND
1053 if (free_allocator
== omp_high_bw_mem_alloc
)
1054 free_memkind
= GOMP_MEMKIND_HBW_PREFERRED
;
1055 else if (free_allocator
== omp_large_cap_mem_alloc
)
1056 free_memkind
= GOMP_MEMKIND_DAX_KMEM_ALL
;
1059 struct gomp_memkind_data
*memkind_data
= gomp_get_memkind ();
1060 if (!memkind_data
->kinds
[free_memkind
])
1061 free_memkind
= GOMP_MEMKIND_NONE
;
1065 old_alignment
= (uintptr_t) ptr
- (uintptr_t) (data
->ptr
);
1067 new_size
= sizeof (struct omp_mem_header
);
1068 if (new_alignment
> sizeof (void *))
1069 new_size
+= new_alignment
- sizeof (void *);
1070 if (__builtin_add_overflow (size
, new_size
, &new_size
))
1072 old_size
= data
->size
;
1073 #ifdef OMP_LOW_LAT_MEM_ALLOC_INVALID
1074 if (allocator
== omp_low_lat_mem_alloc
)
1078 if (__builtin_expect (allocator_data
1079 && allocator_data
->pool_size
< ~(uintptr_t) 0, 0))
1081 uintptr_t used_pool_size
;
1082 size_t prev_size
= 0;
1083 /* Check if we can use realloc. Don't use it if extra alignment
1084 was used previously or newly, because realloc might return a pointer
1085 with different alignment and then we'd need to memmove the data
1087 if (free_allocator_data
1088 && free_allocator_data
== allocator_data
1089 && new_alignment
== sizeof (void *)
1090 && old_alignment
== sizeof (struct omp_mem_header
))
1091 prev_size
= old_size
;
1092 if (new_size
> prev_size
1093 && new_size
- prev_size
> allocator_data
->pool_size
)
1095 #ifdef HAVE_SYNC_BUILTINS
1096 used_pool_size
= __atomic_load_n (&allocator_data
->used_pool_size
,
1100 uintptr_t new_pool_size
;
1101 if (new_size
> prev_size
)
1103 if (__builtin_add_overflow (used_pool_size
, new_size
- prev_size
,
1105 || new_pool_size
> allocator_data
->pool_size
)
1109 new_pool_size
= used_pool_size
+ new_size
- prev_size
;
1110 if (__atomic_compare_exchange_n (&allocator_data
->used_pool_size
,
1111 &used_pool_size
, new_pool_size
,
1112 true, MEMMODEL_RELAXED
,
1118 gomp_mutex_lock (&allocator_data
->lock
);
1119 if (new_size
> prev_size
)
1121 if (__builtin_add_overflow (allocator_data
->used_pool_size
,
1122 new_size
- prev_size
,
1124 || used_pool_size
> allocator_data
->pool_size
)
1126 gomp_mutex_unlock (&allocator_data
->lock
);
1131 used_pool_size
= (allocator_data
->used_pool_size
1132 + new_size
- prev_size
);
1133 allocator_data
->used_pool_size
= used_pool_size
;
1134 gomp_mutex_unlock (&allocator_data
->lock
);
1136 #ifdef LIBGOMP_USE_LIBNUMA
1137 if (memkind
== GOMP_MEMKIND_LIBNUMA
)
1140 new_ptr
= libnuma_data
->numa_realloc (data
->ptr
, data
->size
,
1143 new_ptr
= libnuma_data
->numa_alloc_local (new_size
);
1145 # ifdef LIBGOMP_USE_MEMKIND
1149 #ifdef LIBGOMP_USE_MEMKIND
1152 struct gomp_memkind_data
*memkind_data
= gomp_get_memkind ();
1153 void *kind
= *memkind_data
->kinds
[memkind
];
1155 new_ptr
= memkind_data
->memkind_realloc (kind
, data
->ptr
,
1158 new_ptr
= memkind_data
->memkind_malloc (kind
, new_size
);
1163 new_ptr
= MEMSPACE_REALLOC (allocator_data
->memspace
, data
->ptr
,
1164 data
->size
, new_size
);
1166 new_ptr
= MEMSPACE_ALLOC (allocator_data
->memspace
, new_size
);
1167 if (new_ptr
== NULL
)
1169 #ifdef HAVE_SYNC_BUILTINS
1170 __atomic_add_fetch (&allocator_data
->used_pool_size
,
1171 prev_size
- new_size
,
1174 gomp_mutex_lock (&allocator_data
->lock
);
1175 allocator_data
->used_pool_size
-= new_size
- prev_size
;
1176 gomp_mutex_unlock (&allocator_data
->lock
);
1182 ret
= (char *) new_ptr
+ sizeof (struct omp_mem_header
);
1183 ((struct omp_mem_header
*) ret
)[-1].ptr
= new_ptr
;
1184 ((struct omp_mem_header
*) ret
)[-1].size
= new_size
;
1185 ((struct omp_mem_header
*) ret
)[-1].allocator
= allocator
;
1189 else if (new_alignment
== sizeof (void *)
1190 && old_alignment
== sizeof (struct omp_mem_header
)
1191 #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
1192 && memkind
== free_memkind
1194 && (free_allocator_data
== NULL
1195 || free_allocator_data
->pool_size
== ~(uintptr_t) 0))
1197 #ifdef LIBGOMP_USE_LIBNUMA
1198 if (memkind
== GOMP_MEMKIND_LIBNUMA
)
1199 new_ptr
= libnuma_data
->numa_realloc (data
->ptr
, data
->size
, new_size
);
1200 # ifdef LIBGOMP_USE_MEMKIND
1204 #ifdef LIBGOMP_USE_MEMKIND
1207 struct gomp_memkind_data
*memkind_data
= gomp_get_memkind ();
1208 void *kind
= *memkind_data
->kinds
[memkind
];
1209 new_ptr
= memkind_data
->memkind_realloc (kind
, data
->ptr
,
1215 omp_memspace_handle_t memspace
;
1216 memspace
= (allocator_data
1217 ? allocator_data
->memspace
1218 : predefined_alloc_mapping
[allocator
]);
1219 new_ptr
= MEMSPACE_REALLOC (memspace
, data
->ptr
, data
->size
, new_size
);
1221 if (new_ptr
== NULL
)
1223 ret
= (char *) new_ptr
+ sizeof (struct omp_mem_header
);
1224 ((struct omp_mem_header
*) ret
)[-1].ptr
= new_ptr
;
1225 ((struct omp_mem_header
*) ret
)[-1].size
= new_size
;
1226 ((struct omp_mem_header
*) ret
)[-1].allocator
= allocator
;
1231 #ifdef LIBGOMP_USE_LIBNUMA
1232 if (memkind
== GOMP_MEMKIND_LIBNUMA
)
1233 new_ptr
= libnuma_data
->numa_alloc_local (new_size
);
1234 # ifdef LIBGOMP_USE_MEMKIND
1238 #ifdef LIBGOMP_USE_MEMKIND
1241 struct gomp_memkind_data
*memkind_data
= gomp_get_memkind ();
1242 void *kind
= *memkind_data
->kinds
[memkind
];
1243 new_ptr
= memkind_data
->memkind_malloc (kind
, new_size
);
1248 omp_memspace_handle_t memspace
;
1249 memspace
= (allocator_data
1250 ? allocator_data
->memspace
1251 : predefined_alloc_mapping
[allocator
]);
1252 new_ptr
= MEMSPACE_ALLOC (memspace
, new_size
);
1254 if (new_ptr
== NULL
)
1258 if (new_alignment
> sizeof (void *))
1259 ret
= (void *) (((uintptr_t) new_ptr
1260 + sizeof (struct omp_mem_header
)
1261 + new_alignment
- sizeof (void *))
1262 & ~(new_alignment
- 1));
1264 ret
= (char *) new_ptr
+ sizeof (struct omp_mem_header
);
1265 ((struct omp_mem_header
*) ret
)[-1].ptr
= new_ptr
;
1266 ((struct omp_mem_header
*) ret
)[-1].size
= new_size
;
1267 ((struct omp_mem_header
*) ret
)[-1].allocator
= allocator
;
1268 if (old_size
- old_alignment
< size
)
1269 size
= old_size
- old_alignment
;
1270 memcpy (ret
, ptr
, size
);
1271 if (__builtin_expect (free_allocator_data
1272 && free_allocator_data
->pool_size
< ~(uintptr_t) 0, 0))
1274 #ifdef HAVE_SYNC_BUILTINS
1275 __atomic_add_fetch (&free_allocator_data
->used_pool_size
, -data
->size
,
1278 gomp_mutex_lock (&free_allocator_data
->lock
);
1279 free_allocator_data
->used_pool_size
-= data
->size
;
1280 gomp_mutex_unlock (&free_allocator_data
->lock
);
1283 #ifdef LIBGOMP_USE_LIBNUMA
1284 if (free_memkind
== GOMP_MEMKIND_LIBNUMA
)
1286 libnuma_data
->numa_free (data
->ptr
, data
->size
);
1289 # ifdef LIBGOMP_USE_MEMKIND
1293 #ifdef LIBGOMP_USE_MEMKIND
1296 struct gomp_memkind_data
*memkind_data
= gomp_get_memkind ();
1297 void *kind
= *memkind_data
->kinds
[free_memkind
];
1298 memkind_data
->memkind_free (kind
, data
->ptr
);
1303 omp_memspace_handle_t was_memspace
;
1304 was_memspace
= (free_allocator_data
1305 ? free_allocator_data
->memspace
1306 : predefined_alloc_mapping
[free_allocator
]);
1307 MEMSPACE_FREE (was_memspace
, data
->ptr
, data
->size
);
1312 int fallback
= (allocator_data
1313 ? allocator_data
->fallback
1314 : allocator
== omp_default_mem_alloc
1316 : omp_atv_default_mem_fb
);
1319 case omp_atv_default_mem_fb
:
1320 allocator
= omp_default_mem_alloc
;
1322 case omp_atv_null_fb
:
1325 case omp_atv_abort_fb
:
1326 gomp_fatal ("Out of memory allocating %lu bytes",
1327 (unsigned long) size
);
1328 case omp_atv_allocator_fb
:
1329 allocator
= allocator_data
->fb_data
;