public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* openmp: Add basic library allocator support
@ 2020-05-19  8:24 Jakub Jelinek
  2020-05-19 11:56 ` H.J. Lu
  2020-06-02  9:26 ` Sebastian Huber
  0 siblings, 2 replies; 5+ messages in thread
From: Jakub Jelinek @ 2020-05-19  8:24 UTC (permalink / raw)
  To: gcc-patches

Hi!

This patch adds very basic allocator support (omp_{init,destroy}_allocator,
omp_{alloc,free}, omp_[sg]et_default_allocator).
The plan is to use memkind (likely dlopened) for high bandwidth memory, but
that part isn't implemented yet, probably mlock for pinned memory and see
what other options there are for other kinds of memory.
For offloading targets, we need to decide if we want to support the
dynamic allocators (and on which targets), or if e.g. all we do is at compile
time replace omp_alloc/omp_free calls with constexpr predefined allocators 
with something special.

And allocate directive and allocator/uses_allocators clauses are future work
too.

Bootstrapped/regtested on x86_64-linux and i686-linux, committed to trunk.

2020-05-19  Jakub Jelinek  <jakub@redhat.com>

	* omp.h.in (omp_uintptr_t): New typedef.
	(__GOMP_UINTPTR_T_ENUM): Define.
	(omp_memspace_handle_t, omp_allocator_handle_t, omp_alloctrait_key_t,
	omp_alloctrait_value_t, omp_alloctrait_t): New typedefs.
	(__GOMP_DEFAULT_NULL_ALLOCATOR): Define.
	(omp_init_allocator, omp_destroy_allocator, omp_set_default_allocator,
	omp_get_default_allocator, omp_alloc, omp_free): Declare.
	* libgomp.h (struct gomp_team_state): Add def_allocator field.
	(gomp_def_allocator): Declare.
	* libgomp.map (OMP_5.0.1): Export omp_set_default_allocator,
	omp_get_default_allocator, omp_init_allocator, omp_destroy_allocator,
	omp_alloc and omp_free.
	* team.c (gomp_team_start): Copy over ts.def_allocator.
	* env.c (gomp_def_allocator): New variable.
	(parse_wait_policy): Adjust function comment.
	(parse_allocator): New function.
	(handle_omp_display_env): Print OMP_ALLOCATOR.
	(initialize_env): Call parse_allocator.
	* Makefile.am (libgomp_la_SOURCES): Add allocator.c.
	* allocator.c: New file.
	* icv.c (omp_set_default_allocator, omp_get_default_allocator): New
	functions.
	* testsuite/libgomp.c-c++-common/alloc-1.c: New test.
	* testsuite/libgomp.c-c++-common/alloc-2.c: New test.
	* testsuite/libgomp.c-c++-common/alloc-3.c: New test.
	* Makefile.in: Regenerated.

--- libgomp/omp.h.in.jj	2020-01-12 11:54:39.018374107 +0100
+++ libgomp/omp.h.in	2020-05-14 15:46:58.046243665 +0200
@@ -90,11 +90,87 @@ typedef enum omp_pause_resource_t
   omp_pause_hard = 2
 } omp_pause_resource_t;
 
+typedef __UINTPTR_TYPE__ omp_uintptr_t;
+
+#if __cplusplus >= 201103L
+# define __GOMP_UINTPTR_T_ENUM : omp_uintptr_t
+#else
+# define __GOMP_UINTPTR_T_ENUM
+#endif
+
+typedef enum omp_memspace_handle_t __GOMP_UINTPTR_T_ENUM
+{
+  omp_default_mem_space = 0,
+  omp_large_cap_mem_space = 1,
+  omp_const_mem_space = 2,
+  omp_high_bw_mem_space = 3,
+  omp_low_lat_mem_space = 4,
+  __omp_memspace_handle_t_max__ = __UINTPTR_MAX__
+} omp_memspace_handle_t;
+
+typedef enum omp_allocator_handle_t __GOMP_UINTPTR_T_ENUM
+{
+  omp_null_allocator = 0,
+  omp_default_mem_alloc = 1,
+  omp_large_cap_mem_alloc = 2,
+  omp_const_mem_alloc = 3,
+  omp_high_bw_mem_alloc = 4,
+  omp_low_lat_mem_alloc = 5,
+  omp_cgroup_mem_alloc = 6,
+  omp_pteam_mem_alloc = 7,
+  omp_thread_mem_alloc = 8,
+  __omp_allocator_handle_t_max__ = __UINTPTR_MAX__
+} omp_allocator_handle_t;
+
+typedef enum omp_alloctrait_key_t
+{
+  omp_atk_sync_hint = 1,
+  omp_atk_alignment = 2,
+  omp_atk_access = 3,
+  omp_atk_pool_size = 4,
+  omp_atk_fallback = 5,
+  omp_atk_fb_data = 6,
+  omp_atk_pinned = 7,
+  omp_atk_partition = 8
+} omp_alloctrait_key_t;
+
+typedef enum omp_alloctrait_value_t
+{
+  omp_atv_false = 0,
+  omp_atv_true = 1,
+  omp_atv_default = 2,
+  omp_atv_contended = 3,
+  omp_atv_uncontended = 4,
+  omp_atv_sequential = 5,
+  omp_atv_private = 6,
+  omp_atv_all = 7,
+  omp_atv_thread = 8,
+  omp_atv_pteam = 9,
+  omp_atv_cgroup = 10,
+  omp_atv_default_mem_fb = 11,
+  omp_atv_null_fb = 12,
+  omp_atv_abort_fb = 13,
+  omp_atv_allocator_fb = 14,
+  omp_atv_environment = 15,
+  omp_atv_nearest = 16,
+  omp_atv_blocked = 17,
+  omp_atv_interleaved = 18,
+  __omp_alloctrait_value_max__ = __UINTPTR_MAX__
+} omp_alloctrait_value_t;
+
+typedef struct omp_alloctrait_t
+{
+  omp_alloctrait_key_t key;
+  omp_uintptr_t value;
+} omp_alloctrait_t;
+
 #ifdef __cplusplus
 extern "C" {
 # define __GOMP_NOTHROW throw ()
+# define __GOMP_DEFAULT_NULL_ALLOCATOR = omp_null_allocator
 #else
 # define __GOMP_NOTHROW __attribute__((__nothrow__))
+# define __GOMP_DEFAULT_NULL_ALLOCATOR
 #endif
 
 extern void omp_set_num_threads (int) __GOMP_NOTHROW;
@@ -188,6 +264,20 @@ extern __SIZE_TYPE__ omp_capture_affinit
 extern int omp_pause_resource (omp_pause_resource_t, int) __GOMP_NOTHROW;
 extern int omp_pause_resource_all (omp_pause_resource_t) __GOMP_NOTHROW;
 
+extern omp_allocator_handle_t omp_init_allocator (omp_memspace_handle_t,
+						  int,
+						  const omp_alloctrait_t [])
+  __GOMP_NOTHROW;
+extern void omp_destroy_allocator (omp_allocator_handle_t) __GOMP_NOTHROW;
+extern void omp_set_default_allocator (omp_allocator_handle_t) __GOMP_NOTHROW;
+extern omp_allocator_handle_t omp_get_default_allocator (void) __GOMP_NOTHROW;
+extern void *omp_alloc (__SIZE_TYPE__,
+			omp_allocator_handle_t __GOMP_DEFAULT_NULL_ALLOCATOR)
+  __GOMP_NOTHROW;
+extern void omp_free (void *,
+		      omp_allocator_handle_t __GOMP_DEFAULT_NULL_ALLOCATOR)
+  __GOMP_NOTHROW;
+
 #ifdef __cplusplus
 }
 #endif
--- libgomp/libgomp.h.jj	2020-01-12 11:54:39.016374137 +0100
+++ libgomp/libgomp.h	2020-05-14 19:21:25.791171136 +0200
@@ -397,6 +397,9 @@ struct gomp_team_state
   unsigned place_partition_off;
   unsigned place_partition_len;
 
+  /* Def-allocator-var ICV.  */
+  uintptr_t def_allocator;
+
 #ifdef HAVE_SYNC_BUILTINS
   /* Number of single stmts encountered.  */
   unsigned long single_count;
@@ -450,6 +453,7 @@ extern int gomp_debug_var;
 extern bool gomp_display_affinity_var;
 extern char *gomp_affinity_format_var;
 extern size_t gomp_affinity_format_len;
+extern uintptr_t gomp_def_allocator;
 extern int goacc_device_num;
 extern char *goacc_device_type;
 extern int goacc_default_dims[GOMP_DIM_MAX];
--- libgomp/libgomp.map.jj	2020-01-12 11:54:39.016374137 +0100
+++ libgomp/libgomp.map	2020-05-15 16:14:40.084583166 +0200
@@ -180,6 +180,16 @@ OMP_5.0 {
 	omp_pause_resource_all_;
 } OMP_4.5;
 
+OMP_5.0.1 {
+  global:
+	omp_set_default_allocator;
+	omp_get_default_allocator;
+	omp_init_allocator;
+	omp_destroy_allocator;
+	omp_alloc;
+	omp_free;
+} OMP_5.0;
+
 GOMP_1.0 {
   global:
 	GOMP_atomic_end;
--- libgomp/team.c.jj	2020-01-12 11:54:39.020374077 +0100
+++ libgomp/team.c	2020-05-18 14:42:26.221685703 +0200
@@ -636,6 +636,7 @@ gomp_team_start (void (*fn) (void *), vo
 	  nthr->ts.active_level = thr->ts.active_level;
 	  nthr->ts.place_partition_off = place_partition_off;
 	  nthr->ts.place_partition_len = place_partition_len;
+	  nthr->ts.def_allocator = thr->ts.def_allocator;
 #ifdef HAVE_SYNC_BUILTINS
 	  nthr->ts.single_count = 0;
 #endif
@@ -823,6 +824,7 @@ gomp_team_start (void (*fn) (void *), vo
       start_data->ts.team_id = i;
       start_data->ts.level = team->prev_ts.level + 1;
       start_data->ts.active_level = thr->ts.active_level;
+      start_data->ts.def_allocator = thr->ts.def_allocator;
 #ifdef HAVE_SYNC_BUILTINS
       start_data->ts.single_count = 0;
 #endif
--- libgomp/env.c.jj	2020-01-12 11:54:39.016374137 +0100
+++ libgomp/env.c	2020-05-14 19:24:58.280004821 +0200
@@ -86,6 +86,7 @@ char *gomp_bind_var_list;
 unsigned long gomp_bind_var_list_len;
 void **gomp_places_list;
 unsigned long gomp_places_list_len;
+uintptr_t gomp_def_allocator = omp_default_mem_alloc;
 int gomp_debug_var;
 unsigned int gomp_num_teams_var;
 bool gomp_display_affinity_var;
@@ -949,8 +950,7 @@ parse_boolean (const char *name, bool *v
     gomp_error ("Invalid value for environment variable %s", name);
 }
 
-/* Parse the OMP_WAIT_POLICY environment variable and store the
-   result in gomp_active_wait_policy.  */
+/* Parse the OMP_WAIT_POLICY environment variable and return the value.  */
 
 static int
 parse_wait_policy (void)
@@ -1084,6 +1084,47 @@ parse_affinity (bool ignore)
   return false;
 }
 
+/* Parse the OMP_ALLOCATOR environment variable and return the value.  */
+
+static uintptr_t
+parse_allocator (void)
+{
+  const char *env;
+  uintptr_t ret = omp_default_mem_alloc;
+
+  env = getenv ("OMP_ALLOCATOR");
+  if (env == NULL)
+    return ret;
+
+  while (isspace ((unsigned char) *env))
+    ++env;
+  if (0)
+    ;
+#define C(v) \
+  else if (strncasecmp (env, #v, sizeof (#v) - 1) == 0)	\
+    {							\
+      ret = v;						\
+      env += sizeof (#v) - 1;				\
+    }
+  C (omp_default_mem_alloc)
+  C (omp_large_cap_mem_alloc)
+  C (omp_const_mem_alloc)
+  C (omp_high_bw_mem_alloc)
+  C (omp_low_lat_mem_alloc)
+  C (omp_cgroup_mem_alloc)
+  C (omp_pteam_mem_alloc)
+  C (omp_thread_mem_alloc)
+#undef C
+  else
+    env = "X";
+  while (isspace ((unsigned char) *env))
+    ++env;
+  if (*env == '\0')
+    return ret;
+  gomp_error ("Invalid value for environment variable OMP_ALLOCATOR");
+  return omp_default_mem_alloc;
+}
+
 static void
 parse_acc_device_type (void)
 {
@@ -1276,6 +1317,22 @@ handle_omp_display_env (unsigned long st
 	   gomp_display_affinity_var ? "TRUE" : "FALSE");
   fprintf (stderr, "  OMP_AFFINITY_FORMAT = '%s'\n",
 	   gomp_affinity_format_var);
+  fprintf (stderr, "  OMP_ALLOCATOR = '");
+  switch (gomp_def_allocator)
+    {
+#define C(v) case v: fputs (#v, stderr); break;
+    C (omp_default_mem_alloc)
+    C (omp_large_cap_mem_alloc)
+    C (omp_const_mem_alloc)
+    C (omp_high_bw_mem_alloc)
+    C (omp_low_lat_mem_alloc)
+    C (omp_cgroup_mem_alloc)
+    C (omp_pteam_mem_alloc)
+    C (omp_thread_mem_alloc)
+#undef C
+    default: break;
+    }
+  fputs ("'\n", stderr);
 
   if (verbose)
     {
@@ -1312,6 +1369,7 @@ initialize_env (void)
   parse_int ("OMP_MAX_TASK_PRIORITY", &gomp_max_task_priority_var, true);
   parse_unsigned_long ("OMP_MAX_ACTIVE_LEVELS", &gomp_max_active_levels_var,
 		       true);
+  gomp_def_allocator = parse_allocator ();
   if (parse_unsigned_long ("OMP_THREAD_LIMIT", &thread_limit_var, false))
     {
       gomp_global_icv.thread_limit_var
--- libgomp/Makefile.am.jj	2020-01-12 11:54:39.010374227 +0100
+++ libgomp/Makefile.am	2020-05-15 18:51:40.796738975 +0200
@@ -65,7 +65,7 @@ libgomp_la_SOURCES = alloc.c atomic.c ba
 	proc.c sem.c bar.c ptrlock.c time.c fortran.c affinity.c target.c \
 	splay-tree.c libgomp-plugin.c oacc-parallel.c oacc-host.c oacc-init.c \
 	oacc-mem.c oacc-async.c oacc-plugin.c oacc-cuda.c priority_queue.c \
-	affinity-fmt.c teams.c oacc-profiling.c oacc-target.c
+	affinity-fmt.c teams.c allocator.c oacc-profiling.c oacc-target.c
 
 include $(top_srcdir)/plugin/Makefrag.am
 
--- libgomp/allocator.c.jj	2020-05-14 17:00:13.351421911 +0200
+++ libgomp/allocator.c	2020-05-18 13:15:55.037731741 +0200
@@ -0,0 +1,354 @@
+/* Copyright (C) 2020 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>
+
+#define omp_max_predefined_alloc omp_thread_mem_alloc
+
+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;
+#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;
+};
+
+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 };
+  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_sequential:
+	  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 & (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:
+	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 *);
+
+  /* No support for these so far (for hbw will use memkind).  */
+  if (data.pinned || data.memspace == omp_high_bw_mem_space)
+    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);
+    }
+}
+
+void *
+omp_alloc (size_t size, omp_allocator_handle_t allocator)
+{
+  struct omp_allocator_data *allocator_data;
+  size_t alignment, new_size;
+  void *ptr, *ret;
+
+retry:
+  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;
+      alignment = allocator_data->alignment;
+    }
+  else
+    {
+      allocator_data = NULL;
+      alignment = sizeof (void *);
+    }
+
+  new_size = sizeof (struct omp_mem_header);
+  if (alignment > sizeof (void *))
+    new_size += alignment - sizeof (void *);
+  if (__builtin_add_overflow (size, new_size, &new_size))
+    goto fail;
+
+  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
+      ptr = malloc (new_size);
+      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
+    {
+      ptr = malloc (new_size);
+      if (ptr == NULL)
+	goto fail;
+    }
+
+  if (alignment > sizeof (void *))
+    ret = (void *) (((uintptr_t) ptr
+		     + sizeof (struct omp_mem_header)
+		     + alignment - sizeof (void *)) & ~(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:
+  if (allocator_data)
+    {
+      switch (allocator_data->fallback)
+	{
+	case omp_atv_default_mem_fb:
+	  if (alignment > sizeof (void *)
+	      || (allocator_data
+		  && allocator_data->pool_size < ~(uintptr_t) 0))
+	    {
+	      allocator = omp_default_mem_alloc;
+	      goto retry;
+	    }
+	  /* Otherwise, we've already performed default mem allocation
+	     and if that failed, it won't succeed again (unless it was
+	     intermitent.  Return NULL then, as that is the fallback.  */
+	  break;
+	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;
+}
+
+void
+omp_free (void *ptr, omp_allocator_handle_t allocator)
+{
+  struct omp_mem_header *data;
+
+  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->new_size;
+	  gomp_mutex_unlock (&allocator_data->lock);
+#endif
+	}
+    }
+  free (data->ptr);
+}
--- libgomp/icv.c.jj	2020-01-12 11:54:39.016374137 +0100
+++ libgomp/icv.c	2020-05-14 19:24:18.074603926 +0200
@@ -197,6 +197,25 @@ omp_get_partition_place_nums (int *place
     *place_nums++ = thr->ts.place_partition_off + i;
 }
 
+void
+omp_set_default_allocator (omp_allocator_handle_t allocator)
+{
+  struct gomp_thread *thr = gomp_thread ();
+  if (allocator == omp_null_allocator)
+    allocator = omp_default_mem_alloc;
+  thr->ts.def_allocator = (uintptr_t) allocator;
+}
+
+omp_allocator_handle_t
+omp_get_default_allocator (void)
+{
+  struct gomp_thread *thr = gomp_thread ();
+  if (thr->ts.def_allocator == omp_null_allocator)
+    return (omp_allocator_handle_t) gomp_def_allocator;
+  else
+    return (omp_allocator_handle_t) thr->ts.def_allocator;
+}
+
 ialias (omp_set_dynamic)
 ialias (omp_set_nested)
 ialias (omp_set_num_threads)
--- libgomp/testsuite/libgomp.c-c++-common/alloc-1.c.jj	2020-05-18 12:46:01.630710546 +0200
+++ libgomp/testsuite/libgomp.c-c++-common/alloc-1.c	2020-05-18 14:18:16.091446145 +0200
@@ -0,0 +1,157 @@
+#include <omp.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+const omp_alloctrait_t traits2[]
+= { { omp_atk_alignment, 16 },
+    { omp_atk_sync_hint, omp_atv_default },
+    { omp_atk_access, omp_atv_default },
+    { omp_atk_pool_size, 1024 },
+    { omp_atk_fallback, omp_atv_default_mem_fb },
+    { omp_atk_partition, omp_atv_environment } };
+omp_alloctrait_t traits3[]
+= { { omp_atk_sync_hint, omp_atv_uncontended },
+    { omp_atk_alignment, 32 },
+    { omp_atk_access, omp_atv_all },
+    { omp_atk_pool_size, 512 },
+    { omp_atk_fallback, omp_atv_allocator_fb },
+    { omp_atk_fb_data, 0 },
+    { omp_atk_partition, omp_atv_default } };
+const omp_alloctrait_t traits4[]
+= { { omp_atk_alignment, 128 },
+    { omp_atk_pool_size, 1024 },
+    { omp_atk_fallback, omp_atv_null_fb } };
+
+int
+main ()
+{
+  int *volatile p = (int *) omp_alloc (3 * sizeof (int), omp_default_mem_alloc);
+  int *volatile q;
+  int *volatile r;
+  omp_alloctrait_t traits[3]
+    = { { omp_atk_alignment, 64 },
+	{ omp_atk_fallback, omp_atv_null_fb },
+	{ omp_atk_pool_size, 4096 } };
+  omp_allocator_handle_t a, a2;
+
+  if ((((uintptr_t) p) % __alignof (int)) != 0)
+    abort ();
+  p[0] = 1;
+  p[1] = 2;
+  p[2] = 3;
+  omp_free (p, omp_default_mem_alloc);
+  p = (int *) omp_alloc (2 * sizeof (int), omp_default_mem_alloc);
+  if ((((uintptr_t) p) % __alignof (int)) != 0)
+    abort ();
+  p[0] = 1;
+  p[1] = 2;
+  omp_free (p, omp_null_allocator);
+  omp_set_default_allocator (omp_default_mem_alloc);
+  p = (int *) omp_alloc (sizeof (int), omp_null_allocator);
+  if ((((uintptr_t) p) % __alignof (int)) != 0)
+    abort ();
+  p[0] = 3;
+  omp_free (p, omp_get_default_allocator ());
+
+  a = omp_init_allocator (omp_default_mem_space, 3, traits);
+  if (a == omp_null_allocator)
+    abort ();
+  p = (int *) omp_alloc (3072, a);
+  if ((((uintptr_t) p) % 64) != 0)
+    abort ();
+  p[0] = 1;
+  p[3071 / sizeof (int)] = 2;
+  if (omp_alloc (3072, a) != NULL)
+    abort ();
+  omp_free (p, a);
+  p = (int *) omp_alloc (3072, a);
+  p[0] = 3;
+  p[3071 / sizeof (int)] = 4;
+  omp_free (p, omp_null_allocator);
+  omp_set_default_allocator (a);
+  if (omp_get_default_allocator () != a)
+    abort ();
+  p = (int *) omp_alloc (3072, omp_null_allocator);
+  if (omp_alloc (3072, omp_null_allocator) != NULL)
+    abort ();
+  omp_free (p, a);
+  omp_destroy_allocator (a);
+
+  a = omp_init_allocator (omp_default_mem_space,
+			  sizeof (traits2) / sizeof (traits2[0]),
+			  traits2);
+  if (a == omp_null_allocator)
+    abort ();
+  if (traits3[5].key != omp_atk_fb_data)
+    abort ();
+  traits3[5].value = (uintptr_t) a;
+  a2 = omp_init_allocator (omp_default_mem_space,
+			   sizeof (traits3) / sizeof (traits3[0]),
+			   traits3);
+  if (a2 == omp_null_allocator)
+    abort ();
+  p = (int *) omp_alloc (420, a2);
+  if ((((uintptr_t) p) % 32) != 0)
+    abort ();
+  p[0] = 5;
+  p[419 / sizeof (int)] = 6;
+  q = (int *) omp_alloc (768, a2);
+  if ((((uintptr_t) q) % 16) != 0)
+    abort ();
+  q[0] = 7;
+  q[767 / sizeof (int)] = 8;
+  r = (int *) omp_alloc (512, a2);
+  if ((((uintptr_t) r) % __alignof (int)) != 0)
+    abort ();
+  r[0] = 9;
+  r[511 / sizeof (int)] = 10;
+  omp_free (p, omp_null_allocator);
+  omp_free (q, a2);
+  omp_free (r, omp_null_allocator);
+  omp_destroy_allocator (a2);
+  omp_destroy_allocator (a);
+
+  a = omp_init_allocator (omp_default_mem_space,
+			  sizeof (traits4) / sizeof (traits4[0]),
+			  traits4);
+  if (a == omp_null_allocator)
+    abort ();
+  if (traits3[5].key != omp_atk_fb_data)
+    abort ();
+  traits3[5].value = (uintptr_t) a;
+  a2 = omp_init_allocator (omp_default_mem_space,
+			   sizeof (traits3) / sizeof (traits3[0]),
+			   traits3);
+  if (a2 == omp_null_allocator)
+    abort ();
+  omp_set_default_allocator (a2);
+#ifdef __cplusplus
+  p = static_cast <int *> (omp_alloc (420));
+#else
+  p = (int *) omp_alloc (420, omp_null_allocator);
+#endif
+  if ((((uintptr_t) p) % 32) != 0)
+    abort ();
+  p[0] = 5;
+  p[419 / sizeof (int)] = 6;
+  q = (int *) omp_alloc (768, omp_null_allocator);
+  if ((((uintptr_t) q) % 128) != 0)
+    abort ();
+  q[0] = 7;
+  q[767 / sizeof (int)] = 8;
+  if (omp_alloc (768, omp_null_allocator) != NULL)
+    abort ();
+#ifdef __cplusplus
+  omp_free (p);
+  omp_free (q);
+  omp_free (NULL);
+#else
+  omp_free (p, omp_null_allocator);
+  omp_free (q, omp_null_allocator);
+  omp_free (NULL, omp_null_allocator);
+#endif
+  omp_free (NULL, omp_null_allocator);
+  omp_destroy_allocator (a2);
+  omp_destroy_allocator (a);
+  return 0;
+}
--- libgomp/testsuite/libgomp.c-c++-common/alloc-2.c.jj	2020-05-18 13:32:30.862751223 +0200
+++ libgomp/testsuite/libgomp.c-c++-common/alloc-2.c	2020-05-18 14:20:18.269607455 +0200
@@ -0,0 +1,46 @@
+#include <omp.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+int
+main ()
+{
+  omp_alloctrait_t traits[3]
+    = { { omp_atk_alignment, 64 },
+	{ omp_atk_fallback, omp_atv_null_fb },
+	{ omp_atk_pool_size, 4096 } };
+  omp_allocator_handle_t a
+    = omp_init_allocator (omp_default_mem_space, 3, traits);
+  if (a == omp_null_allocator)
+    abort ();
+
+  #pragma omp parallel num_threads(4)
+  {
+    int n = omp_get_thread_num ();
+    double *volatile p, *volatile q;
+    omp_set_default_allocator ((n & 1) ? a : omp_default_mem_alloc);
+    p = (double *) omp_alloc (1696, omp_null_allocator);
+    if (p == NULL)
+      abort ();
+    p[0] = 1.0;
+    p[1695 / sizeof (double *)] = 2.0;
+    #pragma omp barrier
+    omp_set_default_allocator ((n & 1) ? omp_default_mem_alloc : a);
+    q = (double *) omp_alloc (1696, omp_null_allocator);
+    if (n & 1)
+      {
+	if (q == NULL)
+	  abort ();
+	q[0] = 3.0;
+	q[1695 / sizeof (double *)] = 4.0;
+      }
+    else if (q != NULL)
+      abort ();
+    #pragma omp barrier
+    omp_free (p, omp_null_allocator);
+    omp_free (q, omp_null_allocator);
+    omp_set_default_allocator (omp_default_mem_alloc);
+  }
+  omp_destroy_allocator (a);
+  return 0;
+}
--- libgomp/testsuite/libgomp.c-c++-common/alloc-3.c.jj	2020-05-18 14:29:03.975695993 +0200
+++ libgomp/testsuite/libgomp.c-c++-common/alloc-3.c	2020-05-18 14:32:26.953650327 +0200
@@ -0,0 +1,28 @@
+/* { dg-set-target-env-var OMP_ALLOCATOR "omp_cgroup_mem_alloc" } */
+/* { dg-set-target-env-var OMP_DISPLAY_ENV "true" } */
+
+#include <string.h>
+#include <stdlib.h>
+#include <omp.h>
+
+int
+main ()
+{
+  const char *p = getenv ("OMP_ALLOCATOR");
+  if (p && strcmp (p, "omp_cgroup_mem_alloc") == 0)
+    {
+      if (omp_get_default_allocator () != omp_cgroup_mem_alloc)
+	abort ();
+      #pragma omp parallel num_threads (2)
+      {
+	if (omp_get_default_allocator () != omp_cgroup_mem_alloc)
+	  abort ();
+	#pragma omp parallel num_threads (2)
+	{
+	  if (omp_get_default_allocator () != omp_cgroup_mem_alloc)
+	    abort ();
+	}
+      }
+    }
+  return 0;
+}
--- libgomp/Makefile.in.jj	2020-01-24 22:34:36.386640520 +0100
+++ libgomp/Makefile.in	2020-05-15 18:57:45.661185739 +0200
@@ -231,7 +231,8 @@ am_libgomp_la_OBJECTS = alloc.lo atomic.
 	target.lo splay-tree.lo libgomp-plugin.lo oacc-parallel.lo \
 	oacc-host.lo oacc-init.lo oacc-mem.lo oacc-async.lo \
 	oacc-plugin.lo oacc-cuda.lo priority_queue.lo affinity-fmt.lo \
-	teams.lo oacc-profiling.lo oacc-target.lo $(am__objects_1)
+	teams.lo allocator.lo oacc-profiling.lo oacc-target.lo \
+	$(am__objects_1)
 libgomp_la_OBJECTS = $(am_libgomp_la_OBJECTS)
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
@@ -572,7 +573,7 @@ libgomp_la_SOURCES = alloc.c atomic.c ba
 	affinity.c target.c splay-tree.c libgomp-plugin.c \
 	oacc-parallel.c oacc-host.c oacc-init.c oacc-mem.c \
 	oacc-async.c oacc-plugin.c oacc-cuda.c priority_queue.c \
-	affinity-fmt.c teams.c oacc-profiling.c oacc-target.c \
+	affinity-fmt.c teams.c allocator.c oacc-profiling.c oacc-target.c \
 	$(am__append_4)
 
 # Nvidia PTX OpenACC plugin.
@@ -765,6 +766,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/affinity-fmt.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/affinity.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/allocator.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atomic.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bar.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/barrier.Plo@am__quote@


	Jakub


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: openmp: Add basic library allocator support
  2020-05-19  8:24 openmp: Add basic library allocator support Jakub Jelinek
@ 2020-05-19 11:56 ` H.J. Lu
  2020-05-19 12:10   ` Jakub Jelinek
  2020-06-02  9:26 ` Sebastian Huber
  1 sibling, 1 reply; 5+ messages in thread
From: H.J. Lu @ 2020-05-19 11:56 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

On Tue, May 19, 2020 at 1:27 AM Jakub Jelinek via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> Hi!
>
> This patch adds very basic allocator support (omp_{init,destroy}_allocator,
> omp_{alloc,free}, omp_[sg]et_default_allocator).
> The plan is to use memkind (likely dlopened) for high bandwidth memory, but
> that part isn't implemented yet, probably mlock for pinned memory and see
> what other options there are for other kinds of memory.
> For offloading targets, we need to decide if we want to support the
> dynamic allocators (and on which targets), or if e.g. all we do is at compile
> time replace omp_alloc/omp_free calls with constexpr predefined allocators
> with something special.
>
> And allocate directive and allocator/uses_allocators clauses are future work
> too.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, committed to trunk.
>
> 2020-05-19  Jakub Jelinek  <jakub@redhat.com>
>
>         * omp.h.in (omp_uintptr_t): New typedef.
>         (__GOMP_UINTPTR_T_ENUM): Define.
>         (omp_memspace_handle_t, omp_allocator_handle_t, omp_alloctrait_key_t,
>         omp_alloctrait_value_t, omp_alloctrait_t): New typedefs.
>         (__GOMP_DEFAULT_NULL_ALLOCATOR): Define.
>         (omp_init_allocator, omp_destroy_allocator, omp_set_default_allocator,
>         omp_get_default_allocator, omp_alloc, omp_free): Declare.
>         * libgomp.h (struct gomp_team_state): Add def_allocator field.
>         (gomp_def_allocator): Declare.
>         * libgomp.map (OMP_5.0.1): Export omp_set_default_allocator,
>         omp_get_default_allocator, omp_init_allocator, omp_destroy_allocator,
>         omp_alloc and omp_free.
>         * team.c (gomp_team_start): Copy over ts.def_allocator.
>         * env.c (gomp_def_allocator): New variable.
>         (parse_wait_policy): Adjust function comment.
>         (parse_allocator): New function.
>         (handle_omp_display_env): Print OMP_ALLOCATOR.
>         (initialize_env): Call parse_allocator.
>         * Makefile.am (libgomp_la_SOURCES): Add allocator.c.
>         * allocator.c: New file.
>         * icv.c (omp_set_default_allocator, omp_get_default_allocator): New
>         functions.
>         * testsuite/libgomp.c-c++-common/alloc-1.c: New test.
>         * testsuite/libgomp.c-c++-common/alloc-2.c: New test.
>         * testsuite/libgomp.c-c++-common/alloc-3.c: New test.
>         * Makefile.in: Regenerated.

Did you check in allocator.c?

-- 
H.J.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: openmp: Add basic library allocator support
  2020-05-19 11:56 ` H.J. Lu
@ 2020-05-19 12:10   ` Jakub Jelinek
  0 siblings, 0 replies; 5+ messages in thread
From: Jakub Jelinek @ 2020-05-19 12:10 UTC (permalink / raw)
  To: H.J. Lu; +Cc: GCC Patches

On Tue, May 19, 2020 at 04:56:51AM -0700, H.J. Lu wrote:
> > 2020-05-19  Jakub Jelinek  <jakub@redhat.com>
> >
> >         * omp.h.in (omp_uintptr_t): New typedef.
> >         (__GOMP_UINTPTR_T_ENUM): Define.
> >         (omp_memspace_handle_t, omp_allocator_handle_t, omp_alloctrait_key_t,
> >         omp_alloctrait_value_t, omp_alloctrait_t): New typedefs.
> >         (__GOMP_DEFAULT_NULL_ALLOCATOR): Define.
> >         (omp_init_allocator, omp_destroy_allocator, omp_set_default_allocator,
> >         omp_get_default_allocator, omp_alloc, omp_free): Declare.
> >         * libgomp.h (struct gomp_team_state): Add def_allocator field.
> >         (gomp_def_allocator): Declare.
> >         * libgomp.map (OMP_5.0.1): Export omp_set_default_allocator,
> >         omp_get_default_allocator, omp_init_allocator, omp_destroy_allocator,
> >         omp_alloc and omp_free.
> >         * team.c (gomp_team_start): Copy over ts.def_allocator.
> >         * env.c (gomp_def_allocator): New variable.
> >         (parse_wait_policy): Adjust function comment.
> >         (parse_allocator): New function.
> >         (handle_omp_display_env): Print OMP_ALLOCATOR.
> >         (initialize_env): Call parse_allocator.
> >         * Makefile.am (libgomp_la_SOURCES): Add allocator.c.
> >         * allocator.c: New file.
> >         * icv.c (omp_set_default_allocator, omp_get_default_allocator): New
> >         functions.
> >         * testsuite/libgomp.c-c++-common/alloc-1.c: New test.
> >         * testsuite/libgomp.c-c++-common/alloc-2.c: New test.
> >         * testsuite/libgomp.c-c++-common/alloc-3.c: New test.
> >         * Makefile.in: Regenerated.
> 
> Did you check in allocator.c?

Forgot to git add it, fixed in r11-494-ge107157171af25f6c89be02d62b0a7235a5c988d
On the bright side, Martin's pre-commit-hook would reject it because of
that, but I've committed it immediately before installing the hook :(.
Sorry.

	Jakub


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: openmp: Add basic library allocator support
  2020-05-19  8:24 openmp: Add basic library allocator support Jakub Jelinek
  2020-05-19 11:56 ` H.J. Lu
@ 2020-06-02  9:26 ` Sebastian Huber
  2020-06-02  9:58   ` Jakub Jelinek
  1 sibling, 1 reply; 5+ messages in thread
From: Sebastian Huber @ 2020-06-02  9:26 UTC (permalink / raw)
  To: Jakub Jelinek, gcc-patches

Hello,

On 19/05/2020 10:24, Jakub Jelinek via Gcc-patches wrote:
> +      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
> +      ptr = malloc (new_size);
> +      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

with this patch I get the following error for target arm-rtems6:

../../../gnu-mirror-gcc-86b14bb/libgomp/allocator.c: In function 'omp_free':
../../../gnu-mirror-gcc-86b14bb/libgomp/allocator.c:351:42: error: 
'struct omp_mem_header' has no member named 'new_size'
   351 |    allocator_data->used_pool_size -= data->new_size;
       |                                          ^~


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: openmp: Add basic library allocator support
  2020-06-02  9:26 ` Sebastian Huber
@ 2020-06-02  9:58   ` Jakub Jelinek
  0 siblings, 0 replies; 5+ messages in thread
From: Jakub Jelinek @ 2020-06-02  9:58 UTC (permalink / raw)
  To: Sebastian Huber; +Cc: gcc-patches

On Tue, Jun 02, 2020 at 11:26:37AM +0200, Sebastian Huber wrote:
> with this patch I get the following error for target arm-rtems6:
> 
> ../../../gnu-mirror-gcc-86b14bb/libgomp/allocator.c: In function 'omp_free':
> ../../../gnu-mirror-gcc-86b14bb/libgomp/allocator.c:351:42: error: 'struct
> omp_mem_header' has no member named 'new_size'
>   351 |    allocator_data->used_pool_size -= data->new_size;
>       |                                          ^~

Oops, sorry, fixed thusly, tested also with #undef HAVE_SYNC_BUILTINS early
in the file, committed to trunk.

2020-06-02  Jakub Jelinek  <jakub@redhat.com>

	* allocator.c (omp_free): Fix up build if HAVE_SYNC_BUILTINS is not
	defined.

--- libgomp/allocator.c
+++ libgomp/allocator.c
@@ -348,7 +348,7 @@ omp_free (void *ptr, omp_allocator_handle_t allocator)
 			      MEMMODEL_RELAXED);
 #else
 	  gomp_mutex_lock (&allocator_data->lock);
-	  allocator_data->used_pool_size -= data->new_size;
+	  allocator_data->used_pool_size -= data->size;
 	  gomp_mutex_unlock (&allocator_data->lock);
 #endif
 	}


	Jakub


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-06-02  9:58 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-19  8:24 openmp: Add basic library allocator support Jakub Jelinek
2020-05-19 11:56 ` H.J. Lu
2020-05-19 12:10   ` Jakub Jelinek
2020-06-02  9:26 ` Sebastian Huber
2020-06-02  9:58   ` Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).