| /* Copyright (C) 2020-2025 Free Software Foundation, Inc. | |
| Contributed by Jakub Jelinek <jakub@redhat.com>. | |
| This file is part of the GNU Offloading and Multi Processing Library | |
| (libgomp). | |
| Libgomp is free software; you can redistribute it and/or modify it | |
| under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 3, or (at your option) | |
| any later version. | |
| Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY | |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| more details. | |
| Under Section 7 of GPL version 3, you are granted additional | |
| permissions described in the GCC Runtime Library Exception, version | |
| 3.1, as published by the Free Software Foundation. | |
| You should have received a copy of the GNU General Public License and | |
| a copy of the GCC Runtime Library Exception along with this program; | |
| see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
| <http://www.gnu.org/licenses/>. */ | |
| /* This file contains wrappers for the system allocation routines. Most | |
| places in the OpenMP API do not make any provision for failure, so in | |
| general we cannot allow memory allocation to fail. */ | |
| #define _GNU_SOURCE | |
| #include "libgomp.h" | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| #include <dlfcn.h> | |
| #endif | |
| /* Keeping track whether a Fortran scalar allocatable/pointer has been | |
| allocated via 'omp allocators'/'omp allocate'. */ | |
| struct fort_alloc_splay_tree_key_s { | |
| void *ptr; | |
| }; | |
| typedef struct fort_alloc_splay_tree_node_s *fort_alloc_splay_tree_node; | |
| typedef struct fort_alloc_splay_tree_s *fort_alloc_splay_tree; | |
| typedef struct fort_alloc_splay_tree_key_s *fort_alloc_splay_tree_key; | |
| static inline int | |
| fort_alloc_splay_compare (fort_alloc_splay_tree_key x, fort_alloc_splay_tree_key y) | |
| { | |
| if (x->ptr < y->ptr) | |
| return -1; | |
| if (x->ptr > y->ptr) | |
| return 1; | |
| return 0; | |
| } | |
| #define splay_tree_prefix fort_alloc | |
| #define splay_tree_static | |
| #include "splay-tree.h" | |
| #define splay_tree_prefix fort_alloc | |
| #define splay_tree_static | |
| #define splay_tree_c | |
| #include "splay-tree.h" | |
| static struct fort_alloc_splay_tree_s fort_alloc_scalars; | |
| /* Add pointer as being alloced by GOMP_alloc. */ | |
| void | |
| GOMP_add_alloc (void *ptr) | |
| { | |
| if (ptr == NULL) | |
| return; | |
| fort_alloc_splay_tree_node item; | |
| item = gomp_malloc (sizeof (struct splay_tree_node_s)); | |
| item->key.ptr = ptr; | |
| item->left = NULL; | |
| item->right = NULL; | |
| fort_alloc_splay_tree_insert (&fort_alloc_scalars, item); | |
| } | |
| /* Remove pointer, either called by FREE or by REALLOC, | |
| either of them can change the allocation status. */ | |
| bool | |
| GOMP_is_alloc (void *ptr) | |
| { | |
| struct fort_alloc_splay_tree_key_s needle; | |
| fort_alloc_splay_tree_node n; | |
| needle.ptr = ptr; | |
| n = fort_alloc_splay_tree_lookup_node (&fort_alloc_scalars, &needle); | |
| if (n) | |
| { | |
| fort_alloc_splay_tree_remove (&fort_alloc_scalars, &n->key); | |
| free (n); | |
| } | |
| return n != NULL; | |
| } | |
| #define omp_max_predefined_alloc omp_thread_mem_alloc | |
| #define ompx_gnu_min_predefined_alloc ompx_gnu_pinned_mem_alloc | |
| #define ompx_gnu_max_predefined_alloc ompx_gnu_pinned_mem_alloc | |
| _Static_assert (GOMP_OMP_PREDEF_ALLOC_MAX == omp_thread_mem_alloc, | |
| "GOMP_OMP_PREDEF_ALLOC_MAX == omp_thread_mem_alloc"); | |
| _Static_assert (GOMP_OMPX_PREDEF_ALLOC_MIN == ompx_gnu_min_predefined_alloc, | |
| "GOMP_OMP_PREDEF_ALLOC_MAX == omp_thread_mem_alloc"); | |
| _Static_assert (GOMP_OMPX_PREDEF_ALLOC_MAX == ompx_gnu_max_predefined_alloc, | |
| "GOMP_OMP_PREDEF_ALLOC_MAX == omp_thread_mem_alloc"); | |
| _Static_assert (GOMP_OMP_PREDEF_ALLOC_THREADS == omp_thread_mem_alloc, | |
| "GOMP_OMP_PREDEF_ALLOC_THREADS == omp_thread_mem_alloc"); | |
| /* These macros may be overridden in config/<target>/allocator.c. | |
| The defaults (no override) are to return NULL for pinned memory requests | |
| and pass through to the regular OS calls otherwise. | |
| The following definitions (ab)use comma operators to avoid unused | |
| variable errors. */ | |
| #ifndef MEMSPACE_ALLOC | |
| #define MEMSPACE_ALLOC(MEMSPACE, SIZE, PIN) \ | |
| (PIN ? NULL : malloc (((void)(MEMSPACE), (SIZE)))) | |
| #endif | |
| #ifndef MEMSPACE_CALLOC | |
| #define MEMSPACE_CALLOC(MEMSPACE, SIZE, PIN) \ | |
| (PIN ? NULL : calloc (1, (((void)(MEMSPACE), (SIZE))))) | |
| #endif | |
| #ifndef MEMSPACE_REALLOC | |
| #define MEMSPACE_REALLOC(MEMSPACE, ADDR, OLDSIZE, SIZE, OLDPIN, PIN) \ | |
| ((PIN) || (OLDPIN) ? NULL \ | |
| : realloc (ADDR, (((void)(MEMSPACE), (void)(OLDSIZE), (SIZE))))) | |
| #endif | |
| #ifndef MEMSPACE_FREE | |
| #define MEMSPACE_FREE(MEMSPACE, ADDR, SIZE, PIN) \ | |
| if (PIN) free (((void)(MEMSPACE), (void)(SIZE), (ADDR))) | |
| #endif | |
| #ifndef MEMSPACE_VALIDATE | |
| #define MEMSPACE_VALIDATE(MEMSPACE, ACCESS, PIN) \ | |
| (PIN ? 0 : ((void)(MEMSPACE), (void)(ACCESS), 1)) | |
| #endif | |
| /* Map the predefined allocators to the correct memory space. | |
| The index to this table is the omp_allocator_handle_t enum value. | |
| When the user calls omp_alloc with a predefined allocator this | |
| table determines what memory they get. */ | |
| static const omp_memspace_handle_t predefined_omp_alloc_mapping[] = { | |
| omp_default_mem_space, /* omp_null_allocator doesn't actually use this. */ | |
| omp_default_mem_space, /* omp_default_mem_alloc. */ | |
| omp_large_cap_mem_space, /* omp_large_cap_mem_alloc. */ | |
| omp_const_mem_space, /* omp_const_mem_alloc. */ | |
| omp_high_bw_mem_space, /* omp_high_bw_mem_alloc. */ | |
| omp_low_lat_mem_space, /* omp_low_lat_mem_alloc. */ | |
| omp_low_lat_mem_space, /* omp_cgroup_mem_alloc (implementation defined). */ | |
| omp_low_lat_mem_space, /* omp_pteam_mem_alloc (implementation defined). */ | |
| omp_low_lat_mem_space, /* omp_thread_mem_alloc (implementation defined). */ | |
| }; | |
| static const omp_memspace_handle_t predefined_ompx_gnu_alloc_mapping[] = { | |
| omp_default_mem_space, /* ompx_gnu_pinned_mem_alloc. */ | |
| }; | |
| #define ARRAY_SIZE(A) (sizeof (A) / sizeof ((A)[0])) | |
| _Static_assert (ARRAY_SIZE (predefined_omp_alloc_mapping) | |
| == omp_max_predefined_alloc + 1, | |
| "predefined_omp_alloc_mapping must match omp_memspace_handle_t"); | |
| _Static_assert (ARRAY_SIZE (predefined_ompx_gnu_alloc_mapping) | |
| == (ompx_gnu_max_predefined_alloc | |
| - ompx_gnu_min_predefined_alloc) + 1, | |
| "predefined_ompx_gnu_alloc_mapping must match" | |
| " omp_memspace_handle_t"); | |
| static inline bool | |
| predefined_allocator_p (omp_allocator_handle_t allocator) | |
| { | |
| return allocator <= ompx_gnu_max_predefined_alloc; | |
| } | |
| static inline omp_memspace_handle_t | |
| predefined_alloc_mapping (omp_allocator_handle_t allocator) | |
| { | |
| if (allocator <= omp_max_predefined_alloc) | |
| return predefined_omp_alloc_mapping[allocator]; | |
| else if (allocator >= ompx_gnu_min_predefined_alloc | |
| && allocator <= ompx_gnu_max_predefined_alloc) | |
| { | |
| int index = allocator - ompx_gnu_min_predefined_alloc; | |
| return predefined_ompx_gnu_alloc_mapping[index]; | |
| } | |
| else | |
| /* This should never happen. */ | |
| return omp_default_mem_space; | |
| } | |
| enum gomp_numa_memkind_kind | |
| { | |
| GOMP_MEMKIND_NONE = 0, | |
| #define GOMP_MEMKIND_KINDS \ | |
| GOMP_MEMKIND_KIND (HBW_INTERLEAVE), \ | |
| GOMP_MEMKIND_KIND (HBW_PREFERRED), \ | |
| GOMP_MEMKIND_KIND (DAX_KMEM_ALL), \ | |
| GOMP_MEMKIND_KIND (DAX_KMEM), \ | |
| GOMP_MEMKIND_KIND (INTERLEAVE), \ | |
| GOMP_MEMKIND_KIND (DEFAULT) | |
| #define GOMP_MEMKIND_KIND(kind) GOMP_MEMKIND_##kind | |
| GOMP_MEMKIND_KINDS, | |
| #undef GOMP_MEMKIND_KIND | |
| GOMP_MEMKIND_COUNT, | |
| GOMP_MEMKIND_LIBNUMA = GOMP_MEMKIND_COUNT | |
| }; | |
| struct omp_allocator_data | |
| { | |
| omp_memspace_handle_t memspace; | |
| omp_uintptr_t alignment; | |
| omp_uintptr_t pool_size; | |
| omp_uintptr_t used_pool_size; | |
| omp_allocator_handle_t fb_data; | |
| unsigned int sync_hint : 8; | |
| unsigned int access : 8; | |
| unsigned int fallback : 8; | |
| unsigned int pinned : 1; | |
| unsigned int partition : 7; | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| unsigned int memkind : 8; | |
| #endif | |
| #ifndef HAVE_SYNC_BUILTINS | |
| gomp_mutex_t lock; | |
| #endif | |
| }; | |
| struct omp_mem_header | |
| { | |
| void *ptr; | |
| size_t size; | |
| omp_allocator_handle_t allocator; | |
| void *pad; | |
| }; | |
| struct gomp_libnuma_data | |
| { | |
| void *numa_handle; | |
| void *(*numa_alloc_local) (size_t); | |
| void *(*numa_realloc) (void *, size_t, size_t); | |
| void (*numa_free) (void *, size_t); | |
| }; | |
| struct gomp_memkind_data | |
| { | |
| void *memkind_handle; | |
| void *(*memkind_malloc) (void *, size_t); | |
| void *(*memkind_calloc) (void *, size_t, size_t); | |
| void *(*memkind_realloc) (void *, void *, size_t); | |
| void (*memkind_free) (void *, void *); | |
| int (*memkind_check_available) (void *); | |
| void **kinds[GOMP_MEMKIND_COUNT]; | |
| }; | |
| #ifdef LIBGOMP_USE_LIBNUMA | |
| static struct gomp_libnuma_data *libnuma_data; | |
| static pthread_once_t libnuma_data_once = PTHREAD_ONCE_INIT; | |
| static void | |
| gomp_init_libnuma (void) | |
| { | |
| void *handle = dlopen ("libnuma.so.1", RTLD_LAZY); | |
| struct gomp_libnuma_data *data; | |
| data = calloc (1, sizeof (struct gomp_libnuma_data)); | |
| if (data == NULL) | |
| { | |
| if (handle) | |
| dlclose (handle); | |
| return; | |
| } | |
| if (handle) | |
| { | |
| int (*numa_available) (void); | |
| numa_available | |
| = (__typeof (numa_available)) dlsym (handle, "numa_available"); | |
| if (!numa_available || numa_available () != 0) | |
| { | |
| dlclose (handle); | |
| handle = NULL; | |
| } | |
| } | |
| if (!handle) | |
| { | |
| __atomic_store_n (&libnuma_data, data, MEMMODEL_RELEASE); | |
| return; | |
| } | |
| data->numa_handle = handle; | |
| data->numa_alloc_local | |
| = (__typeof (data->numa_alloc_local)) dlsym (handle, "numa_alloc_local"); | |
| data->numa_realloc | |
| = (__typeof (data->numa_realloc)) dlsym (handle, "numa_realloc"); | |
| data->numa_free | |
| = (__typeof (data->numa_free)) dlsym (handle, "numa_free"); | |
| __atomic_store_n (&libnuma_data, data, MEMMODEL_RELEASE); | |
| } | |
| static struct gomp_libnuma_data * | |
| gomp_get_libnuma (void) | |
| { | |
| struct gomp_libnuma_data *data | |
| = __atomic_load_n (&libnuma_data, MEMMODEL_ACQUIRE); | |
| if (data) | |
| return data; | |
| pthread_once (&libnuma_data_once, gomp_init_libnuma); | |
| return __atomic_load_n (&libnuma_data, MEMMODEL_ACQUIRE); | |
| } | |
| #endif | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| static struct gomp_memkind_data *memkind_data; | |
| static pthread_once_t memkind_data_once = PTHREAD_ONCE_INIT; | |
| static void | |
| gomp_init_memkind (void) | |
| { | |
| void *handle = dlopen ("libmemkind.so.0", RTLD_LAZY); | |
| struct gomp_memkind_data *data; | |
| int i; | |
| static const char *kinds[] = { | |
| NULL, | |
| #define GOMP_MEMKIND_KIND(kind) "MEMKIND_" #kind | |
| GOMP_MEMKIND_KINDS | |
| #undef GOMP_MEMKIND_KIND | |
| }; | |
| data = calloc (1, sizeof (struct gomp_memkind_data)); | |
| if (data == NULL) | |
| { | |
| if (handle) | |
| dlclose (handle); | |
| return; | |
| } | |
| if (!handle) | |
| { | |
| __atomic_store_n (&memkind_data, data, MEMMODEL_RELEASE); | |
| return; | |
| } | |
| data->memkind_handle = handle; | |
| data->memkind_malloc | |
| = (__typeof (data->memkind_malloc)) dlsym (handle, "memkind_malloc"); | |
| data->memkind_calloc | |
| = (__typeof (data->memkind_calloc)) dlsym (handle, "memkind_calloc"); | |
| data->memkind_realloc | |
| = (__typeof (data->memkind_realloc)) dlsym (handle, "memkind_realloc"); | |
| data->memkind_free | |
| = (__typeof (data->memkind_free)) dlsym (handle, "memkind_free"); | |
| data->memkind_check_available | |
| = (__typeof (data->memkind_check_available)) | |
| dlsym (handle, "memkind_check_available"); | |
| if (data->memkind_malloc | |
| && data->memkind_calloc | |
| && data->memkind_realloc | |
| && data->memkind_free | |
| && data->memkind_check_available) | |
| for (i = 1; i < GOMP_MEMKIND_COUNT; ++i) | |
| { | |
| data->kinds[i] = (void **) dlsym (handle, kinds[i]); | |
| if (data->kinds[i] && data->memkind_check_available (*data->kinds[i])) | |
| data->kinds[i] = NULL; | |
| } | |
| __atomic_store_n (&memkind_data, data, MEMMODEL_RELEASE); | |
| } | |
| static struct gomp_memkind_data * | |
| gomp_get_memkind (void) | |
| { | |
| struct gomp_memkind_data *data | |
| = __atomic_load_n (&memkind_data, MEMMODEL_ACQUIRE); | |
| if (data) | |
| return data; | |
| pthread_once (&memkind_data_once, gomp_init_memkind); | |
| return __atomic_load_n (&memkind_data, MEMMODEL_ACQUIRE); | |
| } | |
| #endif | |
| omp_allocator_handle_t | |
| omp_init_allocator (omp_memspace_handle_t memspace, int ntraits, | |
| const omp_alloctrait_t traits[]) | |
| { | |
| struct omp_allocator_data data | |
| = { memspace, 1, ~(uintptr_t) 0, 0, 0, omp_atv_contended, omp_atv_all, | |
| omp_atv_default_mem_fb, omp_atv_false, omp_atv_environment, | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| GOMP_MEMKIND_NONE | |
| #endif | |
| }; | |
| struct omp_allocator_data *ret; | |
| int i; | |
| if (memspace > omp_low_lat_mem_space) | |
| return omp_null_allocator; | |
| for (i = 0; i < ntraits; i++) | |
| switch (traits[i].key) | |
| { | |
| case omp_atk_sync_hint: | |
| switch (traits[i].value) | |
| { | |
| case omp_atv_default: | |
| data.sync_hint = omp_atv_contended; | |
| break; | |
| case omp_atv_contended: | |
| case omp_atv_uncontended: | |
| case omp_atv_serialized: | |
| case omp_atv_private: | |
| data.sync_hint = traits[i].value; | |
| break; | |
| default: | |
| return omp_null_allocator; | |
| } | |
| break; | |
| case omp_atk_alignment: | |
| if (traits[i].value == omp_atv_default) | |
| { | |
| data.alignment = 1; | |
| break; | |
| } | |
| if ((traits[i].value & (traits[i].value - 1)) != 0 | |
| || !traits[i].value) | |
| return omp_null_allocator; | |
| data.alignment = traits[i].value; | |
| break; | |
| case omp_atk_access: | |
| switch (traits[i].value) | |
| { | |
| case omp_atv_default: | |
| data.access = omp_atv_all; | |
| break; | |
| case omp_atv_all: | |
| case omp_atv_cgroup: | |
| case omp_atv_pteam: | |
| case omp_atv_thread: | |
| data.access = traits[i].value; | |
| break; | |
| default: | |
| return omp_null_allocator; | |
| } | |
| break; | |
| case omp_atk_pool_size: | |
| if (traits[i].value == omp_atv_default) | |
| data.pool_size = ~(uintptr_t) 0; | |
| else | |
| data.pool_size = traits[i].value; | |
| break; | |
| case omp_atk_fallback: | |
| switch (traits[i].value) | |
| { | |
| case omp_atv_default: | |
| data.fallback = omp_atv_default_mem_fb; | |
| break; | |
| case omp_atv_default_mem_fb: | |
| case omp_atv_null_fb: | |
| case omp_atv_abort_fb: | |
| case omp_atv_allocator_fb: | |
| data.fallback = traits[i].value; | |
| break; | |
| default: | |
| return omp_null_allocator; | |
| } | |
| break; | |
| case omp_atk_fb_data: | |
| data.fb_data = traits[i].value; | |
| break; | |
| case omp_atk_pinned: | |
| switch (traits[i].value) | |
| { | |
| case omp_atv_default: | |
| case omp_atv_false: | |
| data.pinned = omp_atv_false; | |
| break; | |
| case omp_atv_true: | |
| data.pinned = omp_atv_true; | |
| break; | |
| default: | |
| return omp_null_allocator; | |
| } | |
| break; | |
| case omp_atk_partition: | |
| switch (traits[i].value) | |
| { | |
| case omp_atv_default: | |
| data.partition = omp_atv_environment; | |
| break; | |
| case omp_atv_environment: | |
| case omp_atv_nearest: | |
| case omp_atv_blocked: | |
| case omp_atv_interleaved: | |
| data.partition = traits[i].value; | |
| break; | |
| default: | |
| return omp_null_allocator; | |
| } | |
| break; | |
| default: | |
| return omp_null_allocator; | |
| } | |
| if (data.alignment < sizeof (void *)) | |
| data.alignment = sizeof (void *); | |
| switch (memspace) | |
| { | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| case omp_high_bw_mem_space: | |
| struct gomp_memkind_data *memkind_data; | |
| memkind_data = gomp_get_memkind (); | |
| if (data.partition == omp_atv_interleaved | |
| && memkind_data->kinds[GOMP_MEMKIND_HBW_INTERLEAVE]) | |
| { | |
| data.memkind = GOMP_MEMKIND_HBW_INTERLEAVE; | |
| break; | |
| } | |
| else if (memkind_data->kinds[GOMP_MEMKIND_HBW_PREFERRED]) | |
| { | |
| data.memkind = GOMP_MEMKIND_HBW_PREFERRED; | |
| break; | |
| } | |
| break; | |
| case omp_large_cap_mem_space: | |
| memkind_data = gomp_get_memkind (); | |
| if (memkind_data->kinds[GOMP_MEMKIND_DAX_KMEM_ALL]) | |
| data.memkind = GOMP_MEMKIND_DAX_KMEM_ALL; | |
| else if (memkind_data->kinds[GOMP_MEMKIND_DAX_KMEM]) | |
| data.memkind = GOMP_MEMKIND_DAX_KMEM; | |
| break; | |
| #endif | |
| default: | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| if (data.partition == omp_atv_interleaved) | |
| { | |
| memkind_data = gomp_get_memkind (); | |
| if (memkind_data->kinds[GOMP_MEMKIND_INTERLEAVE]) | |
| data.memkind = GOMP_MEMKIND_INTERLEAVE; | |
| } | |
| #endif | |
| break; | |
| } | |
| #ifdef LIBGOMP_USE_LIBNUMA | |
| if (data.memkind == GOMP_MEMKIND_NONE && data.partition == omp_atv_nearest) | |
| { | |
| libnuma_data = gomp_get_libnuma (); | |
| if (libnuma_data->numa_alloc_local != NULL) | |
| data.memkind = GOMP_MEMKIND_LIBNUMA; | |
| } | |
| #endif | |
| /* Reject unsupported memory spaces. */ | |
| if (!MEMSPACE_VALIDATE (data.memspace, data.access, data.pinned)) | |
| return omp_null_allocator; | |
| ret = gomp_malloc (sizeof (struct omp_allocator_data)); | |
| *ret = data; | |
| #ifndef HAVE_SYNC_BUILTINS | |
| gomp_mutex_init (&ret->lock); | |
| #endif | |
| return (omp_allocator_handle_t) ret; | |
| } | |
| void | |
| omp_destroy_allocator (omp_allocator_handle_t allocator) | |
| { | |
| if (allocator != omp_null_allocator) | |
| { | |
| #ifndef HAVE_SYNC_BUILTINS | |
| gomp_mutex_destroy (&((struct omp_allocator_data *) allocator)->lock); | |
| #endif | |
| free ((void *) allocator); | |
| } | |
| } | |
| ialias (omp_init_allocator) | |
| ialias (omp_destroy_allocator) | |
| void * | |
| omp_aligned_alloc (size_t alignment, size_t size, | |
| omp_allocator_handle_t allocator) | |
| { | |
| struct omp_allocator_data *allocator_data; | |
| size_t new_size, new_alignment; | |
| void *ptr, *ret; | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| enum gomp_numa_memkind_kind memkind; | |
| #endif | |
| if (__builtin_expect (size == 0, 0)) | |
| return NULL; | |
| retry: | |
| new_alignment = alignment; | |
| if (allocator == omp_null_allocator) | |
| { | |
| struct gomp_thread *thr = gomp_thread (); | |
| if (thr->ts.def_allocator == omp_null_allocator) | |
| thr->ts.def_allocator = gomp_def_allocator; | |
| allocator = (omp_allocator_handle_t) thr->ts.def_allocator; | |
| } | |
| if (!predefined_allocator_p (allocator)) | |
| { | |
| allocator_data = (struct omp_allocator_data *) allocator; | |
| if (new_alignment < allocator_data->alignment) | |
| new_alignment = allocator_data->alignment; | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| memkind = allocator_data->memkind; | |
| #endif | |
| } | |
| else | |
| { | |
| allocator_data = NULL; | |
| if (new_alignment < sizeof (void *)) | |
| new_alignment = sizeof (void *); | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| memkind = GOMP_MEMKIND_NONE; | |
| #endif | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| if (allocator == omp_high_bw_mem_alloc) | |
| memkind = GOMP_MEMKIND_HBW_PREFERRED; | |
| else if (allocator == omp_large_cap_mem_alloc) | |
| memkind = GOMP_MEMKIND_DAX_KMEM_ALL; | |
| if (memkind) | |
| { | |
| struct gomp_memkind_data *memkind_data = gomp_get_memkind (); | |
| if (!memkind_data->kinds[memkind]) | |
| memkind = GOMP_MEMKIND_NONE; | |
| } | |
| #endif | |
| } | |
| new_size = sizeof (struct omp_mem_header); | |
| if (new_alignment > sizeof (void *)) | |
| new_size += new_alignment - sizeof (void *); | |
| if (__builtin_add_overflow (size, new_size, &new_size)) | |
| goto fail; | |
| #ifdef OMP_LOW_LAT_MEM_ALLOC_INVALID | |
| if (allocator == omp_low_lat_mem_alloc) | |
| goto fail; | |
| #endif | |
| if (__builtin_expect (allocator_data | |
| && allocator_data->pool_size < ~(uintptr_t) 0, 0)) | |
| { | |
| uintptr_t used_pool_size; | |
| if (new_size > allocator_data->pool_size) | |
| goto fail; | |
| #ifdef HAVE_SYNC_BUILTINS | |
| used_pool_size = __atomic_load_n (&allocator_data->used_pool_size, | |
| MEMMODEL_RELAXED); | |
| do | |
| { | |
| uintptr_t new_pool_size; | |
| if (__builtin_add_overflow (used_pool_size, new_size, | |
| &new_pool_size) | |
| || new_pool_size > allocator_data->pool_size) | |
| goto fail; | |
| if (__atomic_compare_exchange_n (&allocator_data->used_pool_size, | |
| &used_pool_size, new_pool_size, | |
| true, MEMMODEL_RELAXED, | |
| MEMMODEL_RELAXED)) | |
| break; | |
| } | |
| while (1); | |
| #else | |
| gomp_mutex_lock (&allocator_data->lock); | |
| if (__builtin_add_overflow (allocator_data->used_pool_size, new_size, | |
| &used_pool_size) | |
| || used_pool_size > allocator_data->pool_size) | |
| { | |
| gomp_mutex_unlock (&allocator_data->lock); | |
| goto fail; | |
| } | |
| allocator_data->used_pool_size = used_pool_size; | |
| gomp_mutex_unlock (&allocator_data->lock); | |
| #endif | |
| #ifdef LIBGOMP_USE_LIBNUMA | |
| if (memkind == GOMP_MEMKIND_LIBNUMA) | |
| ptr = libnuma_data->numa_alloc_local (new_size); | |
| # ifdef LIBGOMP_USE_MEMKIND | |
| else | |
| # endif | |
| #endif | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| if (memkind) | |
| { | |
| struct gomp_memkind_data *memkind_data = gomp_get_memkind (); | |
| void *kind = *memkind_data->kinds[memkind]; | |
| ptr = memkind_data->memkind_malloc (kind, new_size); | |
| } | |
| else | |
| #endif | |
| ptr = MEMSPACE_ALLOC (allocator_data->memspace, new_size, | |
| allocator_data->pinned); | |
| if (ptr == NULL) | |
| { | |
| #ifdef HAVE_SYNC_BUILTINS | |
| __atomic_add_fetch (&allocator_data->used_pool_size, -new_size, | |
| MEMMODEL_RELAXED); | |
| #else | |
| gomp_mutex_lock (&allocator_data->lock); | |
| allocator_data->used_pool_size -= new_size; | |
| gomp_mutex_unlock (&allocator_data->lock); | |
| #endif | |
| goto fail; | |
| } | |
| } | |
| else | |
| { | |
| #ifdef LIBGOMP_USE_LIBNUMA | |
| if (memkind == GOMP_MEMKIND_LIBNUMA) | |
| ptr = libnuma_data->numa_alloc_local (new_size); | |
| # ifdef LIBGOMP_USE_MEMKIND | |
| else | |
| # endif | |
| #endif | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| if (memkind) | |
| { | |
| struct gomp_memkind_data *memkind_data = gomp_get_memkind (); | |
| void *kind = *memkind_data->kinds[memkind]; | |
| ptr = memkind_data->memkind_malloc (kind, new_size); | |
| } | |
| else | |
| #endif | |
| { | |
| omp_memspace_handle_t memspace; | |
| memspace = (allocator_data | |
| ? allocator_data->memspace | |
| : predefined_alloc_mapping (allocator)); | |
| int pinned = (allocator_data | |
| ? allocator_data->pinned | |
| : allocator == ompx_gnu_pinned_mem_alloc); | |
| ptr = MEMSPACE_ALLOC (memspace, new_size, pinned); | |
| } | |
| if (ptr == NULL) | |
| goto fail; | |
| } | |
| if (new_alignment > sizeof (void *)) | |
| ret = (void *) (((uintptr_t) ptr | |
| + sizeof (struct omp_mem_header) | |
| + new_alignment - sizeof (void *)) | |
| & ~(new_alignment - 1)); | |
| else | |
| ret = (char *) ptr + sizeof (struct omp_mem_header); | |
| ((struct omp_mem_header *) ret)[-1].ptr = ptr; | |
| ((struct omp_mem_header *) ret)[-1].size = new_size; | |
| ((struct omp_mem_header *) ret)[-1].allocator = allocator; | |
| return ret; | |
| fail:; | |
| int fallback = (allocator_data | |
| ? allocator_data->fallback | |
| : (allocator == omp_default_mem_alloc | |
| || allocator == ompx_gnu_pinned_mem_alloc) | |
| ? omp_atv_null_fb | |
| : omp_atv_default_mem_fb); | |
| switch (fallback) | |
| { | |
| case omp_atv_default_mem_fb: | |
| allocator = omp_default_mem_alloc; | |
| goto retry; | |
| case omp_atv_null_fb: | |
| break; | |
| default: | |
| case omp_atv_abort_fb: | |
| gomp_fatal ("Out of memory allocating %lu bytes", | |
| (unsigned long) size); | |
| case omp_atv_allocator_fb: | |
| allocator = allocator_data->fb_data; | |
| goto retry; | |
| } | |
| return NULL; | |
| } | |
| ialias (omp_aligned_alloc) | |
| void * | |
| omp_alloc (size_t size, omp_allocator_handle_t allocator) | |
| { | |
| return ialias_call (omp_aligned_alloc) (1, size, allocator); | |
| } | |
| /* Like omp_aligned_alloc, but apply on top of that: | |
| "For allocations that arise from this ... the null_fb value of the | |
| fallback allocator trait behaves as if the abort_fb had been specified." */ | |
| void * | |
| GOMP_alloc (size_t alignment, size_t size, uintptr_t allocator) | |
| { | |
| void *ret | |
| = ialias_call (omp_aligned_alloc) (alignment, size, | |
| (omp_allocator_handle_t) allocator); | |
| if (__builtin_expect (ret == NULL, 0) && size) | |
| gomp_fatal ("Out of memory allocating %lu bytes", | |
| (unsigned long) size); | |
| return ret; | |
| } | |
| void | |
| omp_free (void *ptr, omp_allocator_handle_t allocator) | |
| { | |
| struct omp_mem_header *data; | |
| omp_memspace_handle_t memspace = omp_default_mem_space; | |
| int pinned = false; | |
| if (ptr == NULL) | |
| return; | |
| (void) allocator; | |
| data = &((struct omp_mem_header *) ptr)[-1]; | |
| if (!predefined_allocator_p (data->allocator)) | |
| { | |
| struct omp_allocator_data *allocator_data | |
| = (struct omp_allocator_data *) (data->allocator); | |
| if (allocator_data->pool_size < ~(uintptr_t) 0) | |
| { | |
| #ifdef HAVE_SYNC_BUILTINS | |
| __atomic_add_fetch (&allocator_data->used_pool_size, -data->size, | |
| MEMMODEL_RELAXED); | |
| #else | |
| gomp_mutex_lock (&allocator_data->lock); | |
| allocator_data->used_pool_size -= data->size; | |
| gomp_mutex_unlock (&allocator_data->lock); | |
| #endif | |
| } | |
| #ifdef LIBGOMP_USE_LIBNUMA | |
| if (allocator_data->memkind == GOMP_MEMKIND_LIBNUMA) | |
| { | |
| libnuma_data->numa_free (data->ptr, data->size); | |
| return; | |
| } | |
| # ifdef LIBGOMP_USE_MEMKIND | |
| else | |
| # endif | |
| #endif | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| if (allocator_data->memkind) | |
| { | |
| struct gomp_memkind_data *memkind_data = gomp_get_memkind (); | |
| void *kind = *memkind_data->kinds[allocator_data->memkind]; | |
| memkind_data->memkind_free (kind, data->ptr); | |
| return; | |
| } | |
| #endif | |
| memspace = allocator_data->memspace; | |
| pinned = allocator_data->pinned; | |
| } | |
| else | |
| { | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| enum gomp_numa_memkind_kind memkind = GOMP_MEMKIND_NONE; | |
| if (data->allocator == omp_high_bw_mem_alloc) | |
| memkind = GOMP_MEMKIND_HBW_PREFERRED; | |
| else if (data->allocator == omp_large_cap_mem_alloc) | |
| memkind = GOMP_MEMKIND_DAX_KMEM_ALL; | |
| if (memkind) | |
| { | |
| struct gomp_memkind_data *memkind_data = gomp_get_memkind (); | |
| if (memkind_data->kinds[memkind]) | |
| { | |
| void *kind = *memkind_data->kinds[memkind]; | |
| memkind_data->memkind_free (kind, data->ptr); | |
| return; | |
| } | |
| } | |
| #endif | |
| memspace = predefined_alloc_mapping (data->allocator); | |
| pinned = (data->allocator == ompx_gnu_pinned_mem_alloc); | |
| } | |
| MEMSPACE_FREE (memspace, data->ptr, data->size, pinned); | |
| } | |
| ialias (omp_free) | |
| void | |
| GOMP_free (void *ptr, uintptr_t allocator) | |
| { | |
| return ialias_call (omp_free) (ptr, (omp_allocator_handle_t) allocator); | |
| } | |
| void * | |
| omp_aligned_calloc (size_t alignment, size_t nmemb, size_t size, | |
| omp_allocator_handle_t allocator) | |
| { | |
| struct omp_allocator_data *allocator_data; | |
| size_t new_size, size_temp, new_alignment; | |
| void *ptr, *ret; | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| enum gomp_numa_memkind_kind memkind; | |
| #endif | |
| if (__builtin_expect (size == 0 || nmemb == 0, 0)) | |
| return NULL; | |
| retry: | |
| new_alignment = alignment; | |
| if (allocator == omp_null_allocator) | |
| { | |
| struct gomp_thread *thr = gomp_thread (); | |
| if (thr->ts.def_allocator == omp_null_allocator) | |
| thr->ts.def_allocator = gomp_def_allocator; | |
| allocator = (omp_allocator_handle_t) thr->ts.def_allocator; | |
| } | |
| if (!predefined_allocator_p (allocator)) | |
| { | |
| allocator_data = (struct omp_allocator_data *) allocator; | |
| if (new_alignment < allocator_data->alignment) | |
| new_alignment = allocator_data->alignment; | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| memkind = allocator_data->memkind; | |
| #endif | |
| } | |
| else | |
| { | |
| allocator_data = NULL; | |
| if (new_alignment < sizeof (void *)) | |
| new_alignment = sizeof (void *); | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| memkind = GOMP_MEMKIND_NONE; | |
| #endif | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| if (allocator == omp_high_bw_mem_alloc) | |
| memkind = GOMP_MEMKIND_HBW_PREFERRED; | |
| else if (allocator == omp_large_cap_mem_alloc) | |
| memkind = GOMP_MEMKIND_DAX_KMEM_ALL; | |
| if (memkind) | |
| { | |
| struct gomp_memkind_data *memkind_data = gomp_get_memkind (); | |
| if (!memkind_data->kinds[memkind]) | |
| memkind = GOMP_MEMKIND_NONE; | |
| } | |
| #endif | |
| } | |
| new_size = sizeof (struct omp_mem_header); | |
| if (new_alignment > sizeof (void *)) | |
| new_size += new_alignment - sizeof (void *); | |
| if (__builtin_mul_overflow (size, nmemb, &size_temp)) | |
| goto fail; | |
| if (__builtin_add_overflow (size_temp, new_size, &new_size)) | |
| goto fail; | |
| #ifdef OMP_LOW_LAT_MEM_ALLOC_INVALID | |
| if (allocator == omp_low_lat_mem_alloc) | |
| goto fail; | |
| #endif | |
| if (__builtin_expect (allocator_data | |
| && allocator_data->pool_size < ~(uintptr_t) 0, 0)) | |
| { | |
| uintptr_t used_pool_size; | |
| if (new_size > allocator_data->pool_size) | |
| goto fail; | |
| #ifdef HAVE_SYNC_BUILTINS | |
| used_pool_size = __atomic_load_n (&allocator_data->used_pool_size, | |
| MEMMODEL_RELAXED); | |
| do | |
| { | |
| uintptr_t new_pool_size; | |
| if (__builtin_add_overflow (used_pool_size, new_size, | |
| &new_pool_size) | |
| || new_pool_size > allocator_data->pool_size) | |
| goto fail; | |
| if (__atomic_compare_exchange_n (&allocator_data->used_pool_size, | |
| &used_pool_size, new_pool_size, | |
| true, MEMMODEL_RELAXED, | |
| MEMMODEL_RELAXED)) | |
| break; | |
| } | |
| while (1); | |
| #else | |
| gomp_mutex_lock (&allocator_data->lock); | |
| if (__builtin_add_overflow (allocator_data->used_pool_size, new_size, | |
| &used_pool_size) | |
| || used_pool_size > allocator_data->pool_size) | |
| { | |
| gomp_mutex_unlock (&allocator_data->lock); | |
| goto fail; | |
| } | |
| allocator_data->used_pool_size = used_pool_size; | |
| gomp_mutex_unlock (&allocator_data->lock); | |
| #endif | |
| #ifdef LIBGOMP_USE_LIBNUMA | |
| if (memkind == GOMP_MEMKIND_LIBNUMA) | |
| /* numa_alloc_local uses mmap with MAP_ANONYMOUS, returning | |
| memory that is initialized to zero. */ | |
| ptr = libnuma_data->numa_alloc_local (new_size); | |
| # ifdef LIBGOMP_USE_MEMKIND | |
| else | |
| # endif | |
| #endif | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| if (memkind) | |
| { | |
| struct gomp_memkind_data *memkind_data = gomp_get_memkind (); | |
| void *kind = *memkind_data->kinds[memkind]; | |
| ptr = memkind_data->memkind_calloc (kind, 1, new_size); | |
| } | |
| else | |
| #endif | |
| ptr = MEMSPACE_CALLOC (allocator_data->memspace, new_size, | |
| allocator_data->pinned); | |
| if (ptr == NULL) | |
| { | |
| #ifdef HAVE_SYNC_BUILTINS | |
| __atomic_add_fetch (&allocator_data->used_pool_size, -new_size, | |
| MEMMODEL_RELAXED); | |
| #else | |
| gomp_mutex_lock (&allocator_data->lock); | |
| allocator_data->used_pool_size -= new_size; | |
| gomp_mutex_unlock (&allocator_data->lock); | |
| #endif | |
| goto fail; | |
| } | |
| } | |
| else | |
| { | |
| #ifdef LIBGOMP_USE_LIBNUMA | |
| if (memkind == GOMP_MEMKIND_LIBNUMA) | |
| /* numa_alloc_local uses mmap with MAP_ANONYMOUS, returning | |
| memory that is initialized to zero. */ | |
| ptr = libnuma_data->numa_alloc_local (new_size); | |
| # ifdef LIBGOMP_USE_MEMKIND | |
| else | |
| # endif | |
| #endif | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| if (memkind) | |
| { | |
| struct gomp_memkind_data *memkind_data = gomp_get_memkind (); | |
| void *kind = *memkind_data->kinds[memkind]; | |
| ptr = memkind_data->memkind_calloc (kind, 1, new_size); | |
| } | |
| else | |
| #endif | |
| { | |
| omp_memspace_handle_t memspace; | |
| memspace = (allocator_data | |
| ? allocator_data->memspace | |
| : predefined_alloc_mapping (allocator)); | |
| int pinned = (allocator_data | |
| ? allocator_data->pinned | |
| : allocator == ompx_gnu_pinned_mem_alloc); | |
| ptr = MEMSPACE_CALLOC (memspace, new_size, pinned); | |
| } | |
| if (ptr == NULL) | |
| goto fail; | |
| } | |
| if (new_alignment > sizeof (void *)) | |
| ret = (void *) (((uintptr_t) ptr | |
| + sizeof (struct omp_mem_header) | |
| + new_alignment - sizeof (void *)) | |
| & ~(new_alignment - 1)); | |
| else | |
| ret = (char *) ptr + sizeof (struct omp_mem_header); | |
| ((struct omp_mem_header *) ret)[-1].ptr = ptr; | |
| ((struct omp_mem_header *) ret)[-1].size = new_size; | |
| ((struct omp_mem_header *) ret)[-1].allocator = allocator; | |
| return ret; | |
| fail:; | |
| int fallback = (allocator_data | |
| ? allocator_data->fallback | |
| : (allocator == omp_default_mem_alloc | |
| || allocator == ompx_gnu_pinned_mem_alloc) | |
| ? omp_atv_null_fb | |
| : omp_atv_default_mem_fb); | |
| switch (fallback) | |
| { | |
| case omp_atv_default_mem_fb: | |
| allocator = omp_default_mem_alloc; | |
| goto retry; | |
| case omp_atv_null_fb: | |
| break; | |
| default: | |
| case omp_atv_abort_fb: | |
| gomp_fatal ("Out of memory allocating %lu bytes", | |
| (unsigned long) (size * nmemb)); | |
| case omp_atv_allocator_fb: | |
| allocator = allocator_data->fb_data; | |
| goto retry; | |
| } | |
| return NULL; | |
| } | |
| ialias (omp_aligned_calloc) | |
| void * | |
| omp_calloc (size_t nmemb, size_t size, omp_allocator_handle_t allocator) | |
| { | |
| return ialias_call (omp_aligned_calloc) (1, nmemb, size, allocator); | |
| } | |
| void * | |
| omp_realloc (void *ptr, size_t size, omp_allocator_handle_t allocator, | |
| omp_allocator_handle_t free_allocator) | |
| { | |
| struct omp_allocator_data *allocator_data, *free_allocator_data; | |
| size_t new_size, old_size, new_alignment, old_alignment; | |
| void *new_ptr, *ret; | |
| struct omp_mem_header *data; | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| enum gomp_numa_memkind_kind memkind, free_memkind; | |
| #endif | |
| if (__builtin_expect (ptr == NULL, 0)) | |
| return ialias_call (omp_aligned_alloc) (1, size, allocator); | |
| if (__builtin_expect (size == 0, 0)) | |
| { | |
| ialias_call (omp_free) (ptr, free_allocator); | |
| return NULL; | |
| } | |
| data = &((struct omp_mem_header *) ptr)[-1]; | |
| free_allocator = data->allocator; | |
| retry: | |
| new_alignment = sizeof (void *); | |
| if (allocator == omp_null_allocator) | |
| allocator = free_allocator; | |
| if (!predefined_allocator_p (allocator)) | |
| { | |
| allocator_data = (struct omp_allocator_data *) allocator; | |
| if (new_alignment < allocator_data->alignment) | |
| new_alignment = allocator_data->alignment; | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| memkind = allocator_data->memkind; | |
| #endif | |
| } | |
| else | |
| { | |
| allocator_data = NULL; | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| memkind = GOMP_MEMKIND_NONE; | |
| #endif | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| if (allocator == omp_high_bw_mem_alloc) | |
| memkind = GOMP_MEMKIND_HBW_PREFERRED; | |
| else if (allocator == omp_large_cap_mem_alloc) | |
| memkind = GOMP_MEMKIND_DAX_KMEM_ALL; | |
| if (memkind) | |
| { | |
| struct gomp_memkind_data *memkind_data = gomp_get_memkind (); | |
| if (!memkind_data->kinds[memkind]) | |
| memkind = GOMP_MEMKIND_NONE; | |
| } | |
| #endif | |
| } | |
| if (!predefined_allocator_p (free_allocator)) | |
| { | |
| free_allocator_data = (struct omp_allocator_data *) free_allocator; | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| free_memkind = free_allocator_data->memkind; | |
| #endif | |
| } | |
| else | |
| { | |
| free_allocator_data = NULL; | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| free_memkind = GOMP_MEMKIND_NONE; | |
| #endif | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| if (free_allocator == omp_high_bw_mem_alloc) | |
| free_memkind = GOMP_MEMKIND_HBW_PREFERRED; | |
| else if (free_allocator == omp_large_cap_mem_alloc) | |
| free_memkind = GOMP_MEMKIND_DAX_KMEM_ALL; | |
| if (free_memkind) | |
| { | |
| struct gomp_memkind_data *memkind_data = gomp_get_memkind (); | |
| if (!memkind_data->kinds[free_memkind]) | |
| free_memkind = GOMP_MEMKIND_NONE; | |
| } | |
| #endif | |
| } | |
| old_alignment = (uintptr_t) ptr - (uintptr_t) (data->ptr); | |
| new_size = sizeof (struct omp_mem_header); | |
| if (new_alignment > sizeof (void *)) | |
| new_size += new_alignment - sizeof (void *); | |
| if (__builtin_add_overflow (size, new_size, &new_size)) | |
| goto fail; | |
| old_size = data->size; | |
| #ifdef OMP_LOW_LAT_MEM_ALLOC_INVALID | |
| if (allocator == omp_low_lat_mem_alloc) | |
| goto fail; | |
| #endif | |
| if (__builtin_expect (allocator_data | |
| && allocator_data->pool_size < ~(uintptr_t) 0, 0)) | |
| { | |
| uintptr_t used_pool_size; | |
| size_t prev_size = 0; | |
| /* Check if we can use realloc. Don't use it if extra alignment | |
| was used previously or newly, because realloc might return a pointer | |
| with different alignment and then we'd need to memmove the data | |
| again. */ | |
| if (free_allocator_data | |
| && free_allocator_data == allocator_data | |
| && new_alignment == sizeof (void *) | |
| && old_alignment == sizeof (struct omp_mem_header)) | |
| prev_size = old_size; | |
| if (new_size > prev_size | |
| && new_size - prev_size > allocator_data->pool_size) | |
| goto fail; | |
| #ifdef HAVE_SYNC_BUILTINS | |
| used_pool_size = __atomic_load_n (&allocator_data->used_pool_size, | |
| MEMMODEL_RELAXED); | |
| do | |
| { | |
| uintptr_t new_pool_size; | |
| if (new_size > prev_size) | |
| { | |
| if (__builtin_add_overflow (used_pool_size, new_size - prev_size, | |
| &new_pool_size) | |
| || new_pool_size > allocator_data->pool_size) | |
| goto fail; | |
| } | |
| else | |
| new_pool_size = used_pool_size + new_size - prev_size; | |
| if (__atomic_compare_exchange_n (&allocator_data->used_pool_size, | |
| &used_pool_size, new_pool_size, | |
| true, MEMMODEL_RELAXED, | |
| MEMMODEL_RELAXED)) | |
| break; | |
| } | |
| while (1); | |
| #else | |
| gomp_mutex_lock (&allocator_data->lock); | |
| if (new_size > prev_size) | |
| { | |
| if (__builtin_add_overflow (allocator_data->used_pool_size, | |
| new_size - prev_size, | |
| &used_pool_size) | |
| || used_pool_size > allocator_data->pool_size) | |
| { | |
| gomp_mutex_unlock (&allocator_data->lock); | |
| goto fail; | |
| } | |
| } | |
| else | |
| used_pool_size = (allocator_data->used_pool_size | |
| + new_size - prev_size); | |
| allocator_data->used_pool_size = used_pool_size; | |
| gomp_mutex_unlock (&allocator_data->lock); | |
| #endif | |
| #ifdef LIBGOMP_USE_LIBNUMA | |
| if (memkind == GOMP_MEMKIND_LIBNUMA) | |
| { | |
| if (prev_size) | |
| new_ptr = libnuma_data->numa_realloc (data->ptr, data->size, | |
| new_size); | |
| else | |
| new_ptr = libnuma_data->numa_alloc_local (new_size); | |
| } | |
| # ifdef LIBGOMP_USE_MEMKIND | |
| else | |
| # endif | |
| #endif | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| if (memkind) | |
| { | |
| struct gomp_memkind_data *memkind_data = gomp_get_memkind (); | |
| void *kind = *memkind_data->kinds[memkind]; | |
| if (prev_size) | |
| new_ptr = memkind_data->memkind_realloc (kind, data->ptr, | |
| new_size); | |
| else | |
| new_ptr = memkind_data->memkind_malloc (kind, new_size); | |
| } | |
| else | |
| #endif | |
| if (prev_size) | |
| { | |
| int was_pinned = (free_allocator_data | |
| ? free_allocator_data->pinned | |
| : free_allocator == ompx_gnu_pinned_mem_alloc); | |
| new_ptr = MEMSPACE_REALLOC (allocator_data->memspace, data->ptr, | |
| data->size, new_size, was_pinned, | |
| allocator_data->pinned); | |
| } | |
| else | |
| new_ptr = MEMSPACE_ALLOC (allocator_data->memspace, new_size, | |
| allocator_data->pinned); | |
| if (new_ptr == NULL) | |
| { | |
| #ifdef HAVE_SYNC_BUILTINS | |
| __atomic_add_fetch (&allocator_data->used_pool_size, | |
| prev_size - new_size, | |
| MEMMODEL_RELAXED); | |
| #else | |
| gomp_mutex_lock (&allocator_data->lock); | |
| allocator_data->used_pool_size -= new_size - prev_size; | |
| gomp_mutex_unlock (&allocator_data->lock); | |
| #endif | |
| goto fail; | |
| } | |
| else if (prev_size) | |
| { | |
| ret = (char *) new_ptr + sizeof (struct omp_mem_header); | |
| ((struct omp_mem_header *) ret)[-1].ptr = new_ptr; | |
| ((struct omp_mem_header *) ret)[-1].size = new_size; | |
| ((struct omp_mem_header *) ret)[-1].allocator = allocator; | |
| return ret; | |
| } | |
| } | |
| else if (new_alignment == sizeof (void *) | |
| && old_alignment == sizeof (struct omp_mem_header) | |
| #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) | |
| && memkind == free_memkind | |
| #endif | |
| && (free_allocator_data == NULL | |
| || free_allocator_data->pool_size == ~(uintptr_t) 0)) | |
| { | |
| #ifdef LIBGOMP_USE_LIBNUMA | |
| if (memkind == GOMP_MEMKIND_LIBNUMA) | |
| new_ptr = libnuma_data->numa_realloc (data->ptr, data->size, new_size); | |
| # ifdef LIBGOMP_USE_MEMKIND | |
| else | |
| # endif | |
| #endif | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| if (memkind) | |
| { | |
| struct gomp_memkind_data *memkind_data = gomp_get_memkind (); | |
| void *kind = *memkind_data->kinds[memkind]; | |
| new_ptr = memkind_data->memkind_realloc (kind, data->ptr, | |
| new_size); | |
| } | |
| else | |
| #endif | |
| { | |
| omp_memspace_handle_t memspace; | |
| memspace = (allocator_data | |
| ? allocator_data->memspace | |
| : predefined_alloc_mapping (allocator)); | |
| int was_pinned = (free_allocator_data | |
| ? free_allocator_data->pinned | |
| : free_allocator == ompx_gnu_pinned_mem_alloc); | |
| int pinned = (allocator_data | |
| ? allocator_data->pinned | |
| : allocator == ompx_gnu_pinned_mem_alloc); | |
| new_ptr = MEMSPACE_REALLOC (memspace, data->ptr, data->size, new_size, | |
| was_pinned, pinned); | |
| } | |
| if (new_ptr == NULL) | |
| goto fail; | |
| ret = (char *) new_ptr + sizeof (struct omp_mem_header); | |
| ((struct omp_mem_header *) ret)[-1].ptr = new_ptr; | |
| ((struct omp_mem_header *) ret)[-1].size = new_size; | |
| ((struct omp_mem_header *) ret)[-1].allocator = allocator; | |
| return ret; | |
| } | |
| else | |
| { | |
| #ifdef LIBGOMP_USE_LIBNUMA | |
| if (memkind == GOMP_MEMKIND_LIBNUMA) | |
| new_ptr = libnuma_data->numa_alloc_local (new_size); | |
| # ifdef LIBGOMP_USE_MEMKIND | |
| else | |
| # endif | |
| #endif | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| if (memkind) | |
| { | |
| struct gomp_memkind_data *memkind_data = gomp_get_memkind (); | |
| void *kind = *memkind_data->kinds[memkind]; | |
| new_ptr = memkind_data->memkind_malloc (kind, new_size); | |
| } | |
| else | |
| #endif | |
| { | |
| omp_memspace_handle_t memspace; | |
| memspace = (allocator_data | |
| ? allocator_data->memspace | |
| : predefined_alloc_mapping (allocator)); | |
| int pinned = (allocator_data | |
| ? allocator_data->pinned | |
| : allocator == ompx_gnu_pinned_mem_alloc); | |
| new_ptr = MEMSPACE_ALLOC (memspace, new_size, pinned); | |
| } | |
| if (new_ptr == NULL) | |
| goto fail; | |
| } | |
| if (new_alignment > sizeof (void *)) | |
| ret = (void *) (((uintptr_t) new_ptr | |
| + sizeof (struct omp_mem_header) | |
| + new_alignment - sizeof (void *)) | |
| & ~(new_alignment - 1)); | |
| else | |
| ret = (char *) new_ptr + sizeof (struct omp_mem_header); | |
| ((struct omp_mem_header *) ret)[-1].ptr = new_ptr; | |
| ((struct omp_mem_header *) ret)[-1].size = new_size; | |
| ((struct omp_mem_header *) ret)[-1].allocator = allocator; | |
| if (old_size - old_alignment < size) | |
| size = old_size - old_alignment; | |
| memcpy (ret, ptr, size); | |
| if (__builtin_expect (free_allocator_data | |
| && free_allocator_data->pool_size < ~(uintptr_t) 0, 0)) | |
| { | |
| #ifdef HAVE_SYNC_BUILTINS | |
| __atomic_add_fetch (&free_allocator_data->used_pool_size, -data->size, | |
| MEMMODEL_RELAXED); | |
| #else | |
| gomp_mutex_lock (&free_allocator_data->lock); | |
| free_allocator_data->used_pool_size -= data->size; | |
| gomp_mutex_unlock (&free_allocator_data->lock); | |
| #endif | |
| } | |
| #ifdef LIBGOMP_USE_LIBNUMA | |
| if (free_memkind == GOMP_MEMKIND_LIBNUMA) | |
| { | |
| libnuma_data->numa_free (data->ptr, data->size); | |
| return ret; | |
| } | |
| # ifdef LIBGOMP_USE_MEMKIND | |
| else | |
| # endif | |
| #endif | |
| #ifdef LIBGOMP_USE_MEMKIND | |
| if (free_memkind) | |
| { | |
| struct gomp_memkind_data *memkind_data = gomp_get_memkind (); | |
| void *kind = *memkind_data->kinds[free_memkind]; | |
| memkind_data->memkind_free (kind, data->ptr); | |
| return ret; | |
| } | |
| #endif | |
| { | |
| omp_memspace_handle_t was_memspace; | |
| was_memspace = (free_allocator_data | |
| ? free_allocator_data->memspace | |
| : predefined_alloc_mapping (free_allocator)); | |
| int was_pinned = (free_allocator_data | |
| ? free_allocator_data->pinned | |
| : free_allocator == ompx_gnu_pinned_mem_alloc); | |
| MEMSPACE_FREE (was_memspace, data->ptr, data->size, was_pinned); | |
| } | |
| return ret; | |
| fail:; | |
| int fallback = (allocator_data | |
| ? allocator_data->fallback | |
| : (allocator == omp_default_mem_alloc | |
| || allocator == ompx_gnu_pinned_mem_alloc) | |
| ? omp_atv_null_fb | |
| : omp_atv_default_mem_fb); | |
| switch (fallback) | |
| { | |
| case omp_atv_default_mem_fb: | |
| allocator = omp_default_mem_alloc; | |
| goto retry; | |
| case omp_atv_null_fb: | |
| break; | |
| default: | |
| case omp_atv_abort_fb: | |
| gomp_fatal ("Out of memory allocating %lu bytes", | |
| (unsigned long) size); | |
| case omp_atv_allocator_fb: | |
| allocator = allocator_data->fb_data; | |
| goto retry; | |
| } | |
| return NULL; | |
| } |