mirror of
https://github.com/gcc-mirror/gcc.git
synced 2024-11-21 13:40:47 +00:00
1412 lines
40 KiB
C
1412 lines
40 KiB
C
/* Copyright (C) 2020-2024 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
|
|
|
|
/* 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_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). */
|
|
};
|
|
|
|
#define ARRAY_SIZE(A) (sizeof (A) / sizeof ((A)[0]))
|
|
_Static_assert (ARRAY_SIZE (predefined_alloc_mapping)
|
|
== omp_max_predefined_alloc + 1,
|
|
"predefined_alloc_mapping must match omp_memspace_handle_t");
|
|
|
|
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 (allocator > omp_max_predefined_alloc)
|
|
{
|
|
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]);
|
|
ptr = MEMSPACE_ALLOC (memspace, new_size,
|
|
allocator_data && allocator_data->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
|
|
? 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 (data->allocator > omp_max_predefined_alloc)
|
|
{
|
|
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];
|
|
}
|
|
|
|
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 (allocator > omp_max_predefined_alloc)
|
|
{
|
|
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]);
|
|
ptr = MEMSPACE_CALLOC (memspace, new_size,
|
|
allocator_data && allocator_data->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
|
|
? 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 (allocator > omp_max_predefined_alloc)
|
|
{
|
|
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 (free_allocator > omp_max_predefined_alloc)
|
|
{
|
|
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)
|
|
new_ptr = MEMSPACE_REALLOC (allocator_data->memspace, data->ptr,
|
|
data->size, new_size,
|
|
(free_allocator_data
|
|
&& free_allocator_data->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]);
|
|
new_ptr = MEMSPACE_REALLOC (memspace, data->ptr, data->size, new_size,
|
|
(free_allocator_data
|
|
&& free_allocator_data->pinned),
|
|
allocator_data && allocator_data->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]);
|
|
new_ptr = MEMSPACE_ALLOC (memspace, new_size,
|
|
allocator_data && allocator_data->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);
|
|
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
|
|
? 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;
|
|
}
|