This patch is v3 of uses_allocators support.

The issues Tobias raised in the v2 review should all be resolved.
A bit reorg to move the position of MEMSPACE_VALIDATE calls was done.
Largely code logic hasn't really changed.

A new runtime test for alignment had been add for C/C++/Fortran
for a uses_allocators clause + omp_alloc.

Testcases have now all been moved back into libgomp/testsuite,
might consider moving compile tests back to gcc/testsuite after the
"-I<libgomp-build-dir>" patch is approved.

Seeking approval for mainline.

Thanks,
Chung-Lin

2026-06-23  Tobias Burnus  <[email protected]>
            Andrew Stubbs  <[email protected]>
            Chung-Lin Tang  <[email protected]>

gcc/c/ChangeLog:

        * c-typeck.cc (c_finish_omp_clauses): Fix comments.

gcc/fortran/ChangeLog:

        * trans-array.cc (gfc_conv_array_initializer): Always set PURPOSE
        when building constructor for get_initialized_tmp_var, adjust test
        to use integer_zerop instead of != NULL_TREE.
        * trans-openmp.cc (gfc_trans_omp_clauses): Handle uses_allocators.
        * types.def (BT_FN_VOID_PTRMODE, BT_FN_PTRMODE_PTRMODE,
        BT_FN_PTRMODE_PTRMODE_INT_PTR): Add.

gcc/ChangeLog:

        * gimplify.cc (enum gimplify_omp_var_data):
        Add GOVD_USES_ALLOCATORS_ALLOCATOR.
        (gimplify_bind_expr): Diagnose missing uses_allocators clause.
        (omp_get_attachment): Add GOMP_MAP_USES_ALLOCATORS cases.
        (omp_group_base): Likewise.
        (gimplify_scan_omp_clauses): Handle uses_allocators.
        (gimplify_adjust_omp_clauses_1): Handle GOVD_USES_ALLOCATORS_ALLOCATOR.
        * omp-low.cc (scan_sharing_clauses): Check if ALLOCATE clause in target
        region has containing USES_ALLOCATORS clause.
        Add OMP_CLAUSE_USES_ALLOCATORS cases.
        (lower_omp_target): Add OMP_CLAUSE_USES_ALLOCATORS cases.
        * tree-pretty-print.cc (dump_omp_clause): Handle 
GOMP_MAP_USES_ALLOCATORS.

gcc/testsuite/ChangeLog:

        * c-c++-common/gomp/uses_allocators-1.c: Adjust test.
        * c-c++-common/gomp/uses_allocators-4.c: Likewise.
        * gcc.dg/gomp/deprecate-2.c: Likewise.
        * gfortran.dg/gomp/allocate-1.f90: Likewise.
        * gfortran.dg/gomp/scope-6.f90: Likewise.

include/ChangeLog:

        * gomp-constants.h (enum gomp_map_kind): Add GOMP_MAP_USES_ALLOCATORS.

libgomp/ChangeLog:

        * allocator.c (struct omp_allocator_data): Always enable 'memkind' field
        for configurations with offloading. Adjust comments assuming
        HAVE_SYNC_BUILTINS for all offload host/accelerators.
        (gomp_init_allocator_data): New function, created from 
omp_init_allocator
        logic.
        (omp_init_allocator): Adjust to use gomp_init_allocator_data.
        (gomp_map_omp_init_allocator): New function to call
        gomp_init_allocator_data for uses_allocators clauses.
        * config/gcn/allocator.c (gcn_memspace_validate): Add comments.
        * config/nvptx/allocator.c (nvptx_memspace_alloc): Add PTX >= v4.1
        preprocessor symbol check.
        (nvptx_memspace_calloc): Likewise.
        (nvptx_memspace_free): Likewise.
        (nvptx_memspace_realloc): Likewise.
        (nvptx_memspace_validate): Add comments.
        * configure.ac: Require offload hosts and accelerator targets to support
        __sync_* builtins.
        * configure: Regenerate.
        * libgomp-plugin.h (GOMP_OFFLOAD_memspace_validate): New declaration.
        * libgomp.h (gomp_omp_allocator_data_size): New declaration.
        (memspace_validate_func): New plugin hook in struct gomp_device_descr.
        (gomp_map_omp_init_allocator): New declaration.
        * oacc-host.c (host_dispatch): Init .memspace_validate_func = NULL.
        * plugin/plugin-gcn.c (GOMP_OFFLOAD_memspace_validate): New function.
        * plugin/plugin-nvptx.c (GOMP_OFFLOAD_memspace_validate): New function.
        * target.c (gomp_map_vars_internal): Implement GOMP_MAP_USES_ALLOCATORS
        map kind.
        (calculate_firstprivate_requirements): Likewise.
        (copy_firstprivate_data): Likewise.
        (gomp_load_plugin_for_device): Add DLSYM_OPT of memspace_validate_func
        hook.

        * testsuite/libgomp.c++/uses_allocators-1.C: Moved from
        ../gcc/testsuite/g++.dg/gomp/uses_allocators-1.C.
        * testsuite/libgomp.c-c++-common/uses_allocators-1.c: Moved from
        ../gcc/testsuite/c-c++-common/gomp/uses_allocators-1.c.
        * testsuite/libgomp.c-c++-common/uses_allocators-2.c: Moved from
        ../gcc/testsuite/c-c++-common/gomp/uses_allocators-2.c.
        * testsuite/libgomp.c-c++-common/uses_allocators-4.c: Moved from
        ../gcc/testsuite/c-c++-common/gomp/uses_allocators-4.c.
        * testsuite/libgomp.c-c++-common/uses_allocators-7.c: Moved from
        ../gcc/testsuite/c-c++-common/gomp/uses_allocators-7.c.
        * testsuite/libgomp.c-c++-common/uses_allocators-8.c: Moved from
        ../gcc/testsuite/c-c++-common/gomp/uses_allocators-8.c.

        * testsuite/libgomp.c-c++-common/uses_allocators-3.c: New test.
        * testsuite/libgomp.c-c++-common/uses_allocators-5.c: New test.
        * testsuite/libgomp.c-c++-common/uses_allocators-6.c: New test.
        * testsuite/libgomp.c-c++-common/uses_allocators-9.c: New test.

        * testsuite/libgomp.fortran/uses_allocators-1.f90: New test.
        * testsuite/libgomp.fortran/uses_allocators-2.f90:
        Rename from uses_allocators_1.f90.
        * testsuite/libgomp.fortran/uses_allocators-3.f90:
        Rename from uses_allocators_2.f90.
        * testsuite/libgomp.fortran/uses_allocators-4.f90: New test.
        * testsuite/libgomp.fortran/uses_allocators-5.f90: New test.
        * testsuite/libgomp.fortran/uses_allocators-6.f90: New test.
        * testsuite/libgomp.fortran/uses_allocators-7.f90: Adjust test.
        * testsuite/libgomp.fortran/uses_allocators-8.f90: New test.

        * testsuite/libgomp.c++/c++.exp (check_effective_target_c): Add.
        (check_effective_target_c++): Add.
        * testsuite/libgomp.c/c.exp (check_effective_target_c): Add.
        (check_effective_target_c++): Add.
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 0f96de20074..b2e050141aa 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -17419,7 +17419,7 @@ c_finish_omp_clauses (tree clauses, enum 
c_omp_region_type ort)
          else if (TREE_CODE (t) == CONST_DECL)
            {
              /* omp_null_allocator is ignored and for predefined allocators,
-                not special handling is required; thus, remove them removed. */
+                no special handling is required; thus, mark them removed. */
              remove = true;
 
              if (OMP_CLAUSE_USES_ALLOCATORS_MEMSPACE (c)
diff --git a/gcc/fortran/trans-array.cc b/gcc/fortran/trans-array.cc
index 9d8e913a84a..d875fea52d4 100644
--- a/gcc/fortran/trans-array.cc
+++ b/gcc/fortran/trans-array.cc
@@ -7213,10 +7213,7 @@ gfc_conv_array_initializer (tree type, gfc_expr * expr)
                               &expr->where, flag_max_array_constructor);
              return NULL_TREE;
            }
-          if (mpz_cmp_si (c->offset, 0) != 0)
-            index = gfc_conv_mpz_to_tree (c->offset, gfc_index_integer_kind);
-          else
-            index = NULL_TREE;
+         index = gfc_conv_mpz_to_tree (c->offset, gfc_index_integer_kind);
 
          if (mpz_cmp_si (c->repeat, 1) > 0)
            {
@@ -7287,7 +7284,7 @@ gfc_conv_array_initializer (tree type, gfc_expr * expr)
            CONSTRUCTOR_APPEND_ELT (v, index, se.expr);
          else
            {
-             if (index != NULL_TREE)
+             if (!integer_zerop (index))
                CONSTRUCTOR_APPEND_ELT (v, index, se.expr);
              CONSTRUCTOR_APPEND_ELT (v, range, se.expr);
            }
diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc
index 11873d3b6fe..d1d5a7d947b 100644
--- a/gcc/fortran/trans-openmp.cc
+++ b/gcc/fortran/trans-openmp.cc
@@ -4025,7 +4025,12 @@ gfc_trans_omp_clauses (stmtblock_t *block, 
gfc_omp_clauses *clauses,
                              gfc_init_se (&se, NULL);
                              gfc_conv_expr (&se, n->u2.allocator);
                              gfc_add_block_to_block (block, &se.pre);
-                             allocator_ = gfc_evaluate_now (se.expr, block);
+                             t = se.expr;
+                             if (DECL_P (t) && se.post.head == NULL_TREE)
+                               allocator_ = (POINTER_TYPE_P (TREE_TYPE (t))
+                                             ? build_fold_indirect_ref (t): t);
+                             else
+                               allocator_ = gfc_evaluate_now (t, block);
                              gfc_add_block_to_block (block, &se.post);
                            }
                          OMP_CLAUSE_ALLOCATE_ALLOCATOR (node) = allocator_;
@@ -5435,14 +5440,36 @@ gfc_trans_omp_clauses (stmtblock_t *block, 
gfc_omp_clauses *clauses,
            }
          break;
        case OMP_LIST_USES_ALLOCATORS:
-         /* Ignore omp_null_allocator and pre-defined allocators as no
-            special treatment is needed. */
          for (; n != NULL; n = n->next)
-           if (n->sym->attr.flavor == FL_VARIABLE)
-             break;
-         if (n != NULL)
-           sorry_at (input_location, "%<uses_allocators%> clause with traits "
-                                     "and memory spaces");
+           {
+             if (!n->sym->attr.referenced)
+               continue;
+             tree node = build_omp_clause (input_location,
+                                           OMP_CLAUSE_USES_ALLOCATORS);
+             tree t;
+             if (n->sym->attr.flavor == FL_VARIABLE)
+               t = gfc_get_symbol_decl (n->sym);
+             else
+               {
+                 t = gfc_conv_mpz_to_tree (n->sym->value->value.integer,
+                                           n->sym->ts.kind);
+                 t = fold_convert (ptr_type_node, t);
+               }
+             OMP_CLAUSE_USES_ALLOCATORS_ALLOCATOR(node) = t;
+             if (n->u.memspace_sym)
+               {
+                 n->u.memspace_sym->attr.referenced = true;
+                 OMP_CLAUSE_USES_ALLOCATORS_MEMSPACE (node)
+                   = gfc_get_symbol_decl (n->u.memspace_sym);
+               }
+             if (n->u2.traits_sym)
+               {
+                 n->u2.traits_sym->attr.referenced = true;
+                 OMP_CLAUSE_USES_ALLOCATORS_TRAITS (node)
+                   = gfc_get_symbol_decl (n->u2.traits_sym);
+               }
+             omp_clauses = gfc_trans_add_clause (node, omp_clauses);
+           }
          break;
        default:
          break;
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index b1f263a0826..54e245d3ffd 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -185,6 +185,9 @@ enum gimplify_omp_var_data
   /* Flag for GOVD_FIRSTPRIVATE: OMP_CLAUSE_FIRSTPRIVATE_IMPLICIT.  */
   GOVD_FIRSTPRIVATE_IMPLICIT = 0x4000000,
 
+  /* Flag to indicate this an allocator for the uses_allocators clause.  */
+  GOVD_USES_ALLOCATORS_ALLOCATOR = 0x8000000,
+
   GOVD_DATA_SHARE_CLASS = (GOVD_SHARED | GOVD_PRIVATE | GOVD_FIRSTPRIVATE
                           | GOVD_LASTPRIVATE | GOVD_REDUCTION | GOVD_LINEAR
                           | GOVD_LOCAL)
@@ -1291,18 +1294,46 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
                 dynamic_allocators clause is present in the same compilation
                 unit.  */
              bool missing_dyn_alloc = false;
-             if (alloc == NULL_TREE
-                 && ((omp_requires_mask & OMP_REQUIRES_DYNAMIC_ALLOCATORS)
-                     == 0))
+             if ((omp_requires_mask & OMP_REQUIRES_DYNAMIC_ALLOCATORS) == 0)
                {
                  /* This comes too early for omp_discover_declare_target...,
                     but should at least catch the most common cases.  */
                  missing_dyn_alloc
-                   = cgraph_node::get (current_function_decl)->offloadable;
+                   = (alloc == NULL_TREE
+                      && cgraph_node::get 
(current_function_decl)->offloadable);
                  for (struct gimplify_omp_ctx *ctx2 = ctx;
                       ctx2 && !missing_dyn_alloc; ctx2 = ctx2->outer_context)
                    if (ctx2->code == OMP_TARGET)
-                     missing_dyn_alloc = true;
+                     {
+                       if (alloc == NULL_TREE)
+                         missing_dyn_alloc = true;
+                       else if (TREE_CODE (alloc) != INTEGER_CST)
+                         {
+                           tree alloc2 = alloc;
+                           if (TREE_CODE (alloc2) == MEM_REF
+                               || TREE_CODE (alloc2) == INDIRECT_REF)
+                             alloc2 = TREE_OPERAND (alloc2, 0);
+                           tree c2;
+                           for (c2 = ctx2->clauses; c2;
+                                c2 = OMP_CLAUSE_CHAIN (c2))
+                             if (OMP_CLAUSE_CODE (c2)
+                                 == OMP_CLAUSE_USES_ALLOCATORS)
+                               {
+                                 tree t2
+                                   = OMP_CLAUSE_USES_ALLOCATORS_ALLOCATOR (c2);
+                                 if (operand_equal_p (alloc2, t2))
+                                   break;
+                               }
+                           if (c2 == NULL_TREE)
+                             error_at (EXPR_LOC_OR_LOC (
+                                         alloc, DECL_SOURCE_LOCATION (t)),
+                                       "%qE in %<allocator%> clause inside a "
+                                       "target region must be specified in an "
+                                       "%<uses_allocators%> clause on the "
+                                       "%<target%> directive", alloc2);
+                         }
+                       break;
+                     }
                }
              if (missing_dyn_alloc)
                error_at (DECL_SOURCE_LOCATION (t),
@@ -10868,6 +10899,7 @@ omp_get_attachment (omp_mapping_group *grp)
          case GOMP_MAP_FIRSTPRIVATE_POINTER:
          case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
          case GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION:
+         case GOMP_MAP_USES_ALLOCATORS:
            return NULL_TREE;
 
          case GOMP_MAP_ATTACH_DETACH:
@@ -10912,6 +10944,7 @@ omp_get_attachment (omp_mapping_group *grp)
     case GOMP_MAP_FIRSTPRIVATE_INT:
     case GOMP_MAP_USE_DEVICE_PTR:
     case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
+    case GOMP_MAP_USES_ALLOCATORS:
       return NULL_TREE;
 
     default:
@@ -11200,6 +11233,7 @@ omp_group_base (omp_mapping_group *grp, unsigned int 
*chained,
     case GOMP_MAP_FIRSTPRIVATE_INT:
     case GOMP_MAP_USE_DEVICE_PTR:
     case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
+    case GOMP_MAP_USES_ALLOCATORS:
       return NULL_TREE;
 
     case GOMP_MAP_FIRSTPRIVATE_POINTER:
@@ -14826,8 +14860,15 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq 
*pre_p,
          break;
 
        case OMP_CLAUSE_USES_ALLOCATORS:
-         sorry_at (OMP_CLAUSE_LOCATION (c), "%<uses_allocators%> clause");
-         remove = 1;
+         if (TREE_CODE (OMP_CLAUSE_USES_ALLOCATORS_ALLOCATOR (c))
+             != INTEGER_CST)
+           {
+             decl = OMP_CLAUSE_USES_ALLOCATORS_ALLOCATOR (c);
+             omp_add_variable (ctx, decl,
+                               GOVD_SEEN | GOVD_USES_ALLOCATORS_ALLOCATOR);
+           }
+         else
+           remove = true;
          break;
 
        case OMP_CLAUSE_ORDERED:
@@ -14986,6 +15027,49 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq 
*pre_p,
              remove = true;
              break;
            }
+         if ((omp_requires_mask & OMP_REQUIRES_DYNAMIC_ALLOCATORS) == 0
+             && OMP_CLAUSE_ALLOCATE_ALLOCATOR (c)
+             && TREE_CODE (OMP_CLAUSE_ALLOCATE_ALLOCATOR (c)) != INTEGER_CST)
+           {
+             tree allocator = OMP_CLAUSE_ALLOCATE_ALLOCATOR (c);
+             tree clauses = NULL_TREE;
+
+             /* Get clause list of the nearest enclosing target construct.  */
+             if (ctx->code == OMP_TARGET)
+               clauses = *orig_list_p;
+             else
+               {
+                 struct gimplify_omp_ctx *tctx = ctx->outer_context;
+                 while (tctx && tctx->code != OMP_TARGET)
+                   tctx = tctx->outer_context;
+                 if (tctx)
+                   clauses = tctx->clauses;
+               }
+
+             if (clauses)
+               {
+                 tree uc;
+                 if (TREE_CODE (allocator) == MEM_REF
+                     || TREE_CODE (allocator) == INDIRECT_REF)
+                   allocator = TREE_OPERAND (allocator, 0);
+                 for (uc = clauses; uc; uc = OMP_CLAUSE_CHAIN (uc))
+                   if (OMP_CLAUSE_CODE (uc) == OMP_CLAUSE_USES_ALLOCATORS)
+                     {
+                       tree uc_allocator
+                         = OMP_CLAUSE_USES_ALLOCATORS_ALLOCATOR (uc);
+                       if (operand_equal_p (allocator, uc_allocator))
+                         break;
+                     }
+                 if (uc == NULL_TREE)
+                   {
+                     error_at (OMP_CLAUSE_LOCATION (c), "allocator %qE "
+                               "requires %<uses_allocators(%E)%> clause in "
+                               "target region", allocator, allocator);
+                     remove = true;
+                     break;
+                   }
+               }
+           }
          if (gimplify_expr (&OMP_CLAUSE_ALLOCATE_ALLOCATOR (c), pre_p, NULL,
                             is_gimple_val, fb_rvalue) == GS_ERROR)
            {
@@ -15304,6 +15388,8 @@ gimplify_adjust_omp_clauses_1 (splay_tree_node n, void 
*data)
       code = OMP_CLAUSE__CONDTEMP_;
       gimple_add_tmp_var (decl);
     }
+  else if (flags & GOVD_USES_ALLOCATORS_ALLOCATOR)
+    return 0;
   else
     gcc_unreachable ();
 
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index eef0b418b9b..7ffe65d86e6 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -1193,6 +1193,36 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
            && omp_maybe_offloaded_ctx (ctx))
          error_at (OMP_CLAUSE_LOCATION (c), "%<allocate%> clause must"
                    " specify an allocator here");
+       if ((omp_requires_mask & OMP_REQUIRES_DYNAMIC_ALLOCATORS) == 0
+           && OMP_CLAUSE_ALLOCATE_ALLOCATOR (c) != NULL_TREE
+           && DECL_P (OMP_CLAUSE_ALLOCATE_ALLOCATOR (c))
+           && !DECL_ARTIFICIAL (OMP_CLAUSE_ALLOCATE_ALLOCATOR (c)))
+         {
+           tree alloc2 = OMP_CLAUSE_ALLOCATE_ALLOCATOR (c);
+           if (TREE_CODE (alloc2) == MEM_REF
+               || TREE_CODE (alloc2) == INDIRECT_REF)
+             alloc2 = TREE_OPERAND (alloc2, 0);
+           omp_context *ctx2 = ctx;
+           for (; ctx2; ctx2 = ctx2->outer)
+             if (is_gimple_omp_offloaded (ctx2->stmt))
+               break;
+           if (ctx2 != NULL)
+             {
+               tree c2 = gimple_omp_target_clauses (ctx2->stmt);
+               for (; c2; c2 = OMP_CLAUSE_CHAIN (c2))
+                 if (OMP_CLAUSE_CODE (c2) == OMP_CLAUSE_USES_ALLOCATORS
+                     && operand_equal_p (
+                          alloc2, OMP_CLAUSE_USES_ALLOCATORS_ALLOCATOR (c2)))
+                   break;
+               if (c2 == NULL_TREE)
+                 error_at (EXPR_LOC_OR_LOC (OMP_CLAUSE_ALLOCATE_ALLOCATOR (c),
+                                            OMP_CLAUSE_LOCATION (c)),
+                           "allocator %qE in %<allocate%> clause inside a "
+                           "target region must be specified in an "
+                           "%<uses_allocators%> clause on the %<target%> "
+                           "directive", alloc2);
+             }
+         }
        if (ctx->allocate_map == NULL)
          ctx->allocate_map = new hash_map<tree, tree>;
        tree val = integer_zero_node;
@@ -1797,6 +1827,14 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
        case OMP_CLAUSE_DEVICE_TYPE:
          break;
 
+       case OMP_CLAUSE_USES_ALLOCATORS:
+         decl = OMP_CLAUSE_USES_ALLOCATORS_ALLOCATOR (c);
+         gcc_assert (DECL_P (decl));
+         gcc_assert (is_gimple_omp_offloaded (ctx->stmt));
+         install_var_field (decl, false, 3, ctx);
+         install_var_local (decl, ctx);
+         break;
+
        case OMP_CLAUSE_ALIGNED:
          decl = OMP_CLAUSE_DECL (c);
          if (is_global_var (decl)
@@ -2022,6 +2060,7 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
        case OMP_CLAUSE_USE:
        case OMP_CLAUSE_DESTROY:
        case OMP_CLAUSE_DEVICE_TYPE:
+       case OMP_CLAUSE_USES_ALLOCATORS:
          break;
 
        case OMP_CLAUSE__CACHE_:
@@ -13215,6 +13254,14 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, 
omp_context *ctx)
            sorry_at (OMP_CLAUSE_LOCATION (c),
                      "only the %<device_type(any)%> is supported");
          break;
+      case OMP_CLAUSE_USES_ALLOCATORS:
+       allocator = OMP_CLAUSE_USES_ALLOCATORS_ALLOCATOR (c);
+       tree new_allocator = lookup_decl (allocator, ctx);
+       x = build_receiver_ref (allocator, false, ctx);
+       SET_DECL_VALUE_EXPR (new_allocator, x);
+       DECL_HAS_VALUE_EXPR_P (new_allocator) = 1;
+       map_cnt++;
+       break;
       }
 
   if (offloaded)
@@ -13948,6 +13995,80 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, 
omp_context *ctx)
            CONSTRUCTOR_APPEND_ELT (vkind, purpose,
                                    build_int_cstu (tkind_type, tkind));
            break;
+
+         case OMP_CLAUSE_USES_ALLOCATORS:
+           tree allocator = OMP_CLAUSE_USES_ALLOCATORS_ALLOCATOR (c);
+           tree memspace = OMP_CLAUSE_USES_ALLOCATORS_MEMSPACE (c);
+           tree traits = OMP_CLAUSE_USES_ALLOCATORS_TRAITS (c);
+
+           tree ntraits, traits_var;
+           if (traits == NULL_TREE)
+             {
+               ntraits = integer_zero_node;
+               traits_var = null_pointer_node;
+             }
+           else if (DECL_INITIAL (traits))
+             {
+               location_t loc = OMP_CLAUSE_LOCATION (c);
+               ntraits = array_type_nelts_top (TREE_TYPE (traits));
+               tree t = DECL_INITIAL (traits);
+               t = get_initialized_tmp_var (t, &ilist, NULL);
+               traits_var = build_fold_addr_expr_loc (loc, t);
+             }
+           else
+             {
+               /* This happens for VLAs, which probably aren't useful
+                  because they can't be const initialized in the same
+                  scope....  is there something else?  */
+               location_t loc = OMP_CLAUSE_LOCATION (c);
+               gcc_assert (TREE_CODE (TREE_TYPE (traits)) == ARRAY_TYPE);
+               ntraits = array_type_nelts_top (TREE_TYPE (traits));
+               traits_var = build_fold_addr_expr_loc (loc, traits);
+             }
+
+           if (memspace == NULL_TREE)
+             memspace = build_int_cst (pointer_sized_int_node, 0);
+           else
+             memspace = fold_convert (pointer_sized_int_node, memspace);
+
+           tree arr_type
+             = build_array_type_nelts (pointer_sized_int_node, 3);
+           tree uses_allocators_descr
+             = create_tmp_var (arr_type, "uses_allocator_descr");
+           tree ua_descr[3];
+           for (int i = 0; i < 3; i++)
+             ua_descr[i] = build4 (ARRAY_REF, pointer_sized_int_node,
+                                   uses_allocators_descr,
+                                   build_int_cst (size_type_node, i),
+                                   NULL_TREE, NULL_TREE);
+           gimplify_assign (ua_descr[0],
+                            fold_convert (pointer_sized_int_node, memspace),
+                            &ilist);
+           gimplify_assign (ua_descr[1],
+                            fold_convert (pointer_sized_int_node, ntraits),
+                            &ilist);
+           gimplify_assign (ua_descr[2],
+                            fold_convert (pointer_sized_int_node, traits_var),
+                            &ilist);
+
+           x = build_sender_ref (allocator, ctx);
+           tree ptr = build_fold_addr_expr (uses_allocators_descr);
+           gimplify_assign (x, fold_convert (TREE_TYPE (x), ptr), &ilist);
+
+           s = size_int (0);
+           purpose = size_int (map_idx++);
+           CONSTRUCTOR_APPEND_ELT (vsize, purpose, s);
+           tkind = GOMP_MAP_USES_ALLOCATORS;
+           gcc_checking_assert (tkind
+                                < (HOST_WIDE_INT_C (1U) << talign_shift));
+           talign = TYPE_ALIGN_UNIT (pointer_sized_int_node);
+           talign = ceil_log2 (talign);
+           tkind |= talign << talign_shift;
+           gcc_checking_assert (tkind
+                                <= tree_to_uhwi (TYPE_MAX_VALUE (tkind_type)));
+           CONSTRUCTOR_APPEND_ELT (vkind, purpose,
+                                   build_int_cstu (tkind_type, tkind));
+           break;
          }
 
       gcc_assert (map_idx == map_cnt);
diff --git a/gcc/testsuite/c-c++-common/gomp/uses_allocators-7.c 
b/gcc/testsuite/c-c++-common/gomp/uses_allocators-7.c
deleted file mode 100644
index ce43d70ecf2..00000000000
--- a/gcc/testsuite/c-c++-common/gomp/uses_allocators-7.c
+++ /dev/null
@@ -1,83 +0,0 @@
-// { dg-do compile }
-
-//#include <omp.h>
-
-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,
-  ompx_gnu_managed_mem_space = 200,
-  __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,
-  ompx_gnu_pinned_mem_alloc = 200,
-  ompx_gnu_managed_mem_alloc = 201,
-  __omp_allocator_handle_t_max__ = __UINTPTR_MAX__
-} omp_allocator_handle_t;
-
-typedef struct omp_alloctrait_t
-{
-//  omp_alloctrait_key_t key;
-//  omp_uintptr_t value;
-} omp_alloctrait_t;
-
-
-void f()
-{
- omp_allocator_handle_t my;
- struct t {
-   omp_allocator_handle_t a1;
- } s;
-
-const omp_allocator_handle_t my3 = (omp_allocator_handle_t) 300;
-omp_allocator_handle_t my4[1];
-const omp_alloctrait_t t[] = {};
- #pragma omp target uses_allocators(my, omp_default_mem_alloc, 
omp_null_allocator)  // OK
-   ;
- #pragma omp target uses_allocators(my) firstprivate(my) // { dg-error "'my' 
appears more than once in data clauses" }
-   ;
- #pragma omp target private(my) uses_allocators(my) // { dg-error "'my' 
appears more than once in data clauses" }
-   ;
- #pragma omp target uses_allocators(my3)
-   ;
- #pragma omp target uses_allocators(s.a1)
-   // { dg-error "expected '\\)' before '\\.' token" "" { target *-*-* } .-1 }
-   // { dg-error "allocator 's' must be of 'omp_allocator_handle_t' type" "" { 
target *-*-* } .-2 }
-   ;
- #pragma omp target uses_allocators(my4)
-   // { dg-error "allocator 'my4' must be of 'omp_allocator_handle_t' type" "" 
{ target *-*-* } .-1 }
-   ;
- #pragma omp target uses_allocators(my4[0])
-   // { dg-error "expected '\\)' before '\\\[' token" "" { target *-*-* } .-1 }
-   // { dg-error "allocator 'my4' must be of 'omp_allocator_handle_t' type" "" 
{ target *-*-* } .-2 }
-   ;
- #pragma omp target uses_allocators(memspace(omp_default_mem_space) : my, 
my(t)) // { dg-error "legacy 'my\\(t\\)' traits syntax not allowed in 
'uses_allocators' clause when using modifiers" }
-   ;
-}
-
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target 
*-*-* } 57 }
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target 
*-*-* } 61 }
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target 
*-*-* } 63 }
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target 
*-*-* } 76 }
diff --git a/gcc/testsuite/c-c++-common/gomp/uses_allocators-8.c 
b/gcc/testsuite/c-c++-common/gomp/uses_allocators-8.c
deleted file mode 100644
index 3ab87576c41..00000000000
--- a/gcc/testsuite/c-c++-common/gomp/uses_allocators-8.c
+++ /dev/null
@@ -1,61 +0,0 @@
-// { dg-do compile }
-// { dg-additional-options "-fdump-tree-original" }
-
-//#include <omp.h>
-
-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,
-  ompx_gnu_managed_mem_space = 200,
-  __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,
-  ompx_gnu_pinned_mem_alloc = 200,
-  ompx_gnu_managed_mem_alloc = 201,
-  __omp_allocator_handle_t_max__ = __UINTPTR_MAX__
-} omp_allocator_handle_t;
-
-typedef struct omp_alloctrait_t
-{
-//  omp_alloctrait_key_t key;
-//  omp_uintptr_t value;
-} omp_alloctrait_t;
-
-
-void f()
-{
- omp_allocator_handle_t my, my2, my3, my4;
-const omp_alloctrait_t t[] = {};
-const omp_alloctrait_t t2[] = {};
- #pragma omp target uses_allocators(traits(t), memspace(omp_high_bw_mem_space) 
: my; omp_default_mem_alloc, omp_null_allocator; my2; traits(t2) : my3; 
memspace(omp_large_cap_mem_space) : my4)
-   ;
-}
-
-// { dg-final { scan-tree-dump "#pragma omp target 
uses_allocators\\(memspace\\(1\\), traits\\(\\) : my4\\) 
uses_allocators\\(memspace\\(\\), traits\\(t2\\) : my3\\) 
uses_allocators\\(memspace\\(\\), traits\\(\\) : my2\\) 
uses_allocators\\(memspace\\(3\\), traits\\(t\\) : my\\)" "original" { target c 
} } }
-// { dg-final { scan-tree-dump "#pragma omp target 
uses_allocators\\(memspace\\(omp_large_cap_mem_space\\), traits\\(\\) : my4\\) 
uses_allocators\\(memspace\\(\\), traits\\(t2\\) : my3\\) 
uses_allocators\\(memspace\\(\\), traits\\(\\) : my2\\) 
uses_allocators\\(memspace\\(omp_high_bw_mem_space\\), traits\\(t\\) : my\\)" 
"original" { target c++ } } }
-
-
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target 
*-*-* } 53 }
diff --git a/gcc/testsuite/gcc.dg/gomp/deprecate-2.c 
b/gcc/testsuite/gcc.dg/gomp/deprecate-2.c
index 46d286141ef..b7689dd1554 100644
--- a/gcc/testsuite/gcc.dg/gomp/deprecate-2.c
+++ b/gcc/testsuite/gcc.dg/gomp/deprecate-2.c
@@ -42,6 +42,3 @@ void f()
 
 // { dg-warning "42: the specification of arguments to 'uses_allocators' where 
each item is of the form 'allocator\\(traits\\)' is deprecated since OpenMP 5.2 
\\\[-Wdeprecated-openmp\\\]" "" { target *-*-* }  34 }
 }
-
-// { dg-excess-errors "sorry, unimplemented: 'uses_allocators' clause" }
-// { dg-excess-errors "sorry, unimplemented: 'uses_allocators' clause" }
diff --git a/gcc/testsuite/gfortran.dg/gomp/allocate-1.f90 
b/gcc/testsuite/gfortran.dg/gomp/allocate-1.f90
index 8bc6b768778..0463f0e0af9 100644
--- a/gcc/testsuite/gfortran.dg/gomp/allocate-1.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/allocate-1.f90
@@ -24,6 +24,10 @@ module omp_lib_kinds
      parameter :: omp_pteam_mem_alloc = 7
   integer (kind=omp_allocator_handle_kind), &
      parameter :: omp_thread_mem_alloc = 8
+
+  integer, parameter :: omp_memspace_handle_kind = c_intptr_t
+  integer (omp_memspace_handle_kind), &
+     parameter :: omp_default_mem_space = 0
 end module
 
 subroutine bar (a, b, c)
@@ -80,7 +84,8 @@ subroutine foo(x, y)
   
   !$omp target teams distribute parallel do private (x) firstprivate (y) &
   !$omp allocate ((omp_default_mem_alloc + 0):z) allocate &
-  !$omp (omp_default_mem_alloc: x, y) allocate (h: r) lastprivate (z) 
reduction(+:r)
+  !$omp (omp_default_mem_alloc: x, y) allocate (h: r) lastprivate (z) 
reduction(+:r) &
+  !$omp uses_allocators(memspace(omp_default_mem_space) : h)
   do i = 1, 10
     call bar (0, x, z);
     call bar2 (1, y, r);
diff --git a/gcc/testsuite/gfortran.dg/gomp/scope-6.f90 
b/gcc/testsuite/gfortran.dg/gomp/scope-6.f90
index 4c4f5e034f7..39a65904c33 100644
--- a/gcc/testsuite/gfortran.dg/gomp/scope-6.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/scope-6.f90
@@ -20,4 +20,4 @@ contains
   end
 end
 
-! { dg-final { scan-tree-dump "omp scope private\\(a\\) firstprivate\\(b\\) 
reduction\\(\\+:c\\) allocate\\(allocator\\(D\\.\[0-9\]+\\):a\\) 
allocate\\(allocator\\(D\\.\[0-9\]+\\):b\\) 
allocate\\(allocator\\(D\\.\[0-9\]+\\):c\\)" "original" } }
+! { dg-final { scan-tree-dump "omp scope private\\(a\\) firstprivate\\(b\\) 
reduction\\(\\+:c\\) allocate\\(allocator\\(h\\):a\\) 
allocate\\(allocator\\(h\\):b\\) allocate\\(allocator\\(h\\):c\\)" "original" } 
}
diff --git a/gcc/tree-pretty-print.cc b/gcc/tree-pretty-print.cc
index 0674a27501b..96473e52b3b 100644
--- a/gcc/tree-pretty-print.cc
+++ b/gcc/tree-pretty-print.cc
@@ -1154,6 +1154,9 @@ dump_omp_clause (pretty_printer *pp, tree clause, int 
spc, dump_flags_t flags)
        case GOMP_MAP_POP_MAPPER_NAME:
          pp_string (pp, "pop_mapper");
          break;
+       case GOMP_MAP_USES_ALLOCATORS:
+         pp_string (pp, "uses_allocators");
+         break;
        default:
          gcc_unreachable ();
        }
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index def838a949d..3917788d1d1 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -195,6 +195,11 @@ enum gomp_map_kind
     GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION
       =                                        (GOMP_MAP_DEEP_COPY | 2),
 
+    /* Used to pass a descriptor for an OpenMP uses_allocators clause,
+       also serves as an effective 'firstprivate' clause for the allocator
+       variable.  */
+    GOMP_MAP_USES_ALLOCATORS =         (GOMP_MAP_DEEP_COPY | 3),
+
     /* Internal to GCC, not used in libgomp.  */
     /* Do not map, but pointer assign a pointer instead.  */
     GOMP_MAP_FIRSTPRIVATE_POINTER =    (GOMP_MAP_LAST | 1),
diff --git a/libgomp/allocator.c b/libgomp/allocator.c
index d2e88d97846..5576d92e21d 100644
--- a/libgomp/allocator.c
+++ b/libgomp/allocator.c
@@ -247,14 +247,24 @@ struct omp_allocator_data
   unsigned int fallback : 8;
   unsigned int pinned : 1;
   unsigned int partition : 7;
-#if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA)
+  /* To unify the format of this type across host/accelerator, enable
+     this field unconditionally when offload is enabled.  */
+  #if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) ||  \
+    defined(LIBGOMP_OFFLOAD_HOST) || defined(LIBGOMP_OFFLOADED_ONLY)
   unsigned int memkind : 8;
 #endif
+  /* Note: we now require __sync builtins for offload host/accelerator,
+     checked during configuration. This lock should never be enabled
+     for offload configs.  */
 #ifndef HAVE_SYNC_BUILTINS
   gomp_mutex_t lock;
 #endif
 };
 
+/* Size of allocator data, exported within libgomp.  */
+const size_t gomp_omp_allocator_data_size attribute_hidden
+  = sizeof (struct omp_allocator_data);
+
 struct omp_mem_header
 {
   void *ptr;
@@ -404,38 +414,44 @@ gomp_get_memkind (void)
 }
 #endif
 
-omp_allocator_handle_t
-omp_init_allocator (omp_memspace_handle_t memspace, int ntraits,
-                   const omp_alloctrait_t traits[])
+static omp_allocator_handle_t
+gomp_init_allocator_data (struct omp_allocator_data *data,
+                         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
+  data->memspace = memspace;
+  data->alignment = 1;
+  data->pool_size = ~(uintptr_t) 0;
+  data->used_pool_size = 0;
+  data->fb_data = 0;
+  data->sync_hint = omp_atv_contended;
+  data->access = omp_atv_all;
+  data->fallback = omp_atv_default_mem_fb;
+  data->pinned = omp_atv_false;
+  data->partition = omp_atv_environment;
+#if defined(LIBGOMP_USE_MEMKIND) || defined(LIBGOMP_USE_LIBNUMA) ||    \
+  defined(LIBGOMP_OFFLOAD_HOST) || defined(LIBGOMP_OFFLOADED_ONLY)
+  data->memkind = GOMP_MEMKIND_NONE;
 #endif
-      };
-  struct omp_allocator_data *ret;
-  int i;
 
   if (memspace > omp_max_predefined_mem_space
       && (memspace < ompx_gnu_min_predefined_mem_space
          || memspace > ompx_gnu_max_predefined_mem_space))
     return omp_null_allocator;
-  for (i = 0; i < ntraits; i++)
+  for (int 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;
+           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;
+           data->sync_hint = traits[i].value;
            break;
          default:
            return omp_null_allocator;
@@ -444,25 +460,25 @@ omp_init_allocator (omp_memspace_handle_t memspace, int 
ntraits,
       case omp_atk_alignment:
         if (traits[i].value == omp_atv_default)
          {
-           data.alignment = 1;
+           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;
+       data->alignment = traits[i].value;
        break;
       case omp_atk_access:
        switch (traits[i].value)
          {
          case omp_atv_default:
-           data.access = omp_atv_all;
+           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;
+           data->access = traits[i].value;
            break;
          default:
            return omp_null_allocator;
@@ -470,38 +486,38 @@ omp_init_allocator (omp_memspace_handle_t memspace, int 
ntraits,
        break;
       case omp_atk_pool_size:
        if (traits[i].value == omp_atv_default)
-         data.pool_size = ~(uintptr_t) 0;
+         data->pool_size = ~(uintptr_t) 0;
        else
-         data.pool_size = traits[i].value;
+         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;
+           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;
+           data->fallback = traits[i].value;
            break;
          default:
            return omp_null_allocator;
          }
        break;
       case omp_atk_fb_data:
-       data.fb_data = traits[i].value;
+       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;
+           data->pinned = omp_atv_false;
            break;
          case omp_atv_true:
-           data.pinned = omp_atv_true;
+           data->pinned = omp_atv_true;
            break;
          default:
            return omp_null_allocator;
@@ -511,13 +527,13 @@ omp_init_allocator (omp_memspace_handle_t memspace, int 
ntraits,
        switch (traits[i].value)
          {
          case omp_atv_default:
-           data.partition = omp_atv_environment;
+           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;
+           data->partition = traits[i].value;
            break;
          default:
            return omp_null_allocator;
@@ -527,8 +543,8 @@ omp_init_allocator (omp_memspace_handle_t memspace, int 
ntraits,
        return omp_null_allocator;
       }
 
-  if (data.alignment < sizeof (void *))
-    data.alignment = sizeof (void *);
+  if (data->alignment < sizeof (void *))
+    data->alignment = sizeof (void *);
 
   switch (memspace)
     {
@@ -536,57 +552,75 @@ omp_init_allocator (omp_memspace_handle_t memspace, int 
ntraits,
     case omp_high_bw_mem_space:
       struct gomp_memkind_data *memkind_data;
       memkind_data = gomp_get_memkind ();
-      if (data.partition == omp_atv_interleaved
+      if (data->partition == omp_atv_interleaved
          && memkind_data->kinds[GOMP_MEMKIND_HBW_INTERLEAVE])
        {
-         data.memkind = 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;
+         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;
+       data->memkind = GOMP_MEMKIND_DAX_KMEM_ALL;
       else if (memkind_data->kinds[GOMP_MEMKIND_DAX_KMEM])
-       data.memkind = GOMP_MEMKIND_DAX_KMEM;
+       data->memkind = GOMP_MEMKIND_DAX_KMEM;
       break;
 #endif
     default:
 #ifdef LIBGOMP_USE_MEMKIND
-      if (data.partition == omp_atv_interleaved)
+      if (data->partition == omp_atv_interleaved)
        {
          memkind_data = gomp_get_memkind ();
          if (memkind_data->kinds[GOMP_MEMKIND_INTERLEAVE])
-           data.memkind = 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)
+  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;
+       data->memkind = GOMP_MEMKIND_LIBNUMA;
     }
 #endif
 
-  /* Reject unsupported memory spaces.  */
-  if (!MEMSPACE_VALIDATE (data.memspace, data.access, data.pinned))
-    return omp_null_allocator;
+  return (omp_allocator_handle_t) data;
+}
 
-  ret = gomp_malloc (sizeof (struct omp_allocator_data));
-  *ret = data;
+omp_allocator_handle_t
+omp_init_allocator (omp_memspace_handle_t memspace, int ntraits,
+                   const omp_alloctrait_t traits[])
+{
+  struct omp_allocator_data *data
+    = gomp_malloc (sizeof (struct omp_allocator_data));
+
+  omp_allocator_handle_t ret
+    = gomp_init_allocator_data (data, memspace, ntraits, traits);
+
+  if (ret == omp_null_allocator
+      /* Reject unsupported memory spaces.  */
+      || !MEMSPACE_VALIDATE (data->memspace, data->access, data->pinned))
+    {
+      ret = omp_null_allocator;
+      free (data);
+    }
+  else
+    {
 #ifndef HAVE_SYNC_BUILTINS
-  gomp_mutex_init (&ret->lock);
+      gomp_mutex_init (data->lock);
 #endif
-  return (omp_allocator_handle_t) ret;
+    }
+
+  return ret;
 }
 
 void
@@ -1500,3 +1534,61 @@ fail:;
     }
   return NULL;
 }
+
+#if !defined(LIBGOMP_OFFLOADED_ONLY)
+
+/* Called from gomp_maps_vars, used to implement the effective
+   omp_init_allocator() call in an uses_allocators clause.
+
+   The memspace_validate hook is also used here to check things for
+   the offload accelerator on the host side.
+*/
+attribute_hidden uintptr_t
+gomp_map_omp_init_allocator (struct gomp_device_descr *devicep,
+                            struct goacc_asyncqueue *aq,
+                            struct gomp_coalesce_buf *cbuf,
+                            void *allocator_data_devaddr, void *descr_ptr)
+{
+  uintptr_t *descr = (uintptr_t *) descr_ptr;
+
+  omp_memspace_handle_t memspace = (omp_memspace_handle_t) descr[0];
+  int ntraits = (int) descr[1];
+  omp_alloctrait_t *traits = (omp_alloctrait_t *) descr[2];
+
+  struct omp_allocator_data data;
+  omp_allocator_handle_t ret = gomp_init_allocator_data (&data, memspace,
+                                                        ntraits, traits);
+  if (ret != omp_null_allocator)
+    {
+      if (devicep)
+       {
+         /* Do memspace validation for the offload target, using the
+            offload plugin.  */
+         if (devicep->memspace_validate_func
+             && !devicep->memspace_validate_func (data.memspace, data.access))
+           return (uintptr_t) omp_null_allocator;
+
+         /* Copy to device. This is now the device-side omp_allocator_data.  */
+         gomp_copy_host2dev (devicep, aq, allocator_data_devaddr, &data,
+                             sizeof (struct omp_allocator_data), true, cbuf);
+       }
+      else
+       {
+         /* Used for host fallback case.  */
+
+         if (!MEMSPACE_VALIDATE (data.memspace, data.access, data.pinned))
+           /* Reject unsupported memory spaces.  */
+           return omp_null_allocator;
+
+         memcpy (allocator_data_devaddr, &data,
+                 sizeof (struct omp_allocator_data));
+#ifndef HAVE_SYNC_BUILTINS
+         gomp_mutex_init
+           (((struct omp_allocator_data *) allocator_data_devaddr)->lock);
+#endif
+       }
+      ret = (omp_allocator_handle_t) allocator_data_devaddr;
+    }
+  return (uintptr_t) ret;
+}
+#endif
diff --git a/libgomp/config/gcn/allocator.c b/libgomp/config/gcn/allocator.c
index b3d7bcef7d9..f09d19cc755 100644
--- a/libgomp/config/gcn/allocator.c
+++ b/libgomp/config/gcn/allocator.c
@@ -110,6 +110,10 @@ gcn_memspace_realloc (omp_memspace_handle_t memspace, void 
*addr,
     return realloc (addr, size);
 }
 
+/* The MEMSPACE_VALIDATE implementation should be kept in sync with
+   the GOMP_OFFLOAD_memspace_validate plugin hook function in
+   plugin/plugin-gcn.c.  */
+
 static inline int
 gcn_memspace_validate (omp_memspace_handle_t memspace, unsigned access)
 {
diff --git a/libgomp/config/nvptx/allocator.c b/libgomp/config/nvptx/allocator.c
index ebbe9816909..577ebb7d1a5 100644
--- a/libgomp/config/nvptx/allocator.c
+++ b/libgomp/config/nvptx/allocator.c
@@ -54,6 +54,9 @@ asm (".extern .shared .u8 __nvptx_lowlat_pool[];\n");
 static void *
 nvptx_memspace_alloc (omp_memspace_handle_t memspace, size_t size)
 {
+#if __PTX_ISA_VERSION_MAJOR__ > 4                                      \
+    || (__PTX_ISA_VERSION_MAJOR__ == 4 && __PTX_ISA_VERSION_MINOR >= 1)
+  /* Low-latency memory is not available before PTX 4.1.  */
   if (memspace == omp_low_lat_mem_space)
     {
       char *shared_pool;
@@ -65,12 +68,16 @@ nvptx_memspace_alloc (omp_memspace_handle_t memspace, 
size_t size)
     /* No non-standard memspaces are implemented for device-side nvptx.  */
     return NULL;
   else
+#endif
     return malloc (size);
 }
 
 static void *
 nvptx_memspace_calloc (omp_memspace_handle_t memspace, size_t size)
 {
+#if __PTX_ISA_VERSION_MAJOR__ > 4                                      \
+    || (__PTX_ISA_VERSION_MAJOR__ == 4 && __PTX_ISA_VERSION_MINOR >= 1)
+  /* Low-latency memory is not available before PTX 4.1.  */
   if (memspace == omp_low_lat_mem_space)
     {
       char *shared_pool;
@@ -82,12 +89,16 @@ nvptx_memspace_calloc (omp_memspace_handle_t memspace, 
size_t size)
     /* No non-standard memspaces are implemented for device-side nvptx.  */
     return NULL;
   else
+#endif
     return calloc (1, size);
 }
 
 static void
 nvptx_memspace_free (omp_memspace_handle_t memspace, void *addr, size_t size)
 {
+#if __PTX_ISA_VERSION_MAJOR__ > 4                                      \
+    || (__PTX_ISA_VERSION_MAJOR__ == 4 && __PTX_ISA_VERSION_MINOR >= 1)
+  /* Low-latency memory is not available before PTX 4.1.  */
   if (memspace == omp_low_lat_mem_space)
     {
       char *shared_pool;
@@ -96,6 +107,7 @@ nvptx_memspace_free (omp_memspace_handle_t memspace, void 
*addr, size_t size)
       __nvptx_lowlat_free (shared_pool, addr, size);
     }
   else
+#endif
     free (addr);
 }
 
@@ -103,6 +115,9 @@ static void *
 nvptx_memspace_realloc (omp_memspace_handle_t memspace, void *addr,
                        size_t oldsize, size_t size)
 {
+#if __PTX_ISA_VERSION_MAJOR__ > 4 \
+    || (__PTX_ISA_VERSION_MAJOR__ == 4 && __PTX_ISA_VERSION_MINOR >= 1)
+  /* Low-latency memory is not available before PTX 4.1.  */
   if (memspace == omp_low_lat_mem_space)
     {
       char *shared_pool;
@@ -110,13 +125,21 @@ nvptx_memspace_realloc (omp_memspace_handle_t memspace, 
void *addr,
 
       return __nvptx_lowlat_realloc (shared_pool, addr, oldsize, size);
     }
-  else if (memspace > GOMP_OMP_PREDEF_MEMSPACE_MAX)
-    /* No non-standard memspaces are implemented for device-side nvptx.  */
-    return NULL;
   else
-    return realloc (addr, size);
+#endif
+    {
+      if (memspace > GOMP_OMP_PREDEF_MEMSPACE_MAX)
+       /* No non-standard memspaces are implemented for device-side nvptx.  */
+       return NULL;
+      else
+       return realloc (addr, size);
+    }
 }
 
+/* The MEMSPACE_VALIDATE implementation should be kept in sync with
+   the GOMP_OFFLOAD_memspace_validate plugin hook function in
+   plugin/plugin-nvptx.c.  */
+
 static inline int
 nvptx_memspace_validate (omp_memspace_handle_t memspace, unsigned access)
 {
diff --git a/libgomp/configure b/libgomp/configure
index 87e7855ca3e..c88d212504b 100755
--- a/libgomp/configure
+++ b/libgomp/configure
@@ -15782,6 +15782,12 @@ fi
 
 
 
+if test x"$offload_plugins" != x; then
+
+$as_echo "#define LIBGOMP_OFFLOAD_HOST 1" >>confdefs.h
+
+fi
+
 # Check for functions needed.
 for ac_func in getloadavg clock_gettime strtoull
 do :
@@ -17120,6 +17126,19 @@ $as_echo "#define HAVE_SYNC_BUILTINS 1" >>confdefs.h
 
   fi
 
+# If offloading is enabled, both offload hosts and accelerators are required 
to support
+# __sync_* builtins, to simplify architectural assumptions between both sides
+if test x$libgomp_cv_have_sync_builtins = xno; then
+  # We require accelerator targets to support __sync_* builtins.
+  if test x$libgomp_offloaded_only = xyes; then
+    as_fn_error $? "accelerator targets require __sync_val_compare_and_swap to 
build libgomp." "$LINENO" 5
+  fi
+  # Same for offload hosts.
+  if test x"$offload_plugins" = x; then
+    as_fn_error $? "offload hosts require __sync_val_compare_and_swap to build 
libgomp." "$LINENO" 5
+  fi
+fi
+
 XCFLAGS="$XCFLAGS$XPCFLAGS"
 
 # Add CET specific flags if CET is enabled
diff --git a/libgomp/configure.ac b/libgomp/configure.ac
index a7938beb1ca..6936fce3040 100644
--- a/libgomp/configure.ac
+++ b/libgomp/configure.ac
@@ -233,6 +233,11 @@ AC_CHECK_SIZEOF([void *])
 
 m4_include([plugin/configfrag.ac])
 
+if test x"$offload_plugins" != x; then
+  AC_DEFINE(LIBGOMP_OFFLOAD_HOST, 1,
+    [Define to 1 if building libgomp for a offload host])
+fi
+
 # Check for functions needed.
 AC_CHECK_FUNCS(getloadavg clock_gettime strtoull)
 AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign _aligned_malloc)
@@ -365,6 +370,19 @@ CFLAGS="$save_CFLAGS $XCFLAGS"
 # had a chance to set XCFLAGS.
 LIBGOMP_CHECK_SYNC_BUILTINS
 
+# If offloading is enabled, both offload hosts and accelerators are required 
to support
+# __sync_* builtins, to simplify architectural assumptions between both sides
+if test x$libgomp_cv_have_sync_builtins = xno; then
+  # We require accelerator targets to support __sync_* builtins.
+  if test x$libgomp_offloaded_only = xyes; then
+    AC_MSG_ERROR([accelerator targets require __sync_val_compare_and_swap to 
build libgomp.])
+  fi
+  # Same for offload hosts.
+  if test x"$offload_plugins" = x; then
+    AC_MSG_ERROR([offload hosts require __sync_val_compare_and_swap to build 
libgomp.])
+  fi
+fi
+
 XCFLAGS="$XCFLAGS$XPCFLAGS"
 
 # Add CET specific flags if CET is enabled
diff --git a/libgomp/libgomp-plugin.h b/libgomp/libgomp-plugin.h
index 60ac7201597..db7c03fb350 100644
--- a/libgomp/libgomp-plugin.h
+++ b/libgomp/libgomp-plugin.h
@@ -191,6 +191,7 @@ extern int GOMP_OFFLOAD_memcpy3d (int, int, size_t, size_t, 
size_t, void *,
                                  const void *, size_t, size_t, size_t, size_t,
                                  size_t);
 extern bool GOMP_OFFLOAD_memset (int, void *, int, size_t);
+extern int GOMP_OFFLOAD_memspace_validate (omp_memspace_handle_t, unsigned 
int);
 extern bool GOMP_OFFLOAD_can_run (void *);
 
 /* An opaque type, encapsulating the state required to launch a single
diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h
index 9510b148ed8..3c8a2a1115e 100644
--- a/libgomp/libgomp.h
+++ b/libgomp/libgomp.h
@@ -616,6 +616,7 @@ extern size_t gomp_affinity_format_len;
 extern int gomp_get_current_numa_node ();
 extern int gomp_get_numa_distance (int, int);
 extern uintptr_t gomp_def_allocator;
+extern const size_t gomp_omp_allocator_data_size;
 extern const struct gomp_default_icv gomp_default_icv_values;
 extern struct gomp_icv_list *gomp_initial_icv_list;
 extern struct gomp_offload_icv_list *gomp_offload_icv_list;
@@ -1472,6 +1473,7 @@ struct gomp_device_descr
   __typeof (GOMP_OFFLOAD_memcpy2d) *memcpy2d_func;
   __typeof (GOMP_OFFLOAD_memcpy3d) *memcpy3d_func;
   __typeof (GOMP_OFFLOAD_memset) *memset_func;
+  __typeof (GOMP_OFFLOAD_memspace_validate) *memspace_validate_func;
   struct {
     __typeof (GOMP_OFFLOAD_session_start) *start_func;
     __typeof (GOMP_OFFLOAD_session_allocate_target_var_table) *alloc_tvt_func;
@@ -1561,6 +1563,13 @@ extern bool gomp_remove_var (struct gomp_device_descr *, 
splay_tree_key);
 extern void gomp_remove_var_async (struct gomp_device_descr *, splay_tree_key,
                                   struct goacc_asyncqueue *);
 
+/* allocator.c */
+
+extern uintptr_t gomp_map_omp_init_allocator (struct gomp_device_descr *,
+                                             struct goacc_asyncqueue *,
+                                             struct gomp_coalesce_buf *,
+                                             void *, void *);
+
 /* work.c */
 
 extern void gomp_init_work_share (struct gomp_work_share *, size_t, unsigned);
diff --git a/libgomp/oacc-host.c b/libgomp/oacc-host.c
index 2cc7e6464ea..b0546c87405 100644
--- a/libgomp/oacc-host.c
+++ b/libgomp/oacc-host.c
@@ -320,6 +320,7 @@ static struct gomp_device_descr host_dispatch =
     .host2dev_func = host_host2dev,
     .memcpy2d_func = NULL,
     .memcpy3d_func = NULL,
+    .memspace_validate_func = NULL,
     .run_func = host_run,
 
     .session = {
diff --git a/libgomp/plugin/plugin-gcn.c b/libgomp/plugin/plugin-gcn.c
index 81426168da9..cc47d205e1e 100644
--- a/libgomp/plugin/plugin-gcn.c
+++ b/libgomp/plugin/plugin-gcn.c
@@ -4896,6 +4896,22 @@ unlock:
   return retval;
 }
 
+/* This plugin hook function should be kept in sync with gcn_memspace_validate
+   in config/gcn/allocator.c.  */
+
+int
+GOMP_OFFLOAD_memspace_validate (omp_memspace_handle_t memspace, unsigned 
access)
+{
+  /* Disallow use of low-latency memory when it must be accessible by
+     all threads.  */
+  return (memspace != omp_low_lat_mem_space
+         || access != omp_atv_all);
+
+  /* Otherwise, standard memspaces are accepted, even when we don't have
+     anything special to do with them, and non-standard memspaces are assumed
+     to need explicit support.  */
+  return (memspace <= GOMP_OMP_PREDEF_MEMSPACE_MAX);
+}
 
 static bool
 init_hip_runtime_functions (void)
diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
index 59f2ce07f0e..4ee54e732bd 100644
--- a/libgomp/plugin/plugin-nvptx.c
+++ b/libgomp/plugin/plugin-nvptx.c
@@ -2581,6 +2581,24 @@ GOMP_OFFLOAD_memset (int ord, void *ptr, int val, size_t 
count)
   return true;
 }
 
+/* This plugin hook function should be kept in sync with 
nvptx_memspace_validate
+   in config/nvptx/allocator.c.  */
+
+int
+GOMP_OFFLOAD_memspace_validate (omp_memspace_handle_t memspace, unsigned 
access)
+{
+  /* Disallow use of low-latency memory when it must be accessible by
+     all threads.  */
+  if (memspace == omp_low_lat_mem_space
+      && access == omp_atv_all)
+    return false;
+
+  /* Otherwise, standard memspaces are accepted, even when we don't have
+     anything special to do with them, and non-standard memspaces are assumed
+     to need explicit support.  */
+  return (memspace <= GOMP_OMP_PREDEF_MEMSPACE_MAX);
+}
+
 bool
 GOMP_OFFLOAD_openacc_async_host2dev (int ord, void *dst, const void *src,
                                     size_t n, struct goacc_asyncqueue *aq)
diff --git a/libgomp/target.c b/libgomp/target.c
index 47fdd7458ea..cd4f8788e76 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -1322,6 +1322,23 @@ gomp_map_vars_internal (struct gomp_device_descr 
*devicep,
        {
          tgt->list[i].key = NULL;
          tgt->list[i].offset = OFFSET_INLINED;
+         has_firstprivate = true;
+         continue;
+       }
+      else if ((kind & typemask) == GOMP_MAP_USES_ALLOCATORS)
+       {
+         tgt->list[i].key = NULL;
+         tgt->list[i].offset = OFFSET_INLINED;
+
+         size_t align = (size_t) 1 << (kind >> rshift);
+         if (tgt_align < align)
+           tgt_align = align;
+         tgt_size = (tgt_size + align - 1) & ~(align - 1);
+
+         /* Allocate space for omp_allocator_data.  */
+         tgt_size += gomp_omp_allocator_data_size;
+
+         has_firstprivate = true;
          continue;
        }
       else if ((kind & typemask) == GOMP_MAP_USE_DEVICE_PTR
@@ -1753,6 +1770,21 @@ gomp_map_vars_internal (struct gomp_device_descr 
*devicep,
                    tgt->list[i].offset = OFFSET_INLINED;
                  }
                continue;
+
+             case GOMP_MAP_USES_ALLOCATORS:
+               align = (size_t) 1 << (kind >> rshift);
+               tgt_size = (tgt_size + align - 1) & ~(align - 1);
+               tgt->list[i].offset = tgt_size;
+
+               void *descr_ptr = hostaddrs[i];
+               void *devaddr = (void *) (tgt->tgt_start + tgt_size);
+               uintptr_t dev_allocator
+                 = gomp_map_omp_init_allocator (devicep, aq, cbufp, devaddr,
+                                                descr_ptr);
+               hostaddrs[i] = (void *) dev_allocator;
+               tgt_size += gomp_omp_allocator_data_size;
+               continue;
+
              case GOMP_MAP_STRUCT_UNORD:
                if (sizes[i] > 1)
                  {
@@ -3204,6 +3236,14 @@ calculate_firstprivate_requirements (size_t mapnum, 
size_t *sizes,
        *tgt_size = (*tgt_size + align - 1) & ~(align - 1);
        *tgt_size += sizes[i];
       }
+    else if ((kinds[i] & 0xff) == GOMP_MAP_USES_ALLOCATORS)
+      {
+       size_t align = (size_t) 1 << (kinds[i] >> 8);
+       if (*tgt_align < align)
+         *tgt_align = align;
+       *tgt_size = (*tgt_size + align - 1) & ~(align - 1);
+       *tgt_size += gomp_omp_allocator_data_size;
+      }
 }
 
 /* Copy data shared as GOMP_MAP_FIRSTPRIVATE to DST.  */
@@ -3226,12 +3266,23 @@ copy_firstprivate_data (char *tgt, size_t mapnum, void 
**hostaddrs,
        memcpy (tgt + tgt_size, hostaddrs[i], sizes[i]);
        hostaddrs[i] = tgt + tgt_size;
        tgt_size = tgt_size + sizes[i];
-       if (i + 1 < mapnum && (kinds[i+1] & 0xff) == GOMP_MAP_ATTACH)
+       if (i + 1 < mapnum && (kinds[i + 1] & 0xff) == GOMP_MAP_ATTACH)
          {
-           *(*(uintptr_t**) hostaddrs[i+1] + sizes[i+1]) = (uintptr_t) 
hostaddrs[i];
+           *(*(uintptr_t**) hostaddrs[i + 1] + sizes[i + 1])
+             = (uintptr_t) hostaddrs[i];
            ++i;
          }
       }
+    else if ((kinds[i] & 0xff) == GOMP_MAP_USES_ALLOCATORS)
+      {
+       assert (hostaddrs[i] != NULL);
+       size_t align = (size_t) 1 << (kinds[i] >> 8);
+       tgt_size = (tgt_size + align - 1) & ~(align - 1);
+       uintptr_t allocator = gomp_map_omp_init_allocator (NULL, NULL, NULL,
+                                                          tgt + tgt_size,
+                                                          hostaddrs[i]);
+       hostaddrs[i] = (void *) allocator;
+      }
 }
 
 /* Helper function of GOMP_target{,_ext} routines.  */
@@ -6256,6 +6307,7 @@ gomp_load_plugin_for_device (struct gomp_device_descr 
*device,
   DLSYM (host2dev);
   DLSYM_OPT (memcpy2d, memcpy2d);
   DLSYM_OPT (memcpy3d, memcpy3d);
+  DLSYM_OPT (memspace_validate, memspace_validate);
   if (DLSYM_OPT (interop, interop))
     {
       DLSYM (get_interop_int);
diff --git a/libgomp/testsuite/libgomp.c++/c++.exp 
b/libgomp/testsuite/libgomp.c++/c++.exp
index ed096e17b9c..5be949bb611 100644
--- a/libgomp/testsuite/libgomp.c++/c++.exp
+++ b/libgomp/testsuite/libgomp.c++/c++.exp
@@ -1,6 +1,15 @@
 load_lib libgomp-dg.exp
 load_gcc_lib gcc-dg.exp
 
+proc check_effective_target_c { } {
+    return 0
+}
+
+proc check_effective_target_c++ { } {
+    return 1
+}
+
+
 if { $blddir != "" } {
     set libstdc++_library_path "../libstdc++-v3/src/.libs"
     set shlib_ext [get_shlib_extension]
diff --git a/gcc/testsuite/g++.dg/gomp/uses_allocators-1.C 
b/libgomp/testsuite/libgomp.c++/uses_allocators-1.C
similarity index 89%
rename from gcc/testsuite/g++.dg/gomp/uses_allocators-1.C
rename to libgomp/testsuite/libgomp.c++/uses_allocators-1.C
index 79e51638534..79b5cb0883f 100644
--- a/gcc/testsuite/g++.dg/gomp/uses_allocators-1.C
+++ b/libgomp/testsuite/libgomp.c++/uses_allocators-1.C
@@ -1,8 +1,8 @@
 // { dg-do compile }
 /* { dg-additional-options "-Wno-deprecated-openmp" } */
 
-//#include <omp.h>
-
+#include <omp.h>
+/*
 typedef __UINTPTR_TYPE__ omp_uintptr_t;
 
 #if __cplusplus >= 201103L
@@ -43,7 +43,7 @@ typedef struct omp_alloctrait_t
 //  omp_alloctrait_key_t key;
 //  omp_uintptr_t value;
 } omp_alloctrait_t;
-
+*/
 
 template<typename TH, TH alloc>
 void f()
@@ -99,7 +99,3 @@ void use2()
 
   g2<omp_allocator_handle_t, const omp_alloctrait_t[]>(my); // OK
 }
-
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target 
*-*-* } 51 }
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target 
*-*-* } 58 }
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target 
*-*-* } 89 }
diff --git a/gcc/testsuite/c-c++-common/gomp/uses_allocators-1.c 
b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-1.c
similarity index 64%
rename from gcc/testsuite/c-c++-common/gomp/uses_allocators-1.c
rename to libgomp/testsuite/libgomp.c-c++-common/uses_allocators-1.c
index df82cbbcba9..2029ee5a9a3 100644
--- a/gcc/testsuite/c-c++-common/gomp/uses_allocators-1.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-1.c
@@ -1,31 +1,16 @@
-typedef enum omp_allocator_handle_t
-#if __cplusplus >= 201103L
-: __UINTPTR_TYPE__
-#endif
-{
-  omp_default_mem_alloc = 1,
-  omp_low_lat_mem_alloc = 5,
-  __omp_allocator_handle_t_max__ = __UINTPTR_MAX__
-} omp_allocator_handle_t;
-
-typedef struct omp_alloctrait_t
-{
-  int key;
-  int value;
-} omp_alloctrait_t;
-
-extern void *omp_alloc (__SIZE_TYPE__, omp_allocator_handle_t);
+/* { dg-do compile } */
+#include <omp.h>
 
 void
 f (omp_allocator_handle_t my_alloc)
 {
   #pragma omp target
   {
-    int a; /* { dg-error "'my_alloc' in 'allocator' clause inside a target 
region must be specified in an 'uses_allocators' clause on the 'target' 
directive" "not yet implemented" { xfail *-*-* } } */
+    int a; /* { dg-error "'my_alloc' in 'allocator' clause inside a target 
region must be specified in an 'uses_allocators' clause on the 'target' 
directive" "" { target c } } */
     #pragma omp allocate(a) allocator(my_alloc) /* { dg-message "sorry, 
unimplemented: '#pragma omp allocate' not yet supported" "" { target c++ } }  */
     a  = 5;
     void *prt = omp_alloc(32, my_alloc);
-    #pragma omp parallel allocate(allocator(my_alloc) : a) firstprivate(a) /* 
{ dg-error "allocator 'my_alloc' in 'allocate' clause inside a target region 
must be specified in an 'uses_allocators' clause on the 'target' directive" 
"not yet implemented" { xfail *-*-* } } */
+    #pragma omp parallel allocate(allocator(my_alloc) : a) firstprivate(a) /* 
{ dg-error "allocator 'my_alloc' in 'allocate' clause inside a target region 
must be specified in an 'uses_allocators' clause on the 'target' directive" } */
       a = 7;
   }
 }
@@ -44,5 +29,3 @@ g (omp_allocator_handle_t my_alloc)
       a = 7;
   }
 }
-
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target 
*-*-* } 37 }
diff --git a/gcc/testsuite/c-c++-common/gomp/uses_allocators-2.c 
b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-2.c
similarity index 51%
rename from gcc/testsuite/c-c++-common/gomp/uses_allocators-2.c
rename to libgomp/testsuite/libgomp.c-c++-common/uses_allocators-2.c
index c766ea3559d..2babb11018e 100644
--- a/gcc/testsuite/c-c++-common/gomp/uses_allocators-2.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-2.c
@@ -1,18 +1,5 @@
-typedef enum omp_allocator_handle_t
-#if __cplusplus >= 201103L
-: __UINTPTR_TYPE__
-#endif
-{
-  omp_default_mem_alloc = 1,
-  omp_low_lat_mem_alloc = 5,
-  __omp_allocator_handle_t_max__ = __UINTPTR_MAX__
-} omp_allocator_handle_t;
-
-typedef struct omp_alloctrait_t
-{
-  int key;
-  int value;
-} omp_alloctrait_t;
+/* { dg-do compile } */
+#include <omp.h>
 
 void
 f ()
@@ -31,5 +18,3 @@ g ()
    #pragma omp target uses_allocators(traits(trait) : my_alloc)
      ;
 }
-
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target 
*-*-* } 31 }
diff --git a/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-3.c 
b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-3.c
new file mode 100644
index 00000000000..a371d7fe823
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-3.c
@@ -0,0 +1,50 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-original -fdump-tree-gimple" } */
+
+#include <omp.h>
+
+int n = 2;
+
+int main (void)
+{
+  omp_allocator_handle_t foo, bar;
+  const omp_alloctrait_t foo_traits[] = { { omp_atk_pinned,    omp_atv_true },
+                                         { omp_atk_partition, omp_atv_nearest 
} };
+
+  #pragma omp target
+    ;
+  #pragma omp target uses_allocators (bar)
+    ;
+  #pragma omp target uses_allocators (foo (foo_traits)) /* { dg-warning "the 
specification of arguments to 'uses_allocators' where each item is of the form 
'allocator\\(traits\\)' is deprecated since OpenMP 5.2 
\\\[-Wdeprecated-openmp\\\]" } */
+    ;
+  #pragma omp target uses_allocators (foo (foo_traits), bar (foo_traits)) /* { 
dg-warning "the specification of arguments to 'uses_allocators' where each item 
is of the form 'allocator\\(traits\\)' is deprecated since OpenMP 5.2 
\\\[-Wdeprecated-openmp\\\]" } */
+    ;
+  #pragma omp target uses_allocators (memspace(omp_high_bw_mem_space) : foo)
+    ;
+  #pragma omp target uses_allocators (traits(foo_traits) : bar)
+    ;
+  #pragma omp target parallel uses_allocators 
(memspace(omp_high_bw_mem_space), traits(foo_traits) : bar)
+    ;
+  #pragma omp target parallel uses_allocators (traits(foo_traits), 
memspace(omp_high_bw_mem_space) : bar) uses_allocators(foo)
+  {
+    void *p = omp_alloc ((unsigned long) 32, bar);
+    omp_free (p, bar);
+  }
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(\\), traits\\(\\) : bar\\)" "original" } } */
+/* { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(\\), traits\\(foo_traits\\) : foo\\)" "original" } 
} */
+/* { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(\\), traits\\(foo_traits\\) : bar\\) 
uses_allocators\\(memspace\\(\\), traits\\(foo_traits\\) : foo\\)" "original" } 
} */
+/* { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(.+\\), traits\\(\\) : foo\\)" "original" } } */
+/* { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(\\), traits\\(foo_traits\\) : bar\\)" "original" } 
} */
+/* { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(.+\\), traits\\(foo_traits\\) : bar\\)" "original" 
} } */
+/* { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(.+\\), traits\\(foo_traits\\) : bar\\) 
uses_allocators\\(memspace\\(\\), traits\\(\\) : foo\\)" "original" } } */
+
+/* { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(\\), traits\\(\\) : bar\\)" 
"gimple" } } */
+/* { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(\\), traits\\(foo_traits\\) : 
foo\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(\\), traits\\(foo_traits\\) : 
bar\\) uses_allocators\\(memspace\\(\\), traits\\(foo_traits\\) : foo\\)" 
"gimple" } } */
+/* { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(.+\\), traits\\(\\) : foo\\)" 
"gimple" } } */
+/* { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(\\), traits\\(foo_traits\\) : 
bar\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(.+\\), traits\\(foo_traits\\) 
: bar\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(.+\\), traits\\(foo_traits\\) 
: bar\\) uses_allocators\\(memspace\\(\\), traits\\(\\) : foo\\)" "gimple" } } 
*/
diff --git a/gcc/testsuite/c-c++-common/gomp/uses_allocators-4.c 
b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-4.c
similarity index 70%
rename from gcc/testsuite/c-c++-common/gomp/uses_allocators-4.c
rename to libgomp/testsuite/libgomp.c-c++-common/uses_allocators-4.c
index 5f3650157fc..389a51cd1c5 100644
--- a/gcc/testsuite/c-c++-common/gomp/uses_allocators-4.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-4.c
@@ -1,86 +1,7 @@
 /* { dg-do compile } */
 /* { dg-additional-options "-Wno-deprecated-openmp" } */
 
-//#include <omp.h>
-
-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,
-  ompx_gnu_managed_mem_space = 200,
-  __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,
-  ompx_gnu_pinned_mem_alloc = 200,
-  ompx_gnu_managed_mem_alloc = 201,
-  __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_default = (__UINTPTR_TYPE__) -1,
-  omp_atv_false = 0,
-  omp_atv_true = 1,
-  omp_atv_contended = 3,
-  omp_atv_uncontended = 4,
-  omp_atv_serialized = 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_t;
-
-typedef struct omp_alloctrait_t
-{
-  omp_alloctrait_key_t key;
-  omp_uintptr_t value;
-} omp_alloctrait_t;
-
-omp_alloctrait_key_t k;
-omp_alloctrait_value_t v;
+#include <omp.h>
 
 int f (const omp_alloctrait_t arg_traits[], int n)
 {
@@ -135,10 +56,3 @@ int f (const omp_alloctrait_t arg_traits[], int n)
                                                                                
          /* { dg-error "legacy 'foo\\\(foo_traits\\\)' traits syntax not 
allowed in 'uses_allocators' clause when using modifiers" "" { target *-*-* } 
.-2 } */
   return 0;
 }
-
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target 
*-*-* } 103 }
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target c 
} 111 }
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target c 
} 113 }
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target c 
} 117 }
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target c 
} 119 }
-// { dg-message "sorry, unimplemented: 'uses_allocators' clause" "" { target 
*-*-* } 131 }
diff --git a/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-5.c 
b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-5.c
new file mode 100644
index 00000000000..eeac5f097cb
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-5.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-original -fdump-tree-gimple" } */
+
+#include <omp.h>
+
+int main (void)
+{
+  omp_allocator_handle_t memspace, traits;
+  const omp_alloctrait_t mytraits[] = { { omp_atk_pinned,    omp_atv_true },
+                                       { omp_atk_partition, omp_atv_nearest } 
};
+  #pragma omp target uses_allocators (memspace)
+    ;
+  #pragma omp target uses_allocators (traits)
+    ;
+  #pragma omp target uses_allocators (traits, memspace)
+    ;
+  #pragma omp target uses_allocators (traits (mytraits)) /* { dg-warning "the 
specification of arguments to 'uses_allocators' where each item is of the form 
'allocator\\(traits\\)' is deprecated since OpenMP 5.2" } */
+    ;
+  #pragma omp target uses_allocators (memspace (mytraits), 
omp_default_mem_alloc) /* { dg-warning "the specification of arguments to 
'uses_allocators' where each item is of the form 'allocator\\(traits\\)' is 
deprecated since OpenMP 5.2" } */
+    ;
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(\\), traits\\(\\) : memspace\\)" "original" } } */
+/* { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(\\), traits\\(\\) : traits\\)" "original" } } */
+/* { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(\\), traits\\(\\) : memspace\\) 
uses_allocators\\(memspace\\(\\), traits\\(\\) : traits\\)" "original" } } */
+/* { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(\\), traits\\(mytraits\\) : traits\\)" "original" 
} } */
+/* { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(\\), traits\\(mytraits\\) : memspace\\)" 
"original" } } */
+
+/* { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(\\), traits\\(\\) : 
memspace\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(\\), traits\\(\\) : traits\\)" 
"gimple" } } */
+/* { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(\\), traits\\(\\) : 
memspace\\) uses_allocators\\(memspace\\(\\), traits\\(\\) : traits\\)" 
"gimple" } } */
+/* { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(\\), traits\\(mytraits\\) : 
traits\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(\\), traits\\(mytraits\\) : 
memspace\\)" "gimple" } } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-6.c 
b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-6.c
new file mode 100644
index 00000000000..23a831e8ba9
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-6.c
@@ -0,0 +1,53 @@
+/* { dg-additional-options "-fdump-tree-gimple" } */
+
+#include <stdint.h>
+#include <omp.h>
+
+int
+main ()
+{
+  int x, *xbuf[10];
+  omp_allocator_handle_t my_alloc;
+  const omp_alloctrait_t trait[1]= {{omp_atk_alignment,128}};
+
+  #pragma omp target uses_allocators(omp_low_lat_mem_alloc) map(tofrom: x, 
xbuf) defaultmap(none)
+    #pragma omp parallel allocate(allocator(omp_low_lat_mem_alloc), 
align(128): x, xbuf) if(0) firstprivate(x, xbuf)
+      {
+       if ((uintptr_t) &x % 128 != 0)
+         __builtin_abort ();
+       if ((uintptr_t) xbuf % 128 != 0)
+         __builtin_abort ();
+      }
+
+  my_alloc = (omp_allocator_handle_t) 0xABCD;
+
+  #pragma omp target uses_allocators(traits(trait): my_alloc) defaultmap(none) 
map(tofrom: x, xbuf)
+    #pragma omp parallel allocate(allocator(my_alloc): x, xbuf) if(0) 
firstprivate(x, xbuf)
+      {
+       if ((uintptr_t) &x % 128 != 0)
+         __builtin_abort ();
+       if ((uintptr_t) xbuf % 128 != 0)
+         __builtin_abort ();
+      }
+
+  if (my_alloc != (omp_allocator_handle_t) 0xABCD)
+    __builtin_abort ();
+
+  /* The following creates an allocator with empty traits + default mem space. 
*/
+  #pragma omp target uses_allocators(my_alloc) map(tofrom: x, xbuf) 
defaultmap(none)
+    #pragma omp parallel allocate(allocator(my_alloc), align(128): x, xbuf) 
if(0) firstprivate(x, xbuf)
+      {
+       if ((uintptr_t) &x % 128 != 0)
+         __builtin_abort ();
+       if ((uintptr_t) xbuf % 128 != 0)
+         __builtin_abort ();
+      }
+
+  if (my_alloc != (omp_allocator_handle_t) 0xABCD)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "#pragma omp target .* 
uses_allocators\\(memspace\\(\\), traits\\(trait\\) : my_alloc\\)" 1 "gimple" } 
} */
+/* { dg-final { scan-tree-dump-times "#pragma omp target .* 
uses_allocators\\(memspace\\(\\), traits\\(\\) : my_alloc\\)" 1 "gimple" } } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-7.c 
b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-7.c
new file mode 100644
index 00000000000..7603dfcd322
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-7.c
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+#include <omp.h>
+
+void f()
+{
+ omp_allocator_handle_t my;
+ struct t {
+   omp_allocator_handle_t a1;
+ } s;
+
+const omp_allocator_handle_t my3 = (omp_allocator_handle_t) 300;
+omp_allocator_handle_t my4[1];
+const omp_alloctrait_t t[] = {};
+ #pragma omp target uses_allocators(my, omp_default_mem_alloc, 
omp_null_allocator)  // OK
+   ;
+ #pragma omp target uses_allocators(my) firstprivate(my) // { dg-error "'my' 
appears more than once in data clauses" }
+   ;
+ #pragma omp target private(my) uses_allocators(my) // { dg-error "'my' 
appears more than once in data clauses" }
+   ;
+ #pragma omp target uses_allocators(my3)
+   ;
+ #pragma omp target uses_allocators(s.a1)
+   // { dg-error "expected '\\)' before '\\.' token" "" { target *-*-* } .-1 }
+   // { dg-error "allocator 's' must be of 'omp_allocator_handle_t' type" "" { 
target *-*-* } .-2 }
+   ;
+ #pragma omp target uses_allocators(my4)
+   // { dg-error "allocator 'my4' must be of 'omp_allocator_handle_t' type" "" 
{ target *-*-* } .-1 }
+   ;
+ #pragma omp target uses_allocators(my4[0])
+   // { dg-error "expected '\\)' before '\\\[' token" "" { target *-*-* } .-1 }
+   // { dg-error "allocator 'my4' must be of 'omp_allocator_handle_t' type" "" 
{ target *-*-* } .-2 }
+   ;
+ #pragma omp target uses_allocators(memspace(omp_default_mem_space) : my, 
my(t)) // { dg-error "legacy 'my\\(t\\)' traits syntax not allowed in 
'uses_allocators' clause when using modifiers" }
+   ;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-8.c 
b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-8.c
new file mode 100644
index 00000000000..fef13585dbf
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-8.c
@@ -0,0 +1,16 @@
+// { dg-do compile }
+// { dg-additional-options "-fdump-tree-original" }
+
+#include <omp.h>
+
+void f()
+{
+ omp_allocator_handle_t my, my2, my3, my4;
+const omp_alloctrait_t t[] = {};
+const omp_alloctrait_t t2[] = {};
+ #pragma omp target uses_allocators(traits(t), memspace(omp_high_bw_mem_space) 
: my; omp_default_mem_alloc, omp_null_allocator; my2; traits(t2) : my3; 
memspace(omp_large_cap_mem_space) : my4)
+   ;
+}
+
+// { dg-final { scan-tree-dump "#pragma omp target 
uses_allocators\\(memspace\\(1\\), traits\\(\\) : my4\\) 
uses_allocators\\(memspace\\(\\), traits\\(t2\\) : my3\\) 
uses_allocators\\(memspace\\(\\), traits\\(\\) : my2\\) 
uses_allocators\\(memspace\\(3\\), traits\\(t\\) : my\\)" "original" { target c 
} } }
+// { dg-final { scan-tree-dump "#pragma omp target 
uses_allocators\\(memspace\\(omp_large_cap_mem_space\\), traits\\(\\) : my4\\) 
uses_allocators\\(memspace\\(\\), traits\\(t2\\) : my3\\) 
uses_allocators\\(memspace\\(\\), traits\\(\\) : my2\\) 
uses_allocators\\(memspace\\(omp_high_bw_mem_space\\), traits\\(t\\) : my\\)" 
"original" { target c++ } } }
diff --git a/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-9.c 
b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-9.c
new file mode 100644
index 00000000000..258dba10ebd
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/uses_allocators-9.c
@@ -0,0 +1,44 @@
+#include <stdint.h>
+#include <omp.h>
+
+int main (void)
+{
+  omp_allocator_handle_t my_alloc;
+  const omp_alloctrait_t trait[] = { { omp_atk_alignment, 128 } };
+
+  #pragma omp target uses_allocators (traits (trait): my_alloc)
+  {
+    int *x = omp_alloc (sizeof (int), my_alloc);
+    if ((uintptr_t) x % 128 != 0)
+      __builtin_abort ();
+    omp_free (x, my_alloc);
+  }
+
+  my_alloc = (omp_allocator_handle_t) 0xABCD;
+
+  #pragma omp target uses_allocators (traits (trait): my_alloc)
+  {
+    int *x = omp_alloc (sizeof (int), my_alloc);
+    if ((uintptr_t) x % 128 != 0)
+      __builtin_abort ();
+    omp_free (x, my_alloc);
+  }
+
+  if (my_alloc != (omp_allocator_handle_t) 0xABCD)
+    __builtin_abort ();
+
+  /* The following creates an allocator with empty traits + default mem space. 
*/
+  #pragma omp target uses_allocators (my_alloc)
+  {
+    int *x = omp_aligned_alloc (128, sizeof (int), my_alloc);
+    if ((uintptr_t) x % 128 != 0)
+      __builtin_abort ();
+    omp_free (x, my_alloc);
+  }
+
+  if (my_alloc != (omp_allocator_handle_t) 0xABCD)
+    __builtin_abort ();
+
+  return 0;
+}
+
diff --git a/libgomp/testsuite/libgomp.c/c.exp 
b/libgomp/testsuite/libgomp.c/c.exp
index aae282478db..4b59957d1f3 100644
--- a/libgomp/testsuite/libgomp.c/c.exp
+++ b/libgomp/testsuite/libgomp.c/c.exp
@@ -3,6 +3,14 @@ load_gcc_lib gcc-dg.exp
 
 lappend ALWAYS_CFLAGS "compiler=$GCC_UNDER_TEST"
 
+proc check_effective_target_c { } {
+    return 1
+}
+
+proc check_effective_target_c++ { } {
+    return 0
+}
+
 # If a testcase doesn't have special options, use these.
 if ![info exists DEFAULT_CFLAGS] then {
     set DEFAULT_CFLAGS "-O2"
diff --git a/libgomp/testsuite/libgomp.fortran/uses_allocators-1.f90 
b/libgomp/testsuite/libgomp.fortran/uses_allocators-1.f90
new file mode 100644
index 00000000000..cdabbed04e4
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/uses_allocators-1.f90
@@ -0,0 +1,18 @@
+! { dg-do compile }
+use iso_c_binding
+use omp_lib
+implicit none
+contains
+subroutine x
+integer :: mem
+type(omp_alloctrait), parameter:: mem2(1) = [omp_alloctrait(1,1)]
+integer(omp_allocator_handle_kind) :: var
+!$omp target uses_allocators(memspace(omp_default_mem_space), traits(mem2) : 
var) defaultmap(none)
+block;
+type(c_ptr) ::c
+c = omp_alloc(omp_default_mem_space, 20_8)
+end block
+!$omp target uses_allocators(omp_default_mem_alloc, var(mem2))  ! { dg-warning 
"The specification of arguments to 'uses_allocators' at \\(1\\) where each item 
is of the form 'allocator\\(traits\\)' is deprecated since OpenMP 5.2; instead 
use 'uses_allocators\\(traits\\(mem2\\): var\\)' \\\[-Wdeprecated-openmp\\\]" }
+block; end block
+end
+end
diff --git a/libgomp/testsuite/libgomp.fortran/uses_allocators_1.f90 
b/libgomp/testsuite/libgomp.fortran/uses_allocators-2.f90
similarity index 100%
rename from libgomp/testsuite/libgomp.fortran/uses_allocators_1.f90
rename to libgomp/testsuite/libgomp.fortran/uses_allocators-2.f90
diff --git a/libgomp/testsuite/libgomp.fortran/uses_allocators_2.f90 
b/libgomp/testsuite/libgomp.fortran/uses_allocators-3.f90
similarity index 70%
rename from libgomp/testsuite/libgomp.fortran/uses_allocators_2.f90
rename to libgomp/testsuite/libgomp.fortran/uses_allocators-3.f90
index 0ab09975f49..005ead56ee4 100644
--- a/libgomp/testsuite/libgomp.fortran/uses_allocators_2.f90
+++ b/libgomp/testsuite/libgomp.fortran/uses_allocators-3.f90
@@ -3,8 +3,6 @@
 ! Minimal test for valid code:
 ! - predefined allocators do not need any special treatment in uses_allocators
 !   (as 'requires dynamic_allocators' is the default).
-!
-! - Non-predefined allocators are currently rejected ('sorry)'
 
 subroutine test
   use omp_lib
@@ -35,22 +33,22 @@ subroutine non_predef
 
   integer(kind=omp_allocator_handle_kind) :: a1, a2, a3
 
-  !$omp target uses_allocators(omp_default_mem_alloc, a1(trait), a2(trait2))  
! { dg-message "sorry, unimplemented: 'uses_allocators' clause with traits and 
memory spaces" }
+  !$omp target uses_allocators(omp_default_mem_alloc, a1(trait), a2(trait2))
   block; end block
 
-  !$omp target parallel uses_allocators(omp_default_mem_alloc, a1(trait), 
a2(trait2))  ! { dg-message "sorry, unimplemented: 'uses_allocators' clause 
with traits and memory spaces" }
+  !$omp target parallel uses_allocators(omp_default_mem_alloc, a1(trait), 
a2(trait2))
   block; end block
 
 
   !$omp target uses_allocators(traits(trait):a1) &
-  !$omp&        uses_allocators ( memspace ( omp_low_lat_mem_space ) , traits 
( trait2 ) : a2 , a3)  ! { dg-message "sorry, unimplemented: 'uses_allocators' 
clause with traits and memory spaces" }
+  !$omp&        uses_allocators ( memspace ( omp_low_lat_mem_space ) , traits 
( trait2 ) : a2 , a3)
   block; end block
 
   !$omp target parallel uses_allocators(traits(trait):a1) &
-  !$omp&        uses_allocators ( memspace ( omp_low_lat_mem_space ) , traits 
( trait2 ) : a2 , a3)  ! { dg-message "sorry, unimplemented: 'uses_allocators' 
clause with traits and memory spaces" }
+  !$omp&        uses_allocators ( memspace ( omp_low_lat_mem_space ) , traits 
( trait2 ) : a2 , a3)
   block; end block
 
-  !$omp target uses_allocators ( traits(trait2) , memspace ( 
omp_low_lat_mem_space ) : a2 , a3)  ! { dg-message "sorry, unimplemented: 
'uses_allocators' clause with traits and memory spaces" }
+  !$omp target uses_allocators ( traits(trait2) , memspace ( 
omp_low_lat_mem_space ) : a2 , a3)
   block; end block
 end subroutine
 
@@ -62,7 +60,7 @@ subroutine trait_present
   integer(kind=omp_allocator_handle_kind) :: a1
 
   ! Invalid in OpenMP 5.0 / 5.1, but valid since 5.2 the same as 
omp_default_mem_space + emptry traits array
-  !$omp target uses_allocators ( a1 )  ! { dg-message "sorry, unimplemented: 
'uses_allocators' clause with traits and memory spaces" }
+  !$omp target uses_allocators ( a1 )
   block; end block
 end
 
@@ -76,13 +74,13 @@ subroutine odd_names
   integer(kind=omp_allocator_handle_kind) :: traits
   integer(kind=omp_allocator_handle_kind) :: memspace
 
-  !$omp target uses_allocators ( traits(trait1), memspace(trait1) )  ! { 
dg-message "sorry, unimplemented: 'uses_allocators' clause with traits and 
memory spaces" }
+  !$omp target uses_allocators ( traits(trait1), memspace(trait1) )
   block; end block
 
-  !$omp target uses_allocators ( traits(trait1), 
memspace(omp_low_lat_mem_space)  : traits)  ! { dg-message "sorry, 
unimplemented: 'uses_allocators' clause with traits and memory spaces" }
+  !$omp target uses_allocators ( traits(trait1), 
memspace(omp_low_lat_mem_space)  : traits)
   block; end block
 
-  !$omp target uses_allocators ( memspace(omp_low_lat_mem_space), 
traits(trait1) : memspace)  ! { dg-message "sorry, unimplemented: 
'uses_allocators' clause with traits and memory spaces" }
+  !$omp target uses_allocators ( memspace(omp_low_lat_mem_space), 
traits(trait1) : memspace)
   block; end block
 end
 
@@ -94,6 +92,6 @@ subroutine more_checks
   integer(kind=omp_allocator_handle_kind) :: a1, a2(4)
   integer(kind=1) :: a3
 
-  !$omp target uses_allocators(memspace (omp_low_lat_mem_space) : a1 )  ! { 
dg-message "sorry, unimplemented: 'uses_allocators' clause with traits and 
memory spaces" }
+  !$omp target uses_allocators(memspace (omp_low_lat_mem_space) : a1 )
   block; end block
 end
diff --git a/libgomp/testsuite/libgomp.fortran/uses_allocators-4.f90 
b/libgomp/testsuite/libgomp.fortran/uses_allocators-4.f90
new file mode 100644
index 00000000000..0efdc9ef54b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/uses_allocators-4.f90
@@ -0,0 +1,59 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-original -fdump-tree-gimple" }
+
+program main
+  use omp_lib
+  implicit none
+  integer, allocatable :: arr(:)
+  integer (omp_allocator_handle_kind) :: bar, foo
+
+  type (omp_alloctrait), parameter :: traits_array(*) = &
+       [omp_alloctrait(omp_atk_pinned,omp_atv_true),&
+       omp_alloctrait(omp_atk_partition,omp_atv_nearest)]
+
+  !$omp target allocate(bar : arr) uses_allocators(bar)
+  block
+    allocate(arr(100))
+  end block
+
+  !$omp target uses_allocators(omp_default_mem_alloc)
+  block
+  end block
+
+  !$omp target uses_allocators(bar(traits_array), foo (traits_array)) ! { 
dg-warning "The specification of arguments to 'uses_allocators' at \\(1\\) 
where each item is of the form 'allocator\\(traits\\)' is deprecated since 
OpenMP 5.2; instead use 'uses_allocators\\(traits\\(traits_array\\): foo\\)' 
\\\[-Wdeprecated-openmp\\\]" }
+  block
+    if (foo == 0) stop 1
+  end block
+
+  !$omp target uses_allocators(traits(traits_array) : bar)
+  block
+  end block
+
+  !$omp target parallel uses_allocators(memspace (omp_low_lat_mem_space) : bar)
+  block
+  end block
+
+  !$omp target parallel uses_allocators(memspace (omp_high_bw_mem_space), 
traits(traits_array) : bar)
+  block
+    use iso_c_binding
+    type(c_ptr) :: ptr
+    integer(c_size_t) :: sz = 32
+    ptr = omp_alloc (sz, bar)
+    call omp_free (ptr, bar)
+  end block
+
+end program main
+
+! { dg-final { scan-tree-dump "pragma omp target 
allocate\\(allocator\\(bar\\):arr\\) uses_allocators\\(memspace\\(\\), 
traits\\(\\) : bar\\)" "original" } }
+! { dg-final { scan-tree-dump "pragma omp target" "original" } }
+! { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(\\), traits\\(traits_array\\) : bar\\) 
uses_allocators\\(memspace\\(\\), traits\\(traits_array\\) : foo\\)" "original" 
} }
+! { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(\\), traits\\(traits_array\\) : bar\\)" "original" 
} }
+! { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(omp_low_lat_mem_space\\), traits\\(\\) : bar\\)" 
"original" } }
+! { dg-final { scan-tree-dump "pragma omp target 
uses_allocators\\(memspace\\(omp_high_bw_mem_space\\), traits\\(traits_array\\) 
: bar\\)" "original" } }
+
+! { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) allocate\\(allocator\\(bar\\):arr\\) 
uses_allocators\\(memspace\\(\\), traits\\(\\) : bar\\)" "gimple" } }
+! { dg-final { scan-tree-dump "pragma omp target" "gimple" } }
+! { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(\\), traits\\(traits_array\\) 
: bar\\) uses_allocators\\(memspace\\(\\), traits\\(traits_array\\) : foo\\)" 
"gimple" } }
+! { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(\\), traits\\(traits_array\\) 
: bar\\)" "gimple" } }
+! { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(omp_low_lat_mem_space\\), 
traits\\(\\) : bar\\)" "gimple" } }
+! { dg-final { scan-tree-dump "pragma omp target num_teams\\(-2\\) 
thread_limit\\(0\\) uses_allocators\\(memspace\\(omp_high_bw_mem_space\\), 
traits\\(traits_array\\) : bar\\)" "gimple" } }
diff --git a/libgomp/testsuite/libgomp.fortran/uses_allocators-5.f90 
b/libgomp/testsuite/libgomp.fortran/uses_allocators-5.f90
new file mode 100644
index 00000000000..86a86a08bf0
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/uses_allocators-5.f90
@@ -0,0 +1,54 @@
+! { dg-do compile }
+
+program main
+  use omp_lib
+  implicit none
+  integer (omp_allocator_handle_kind) :: bar, foo
+
+  type (omp_alloctrait), parameter :: traits_array(*) = &
+       [omp_alloctrait(omp_atk_pinned,omp_atv_true),&
+       omp_alloctrait(omp_atk_partition,omp_atv_nearest)]
+
+  !$omp target uses_allocators(omp_non_existant_alloc) ! { dg-error "Allocator 
'omp_non_existant_alloc' at .1. in USES_ALLOCATORS must be a scalar integer of 
kind 'omp_allocator_handle_kind'" }
+  block  ! { dg-error "Symbol 'omp_non_existant_alloc' at .1. has no IMPLICIT 
type; did you mean 'omp_const_mem_alloc'\?" "" { target *-*-* } .-1 }
+  end block
+
+  !$omp target uses_allocators(bar(traits_array), foo (traits_array), ) ! { 
dg-error "Invalid character in name" }
+  block
+  end block
+
+  !$omp target uses_allocators(traits(xyz) : bar) ! { dg-error "Symbol 'xyz' 
at .1. has no IMPLICIT type" }
+  block  ! { dg-error "Traits array 'xyz' in USES_ALLOCATORS .1. must be a 
one-dimensional named constant array of type 'omp_alloctrait'" "" { target 
*-*-* } .-1 }
+  end block
+
+  !$omp target uses_allocators(memspace(omp_non_existant_mem_space) : foo) ! { 
dg-error "Symbol 'omp_non_existant_mem_space' at .1. has no IMPLICIT type; did 
you mean 'omp_const_mem_space'\?" }
+  ! { dg-error "Memspace 'omp_non_existant_mem_space' at .1. in 
USES_ALLOCATORS must be a predefined memory space" "" { target *-*-* } .-1 }
+
+  block
+  end block
+
+  !$omp target uses_allocators(traits(traits_array), traits(traits_array) : 
bar) ! { dg-error "Duplicate TRAITS modifier at .1. in USES_ALLOCATORS clause" }
+  block
+  end block
+
+  !$omp target uses_allocators(memspace(omp_default_mem_space), 
memspace(omp_default_mem_space) : foo) ! { dg-error "Duplicate MEMSPACE 
modifier at .1. in USES_ALLOCATORS clause" }
+  block
+  end block
+
+  !$omp target uses_allocators(memspace(omp_default_mem_space), 
traits(traits_array), traits(traits_array) : foo) ! { dg-error "Duplicate 
TRAITS modifier at .1. in USES_ALLOCATORS clause" }
+  block
+  end block
+
+  !$omp target uses_allocators (omp_null_allocator)
+  block
+  end block
+
+  !$omp target uses_allocators (memspace(omp_high_bw_mem_space) : foo, bar)
+  block
+  end block
+
+  !$omp target uses_allocators (memspace(omp_high_bw_mem_space) : 
foo(foo_traits)) ! { dg-error "70:Unexpected '\\(' at .1." }
+  block
+  end block
+
+end program main
diff --git a/libgomp/testsuite/libgomp.fortran/uses_allocators-6.f90 
b/libgomp/testsuite/libgomp.fortran/uses_allocators-6.f90
new file mode 100644
index 00000000000..00f87109d2c
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/uses_allocators-6.f90
@@ -0,0 +1,14 @@
+! { dg-do compile }
+
+program main
+  use omp_lib
+  implicit none
+  integer, allocatable :: arr(:)
+  integer (omp_allocator_handle_kind) :: bar
+
+  !$omp target allocate(bar : arr) ! { dg-error "allocator 'bar' requires 
'uses_allocators.bar.' clause in target region" }
+  block
+    allocate(arr(100))
+  end block
+
+end program main
diff --git a/libgomp/testsuite/libgomp.fortran/uses_allocators-7.f90 
b/libgomp/testsuite/libgomp.fortran/uses_allocators-7.f90
index 65e2fca082e..64ac5ca189e 100644
--- a/libgomp/testsuite/libgomp.fortran/uses_allocators-7.f90
+++ b/libgomp/testsuite/libgomp.fortran/uses_allocators-7.f90
@@ -1,5 +1,4 @@
-! { dg-do compile }
-! { dg-additional-options "-fdump-tree-gimple" }
+! { dg-additional-options "-fdump-tree-original -fdump-tree-gimple" }
 
 program main
   use iso_c_binding
@@ -28,7 +27,7 @@ program main
 
   my_alloc = transfer(int(z'ABCD', omp_allocator_handle_kind), my_alloc)
 
-  !$omp target uses_allocators(traits(trait): my_alloc) defaultmap(none) 
map(tofrom: x, xbuf) 
+  !$omp target uses_allocators(traits(trait): my_alloc) defaultmap(none) 
map(tofrom: x, xbuf)
     !$omp parallel allocate(allocator(my_alloc): x, xbuf) if(.false.) 
firstprivate(x, xbuf)
       if (mod (TRANSFER (loc(x), iptr), 128) /= 0) &
         stop 3
@@ -54,16 +53,6 @@ program main
     stop 8
 end
 
-
-! FIXME ENABLE: 'dg FIXME final' -> 'dg-final'
-! { dg  FIXME  final { scan-tree-dump-times "#pragma omp target 
.*private\\(my_alloc\\).*uses_allocators\\(my_alloc: memspace\\(\\), 
traits\\(trait\\)\\)" 1 "gimple" } }
-! { dg  FIXME  final { scan-tree-dump-times "#pragma omp target 
.*private\\(my_alloc\\).*uses_allocators\\(my_alloc: memspace\\(\\), 
traits\\(\\)\\)" 1 "gimple" } }
-! { dg  FIXME  final { scan-tree-dump "#pragma omp target 
uses_allocators\\(memspace\\(1\\), traits\\(\\) : my4\\) 
uses_allocators\\(memspace\\(\\), traits\\(t2\\) : my3\\) 
uses_allocators\\(memspace\\(\\), traits\\(\\) : my2\\) 
uses_allocators\\(memspace\\(3\\), traits\\(t\\) : my\\)" 1 "original" } }
-
-
-! FIXME ENABLE code above for "gimple" once it has been implemented:
-! { dg-message "sorry, unimplemented: 'uses_allocators' clause with traits and 
memory spaces" "" { target *-*-* } 16 }
-! { dg-message "sorry, unimplemented: 'uses_allocators' clause with traits and 
memory spaces" "" { target *-*-* } 31 }
-! { dg-message "sorry, unimplemented: 'uses_allocators' clause with traits and 
memory spaces" "" { target *-*-* } 44 }
-! { dg-bogus "'my_alloc' not specified in enclosing 'target'" "bogus issue 
because clause is ignored" { xfail *-*-* } 32 }
-! { dg-bogus "'my_alloc' not specified in enclosing 'target'" "bogus issue 
because clause is ignored" { xfail *-*-* } 45 }
+! { dg-final { scan-tree-dump-times "#pragma omp target 
.*uses_allocators\\(memspace\\(\\), traits\\(trait\\) : my_alloc\\)" 1 "gimple" 
} }
+! { dg-final { scan-tree-dump-times "#pragma omp target 
.*uses_allocators\\(memspace\\(\\), traits\\(\\) : my_alloc\\)" 1 "gimple" } }
+! { dg-final { scan-tree-dump-times "#pragma omp target 
.*uses_allocators\\(memspace\\(1\\), traits\\(\\) : my4\\) 
uses_allocators\\(memspace\\(\\), traits\\(t2\\) : my3\\) 
uses_allocators\\(memspace\\(\\), traits\\(\\) : my2\\) 
uses_allocators\\(memspace\\(3\\), traits\\(t\\) : my\\)" 1 "original" } }
diff --git a/libgomp/testsuite/libgomp.fortran/uses_allocators-8.f90 
b/libgomp/testsuite/libgomp.fortran/uses_allocators-8.f90
new file mode 100644
index 00000000000..63b16d292be
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/uses_allocators-8.f90
@@ -0,0 +1,51 @@
+program main
+  use omp_lib
+  use iso_c_binding
+  implicit none
+  integer(c_intptr_t) :: iptr
+  integer (omp_allocator_handle_kind) :: my_alloc
+  type(omp_alloctrait), parameter :: trait(*) = &
+       [omp_alloctrait(omp_atk_alignment, 128)]
+
+  !$omp target uses_allocators(traits(trait) : my_alloc)
+  block
+    type(c_ptr) :: ptr
+    integer(c_size_t) :: sz = 32
+    ptr = omp_alloc (sz, my_alloc)
+    if (mod (TRANSFER (ptr, iptr), 128) /= 0) &
+      stop 1
+    call omp_free (ptr, my_alloc)
+  end block
+
+  my_alloc = transfer(int(z'ABCD', omp_allocator_handle_kind), my_alloc)
+
+  !$omp target uses_allocators(traits(trait) : my_alloc)
+  block
+    type(c_ptr) :: ptr
+    integer(c_size_t) :: sz = 32
+    ptr = omp_alloc (sz, my_alloc)
+    if (mod (TRANSFER (ptr, iptr), 128) /= 0) &
+      stop 2
+    call omp_free (ptr, my_alloc)
+  end block
+
+  if (transfer(my_alloc, 0_omp_allocator_handle_kind) /= int(z'ABCD', &
+       omp_allocator_handle_kind)) &
+    stop 3
+
+  ! The following creates an allocator with empty traits + default mem space.
+  !$omp target uses_allocators(my_alloc)
+  block
+    type(c_ptr) :: ptr
+    integer(c_size_t) :: sz = 32
+    ptr = omp_aligned_alloc (128_c_size_t, sz, my_alloc)
+    if (mod (TRANSFER (ptr, iptr), 128) /= 0) &
+      stop 4
+    call omp_free (ptr, my_alloc)
+  end block
+
+  if (transfer(my_alloc, 0_omp_allocator_handle_kind) /= int(z'ABCD', &
+       omp_allocator_handle_kind)) &
+    stop 5
+
+end program main

Reply via email to