This patch partially enables use of the OpenMP interop construct by adding
middle end support, mostly in the omplower pass, and in the target-independent
part of the libgomp runtime. It follows up on previous patches for C, C++ and
Fortran front ends support. The full interop feature requires another patch to
enable foreign runtime support in libgomp plugins.

gcc/ChangeLog:

        * builtin-types.def
        (BT_FN_VOID_INT_INT_PTR_PTR_PTR_INT_PTR_INT_PTR_UINT_PTR): New.
        * gimple-low.cc (lower_stmt): Handle GIMPLE_OMP_INTEROP.
        * gimple-pretty-print.cc (dump_gimple_omp_interop): New function.
        (pp_gimple_stmt_1): Handle GIMPLE_OMP_INTEROP.
        * gimple.cc (gimple_build_omp_interop): New function.
        (gimple_copy): Handle GIMPLE_OMP_INTEROP.
        * gimple.def (GIMPLE_OMP_INTEROP): Define.
        * gimple.h (gimple_build_omp_interop): Declare.
        (gimple_omp_interop_clauses): New function.
        (gimple_omp_interop_clauses_ptr): Likewise.
        (gimple_omp_interop_set_clauses): Likewise.
        (gimple_return_set_retval): Handle GIMPLE_OMP_INTEROP.
        * gimplify.cc (gimplify_scan_omp_clauses): Handle OMP_CLAUSE_INIT,
        OMP_CLAUSE_USE and OMP_CLAUSE_DESTROY.
        (gimplify_omp_interop): New function.
        (gimplify_expr): Replace sorry with call to gimplify_omp_interop.
        * omp-builtins.def (BUILT_IN_GOMP_INTEROP): Define.
        * omp-low.cc (scan_sharing_clauses): Handle OMP_CLAUSE_INIT,
        OMP_CLAUSE_USE and OMP_CLAUSE_DESTROY.
        (scan_omp_1_stmt): Handle GIMPLE_OMP_INTEROP.
        (lower_omp_interop_action_clauses): New function.
        (lower_omp_interop): Likewise.
        (lower_omp_1): Handle GIMPLE_OMP_INTEROP.

gcc/c/ChangeLog:

        * c-parser.cc (c_parser_omp_clause_destroy): Make addressable.
        (c_parser_omp_clause_init): Make addressable.

gcc/cp/ChangeLog:

        * parser.cc (cp_parser_omp_clause_init): Make addressable.

gcc/fortran/ChangeLog:

        * trans-openmp.cc (gfc_trans_omp_clauses): Make OMP_CLAUSE_DESTROY and
        OMP_CLAUSE_INIT addressable.
        * types.def (BT_FN_VOID_INT_INT_PTR_PTR_PTR_INT_PTR_INT_PTR_UINT_PTR):
        New.

include/ChangeLog:

        * gomp-constants.h (GOMP_DEVICE_DEFAULT_OMP_61): Define.
        (GOMP_INTEROP_FLAG_TARGET): Define.
        (GOMP_INTEROP_FLAG_TARGETSYNC): Define.

libgomp/ChangeLog:

        * icv-device.c (omp_set_default_device): Check
        GOMP_DEVICE_DEFAULT_OMP_61.
        * libgomp-plugin.h (struct interop_obj_t): New.
        (enum gomp_interop_flag): New.
        (GOMP_OFFLOAD_interop): Declare.
        (GOMP_OFFLOAD_get_interop_int): Declare.
        (GOMP_OFFLOAD_get_interop_ptr): Declare.
        (GOMP_OFFLOAD_get_interop_str): Declare.
        (GOMP_OFFLOAD_get_interop_type_desc): Declare.
        * libgomp.h (_LIBGOMP_OMP_LOCK_DEFINED): Define.
        (struct gomp_device_descr): Add interop_func, get_interop_int_func,
        get_interop_ptr_func, get_interop_str_func, get_interop_type_desc_func.
        * libgomp.map: Add GOMP_interop.
        * libgomp_g.h (GOMP_interop): Declare.
        * target.c (resolve_device): Handle GOMP_DEVICE_DEFAULT_OMP_61.
        (omp_get_interop_int): Replace stub with actual implementation.
        (omp_get_interop_ptr): Likewise.
        (omp_get_interop_str): Likewise.
        (omp_get_interop_type_desc): Likewise.
        (struct interop_data_t): Define.
        (gomp_interop_internal): New function.
        (GOMP_interop): Likewise.
        (gomp_load_plugin_for_device): Load symbols for get_interop_int,
        get_interop_ptr, get_interop_str and get_interop_type_desc.
        * testsuite/libgomp.c-c++-common/interop-1.c: New test.

gcc/testsuite/ChangeLog:

        * c-c++-common/gomp/interop-1.c: Remove dg-prune-output "sorry".
        * c-c++-common/gomp/interop-2.c: Likewise.
        * c-c++-common/gomp/interop-3.c: Likewise.
        * c-c++-common/gomp/interop-4.c: Remove dg-message "not supported".
        * g++.dg/gomp/interop-5.C: Likewise.
        * gfortran.dg/gomp/interop-4.f90: Likewise.
        * c-c++-common/gomp/interop-5.c: New test.
        * gfortran.dg/gomp/interop-5.f90: New test.
---
 gcc/builtin-types.def                         |   3 +
 gcc/c/c-parser.cc                             |   6 +-
 gcc/cp/parser.cc                              |   1 +
 gcc/fortran/trans-openmp.cc                   |  20 +-
 gcc/fortran/types.def                         |   3 +
 gcc/gimple-low.cc                             |   1 +
 gcc/gimple-pretty-print.cc                    |  23 ++
 gcc/gimple.cc                                 |  18 ++
 gcc/gimple.def                                |   4 +
 gcc/gimple.h                                  |  33 ++-
 gcc/gimplify.cc                               |  20 +-
 gcc/omp-builtins.def                          |   3 +
 gcc/omp-low.cc                                | 274 ++++++++++++++++++
 gcc/testsuite/c-c++-common/gomp/interop-1.c   |   2 -
 gcc/testsuite/c-c++-common/gomp/interop-2.c   |   2 -
 gcc/testsuite/c-c++-common/gomp/interop-3.c   |   2 -
 gcc/testsuite/c-c++-common/gomp/interop-4.c   |  12 +-
 gcc/testsuite/c-c++-common/gomp/interop-5.c   |  65 +++++
 gcc/testsuite/g++.dg/gomp/interop-5.C         |  12 +-
 gcc/testsuite/gfortran.dg/gomp/interop-4.f90  |  10 +-
 gcc/testsuite/gfortran.dg/gomp/interop-5.f90  |  61 ++++
 include/gomp-constants.h                      |   9 +-
 libgomp/icv-device.c                          |   7 +-
 libgomp/libgomp-plugin.h                      |  44 +++
 libgomp/libgomp.h                             |  17 +-
 libgomp/libgomp.map                           |   1 +
 libgomp/libgomp_g.h                           |   2 +
 libgomp/target.c                              | 271 +++++++++++++++--
 .../libgomp.c-c++-common/interop-1.c          |  43 +++
 29 files changed, 903 insertions(+), 66 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/gomp/interop-5.c
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/interop-5.f90
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/interop-1.c

diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index 9b7cc9939e4..9583d30dfc0 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -1015,6 +1015,9 @@ DEF_FUNCTION_TYPE_11 
(BT_FN_VOID_OMPFN_PTR_OMPCPYFN_LONG_LONG_UINT_LONG_INT_ULL_
                      BT_PTR_FN_VOID_PTR_PTR, BT_LONG, BT_LONG,
                      BT_UINT, BT_LONG, BT_INT,
                      BT_ULONGLONG, BT_ULONGLONG, BT_ULONGLONG)
+DEF_FUNCTION_TYPE_11 (BT_FN_VOID_INT_INT_PTR_PTR_PTR_INT_PTR_INT_PTR_UINT_PTR,
+                     BT_VOID, BT_INT, BT_INT, BT_PTR, BT_PTR, BT_PTR, BT_INT,
+                     BT_PTR, BT_INT, BT_PTR, BT_UINT, BT_PTR)
 
 DEF_FUNCTION_TYPE_VAR_0 (BT_FN_VOID_VAR, BT_VOID)
 DEF_FUNCTION_TYPE_VAR_0 (BT_FN_INT_VAR, BT_INT)
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 911e7edd481..a231e62276a 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -20473,7 +20473,10 @@ c_parser_omp_clause_detach (c_parser *parser, tree 
list)
 static tree
 c_parser_omp_clause_destroy (c_parser *parser, tree list)
 {
-  return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_DESTROY, list);
+  tree nl = c_parser_omp_var_list_parens (parser, OMP_CLAUSE_DESTROY, list);
+  for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
+    TREE_ADDRESSABLE (OMP_CLAUSE_DECL (c)) = 1;
+  return nl;
 }
 
 /* OpenMP 5.1:
@@ -20855,6 +20858,7 @@ c_parser_omp_clause_init (c_parser *parser, tree list)
 
   for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
     {
+      TREE_ADDRESSABLE (OMP_CLAUSE_DECL (c)) = 1;
       if (target)
        OMP_CLAUSE_INIT_TARGET (c) = 1;
       if (targetsync)
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 94e8e11e398..bab7a407aad 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -43178,6 +43178,7 @@ cp_parser_omp_clause_init (cp_parser *parser, tree list)
                                            NULL, false);
   for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c))
     {
+      TREE_ADDRESSABLE (OMP_CLAUSE_DECL (c)) = 1;
       if (target)
        OMP_CLAUSE_INIT_TARGET (c) = 1;
       if (targetsync)
diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc
index 3e5f92fe2e3..876302c7d64 100644
--- a/gcc/fortran/trans-openmp.cc
+++ b/gcc/fortran/trans-openmp.cc
@@ -2790,9 +2790,6 @@ gfc_trans_omp_clauses (stmtblock_t *block, 
gfc_omp_clauses *clauses,
        case OMP_LIST_USE:
          clause_code = OMP_CLAUSE_USE;
          goto add_clause;
-       case OMP_LIST_DESTROY:
-         clause_code = OMP_CLAUSE_DESTROY;
-         goto add_clause;
        case OMP_LIST_INTEROP:
          clause_code = OMP_CLAUSE_INTEROP;
          goto add_clause;
@@ -2803,6 +2800,22 @@ gfc_trans_omp_clauses (stmtblock_t *block, 
gfc_omp_clauses *clauses,
                                           declare_simd);
          break;
 
+       case OMP_LIST_DESTROY:
+         for (; n != NULL; n = n->next)
+           if (n->sym->attr.referenced)
+             {
+               tree t = gfc_trans_omp_variable (n->sym, declare_simd);
+               if (t != error_mark_node)
+                 {
+                   tree node
+                     = build_omp_clause (input_location, OMP_CLAUSE_DESTROY);
+                   OMP_CLAUSE_DECL (node) = t;
+                   TREE_ADDRESSABLE (OMP_CLAUSE_DECL (node)) = 1;
+                   omp_clauses = gfc_trans_add_clause (node, omp_clauses);
+                 }
+             }
+         break;
+
        case OMP_LIST_INIT:
          {
            tree pref_type = NULL_TREE;
@@ -2816,6 +2829,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, 
gfc_omp_clauses *clauses,
                  tree node = build_omp_clause (input_location,
                                                OMP_CLAUSE_INIT);
                  OMP_CLAUSE_DECL (node) = t;
+                 TREE_ADDRESSABLE (OMP_CLAUSE_DECL (node)) = 1;
                  if (n->u.init.target)
                    OMP_CLAUSE_INIT_TARGET (node) = 1;
                  if (n->u.init.targetsync)
diff --git a/gcc/fortran/types.def b/gcc/fortran/types.def
index 6447cee4ae0..dd9b8df59be 100644
--- a/gcc/fortran/types.def
+++ b/gcc/fortran/types.def
@@ -266,6 +266,9 @@ DEF_FUNCTION_TYPE_11 
(BT_FN_VOID_OMPFN_PTR_OMPCPYFN_LONG_LONG_UINT_LONG_INT_ULL_
                      BT_PTR_FN_VOID_PTR_PTR, BT_LONG, BT_LONG,
                      BT_UINT, BT_LONG, BT_INT,
                      BT_ULONGLONG, BT_ULONGLONG, BT_ULONGLONG)
+DEF_FUNCTION_TYPE_11 (BT_FN_VOID_INT_INT_PTR_PTR_PTR_INT_PTR_INT_PTR_UINT_PTR,
+                     BT_VOID, BT_INT, BT_INT, BT_PTR, BT_PTR, BT_PTR, BT_INT,
+                     BT_PTR, BT_INT, BT_PTR, BT_UINT, BT_PTR)
 
 DEF_FUNCTION_TYPE_VAR_0 (BT_FN_VOID_VAR, BT_VOID)
 
diff --git a/gcc/gimple-low.cc b/gcc/gimple-low.cc
index 26b415c9131..b612970a55a 100644
--- a/gcc/gimple-low.cc
+++ b/gcc/gimple-low.cc
@@ -747,6 +747,7 @@ lower_stmt (gimple_stmt_iterator *gsi, struct lower_data 
*data)
     case GIMPLE_OMP_FOR:
     case GIMPLE_OMP_SCOPE:
     case GIMPLE_OMP_DISPATCH:
+    case GIMPLE_OMP_INTEROP:
     case GIMPLE_OMP_SECTIONS:
     case GIMPLE_OMP_SECTIONS_SWITCH:
     case GIMPLE_OMP_SECTION:
diff --git a/gcc/gimple-pretty-print.cc b/gcc/gimple-pretty-print.cc
index d1531fcd6d4..4e20b4cc371 100644
--- a/gcc/gimple-pretty-print.cc
+++ b/gcc/gimple-pretty-print.cc
@@ -1755,6 +1755,25 @@ dump_gimple_omp_dispatch (pretty_printer *buffer, const 
gimple *gs, int spc,
     }
 }
 
+/* Dump a GIMPLE_OMP_INTEROP tuple on the pretty_printer BUFFER.  */
+
+static void
+dump_gimple_omp_interop (pretty_printer *buffer, const gimple *gs, int spc,
+                        dump_flags_t flags)
+{
+  if (flags & TDF_RAW)
+    {
+      dump_gimple_fmt (buffer, spc, flags, "%G <CLAUSES <", gs);
+      dump_omp_clauses (buffer, gimple_omp_interop_clauses (gs), spc, flags);
+      dump_gimple_fmt (buffer, spc, flags, " >");
+    }
+  else
+    {
+      pp_string (buffer, "#pragma omp interop");
+      dump_omp_clauses (buffer, gimple_omp_interop_clauses (gs), spc, flags);
+    }
+}
+
 /* Dump a GIMPLE_OMP_TARGET tuple on the pretty_printer PP.  */
 
 static void
@@ -2838,6 +2857,10 @@ pp_gimple_stmt_1 (pretty_printer *pp, const gimple *gs, 
int spc,
       dump_gimple_omp_dispatch(pp, gs, spc, flags);
       break;
 
+    case GIMPLE_OMP_INTEROP:
+      dump_gimple_omp_interop (pp, gs, spc, flags);
+      break;
+
     case GIMPLE_OMP_MASTER:
     case GIMPLE_OMP_SECTION:
     case GIMPLE_OMP_STRUCTURED_BLOCK:
diff --git a/gcc/gimple.cc b/gcc/gimple.cc
index 9a58a3a62d0..9acfa38ba95 100644
--- a/gcc/gimple.cc
+++ b/gcc/gimple.cc
@@ -1278,6 +1278,19 @@ gimple_build_omp_dispatch (gimple_seq body, tree clauses)
   return p;
 }
 
+/* Build a GIMPLE_OMP_INTEROP statement.
+
+   CLAUSES are any of the OMP interop construct's clauses.  */
+
+gimple *
+gimple_build_omp_interop (tree clauses)
+{
+  gimple *p = gimple_alloc (GIMPLE_OMP_INTEROP, 0);
+  gimple_omp_interop_set_clauses (p, clauses);
+
+  return p;
+}
+
 /* Build a GIMPLE_OMP_TARGET statement.
 
    BODY is the sequence of statements that will be executed.
@@ -2205,6 +2218,11 @@ gimple_copy (gimple *stmt)
          gimple_omp_dispatch_set_clauses (copy, t);
          goto copy_omp_body;
 
+       case GIMPLE_OMP_INTEROP:
+         t = unshare_expr (gimple_omp_interop_clauses (stmt));
+         gimple_omp_interop_set_clauses (copy, t);
+         break;
+
        case GIMPLE_OMP_TARGET:
          {
            gomp_target *omp_target_stmt = as_a <gomp_target *> (stmt);
diff --git a/gcc/gimple.def b/gcc/gimple.def
index 609c7114f77..54248a80aa6 100644
--- a/gcc/gimple.def
+++ b/gcc/gimple.def
@@ -355,6 +355,10 @@ DEFGSCODE(GIMPLE_OMP_SCOPE, "gimple_omp_scope", 
GSS_OMP_SINGLE_LAYOUT)
    CLAUSES is an OMP_CLAUSE chain holding the associated clauses.  */
 DEFGSCODE(GIMPLE_OMP_DISPATCH, "gimple_omp_dispatch", GSS_OMP_SINGLE_LAYOUT)
 
+/* GIMPLE_OMP_INTEROP <CLAUSES> represents #pragma omp interop
+   CLAUSES is an OMP_CLAUSE chain holding the associated clauses.  */
+DEFGSCODE(GIMPLE_OMP_INTEROP, "gimple_omp_interop", GSS_OMP_SINGLE_LAYOUT)
+
 /* OMP_SECTION <BODY> represents #pragma omp section.
    BODY is the sequence of statements in the section body.  */
 DEFGSCODE(GIMPLE_OMP_SECTION, "gimple_omp_section", GSS_OMP)
diff --git a/gcc/gimple.h b/gcc/gimple.h
index ecbd54cf28d..112e5ae472d 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -745,7 +745,8 @@ struct GTY((tag("GSS_OMP_CONTINUE")))
 };
 
 /* GIMPLE_OMP_SINGLE, GIMPLE_OMP_ORDERED, GIMPLE_OMP_TASKGROUP,
-   GIMPLE_OMP_SCAN, GIMPLE_OMP_MASKED, GIMPLE_OMP_SCOPE, GIMPLE_OMP_DISPATCH. 
*/
+   GIMPLE_OMP_SCAN, GIMPLE_OMP_MASKED, GIMPLE_OMP_SCOPE, GIMPLE_OMP_DISPATCH,
+   GIMPLE_OMP_INTEROP. */
 
 struct GTY((tag("GSS_OMP_SINGLE_LAYOUT")))
   gimple_statement_omp_single_layout : public gimple_statement_omp
@@ -1595,6 +1596,7 @@ gimple *gimple_build_omp_section (gimple_seq);
 gimple *gimple_build_omp_structured_block (gimple_seq);
 gimple *gimple_build_omp_scope (gimple_seq, tree);
 gimple *gimple_build_omp_dispatch (gimple_seq, tree);
+gimple *gimple_build_omp_interop (tree);
 gimple *gimple_build_omp_master (gimple_seq);
 gimple *gimple_build_omp_masked (gimple_seq, tree);
 gimple *gimple_build_omp_taskgroup (gimple_seq, tree);
@@ -5468,6 +5470,34 @@ gimple_omp_dispatch_set_clauses (gimple *gs, tree 
clauses)
   static_cast<gimple_statement_omp_single_layout *> (gs)->clauses = clauses;
 }
 
+/* Return the clauses associated with OMP_INTEROP statement GS.  */
+
+inline tree
+gimple_omp_interop_clauses (const gimple *gs)
+{
+  GIMPLE_CHECK (gs, GIMPLE_OMP_INTEROP);
+  return static_cast<const gimple_statement_omp_single_layout *> (gs)->clauses;
+}
+
+/* Return a pointer to the clauses associated with OMP_INTEROP statement GS.  
*/
+
+inline tree *
+gimple_omp_interop_clauses_ptr (gimple *gs)
+{
+  GIMPLE_CHECK (gs, GIMPLE_OMP_INTEROP);
+  return &static_cast<gimple_statement_omp_single_layout *> (gs)->clauses;
+}
+
+/* Set CLAUSES to be the clauses associated with OMP interop statement
+   GS.  */
+
+inline void
+gimple_omp_interop_set_clauses (gimple *gs, tree clauses)
+{
+  GIMPLE_CHECK (gs, GIMPLE_OMP_INTEROP);
+  static_cast<gimple_statement_omp_single_layout *> (gs)->clauses = clauses;
+}
+
 /* Return the kind of the OMP_FOR statemement G.  */
 
 inline int
@@ -6802,6 +6832,7 @@ gimple_return_set_retval (greturn *gs, tree retval)
     case GIMPLE_OMP_TEAMS:                     \
     case GIMPLE_OMP_SCOPE:                     \
     case GIMPLE_OMP_DISPATCH:                  \
+    case GIMPLE_OMP_INTEROP:                   \
     case GIMPLE_OMP_SECTION:                   \
     case GIMPLE_OMP_STRUCTURED_BLOCK:          \
     case GIMPLE_OMP_MASTER:                    \
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 5bdd970f570..6675ac8b0ce 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -13853,6 +13853,9 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq 
*pre_p,
        case OMP_CLAUSE_IF_PRESENT:
        case OMP_CLAUSE_FINALIZE:
        case OMP_CLAUSE_INTEROP:
+       case OMP_CLAUSE_INIT:
+       case OMP_CLAUSE_USE:
+       case OMP_CLAUSE_DESTROY:
          break;
 
        case OMP_CLAUSE_ORDER:
@@ -18480,6 +18483,19 @@ gimplify_omp_ordered (tree expr, gimple_seq body)
   return gimple_build_omp_ordered (body, OMP_ORDERED_CLAUSES (expr));
 }
 
+static enum gimplify_status
+gimplify_omp_interop (tree *expr_p, gimple_seq *pre_p)
+{
+  tree expr = *expr_p;
+
+  gimplify_scan_omp_clauses (&OMP_INTEROP_CLAUSES (expr), pre_p, ORT_TASK,
+                            OMP_INTEROP);
+  gimple *stmt = gimple_build_omp_interop (OMP_INTEROP_CLAUSES (expr));
+  gimplify_seq_add_stmt (pre_p, stmt);
+  *expr_p = NULL_TREE;
+  return GS_ALL_DONE;
+}
+
 /* Callback for walk_tree to find an IFN_GOMP_DISPATCH.  */
 
 static tree
@@ -19953,9 +19969,7 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, 
gimple_seq *post_p,
          }
 
        case OMP_INTEROP:
-         sorry_at (EXPR_LOCATION (*expr_p),
-                   "%<#pragma omp interop%> not yet supported");
-         ret = GS_ERROR;
+         ret = gimplify_omp_interop (expr_p, pre_p);
          break;
        case OMP_ATOMIC:
        case OMP_ATOMIC_READ:
diff --git a/gcc/omp-builtins.def b/gcc/omp-builtins.def
index 96de7077e79..f73fb7b9dd8 100644
--- a/gcc/omp-builtins.def
+++ b/gcc/omp-builtins.def
@@ -402,6 +402,9 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_DOACROSS_ULL_POST, 
"GOMP_doacross_ull_post",
                  BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_DOACROSS_ULL_WAIT, "GOMP_doacross_ull_wait",
                  BT_FN_VOID_ULL_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_INTEROP, "GOMP_interop",
+                 BT_FN_VOID_INT_INT_PTR_PTR_PTR_INT_PTR_INT_PTR_UINT_PTR,
+                 ATTR_NOTHROW_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL, "GOMP_parallel",
                  BT_FN_VOID_OMPFN_PTR_UINT_UINT, ATTR_NOTHROW_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_REDUCTIONS,
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index c36ae38cf8e..9fa7b96dfde 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -1790,6 +1790,11 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
            install_var_local (decl, ctx);
          break;
 
+       case OMP_CLAUSE_INIT:
+       case OMP_CLAUSE_USE:
+       case OMP_CLAUSE_DESTROY:
+         break;
+
        case OMP_CLAUSE__CACHE_:
        case OMP_CLAUSE_NOHOST:
        default:
@@ -1986,6 +1991,9 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
        case OMP_CLAUSE_FINALIZE:
        case OMP_CLAUSE_FILTER:
        case OMP_CLAUSE__CONDTEMP_:
+       case OMP_CLAUSE_INIT:
+       case OMP_CLAUSE_USE:
+       case OMP_CLAUSE_DESTROY:
          break;
 
        case OMP_CLAUSE__CACHE_:
@@ -4211,6 +4219,10 @@ scan_omp_1_stmt (gimple_stmt_iterator *gsi, bool 
*handled_ops_p,
       scan_omp (gimple_omp_body_ptr (stmt), ctx);
       break;
 
+    case GIMPLE_OMP_INTEROP:
+      ctx = new_omp_context (stmt, ctx);
+      break;
+
     case GIMPLE_OMP_SECTIONS:
       scan_omp_sections (as_a <gomp_sections *> (stmt), ctx);
       break;
@@ -14331,6 +14343,263 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, 
omp_context *ctx)
     }
 }
 
+/* Generate code to implement the action-clauses (destroy, init, use) of an
+   OpenMP interop construct.  */
+
+static void
+lower_omp_interop_action_clauses (gimple_seq *seq, vec<tree> &objs,
+                                 vec<tree> *interop_types = NULL,
+                                 vec<tree> *prefer_types = NULL)
+{
+  if (objs.length () == 0)
+    return;
+
+  enum omp_clause_code action = OMP_CLAUSE_CODE (objs[0]);
+  if (action == OMP_CLAUSE_INIT)
+    gcc_checking_assert (objs.length () == interop_types->length ()
+                        && objs.length () == prefer_types->length ());
+  else
+    gcc_assert (prefer_types == NULL && interop_types == NULL);
+
+  tree ret_objs = NULL_TREE, ret_interop_types = NULL_TREE,
+       ret_prefer_types = NULL_TREE;
+
+  /* If there is only one object then we pass a single pointer,
+     if there are multiple objects then we build an array. */
+  if (objs.length () == 1)
+    {
+      tree obj = OMP_CLAUSE_DECL (objs.pop ());
+      if (TREE_CODE (TREE_TYPE (obj)) == REFERENCE_TYPE)
+       obj = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (obj)), obj);
+      if (action != OMP_CLAUSE_USE
+         && TREE_CODE (TREE_TYPE (obj)) != POINTER_TYPE)
+       {
+         // For modifying actions, we need a pointer.
+         tree obj_ptr = create_tmp_var (build_pointer_type (TREE_TYPE (obj)));
+         gimplify_assign (obj_ptr, build_fold_addr_expr (obj), seq);
+         obj = obj_ptr;
+       }
+      else if (action == OMP_CLAUSE_USE
+              && TREE_CODE (TREE_TYPE (obj)) == POINTER_TYPE)
+       {
+         // For use action, we need the value.
+         tree obj_pointee = create_tmp_var (TREE_TYPE (TREE_TYPE (obj)));
+         gimplify_assign (obj_pointee, build_fold_indirect_ref (obj), seq);
+         obj = obj_pointee;
+       }
+      else
+       {
+         tree obj_ptr = create_tmp_var (TREE_TYPE (obj));
+         gimplify_assign (obj_ptr, obj, seq);
+         obj = obj_ptr;
+       }
+      ret_objs = obj;
+
+      if (action == OMP_CLAUSE_INIT)
+       {
+         ret_interop_types = interop_types->pop ();
+         tree prefer_type = prefer_types->pop ();
+         if (prefer_type != NULL_TREE)
+           {
+             tree prefer_type_ptr
+               = create_tmp_var (build_pointer_type (TREE_TYPE (prefer_type)));
+             gimplify_assign (prefer_type_ptr,
+                              build_fold_addr_expr (prefer_type), seq);
+             ret_prefer_types = prefer_type_ptr;
+           }
+         else
+           ret_prefer_types = null_pointer_node;
+       }
+    }
+  else if (objs.length () > 1)
+    {
+      tree type_obj_pref
+       = build_array_type_nelts (ptr_type_node, objs.length ());
+      ret_objs = create_tmp_var (type_obj_pref, "interopobjs");
+
+      bool have_pref_type = false;
+      if (action == OMP_CLAUSE_INIT)
+       {
+         for (tree pref_type : prefer_types)
+           if (pref_type != NULL_TREE)
+             {
+               have_pref_type = true;
+               break;
+             }
+         tree type_tgtsync
+           = build_array_type_nelts (integer_type_node, objs.length ());
+         ret_interop_types = create_tmp_var (type_tgtsync, "tgt_tgtsync");
+         if (have_pref_type)
+           ret_prefer_types = create_tmp_var (type_obj_pref, "pref_type");
+         else
+           {
+             ret_prefer_types = null_pointer_node;
+             prefer_types->truncate (0);
+           }
+       }
+
+      for (size_t i = 0; !objs.is_empty (); i++)
+       {
+         tree offset = build_int_cst (integer_type_node, i);
+         tree init = build4 (ARRAY_REF, ptr_type_node, ret_objs, offset,
+                             NULL_TREE, NULL_TREE);
+         tree obj = OMP_CLAUSE_DECL (objs.pop ());
+         if (TREE_CODE (TREE_TYPE (obj)) == REFERENCE_TYPE)
+           obj = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (obj)), obj);
+         if (action != OMP_CLAUSE_USE
+             && TREE_CODE (TREE_TYPE (obj)) != POINTER_TYPE)
+           // For modifying actions, we need a pointer.
+           obj = build_fold_addr_expr (obj);
+         else if (action == OMP_CLAUSE_USE
+                  && TREE_CODE (TREE_TYPE (obj)) == POINTER_TYPE)
+           // For use action, we need the value.
+           obj = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (obj)), obj);
+         init = build2 (MODIFY_EXPR, ptr_type_node, init,
+                        fold_convert (ptr_type_node, obj));
+         gimplify_and_add (init, seq);
+
+         if (action == OMP_CLAUSE_INIT)
+           {
+             init = build4 (ARRAY_REF, integer_type_node, ret_interop_types,
+                            offset, NULL_TREE, NULL_TREE);
+             init = build2 (MODIFY_EXPR, integer_type_node, init,
+                            interop_types->pop ());
+             gimplify_and_add (init, seq);
+
+             if (have_pref_type)
+               {
+                 tree prefer_type = prefer_types->pop ();
+                 tree pref = prefer_type == NULL_TREE
+                               ? null_pointer_node
+                               : build_fold_addr_expr (prefer_type);
+                 init = build4 (ARRAY_REF, ptr_type_node, ret_prefer_types,
+                                offset, NULL_TREE, NULL_TREE);
+                 init = build2 (MODIFY_EXPR, ptr_type_node, init, pref);
+                 gimplify_and_add (init, seq);
+               }
+           }
+       }
+      if (action == OMP_CLAUSE_INIT)
+       {
+         ret_prefer_types = build_fold_addr_expr (ret_prefer_types);
+         ret_interop_types = build_fold_addr_expr (ret_interop_types);
+       }
+      ret_objs = build_fold_addr_expr (ret_objs);
+    }
+
+  gcc_assert (objs.is_empty () && (!interop_types || interop_types->is_empty 
())
+             && (!prefer_types || prefer_types->is_empty ()));
+
+  objs.safe_push (ret_objs);
+  if (action == OMP_CLAUSE_INIT)
+    {
+      interop_types->safe_push (ret_interop_types);
+      prefer_types->safe_push (ret_prefer_types);
+    }
+}
+
+/* Lower code for an OMP interop directive.  */
+
+static void
+lower_omp_interop (gimple_stmt_iterator *gsi_p, omp_context *ctx)
+{
+  push_gimplify_context ();
+
+  tree block = make_node (BLOCK);
+  gbind *bind = gimple_build_bind (NULL, NULL, block);
+  gimple_seq bind_body = NULL;
+
+  /* Emit call to GOMP_interop:
+      void
+      GOMP_interop (int device_num, int n_init, void *init,
+                   const void *target_targetsync, const void *prefer_type,
+                   int n_use, void *use, int n_destroy, void *destroy,
+                   unsigned int nowait, void **depend)  */
+
+  tree nowait = NULL_TREE;
+  tree depend = null_pointer_node;
+  tree device_num = NULL_TREE;
+
+  auto_vec<tree> init_objs, use_objs, destroy_objs, prefer_type,
+    target_targetsync;
+  gimple_seq dep_ilist = NULL, dep_olist = NULL;
+  tree clauses = gimple_omp_interop_clauses (gsi_stmt (*gsi_p));
+  for (tree c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
+    {
+      switch (OMP_CLAUSE_CODE (c))
+       {
+       case OMP_CLAUSE_INIT:
+         {
+           init_objs.safe_push (c);
+           int target_targetsync_bits = 0;
+           if (OMP_CLAUSE_INIT_TARGET (c))
+             target_targetsync_bits |= GOMP_INTEROP_FLAG_TARGET;
+           if (OMP_CLAUSE_INIT_TARGETSYNC (c))
+             target_targetsync_bits |= GOMP_INTEROP_FLAG_TARGETSYNC;
+           target_targetsync.safe_push (
+             build_int_cst (integer_type_node, target_targetsync_bits));
+           prefer_type.safe_push (OMP_CLAUSE_INIT_PREFER_TYPE (c));
+         }
+         break;
+       case OMP_CLAUSE_USE:
+         use_objs.safe_push (c);
+         break;
+       case OMP_CLAUSE_DESTROY:
+         destroy_objs.safe_push (c);
+         break;
+       case OMP_CLAUSE_NOWAIT:
+         nowait = build_int_cst (integer_type_node, true);
+         break;
+       case OMP_CLAUSE_DEPEND:
+         {
+           tree *cp = gimple_omp_interop_clauses_ptr (gsi_stmt (*gsi_p));
+           lower_depend_clauses (cp, &dep_ilist, &dep_olist);
+           depend = OMP_CLAUSE_DECL (*cp);
+         }
+         break;
+       case OMP_CLAUSE_DEVICE:
+         device_num = OMP_CLAUSE_DEVICE_ID (c);
+         break;
+       default:
+         gcc_unreachable ();
+       }
+    }
+
+  if (nowait == NULL_TREE)
+    nowait = build_int_cst (integer_type_node, false);
+
+  if (device_num == NULL_TREE)
+    device_num = build_int_cst (integer_type_node, GOMP_DEVICE_DEFAULT_OMP_61);
+
+  tree n_init = build_int_cst (integer_type_node, init_objs.length ());
+  tree n_use = build_int_cst (integer_type_node, use_objs.length ());
+  tree n_destroy = build_int_cst (integer_type_node, destroy_objs.length ());
+
+  lower_omp_interop_action_clauses (&bind_body, init_objs, &target_targetsync,
+                                   &prefer_type);
+  lower_omp_interop_action_clauses (&bind_body, use_objs);
+  lower_omp_interop_action_clauses (&bind_body, destroy_objs);
+
+  gimple_seq_add_seq (&bind_body, dep_ilist);
+  tree fn = builtin_decl_explicit (BUILT_IN_GOMP_INTEROP);
+  gcall *call = gimple_build_call (
+    fn, 11, device_num, n_init,
+    init_objs.length () ? init_objs.pop () : null_pointer_node,
+    target_targetsync.length () ? target_targetsync.pop () : null_pointer_node,
+    prefer_type.length () ? prefer_type.pop () : null_pointer_node, n_use,
+    use_objs.length () ? use_objs.pop () : null_pointer_node, n_destroy,
+    destroy_objs.length () ? destroy_objs.pop () : null_pointer_node, nowait,
+    depend);
+  gimple_seq_add_stmt (&bind_body, call);
+  gimple_seq_add_seq (&bind_body, dep_olist);
+
+  gsi_replace (gsi_p, bind, true);
+  gimple_bind_set_body (bind, bind_body);
+  pop_gimplify_context (bind);
+  gimple_bind_append_vars (bind, ctx->block_vars);
+  BLOCK_VARS (block) = ctx->block_vars;
+}
+
 /* Expand code for an OpenMP teams directive.  */
 
 static void
@@ -14614,6 +14883,11 @@ lower_omp_1 (gimple_stmt_iterator *gsi_p, omp_context 
*ctx)
       gcc_assert (ctx);
       lower_omp_dispatch (gsi_p, ctx);
       break;
+    case GIMPLE_OMP_INTEROP:
+      ctx = maybe_lookup_ctx (stmt);
+      gcc_assert (ctx);
+      lower_omp_interop (gsi_p, ctx);
+      break;
     case GIMPLE_OMP_SINGLE:
       ctx = maybe_lookup_ctx (stmt);
       gcc_assert (ctx);
diff --git a/gcc/testsuite/c-c++-common/gomp/interop-1.c 
b/gcc/testsuite/c-c++-common/gomp/interop-1.c
index de3a4ba4b6b..d68611bfe9c 100644
--- a/gcc/testsuite/c-c++-common/gomp/interop-1.c
+++ b/gcc/testsuite/c-c++-common/gomp/interop-1.c
@@ -2,8 +2,6 @@
 /* { dg-additional-options "-std=c23"  { target c } } */
 /* C++11 and C23 because of 'constexpr'.  */
 
-/* { dg-prune-output "sorry, unimplemented: '#pragma omp interop' not yet 
supported" }  */
-
 /* The following definitions are in omp_lib, which cannot be included
    in gcc/testsuite/  */
 
diff --git a/gcc/testsuite/c-c++-common/gomp/interop-2.c 
b/gcc/testsuite/c-c++-common/gomp/interop-2.c
index 57fd688d55f..af81cc67360 100644
--- a/gcc/testsuite/c-c++-common/gomp/interop-2.c
+++ b/gcc/testsuite/c-c++-common/gomp/interop-2.c
@@ -2,8 +2,6 @@
 /* { dg-additional-options "-std=c23"  { target c } } */
 /* C++11 and C23 because of 'constexpr'.  */
 
-/* { dg-prune-output "sorry, unimplemented: '#pragma omp interop' not yet 
supported" }  */
-
 /* The following definitions are in omp_lib, which cannot be included
    in gcc/testsuite/  */
 
diff --git a/gcc/testsuite/c-c++-common/gomp/interop-3.c 
b/gcc/testsuite/c-c++-common/gomp/interop-3.c
index 42478bf760d..51d26dd179e 100644
--- a/gcc/testsuite/c-c++-common/gomp/interop-3.c
+++ b/gcc/testsuite/c-c++-common/gomp/interop-3.c
@@ -1,5 +1,3 @@
-/* { dg-prune-output "sorry, unimplemented: '#pragma omp interop' not yet 
supported" }  */
-
 /* The following definitions are in omp_lib, which cannot be included
    in gcc/testsuite/  */
 
diff --git a/gcc/testsuite/c-c++-common/gomp/interop-4.c 
b/gcc/testsuite/c-c++-common/gomp/interop-4.c
index 1f9c987108b..bb0bf312d5c 100644
--- a/gcc/testsuite/c-c++-common/gomp/interop-4.c
+++ b/gcc/testsuite/c-c++-common/gomp/interop-4.c
@@ -33,18 +33,18 @@ f()
   omp_interop_t obj1, obj2, obj3, obj4, obj5, obj6, obj7;
   int x[6];
 
-  #pragma omp interop init ( obj1, obj2) use (obj3) destroy(obj4) init(obj5) 
destroy(obj6) use(obj7)   /* { dg-message "'#pragma omp interop' not yet 
supported" }  */
+  #pragma omp interop init ( obj1, obj2) use (obj3) destroy(obj4) init(obj5) 
destroy(obj6) use(obj7)
   /* { dg-final { scan-tree-dump-times "#pragma omp interop use\\(obj7\\) 
destroy\\(obj6\\) init\\(obj5\\) destroy\\(obj4\\) use\\(obj3\\) init\\(obj2\\) 
init\\(obj1\\)\[\r\n\]" 1 "original" } }  */
 
-  #pragma omp interop nowait init (targetsync : obj1, obj2) use (obj3) 
destroy(obj4) init(target, targetsync : obj5) destroy(obj6) use(obj7) 
depend(inout: x)  /* { dg-message "'#pragma omp interop' not yet supported" }  
*/
+  #pragma omp interop nowait init (targetsync : obj1, obj2) use (obj3) 
destroy(obj4) init(target, targetsync : obj5) destroy(obj6) use(obj7) 
depend(inout: x)  
   /* { dg-final { scan-tree-dump-times "#pragma omp interop 
depend\\(inout:x\\) use\\(obj7\\) destroy\\(obj6\\) init\\(target, targetsync: 
obj5\\) destroy\\(obj4\\) use\\(obj3\\) init\\(targetsync: obj2\\) 
init\\(targetsync: obj1\\) nowait\[\r\n\]" 1 "original" } }  */
 
-  #pragma omp interop init ( obj1, obj2) init (target: obj3) init(targetsync : 
obj4) init(target,targetsync: obj5)  /* { dg-message "'#pragma omp interop' not 
yet supported" }  */
+  #pragma omp interop init ( obj1, obj2) init (target: obj3) init(targetsync : 
obj4) init(target,targetsync: obj5)  
   /* { dg-final { scan-tree-dump-times "#pragma omp interop init\\(target, 
targetsync: obj5\\) init\\(targetsync: obj4\\) init\\(target: obj3\\) 
init\\(obj2\\) init\\(obj1\\)\[\r\n\]" 1 "original" } }  */
 
   /* --------------------------------------------  */
 
-  #pragma omp interop init (target, prefer_type(omp_ifr_cuda, omp_ifr_cuda+1, 
"hsa", "myPrivateInterop", omp_ifr_cuda-2) : obj1, obj2) init (target: obj3) 
init(prefer_type(omp_ifr_hip, "sycl", omp_ifr_opencl), targetsync : obj4, obj7) 
init(target,prefer_type("level_zero", omp_ifr_level_zero+0),targetsync: obj5)  
/* { dg-message "'#pragma omp interop' not yet supported" }  */
+  #pragma omp interop init (target, prefer_type(omp_ifr_cuda, omp_ifr_cuda+1, 
"hsa", "myPrivateInterop", omp_ifr_cuda-2) : obj1, obj2) init (target: obj3) 
init(prefer_type(omp_ifr_hip, "sycl", omp_ifr_opencl), targetsync : obj4, obj7) 
init(target,prefer_type("level_zero", omp_ifr_level_zero+0),targetsync: obj5)  
   /*
      { dg-warning "unknown foreign runtime identifier 'myPrivateInterop' 
\\\[-Wopenmp\\\]" "" { target *-*-* } .-2 }
      { dg-warning "unknown foreign runtime identifier '-1' \\\[-Wopenmp\\\]" 
"" { target *-*-* } .-3 }
@@ -55,7 +55,7 @@ f()
 
 /* -------------------------------------------- */
 
-  #pragma omp interop init ( target, prefer_type( {fr("hip"), 
attr("ompx_gnu_prio:1", "ompx_gnu_debug")}, {attr("ompx_gnu_nicest"), 
attr("ompx_something")}) : obj1, obj2) init ( prefer_type( {fr("cuda")}, 
{fr(omp_ifr_cuda_driver), attr("ompx_nix")}, {fr("best")}), targetsync : obj3, 
obj4) nowait use(obj5)  /* { dg-message "'#pragma omp interop' not yet 
supported" }  */
+  #pragma omp interop init ( target, prefer_type( {fr("hip"), 
attr("ompx_gnu_prio:1", "ompx_gnu_debug")}, {attr("ompx_gnu_nicest"), 
attr("ompx_something")}) : obj1, obj2) init ( prefer_type( {fr("cuda")}, 
{fr(omp_ifr_cuda_driver), attr("ompx_nix")}, {fr("best")}), targetsync : obj3, 
obj4) nowait use(obj5)  
   /*
      { dg-warning "unknown foreign runtime identifier 'best' \\\[-Wopenmp\\\]" 
"" { target *-*-* } .-2 }
 
@@ -69,7 +69,7 @@ g (int *y)
 {
   omp_interop_t io1, io2, io3, io4, io5;
 
-  [[omp::directive (interop,init(prefer_type({fr("level_zero")}, 
{fr(omp_ifr_sycl),attr("ompx_in_order"),attr("ompx_queue:in_order")}), 
targetsync : io1, io2),use(io3),destroy(io4,io5),depend(inout:y),nowait)]];  /* 
{ dg-message "'#pragma omp interop' not yet supported" }  */
+  [[omp::directive (interop,init(prefer_type({fr("level_zero")}, 
{fr(omp_ifr_sycl),attr("ompx_in_order"),attr("ompx_queue:in_order")}), 
targetsync : io1, io2),use(io3),destroy(io4,io5),depend(inout:y),nowait)]];  
 
   /* { dg-final { scan-tree-dump-times "#pragma omp interop nowait 
depend\\(inout:y\\) destroy\\(io5\\) destroy\\(io4\\) use\\(io3\\) 
init\\(prefer_type\\(\{fr\\(\"level_zero\"\\)\}, 
\{fr\\(\"sycl\"\\),attr\\(\"ompx_in_order\"\\),attr\\(\"ompx_queue:in_order\"\\)\}\\),
 targetsync: io2\\) init\\(prefer_type\\(\{fr\\(\"level_zero\"\\)\}, 
\{fr\\(\"sycl\"\\),attr\\(\"ompx_in_order\"\\),attr\\(\"ompx_queue:in_order\"\\)\}\\),
 targetsync: io1\\)\[\r\n\]" 1 "original" } }  */
 }
diff --git a/gcc/testsuite/c-c++-common/gomp/interop-5.c 
b/gcc/testsuite/c-c++-common/gomp/interop-5.c
new file mode 100644
index 00000000000..9a70dc5715a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/interop-5.c
@@ -0,0 +1,65 @@
+/* { dg-additional-options "-fdump-tree-omplower" }  */
+
+/* The following definitions are in omp_lib, which cannot be included
+   in gcc/testsuite/  */
+
+#if __cplusplus >= 201103L
+# define __GOMP_UINTPTR_T_ENUM : __UINTPTR_TYPE__
+#else
+# define __GOMP_UINTPTR_T_ENUM
+#endif
+
+typedef enum omp_interop_t __GOMP_UINTPTR_T_ENUM
+{
+  omp_interop_none = 0,
+  __omp_interop_t_max__ = __UINTPTR_MAX__
+} omp_interop_t;
+
+typedef enum omp_interop_fr_t
+{
+  omp_ifr_cuda = 1,
+  omp_ifr_cuda_driver = 2,
+  omp_ifr_opencl = 3,
+  omp_ifr_sycl = 4,
+  omp_ifr_hip = 5,
+  omp_ifr_level_zero = 6,
+  omp_ifr_hsa = 7,
+  omp_ifr_last = omp_ifr_hsa
+} omp_interop_fr_t;
+
+void
+f()
+{
+  omp_interop_t obj1, obj2, obj3, obj4, obj5, obj6, obj7;
+  int x[6];
+
+  #pragma omp interop init (targetsync: obj1, obj2) use (obj3) destroy(obj4) 
init(target: obj5) destroy(obj6) use(obj7)   
+  /* { dg-final { scan-tree-dump-times "void \\* 
interopobjs\.\[0-9\]+.3.;\[\r\n\ ]*int tgt_tgtsync\\.\[0-9\]+.3.;\[\r\n\ ]*void 
\\* interopobjs\\.\[0-9\]+.2.;\[\r\n\ ]*omp_interop_t obj3\\.\[0-9\]+;\[\r\n\ 
]*void \\* obj3\\.\[0-9\]+;\[\r\n\ ]*omp_interop_t obj7\\.\[0-9\]+;\[\r\n\ 
]*void \\* obj7\\.\[0-9\]+;\[\r\n\ ]*void \\* interopobjs\\.\[0-9\]+.2.;\[\r\n\ 
]*interopobjs\.\[0-9\]+.0. = &obj1;\[\r\n \]*tgt_tgtsync\.\[0-9\]+.0. = 
2;\[\r\n \]*interopobjs\.\[0-9\]+.1. = &obj2;\[\r\n \]*tgt_tgtsync\.\[0-9\]+.1. 
= 2;\[\r\n \]*interopobjs\.\[0-9\]+.2. = &obj5;\[\r\n 
\]*tgt_tgtsync\.\[0-9\]+.2. = 1;\[\r\n \]*obj3\.\[0-9\]+ = obj3;\[\r\n 
\]*obj3\.\[0-9\]+ = \\(void \\*\\) obj3\.\[0-9\]+;\[\r\n 
\]*interopobjs\.\[0-9\]+.0. = obj3\.\[0-9\]+;\[\r\n \]*obj7\.\[0-9\]+ = 
obj7;\[\r\n \]*obj7\.\[0-9\]+ = \\(void \\*\\) obj7\.\[0-9\]+;\[\r\n 
\]*interopobjs\.\[0-9\]+.1. = obj7\.\[0-9\]+;\[\r\n \]*interopobjs\.\[0-9\]+.0. 
= &obj4;\[\r\n \]*interopobjs\.\[0-9\]+.1. = &obj6;\[\r\n 
\]*__builtin_GOMP_interop \\(-5, 3, &interopobjs\.\[0-9\]+, 
&tgt_tgtsync\.\[0-9\]+, &0B, 2, &interopobjs\.\[0-9\]+, 2, 
&interopobjs\.\[0-9\]+, 0, 0B\\);" 1 "omplower" } }  */
+
+  #pragma omp interop nowait init (targetsync : obj1, obj2) use (obj3) 
destroy(obj4) init(target, targetsync : obj5) destroy(obj6) use(obj7) 
depend(inout: x)  
+  /* { dg-final { scan-tree-dump-times "void \\* D\\.\[0-9\]+.3.;\[\r\n\ 
]*void \\* interopobjs\\.\[0-9\]+.3.;\[\r\n\ ]*int 
tgt_tgtsync\\.\[0-9\]+.3.;\[\r\n\ ]*void \\* interopobjs\\.\[0-9\]+.2.;\[\r\n\ 
]*omp_interop_t obj3\\.\[0-9\]+;\[\r\n\ ]*void \\* obj3\\.\[0-9\]+;\[\r\n\ 
]*omp_interop_t obj7\\.\[0-9\]+;\[\r\n\ ]*void \\* obj7\\.\[0-9\]+;\[\r\n\ 
]*void \\* interopobjs\\.\[0-9\]+.2.;\[\r\n\ ]*interopobjs\.\[0-9\]+.0. = 
&obj1;\[\r\n\ ]*tgt_tgtsync\.\[0-9\]+.0. = 2;\[\r\n\ ]*interopobjs\.\[0-9\]+.1. 
= &obj2;\[\r\n\ ]*tgt_tgtsync\.\[0-9\]+.1. = 2;\[\r\n\ 
]*interopobjs\.\[0-9\]+.2. = &obj5;\[\r\n\ ]*tgt_tgtsync\.\[0-9\]+.2. = 
3;\[\r\n\ ]*obj3\.\[0-9\]+ = obj3;\[\r\n\ ]*obj3\.\[0-9\]+ = \\(void \\*\\) 
obj3\.\[0-9\]+;\[\r\n\ ]*interopobjs\.\[0-9\]+.0. = obj3\.\[0-9\]+;\[\r\n\ 
]*obj7\.\[0-9\]+ = obj7;\[\r\n\ ]*obj7\.\[0-9\]+ = \\(void \\*\\) 
obj7\.\[0-9\]+;\[\r\n\ ]*interopobjs\.\[0-9\]+.1. = obj7\.\[0-9\]+;\[\r\n\ 
]*interopobjs\.\[0-9\]+.0. = &obj4;\[\r\n\ ]*interopobjs\.\[0-9\]+.1. = 
&obj6;\[\r\n\ ]*D\.\[0-9\]+.0. = 1B;\[\r\n\ ]*D\.\[0-9\]+.1. = 1B;\[\r\n\ 
]*D\.\[0-9\]+.2. = &x;\[\r\n\ ]*__builtin_GOMP_interop \\(-5, 3, 
&interopobjs\.\[0-9\]+, &tgt_tgtsync\.\[0-9\]+, &0B, 2, &interopobjs\.\[0-9\]+, 
2, &interopobjs\.\[0-9\]+, 1, &D\.\[0-9\]+\\);" 1 "omplower" } }  */
+
+  #pragma omp interop init (target: obj1, obj2) init (target: obj3) 
init(targetsync : obj4) init(target,targetsync: obj5)  
+  /* { dg-final { scan-tree-dump-times "void \\* 
interopobjs\\.\[0-9\]+.5.;\[\r\n\ ]*int tgt_tgtsync\\.\[0-9\]+.5.;\[\r\n\ 
]*interopobjs\.\[0-9\]+.0. = &obj1;\[\r\n \]*tgt_tgtsync\.\[0-9\]+.0. = 
1;\[\r\n \]*interopobjs\.\[0-9\]+.1. = &obj2;\[\r\n \]*tgt_tgtsync\.\[0-9\]+.1. 
= 1;\[\r\n \]*interopobjs\.\[0-9\]+.2. = &obj3;\[\r\n 
\]*tgt_tgtsync\.\[0-9\]+.2. = 1;\[\r\n \]*interopobjs\.\[0-9\]+.3. = 
&obj4;\[\r\n \]*tgt_tgtsync\.\[0-9\]+.3. = 2;\[\r\n \]*interopobjs\.\[0-9\]+.4. 
= &obj5;\[\r\n \]*tgt_tgtsync\.\[0-9\]+.4. = 3;\[\r\n\ ]*__builtin_GOMP_interop 
\\(-5, 5, &interopobjs\.\[0-9\]+, &tgt_tgtsync\.\[0-9\]+, &0B, 0, 0B, 0, 0B, 0, 
0B\\);" 1 "omplower" } }  */
+
+  /* --------------------------------------------  */
+
+  #pragma omp interop init (target, prefer_type(omp_ifr_cuda, omp_ifr_cuda+1, 
"hsa") : obj1, obj2) init (target: obj3) init(prefer_type(omp_ifr_hip, "sycl", 
omp_ifr_opencl), targetsync : obj4, obj7) init(target,prefer_type("level_zero", 
omp_ifr_level_zero+0),targetsync: obj5)  
+  /* { dg-final { scan-tree-dump-times "void \\* 
interopobjs\\.\[0-9\]+.6.;\[\r\n\ ]*int tgt_tgtsync\\.\[0-9\]+.6.;\[\r\n\ 
]*void \\* pref_type\.\[0-9\]+.6.;\[\r\n\ ]*interopobjs\.\[0-9\]+.0. = 
&obj1;\[\r\n \]*tgt_tgtsync\.\[0-9\]+.0. = 1;\[\r\n \]*pref_type\.\[0-9\]+.0. = 
.*;\[\r\n \]*interopobjs\.\[0-9\]+.1. = &obj2;\[\r\n 
\]*tgt_tgtsync\.\[0-9\]+.1. = 1;\[\r\n \]*pref_type\.\[0-9\]+.1. = .*;\[\r\n 
\]*interopobjs\.\[0-9\]+.2. = &obj3;\[\r\n \]*tgt_tgtsync\.\[0-9\]+.2. = 
1;\[\r\n \]*pref_type\.\[0-9\]+.2. = 0B;\[\r\n \]*interopobjs\.\[0-9\]+.3. = 
&obj4;\[\r\n \]*tgt_tgtsync\.\[0-9\]+.3. = 2;\[\r\n \]*pref_type\.\[0-9\]+.3. = 
.*;\[\r\n \]*interopobjs\.\[0-9\]+.4. = &obj7;\[\r\n 
\]*tgt_tgtsync\.\[0-9\]+.4. = 2;\[\r\n \]*pref_type\.\[0-9\]+.4. = .*;\[\r\n 
\]*interopobjs\.\[0-9\]+.5. = &obj5;\[\r\n \]*tgt_tgtsync\.\[0-9\]+.5. = 
3;\[\r\n \]*pref_type\.\[0-9\]+.5. = .*;\[\r\n\ ]*__builtin_GOMP_interop \\(-5, 
6, &interopobjs\.\[0-9\]+, &tgt_tgtsync\.\[0-9\]+, &pref_type\.\[0-9\]+, 0, 0B, 
0, 0B, 0, 0B\\);" 1 "omplower" } }  */
+
+
+  /* -------------------------------------------- */
+
+  #pragma omp interop init ( target, prefer_type( {fr("hip"), 
attr("ompx_gnu_prio:1", "ompx_gnu_debug")}, {attr("ompx_gnu_nicest"), 
attr("ompx_something")}) : obj1, obj2) init ( prefer_type( {fr("cuda")}, 
{fr(omp_ifr_cuda_driver), attr("ompx_nix")}), targetsync : obj3, obj4) nowait 
use(obj5)  
+  /* { dg-final { scan-tree-dump-times "void \\* 
interopobjs\\.\[0-9\]+.4.;\[\r\n\ ]*int tgt_tgtsync\\.\[0-9\]+.4.;\[\r\n\ 
]*void \\* pref_type\.\[0-9\]+.4.;\[\r\n\ ]*omp_interop_t D\.\[0-9\]+;\[\r\n\ 
]*interopobjs\.\[0-9\]+.0. = &obj1;\[\r\n ]*tgt_tgtsync\.\[0-9\]+.0. = 1;\[\r\n 
]*pref_type\.\[0-9\]+.0. = .*;\[\r\n ]*interopobjs\.\[0-9\]+.1. = &obj2;\[\r\n 
]*tgt_tgtsync\.\[0-9\]+.1. = 1;\[\r\n ]*pref_type\.\[0-9\]+.1. = .*;\[\r\n 
]*interopobjs\.\[0-9\]+.2. = &obj3;\[\r\n ]*tgt_tgtsync\.\[0-9\]+.2. = 2;\[\r\n 
]*pref_type\.\[0-9\]+.2. = .*;\[\r\n ]*interopobjs\.\[0-9\]+.3. = &obj4;\[\r\n 
]*tgt_tgtsync\.\[0-9\]+.3. = 2;\[\r\n ]*pref_type\.\[0-9\]+.3. = .*;\[\r\n 
]*D\.\[0-9\]+ = obj5;\[\r\n ]*__builtin_GOMP_interop \\(-5, 4, 
&interopobjs\.\[0-9\]+, &tgt_tgtsync\.\[0-9\]+, &pref_type\.\[0-9\]+, 1, 
D\.\[0-9\]+, 0, 0B, 1, 0B\\);" 1 "omplower" } }  */
+
+}
+
+void
+g (int *y)
+{
+  omp_interop_t io1, io2, io3, io4, io5;
+
+  [[omp::directive (interop,init(prefer_type({fr("level_zero")}, 
{fr(omp_ifr_sycl),attr("ompx_in_order"),attr("ompx_queue:in_order")}), 
targetsync : io1, io2),use(io3),destroy(io4,io5),depend(inout:y),nowait)]];  
+  /* { dg-final { scan-tree-dump-times "void \\* D\.\[0-9\]+.3.;\[\r\n\ ]*void 
\\* interopobjs\.\[0-9\]+.2.;\[\r\n\ ]*int tgt_tgtsync\.\[0-9\]+.2.;\[\r\n\ 
]*void \\* pref_type\.\[0-9\]+.2.;\[\r\n\ ]*omp_interop_t D\.\[0-9\]+;\[\r\n\ 
]*void \\* interopobjs\.\[0-9\]+.2.;\[\r\n\ ]*interopobjs\.\[0-9\]+.0. = 
&io1;\[\r\n ]*tgt_tgtsync\.\[0-9\]+.0. = 2;\[\r\n ]*pref_type\.\[0-9\]+.0. = 
.*;\[\r\n ]*interopobjs\.\[0-9\]+.1. = &io2;\[\r\n ]*tgt_tgtsync\.\[0-9\]+.1. = 
2;\[\r\n ]*pref_type\.\[0-9\]+.1. = .*;\[\r\n ]*D\.\[0-9\]+ = io3;\[\r\n 
]*interopobjs\.\[0-9\]+.0. = &io4;\[\r\n ]*interopobjs\.\[0-9\]+.1. = 
&io5;\[\r\n ]*D\.\[0-9\]+.0. = 1B;\[\r\n ]*D\.\[0-9\]+.1. = 1B;\[\r\n 
]*D\.\[0-9\]+.2. = &y;\[\r\n \]*__builtin_GOMP_interop \\(-5, 2, 
&interopobjs\.\[0-9\]+, &tgt_tgtsync\.\[0-9\]+, &pref_type\.\[0-9\]+, 1, 
D\.\[0-9\]+, 2, &interopobjs\.\[0-9\]+, 1, &D\.\[0-9\]+\\);" 1 "omplower" } }  
*/
+}
diff --git a/gcc/testsuite/g++.dg/gomp/interop-5.C 
b/gcc/testsuite/g++.dg/gomp/interop-5.C
index 5109dc4e427..89396cf5437 100644
--- a/gcc/testsuite/g++.dg/gomp/interop-5.C
+++ b/gcc/testsuite/g++.dg/gomp/interop-5.C
@@ -1,8 +1,6 @@
 /* { dg-do compile { target c++11 } } */
 /* { dg-additional-options "-fdump-tree-original" }  */
 
-/* { dg-prune-output "sorry, unimplemented: '#pragma omp interop' not yet 
supported" }  */
-
 /* The following definitions are in omp_lib, which cannot be included
    in gcc/testsuite/  */
 
@@ -43,13 +41,13 @@ f ()
   constexpr T3 ifr_level_zero = (T3) (omp_ifr_sycl + 2);
   constexpr T3 ifr_invalid = (T3) 99;
 
-  #pragma omp interop init ( obj1, obj2) use (obj3) destroy(obj4) init(obj5) 
destroy(obj6) use(obj7)   /* { dg-message "'#pragma omp interop' not yet 
supported" }  */
+  #pragma omp interop init ( obj1, obj2) use (obj3) destroy(obj4) init(obj5) 
destroy(obj6) use(obj7)   
   /* { dg-final { scan-tree-dump-times "#pragma omp interop use\\(obj7\\) 
destroy\\(obj6\\) init\\(obj5\\) destroy\\(obj4\\) use\\(obj3\\) init\\(obj2\\) 
init\\(obj1\\)\[\r\n\]" 2 "original" } }  */
 
-  #pragma omp interop nowait init (targetsync : obj1, obj2) use (obj3) 
destroy(obj4) init(target, targetsync : obj5) destroy(obj6) use(obj7) 
depend(inout: x)  /* { dg-message "'#pragma omp interop' not yet supported" }  
*/
+  #pragma omp interop nowait init (targetsync : obj1, obj2) use (obj3) 
destroy(obj4) init(target, targetsync : obj5) destroy(obj6) use(obj7) 
depend(inout: x)  
   /* { dg-final { scan-tree-dump-times "#pragma omp interop 
depend\\(inout:x\\) use\\(obj7\\) destroy\\(obj6\\) init\\(target, targetsync: 
obj5\\) destroy\\(obj4\\) use\\(obj3\\) init\\(targetsync: obj2\\) 
init\\(targetsync: obj1\\) nowait\[\r\n\]" 2 "original" } }  */
 
-  #pragma omp interop init ( obj1, obj2) init (target: obj3) init(targetsync : 
obj4) init(target,targetsync: obj5)  /* { dg-message "'#pragma omp interop' not 
yet supported" }  */
+  #pragma omp interop init ( obj1, obj2) init (target: obj3) init(targetsync : 
obj4) init(target,targetsync: obj5)  
   /* { dg-final { scan-tree-dump-times "#pragma omp interop init\\(target, 
targetsync: obj5\\) init\\(targetsync: obj4\\) init\\(target: obj3\\) 
init\\(obj2\\) init\\(obj1\\)\[\r\n\]" 2 "original" } }  */
 
   /* --------------------------------------------  */
@@ -64,7 +62,7 @@ f ()
      { dg-final { scan-tree-dump-times "#pragma omp interop 
init\\(prefer_type\\(\{fr\\(\"<unknown>\"\\)\}, \{fr\\(\"<unknown>\"\\)\}, 
\{fr\\(\"cuda\"\\)\}, \{fr\\(\"cuda_driver\"\\)\}, \{fr\\(\"hsa\"\\)\}, 
\{fr\\(\"<unknown>\"\\)\}, \{fr\\(\"<unknown>\"\\)\}\\), target: obj2\\) 
init\\(prefer_type\\(\{fr\\(\"<unknown>\"\\)\}, \{fr\\(\"<unknown>\"\\)\}, 
\{fr\\(\"cuda\"\\)\}, \{fr\\(\"cuda_driver\"\\)\}, \{fr\\(\"hsa\"\\)\}, 
\{fr\\(\"<unknown>\"\\)\}, \{fr\\(\"<unknown>\"\\)\}\\), target: 
obj1\\)\[\r\n\]" 2 "original" } }
   */
 
-  #pragma omp interop init (target, prefer_type(ifr_cuda, ifr_cuda+1, "hsa", 
"myPrivateInterop", ifr_cuda-2) : obj1, obj2) init (target: obj3) 
init(prefer_type(ifr_hip, "sycl", ifr_opencl), targetsync : obj4, obj7) 
init(target,prefer_type("level_zero", ifr_level_zero+0),targetsync: obj5)  /* { 
dg-message "'#pragma omp interop' not yet supported" }  */
+  #pragma omp interop init (target, prefer_type(ifr_cuda, ifr_cuda+1, "hsa", 
"myPrivateInterop", ifr_cuda-2) : obj1, obj2) init (target: obj3) 
init(prefer_type(ifr_hip, "sycl", ifr_opencl), targetsync : obj4, obj7) 
init(target,prefer_type("level_zero", ifr_level_zero+0),targetsync: obj5)  
   /*
      { dg-warning "unknown foreign runtime identifier 'myPrivateInterop' 
\\\[-Wopenmp\\\]" "" { target *-*-* } .-2 }
      { dg-warning "unknown foreign runtime identifier '-1' \\\[-Wopenmp\\\]" 
"" { target *-*-* } .-3 }
@@ -74,7 +72,7 @@ f ()
 
 /* -------------------------------------------- */
 
-  #pragma omp interop init ( target, prefer_type( {fr("hip"), 
attr("ompx_gnu_prio:1", "ompx_gnu_debug")}, {attr("ompx_gnu_nicest"), 
attr("ompx_something")}) : obj1, obj2) init ( prefer_type( {fr("cuda")}, 
{fr(ifr_cuda_driver), attr("ompx_nix")}, {fr("best")}), targetsync : obj3, 
obj4) nowait use(obj5)  /* { dg-message "'#pragma omp interop' not yet 
supported" }  */
+  #pragma omp interop init ( target, prefer_type( {fr("hip"), 
attr("ompx_gnu_prio:1", "ompx_gnu_debug")}, {attr("ompx_gnu_nicest"), 
attr("ompx_something")}) : obj1, obj2) init ( prefer_type( {fr("cuda")}, 
{fr(ifr_cuda_driver), attr("ompx_nix")}, {fr("best")}), targetsync : obj3, 
obj4) nowait use(obj5)  
   /*
      { dg-warning "unknown foreign runtime identifier 'best' \\\[-Wopenmp\\\]" 
"" { target *-*-* } .-2 }
 
diff --git a/gcc/testsuite/gfortran.dg/gomp/interop-4.f90 
b/gcc/testsuite/gfortran.dg/gomp/interop-4.f90
index 8783f4cfb5f..43c28d69668 100644
--- a/gcc/testsuite/gfortran.dg/gomp/interop-4.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/interop-4.f90
@@ -26,18 +26,18 @@ implicit none
 integer(omp_interop_kind) :: obj1, obj2, obj3, obj4, obj5, obj6, obj7
 integer :: x(6)
 
-!$omp interop init ( obj1, obj2) use (obj3) destroy(obj4) init(obj5) 
destroy(obj6) use(obj7) ! { dg-message "'#pragma omp interop' not yet 
supported" }
+!$omp interop init ( obj1, obj2) use (obj3) destroy(obj4) init(obj5) 
destroy(obj6) use(obj7) 
 ! { dg-final { scan-tree-dump-times "#pragma omp interop init\\(obj1\\) 
init\\(obj2\\) init\\(obj5\\) use\\(obj3\\) use\\(obj7\\) destroy\\(obj4\\) 
destroy\\(obj6\\)\[\r\n\]" 1 "original" } }
 
-!$omp interop nowait init (targetsync : obj1, obj2) use (obj3) destroy(obj4) 
init(target, targetsync : obj5) destroy(obj6) use(obj7) depend(inout: x) ! { 
dg-message "'#pragma omp interop' not yet supported" }
+!$omp interop nowait init (targetsync : obj1, obj2) use (obj3) destroy(obj4) 
init(target, targetsync : obj5) destroy(obj6) use(obj7) depend(inout: x) 
 ! { dg-final { scan-tree-dump-times "#pragma omp interop depend\\(inout:x\\) 
init\\(targetsync: obj1\\) init\\(targetsync: obj2\\) init\\(target, 
targetsync: obj5\\) use\\(obj3\\) use\\(obj7\\) destroy\\(obj4\\) 
destroy\\(obj6\\) nowait\[\r\n\]" 1 "original" } }
 
-!$omp interop init ( obj1, obj2) init (target: obj3) init(targetsync : obj4) 
init(target,targetsync: obj5)  ! { dg-message "'#pragma omp interop' not yet 
supported" }
+!$omp interop init ( obj1, obj2) init (target: obj3) init(targetsync : obj4) 
init(target,targetsync: obj5)  
 ! { dg-final { scan-tree-dump-times "#pragma omp interop init\\(obj1\\) 
init\\(obj2\\) init\\(target: obj3\\) init\\(targetsync: obj4\\) init\\(target, 
targetsync: obj5\\)\[\r\n\]" 1 "original" } }
 
 ! --------------------------------------------
 
-!$omp interop init (target, prefer_type(omp_ifr_cuda, omp_ifr_cuda+1, "hsa", 
"myPrivateInterop", omp_ifr_cuda-2) : obj1, obj2) init (target: obj3) 
init(prefer_type(omp_ifr_hip, "sycl", omp_ifr_opencl), targetsync : obj4, obj7) 
init(target,prefer_type("level_zero", omp_ifr_level_zero+0),targetsync: obj5)  
! { dg-message "'#pragma omp interop' not yet supported" }
+!$omp interop init (target, prefer_type(omp_ifr_cuda, omp_ifr_cuda+1, "hsa", 
"myPrivateInterop", omp_ifr_cuda-2) : obj1, obj2) init (target: obj3) 
init(prefer_type(omp_ifr_hip, "sycl", omp_ifr_opencl), targetsync : obj4, obj7) 
init(target,prefer_type("level_zero", omp_ifr_level_zero+0),targetsync: obj5)  
 !
 ! { dg-warning "Unknown foreign runtime identifier 'myPrivateInterop' at 
\\(1\\) \\\[-Wopenmp\\\]" "" { target *-*-* } .-2 }
 ! { dg-warning "Unknown foreign runtime identifier '-1' at \\(1\\) 
\\\[-Wopenmp\\\]" "" { target *-*-* } .-3 }
@@ -47,7 +47,7 @@ integer :: x(6)
 
 ! --------------------------------------------
 
-!$omp interop init ( target, prefer_type( {fr(1_"hip"), 
attr("ompx_gnu_prio:1", 1_"ompx_gnu_debug")}, {attr("ompx_gnu_nicest"), 
attr("ompx_something")}) : obj1, obj2) init ( prefer_type( {fr("cuda")}, 
{fr(omp_ifr_cuda_driver), attr("ompx_nix")}, {fr("best")}), targetsync : obj3, 
obj4) nowait use(obj5)    ! { dg-message "'#pragma omp interop' not yet 
supported" }
+!$omp interop init ( target, prefer_type( {fr(1_"hip"), 
attr("ompx_gnu_prio:1", 1_"ompx_gnu_debug")}, {attr("ompx_gnu_nicest"), 
attr("ompx_something")}) : obj1, obj2) init ( prefer_type( {fr("cuda")}, 
{fr(omp_ifr_cuda_driver), attr("ompx_nix")}, {fr("best")}), targetsync : obj3, 
obj4) nowait use(obj5)    
 !
 ! ! { dg-warning "Unknown foreign runtime identifier 'best' at \\(1\\) 
\\\[-Wopenmp\\\]" "" { target *-*-* } .-2 }
 !
diff --git a/gcc/testsuite/gfortran.dg/gomp/interop-5.f90 
b/gcc/testsuite/gfortran.dg/gomp/interop-5.f90
new file mode 100644
index 00000000000..b2446ba8bb0
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/interop-5.f90
@@ -0,0 +1,61 @@
+! { dg-additional-options "-fdump-tree-omplower" }
+
+subroutine sub1 (a1, a2, a3, a4)
+   use omp_lib, only: omp_interop_kind
+   integer(omp_interop_kind) :: a1  ! by ref
+   integer(omp_interop_kind), optional :: a2 ! as pointer
+   integer(omp_interop_kind), allocatable :: a3 ! ref to pointer
+   integer(omp_interop_kind), value :: a4
+   integer(omp_interop_kind) :: b
+
+   !$omp interop init(target : a1, a2, a3, a4, b)
+   ! { dg-final { scan-tree-dump-times "void \\* 
interopobjs\.\[0-9\]+\\\[5\\\];\[\r\n ]*integer\\(kind=4\\) 
tgt_tgtsync\.\[0-9\]+\\\[5\\\];\[\r\n ]*integer\\(kind=8\\) \\* & 
a3\.\[0-9\]+;\[\r\n ]*integer\\(kind=8\\) \\* D\.\[0-9\]+;\[\r\n 
]*integer\\(kind=8\\) \\* a2\.\[0-9\]+;\[\r\n ]*integer\\(kind=8\\) & 
a1\.\[0-9\]+;\[\r\n ]*interopobjs\.\[0-9\]+\\\[0\\\] = &b;\[\r\n 
]*tgt_tgtsync\.\[0-9\]+\\\[0\\\] = 1;\[\r\n ]*interopobjs\.\[0-9\]+\\\[1\\\] = 
&a4;\[\r\n ]*tgt_tgtsync\.\[0-9\]+\\\[1\\\] = 1;\[\r\n ]*a3\.\[0-9\]+ = 
a3;\[\r\n ]*D\.\[0-9\]+ = \\*a3\.\[0-9\]+;\[\r\n 
]*interopobjs\.\[0-9\]+\\\[2\\\] = D\.\[0-9\]+;\[\r\n 
]*tgt_tgtsync\.\[0-9\]+\\\[2\\\] = 1;\[\r\n ]*a2\.\[0-9\]+ = a2;\[\r\n 
]*interopobjs\.\[0-9\]+\\\[3\\\] = a2\.\[0-9\]+;\[\r\n 
]*tgt_tgtsync\.\[0-9\]+\\\[3\\\] = 1;\[\r\n ]*a1\.\[0-9\]+ = a1;\[\r\n 
]*interopobjs\.\[0-9\]+\\\[4\\\] = a1\.\[0-9\]+;\[\r\n 
]*tgt_tgtsync\.\[0-9\]+\\\[4\\\] = 1;\[\r\n ]*__builtin_GOMP_interop \\(-5, 5, 
&interopobjs\.\[0-9\]+, &tgt_tgtsync\.\[0-9\]+, &0B, 0, 0B, 0, 0B, 0, 0B\\);" 1 
"omplower" } }
+
+   !$omp interop use(a1, a2, a3, a4, b)
+   ! { dg-final { scan-tree-dump-times "void \\* 
interopobjs\.\[0-9\]+\\\[5\\\];\[\r\n ]*integer\\(kind=8\\) b\.\[0-9\]+;\[\r\n 
]*void \\* b\.\[0-9\]+;\[\r\n ]*integer\\(kind=8\\) a4\.\[0-9\]+;\[\r\n ]*void 
\\* a4\.\[0-9\]+;\[\r\n ]*integer\\(kind=8\\) \\* & a3\.\[0-9\]+;\[\r\n 
]*integer\\(kind=8\\) \\* D\.\[0-9\]+;\[\r\n ]*integer\\(kind=8\\) 
D\.\[0-9\]+;\[\r\n ]*void \\* D\.\[0-9\]+;\[\r\n ]*integer\\(kind=8\\) \\* 
a2\.\[0-9\]+;\[\r\n ]*integer\\(kind=8\\) D\.\[0-9\]+;\[\r\n ]*void \\* 
D\.\[0-9\]+;\[\r\n ]*integer\\(kind=8\\) & a1\.\[0-9\]+;\[\r\n 
]*integer\\(kind=8\\) D\.\[0-9\]+;\[\r\n ]*void \\* D\.\[0-9\]+;\[\r\n 
]*;\[\r\n ]*b\.\[0-9\]+ = b;\[\r\n ]*b\.\[0-9\]+ = (void \\*) 
b\.\[0-9\]+;\[\r\n ]*interopobjs\.\[0-9\]+\\\[0\\\] = b\.\[0-9\]+;\[\r\n 
]*a4\.\[0-9\]+ = a4;\[\r\n ]*a4\.\[0-9\]+ = (void \\*) a4\.\[0-9\]+;\[\r\n 
]*interopobjs\.\[0-9\]+\\\[1\\\] = a4\.\[0-9\]+;\[\r\n ]*a3\.\[0-9\]+ = 
a3;\[\r\n ]*D\.\[0-9\]+ = \\*a3\.\[0-9\]+;\[\r\n ]*D\.\[0-9\]+ = 
\\*D\.\[0-9\]+;\[\r\n ]*D\.\[0-9\]+ = (void \\*) D\.\[0-9\]+;\[\r\n 
]*interopobjs\.\[0-9\]+\\\[2\\\] = D\.\[0-9\]+;\[\r\n ]*a2\.\[0-9\]+ = 
a2;\[\r\n ]*D\.\[0-9\]+ = \\*a2\.\[0-9\]+;\[\r\n ]*D\.\[0-9\]+ = (void \\*) 
D\.\[0-9\]+;\[\r\n ]*interopobjs\.\[0-9\]+\\\[3\\\] = D\.\[0-9\]+;\[\r\n 
]*a1\.\[0-9\]+ = a1;\[\r\n ]*D\.\[0-9\]+ = \\*a1\.\[0-9\]+;\[\r\n ]*D\.\[0-9\]+ 
= (void \\*) D\.\[0-9\]+;\[\r\n ]*interopobjs\.\[0-9\]+\\\[4\\\] = 
D\.\[0-9\]+;\[\r\n ]*__builtin_GOMP_interop \\(-5, 0, 0B, 0B, 0B, 5, 
&interopobjs\.\[0-9\]+, 0, 0B, 0, 0B\\);" 1 "omplower" } }
+
+   !$omp interop destroy(a1, a2, a3, a4, b)
+   ! { dg-final { scan-tree-dump-times "void \\* 
interopobjs\.\[0-9\]+\\\[5\\\];\[\r\n ]*integer\\(kind=8\\) \\* & 
a3\.\[0-9\]+;\[\r\n ]*integer\\(kind=8\\) \\* D\.\[0-9\]+;\[\r\n 
]*integer\\(kind=8\\) \\* a2\.\[0-9\]+;\[\r\n ]*integer\\(kind=8\\) & 
a1\.\[0-9\]+;\[\r\n ]*interopobjs\.\[0-9\]+\\\[0\\\] = &b;\[\r\n 
]*interopobjs\.\[0-9\]+\\\[1\\\] = &a4;\[\r\n ]*a3\.\[0-9\]+ = a3;\[\r\n 
]*D\.\[0-9\]+ = \\*a3\.\[0-9\]+;\[\r\n ]*interopobjs\.\[0-9\]+\\\[2\\\] = 
D\.\[0-9\]+;\[\r\n ]*a2\.\[0-9\]+ = a2;\[\r\n ]*interopobjs\.\[0-9\]+\\\[3\\\] 
= a2\.\[0-9\]+;\[\r\n ]*a1\.\[0-9\]+ = a1;\[\r\n 
]*interopobjs\.\[0-9\]+\\\[4\\\] = a1\.\[0-9\]+;\[\r\n ]*__builtin_GOMP_interop 
\\(-5, 0, 0B, 0B, 0B, 0, 0B, 5, &interopobjs\.\[0-9\]+, 0, 0B\\);" 1 "omplower" 
} }
+end subroutine
+
+subroutine sub2 (a1, a2, a3, a4)
+   use omp_lib, only: omp_interop_kind
+   integer(omp_interop_kind) :: a1  ! by ref
+   integer(omp_interop_kind), optional :: a2 ! as pointer
+   integer(omp_interop_kind), allocatable :: a3 ! ref to pointer
+   integer(omp_interop_kind), value :: a4
+   integer(omp_interop_kind) :: b
+   !$omp interop init(target : a1)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) \\* 
D\.\[0-9\]+;\[\r\n ]*D\.\[0-9\]+ = a1;\[\r\n ]*__builtin_GOMP_interop \\(-5, 1, 
D\.\[0-9\]+, 1, 0B, 0, 0B, 0, 0B, 0, 0B\\);" 1 "omplower" } }
+   !$omp interop init(target : a2)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) \\* restrict 
D\.\[0-9\]+;\[\r\n ]*D\.\[0-9\]+ = a2;\[\r\n ]*__builtin_GOMP_interop \\(-5, 1, 
D\.\[0-9\]+, 1, 0B, 0, 0B, 0, 0B, 0, 0B\\);" 1 "omplower" } }
+   !$omp interop init(target : a3)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) \\* 
D\.\[0-9\]+;\[\r\n ]*integer\\(kind=8\\) \\* & a3\.\[0-9\]+;\[\r\n 
]*a3\.\[0-9\]+ = a3;\[\r\n ]*D\.\[0-9\]+ = \\*a3\.\[0-9\]+;\[\r\n 
]*__builtin_GOMP_interop \\(-5, 1, D\.\[0-9\]+, 1, 0B, 0, 0B, 0, 0B, 0, 0B\\);" 
1 "omplower" } }
+   !$omp interop init(target : a4)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) \\* 
D\.\[0-9\]+;\[\r\n ]*D\.\[0-9\]+ = &a4;\[\r\n ]*__builtin_GOMP_interop \\(-5, 
1, D\.\[0-9\]+, 1, 0B, 0, 0B, 0, 0B, 0, 0B\\);" 1 "omplower" } }
+   !$omp interop init(target : b)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) \\* 
D\.\[0-9\]+;\[\r\n ]*D\.\[0-9\]+ = &b;\[\r\n ]*__builtin_GOMP_interop \\(-5, 1, 
D\.\[0-9\]+, 1, 0B, 0, 0B, 0, 0B, 0, 0B\\);" 1 "omplower" } }
+   
+   !$omp interop use(a1)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) D\.\[0-9\]+;\[\r\n 
]*integer\\(kind=8\\) & a1\.\[0-9\]+;\[\r\n ]*a1\.\[0-9\]+ = a1;\[\r\n 
]*D\.\[0-9\]+ = \\*a1\.\[0-9\]+;\[\r\n ]*__builtin_GOMP_interop \\(-5, 0, 0B, 
0B, 0B, 1, D\.\[0-9\]+, 0, 0B, 0, 0B\\);" 1 "omplower" } }
+   !$omp interop use(a2)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) D\.\[0-9\]+;\[\r\n 
]*integer\\(kind=8\\) \\* a2\.\[0-9\]+;\[\r\n ]*a2\.\[0-9\]+ = a2;\[\r\n 
]*D\.\[0-9\]+ = \\*a2\.\[0-9\]+;\[\r\n ]*__builtin_GOMP_interop \\(-5, 0, 0B, 
0B, 0B, 1, D\.\[0-9\]+, 0, 0B, 0, 0B\\);" 1 "omplower" } }
+   !$omp interop use(a3)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) D\.\[0-9\]+;\[\r\n 
]*integer\\(kind=8\\) \\* & a3\.\[0-9\]+;\[\r\n ]*integer\\(kind=8\\) \\* 
D\.\[0-9\]+;\[\r\n ]*a3\.\[0-9\]+ = a3;\[\r\n ]*D\.\[0-9\]+ = 
\\*a3\.\[0-9\]+;\[\r\n ]*D\.\[0-9\]+ = \\*D\.\[0-9\]+;\[\r\n 
]*__builtin_GOMP_interop \\(-5, 0, 0B, 0B, 0B, 1, D\.\[0-9\]+, 0, 0B, 0, 
0B\\);" 1 "omplower" } }
+   !$omp interop use(a4)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) D\.\[0-9\]+;\[\r\n 
]*D\.\[0-9\]+ = a4;\[\r\n ]*__builtin_GOMP_interop \\(-5, 0, 0B, 0B, 0B, 1, 
D\.\[0-9\]+, 0, 0B, 0, 0B\\);" 1 "omplower" } }
+   !$omp interop use(b)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) D\.\[0-9\]+;\[\r\n 
]*D\.\[0-9\]+ = b;\[\r\n ]*__builtin_GOMP_interop \\(-5, 0, 0B, 0B, 0B, 1, 
D\.\[0-9\]+, 0, 0B, 0, 0B\\);" 1 "omplower" } }
+   
+   !$omp interop destroy(a1)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) \\* 
D\.\[0-9\]+;\[\r\n ]*D\.\[0-9\]+ = a1;\[\r\n ]*__builtin_GOMP_interop \\(-5, 0, 
0B, 0B, 0B, 0, 0B, 1, D\.\[0-9\]+, 0, 0B\\);" 1 "omplower" } }
+   !$omp interop destroy(a2)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) \\* restrict 
D\.\[0-9\]+;\[\r\n ]*D\.\[0-9\]+ = a2;\[\r\n ]*__builtin_GOMP_interop \\(-5, 0, 
0B, 0B, 0B, 0, 0B, 1, D\.\[0-9\]+, 0, 0B\\);" 1 "omplower" } }
+   !$omp interop destroy(a3)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) \\* 
D\.\[0-9\]+;\[\r\n ]*integer\\(kind=8\\) \\* & a3\.\[0-9\]+;\[\r\n 
]*a3\.\[0-9\]+ = a3;\[\r\n ]*D\.\[0-9\]+ = \\*a3\.\[0-9\]+;\[\r\n 
]*__builtin_GOMP_interop \\(-5, 0, 0B, 0B, 0B, 0, 0B, 1, D\.\[0-9\]+, 0, 
0B\\);" 1 "omplower" } }
+   !$omp interop destroy(a4)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) \\* 
D\.\[0-9\]+;\[\r\n ]*D\.\[0-9\]+ = &a4;\[\r\n ]*__builtin_GOMP_interop \\(-5, 
0, 0B, 0B, 0B, 0, 0B, 1, D\.\[0-9\]+, 0, 0B\\);" 1 "omplower" } }
+   !$omp interop destroy(b)
+   ! { dg-final { scan-tree-dump-times "integer\\(kind=8\\) \\* 
D\.\[0-9\]+;\[\r\n ]*D\.\[0-9\]+ = &b;\[\r\n ]*__builtin_GOMP_interop \\(-5, 0, 
0B, 0B, 0B, 0, 0B, 1, D\.\[0-9\]+, 0, 0B\\);" 1 "omplower" } }
+
+end subroutine
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index ccf61ca0ae0..13123d11614 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -280,10 +280,14 @@ enum gomp_map_kind
    omp_invalid_device) to -3 (so that for dev_num >= -2U we can
    subtract 1).  -4 is then what we use for omp_invalid_device,
    which unlike the other non-conforming device numbers results
-   in fatal error regardless of OMP_TARGET_OFFLOAD.  */
+   in fatal error regardless of OMP_TARGET_OFFLOAD.
+   Furthermore, OpenMP 6.1 exposes the default device to the user; hence,
+   GOMP_DEVICE_DEFAULT_OMP_61 can be used for it,
+   with and without remapped device numbers.  */
 #define GOMP_DEVICE_ICV                        -1
 #define GOMP_DEVICE_HOST_FALLBACK      -2
 #define GOMP_DEVICE_INVALID            -4
+#define GOMP_DEVICE_DEFAULT_OMP_61     -5
 
 /* GOMP_task/GOMP_taskloop* flags argument.  */
 #define GOMP_TASK_FLAG_UNTIED          (1 << 0)
@@ -406,6 +410,9 @@ enum gomp_map_kind
 #define GOMP_INTEROP_IFR_SEPARATOR ((char)(-__INT8_MAX__-1))
 #define GOMP_INTEROP_IFR_UNKNOWN ((char)(-__INT8_MAX__))
 
+#define GOMP_INTEROP_FLAG_TARGET       (1 << 0)
+#define GOMP_INTEROP_FLAG_TARGETSYNC   (1 << 1)
+
 /* HSA specific data structures.  */
 
 /* Identifiers of device-specific target arguments.  */
diff --git a/libgomp/icv-device.c b/libgomp/icv-device.c
index ba06f50a7c0..40bf7cd658a 100644
--- a/libgomp/icv-device.c
+++ b/libgomp/icv-device.c
@@ -32,8 +32,11 @@
 void
 omp_set_default_device (int device_num)
 {
-  struct gomp_task_icv *icv = gomp_icv (true);
-  icv->default_device_var = device_num;
+  if (device_num != GOMP_DEVICE_DEFAULT_OMP_61)
+    {
+      struct gomp_task_icv *icv = gomp_icv (true);
+      icv->default_device_var = device_num;
+    }
 }
 
 ialias (omp_set_default_device)
diff --git a/libgomp/libgomp-plugin.h b/libgomp/libgomp-plugin.h
index 62bf43da937..924fc1f44b1 100644
--- a/libgomp/libgomp-plugin.h
+++ b/libgomp/libgomp-plugin.h
@@ -33,6 +33,14 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#ifdef _LIBGOMP_PLUGIN_INCLUDE
+  /* Include 'omp.h' for the interop definitions.  */
+  #define _LIBGOMP_OMP_LOCK_DEFINED 1
+  typedef struct omp_lock_t omp_lock_t;
+  typedef struct omp_nest_lock_t omp_nest_lock_t;
+  #include "omp.h.in"
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -101,6 +109,25 @@ struct addr_pair
   uintptr_t end;
 };
 
+
+#ifdef _LIBGOMP_OMP_LOCK_DEFINED
+/* Only define when omp.h.in was included, as in plugin/ and in libgomp.h.   */
+struct interop_obj_t
+{
+  void *stream;
+  void *device_data;
+  omp_interop_fr_t fr;
+  int device_num;
+};
+
+enum gomp_interop_flag
+{
+  gomp_interop_flag_init,
+  gomp_interop_flag_use,
+  gomp_interop_flag_destroy
+};
+#endif
+
 /* This following symbol is used to name the target side variable struct that
    holds the designated ICVs of the target device. The symbol needs to be
    available to libgomp code and the offload plugin (which in the latter case
@@ -181,6 +208,23 @@ extern int GOMP_OFFLOAD_openacc_cuda_set_stream (struct 
goacc_asyncqueue *,
 extern union goacc_property_value
   GOMP_OFFLOAD_openacc_get_property (int, enum goacc_property);
 
+#ifdef _LIBGOMP_OMP_LOCK_DEFINED
+/* Only define when omp.h.in was included, as in plugin/ and in libgomp.h.   */
+extern void GOMP_OFFLOAD_interop (struct interop_obj_t *, int,
+                                 enum gomp_interop_flag, bool, const char *);
+extern intptr_t GOMP_OFFLOAD_get_interop_int (struct interop_obj_t *,
+                                             omp_interop_property_t,
+                                             omp_interop_rc_t *);
+extern void *GOMP_OFFLOAD_get_interop_ptr (struct interop_obj_t *,
+                                          omp_interop_property_t,
+                                          omp_interop_rc_t *);
+extern const char *GOMP_OFFLOAD_get_interop_str (struct interop_obj_t *obj,
+                                                omp_interop_property_t,
+                                                omp_interop_rc_t *);
+extern const char *GOMP_OFFLOAD_get_interop_type_desc (struct interop_obj_t *,
+                                                      omp_interop_property_t);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h
index 44ad980eed7..d97768f5125 100644
--- a/libgomp/libgomp.h
+++ b/libgomp/libgomp.h
@@ -43,7 +43,14 @@
 
 #include "config.h"
 #include <stdint.h>
+
+/* Include omp.h by parts.  */
+#include "omp-lock.h"
+#define _LIBGOMP_OMP_LOCK_DEFINED 1
+#include "omp.h.in"
+
 #include "libgomp-plugin.h"
+
 #include "gomp-constants.h"
 
 #ifdef HAVE_PTHREAD_H
@@ -1419,6 +1426,11 @@ struct gomp_device_descr
   __typeof (GOMP_OFFLOAD_can_run) *can_run_func;
   __typeof (GOMP_OFFLOAD_run) *run_func;
   __typeof (GOMP_OFFLOAD_async_run) *async_run_func;
+  __typeof (GOMP_OFFLOAD_interop) *interop_func;
+  __typeof (GOMP_OFFLOAD_get_interop_int) *get_interop_int_func;
+  __typeof (GOMP_OFFLOAD_get_interop_ptr) *get_interop_ptr_func;
+  __typeof (GOMP_OFFLOAD_get_interop_str) *get_interop_str_func;
+  __typeof (GOMP_OFFLOAD_get_interop_type_desc) *get_interop_type_desc_func;
 
   /* Splay tree containing information about mapped memory regions.  */
   struct splay_tree_s mem_map;
@@ -1501,11 +1513,6 @@ gomp_work_share_init_done (void)
 /* Now that we're back to default visibility, include the globals.  */
 #include "libgomp_g.h"
 
-/* Include omp.h by parts.  */
-#include "omp-lock.h"
-#define _LIBGOMP_OMP_LOCK_DEFINED 1
-#include "omp.h.in"
-
 #if !defined (HAVE_ATTRIBUTE_VISIBILITY) \
     || !defined (HAVE_ATTRIBUTE_ALIAS) \
     || !defined (HAVE_AS_SYMVER_DIRECTIVE) \
diff --git a/libgomp/libgomp.map b/libgomp/libgomp.map
index 4530b3adc94..eae2f53bab1 100644
--- a/libgomp/libgomp.map
+++ b/libgomp/libgomp.map
@@ -430,6 +430,7 @@ GOMP_5.1.2 {
 
 GOMP_5.1.3 {
   global:
+       GOMP_interop;
        omp_get_num_interop_properties;
        omp_get_interop_int;
        omp_get_interop_ptr;
diff --git a/libgomp/libgomp_g.h b/libgomp/libgomp_g.h
index eed800b5cc9..af963b9b670 100644
--- a/libgomp/libgomp_g.h
+++ b/libgomp/libgomp_g.h
@@ -358,6 +358,8 @@ extern void GOMP_target_enter_exit_data (int, size_t, void 
**, size_t *,
 extern void GOMP_teams (unsigned int, unsigned int);
 extern bool GOMP_teams4 (unsigned int, unsigned int, unsigned int, bool);
 extern void *GOMP_target_map_indirect_ptr (void *);
+extern void GOMP_interop (int, int, void *, const void *, const void *,
+                         int, void *, int, void *, unsigned, void **);
 
 /* teams.c */
 
diff --git a/libgomp/target.c b/libgomp/target.c
index dbc4535b96f..827999302f2 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -146,7 +146,8 @@ resolve_device (int device_id, bool remapped)
      called, which must be done before using default_device_var.  */
   int num_devices = gomp_get_num_devices ();
 
-  if (remapped && device_id == GOMP_DEVICE_ICV)
+  if ((remapped && device_id == GOMP_DEVICE_ICV)
+      || device_id == GOMP_DEVICE_DEFAULT_OMP_61)
     {
       struct gomp_task_icv *icv = gomp_icv (false);
       device_id = icv->default_device_var;
@@ -5136,45 +5137,78 @@ omp_get_num_interop_properties (const omp_interop_t 
interop
 }
 
 omp_intptr_t
-omp_get_interop_int (const omp_interop_t interop __attribute__ ((unused)),
+omp_get_interop_int (const omp_interop_t interop,
                     omp_interop_property_t property_id,
                     omp_interop_rc_t *ret_code)
 {
-  if (ret_code == NULL)
-    return 0;
+  struct interop_obj_t *obj = (struct interop_obj_t *) interop;
+  struct gomp_device_descr *devicep;
+
   if (property_id < omp_ipr_first || property_id >= 0)
-    *ret_code = omp_irc_out_of_range;
-  else
-    *ret_code = omp_irc_empty;  /* Assume omp_interop_none.  */
-  return 0;
+    {
+      if (ret_code)
+       *ret_code = omp_irc_out_of_range;
+      return 0;
+    }
+  if (obj == NULL
+      || (devicep = resolve_device (obj->device_num, false)) == NULL
+      || devicep->get_interop_int_func == NULL)
+    {
+      if (ret_code)
+       *ret_code = omp_irc_empty;  /* Assume omp_interop_none.  */
+      return 0;
+    }
+  return devicep->get_interop_int_func (obj, property_id, ret_code);
 }
 
 void *
-omp_get_interop_ptr (const omp_interop_t interop __attribute__ ((unused)),
+omp_get_interop_ptr (const omp_interop_t interop,
                     omp_interop_property_t property_id,
                     omp_interop_rc_t *ret_code)
 {
-  if (ret_code == NULL)
-    return NULL;
+  struct interop_obj_t *obj = (struct interop_obj_t *) interop;
+  struct gomp_device_descr *devicep;
+
   if (property_id < omp_ipr_first || property_id >= 0)
-    *ret_code = omp_irc_out_of_range;
-  else
-    *ret_code = omp_irc_empty;  /* Assume omp_interop_none.  */
-  return NULL;
+    {
+      if (ret_code)
+       *ret_code = omp_irc_out_of_range;
+      return 0;
+    }
+  if (obj == NULL
+      || (devicep = resolve_device (obj->device_num, false)) == NULL
+      || devicep->get_interop_int_func == NULL)
+    {
+      if (ret_code)
+       *ret_code = omp_irc_empty;  /* Assume omp_interop_none.  */
+      return 0;
+    }
+  return devicep->get_interop_ptr_func (obj, property_id, ret_code);
 }
 
 const char *
-omp_get_interop_str (const omp_interop_t interop __attribute__ ((unused)),
+omp_get_interop_str (const omp_interop_t interop,
                     omp_interop_property_t property_id,
                     omp_interop_rc_t *ret_code)
 {
-  if (ret_code == NULL)
-    return NULL;
+  struct interop_obj_t *obj = (struct interop_obj_t *) interop;
+  struct gomp_device_descr *devicep;
+
   if (property_id < omp_ipr_first || property_id >= 0)
-    *ret_code = omp_irc_out_of_range;
-  else
-    *ret_code = omp_irc_empty;  /* Assume omp_interop_none.  */
-  return NULL;
+    {
+      if (ret_code)
+       *ret_code = omp_irc_out_of_range;
+      return 0;
+    }
+  if (obj == NULL
+      || (devicep = resolve_device (obj->device_num, false)) == NULL
+      || devicep->get_interop_int_func == NULL)
+    {
+      if (ret_code)
+       *ret_code = omp_irc_empty;  /* Assume omp_interop_none.  */
+      return 0;
+    }
+  return devicep->get_interop_str_func (obj, property_id, ret_code);
 }
 
 const char *
@@ -5194,18 +5228,24 @@ omp_get_interop_type_desc (const omp_interop_t interop,
                           omp_interop_property_t property_id)
 {
   static const char *desc[omp_ipr_fr_id - omp_ipr_device_num + 1]
-    = {"omp_interop_t",        /* fr_id */
-       "const char*",  /* fr_name */
+    = {"omp_interop_t", /* fr_id */
+       "const char *", /* fr_name */
        "int",          /* vendor */
        "const char *", /* vendor_name */
        "int"};         /* device_num */
+
+  struct interop_obj_t *obj = (struct interop_obj_t *) interop;
+  struct gomp_device_descr *devicep;
+
   if (property_id > omp_ipr_fr_id || property_id < omp_ipr_first)
     return NULL;
-  if (interop == omp_interop_none)
+  if (obj == NULL
+      || (devicep = resolve_device (obj->device_num, false)) == NULL
+      || devicep->get_interop_int_func == NULL)
     return NULL;
   if (property_id >= omp_ipr_device_num)
     return desc[omp_ipr_fr_id - property_id];
-  return NULL;  /* FIXME: Call plugin.  */
+  return devicep->get_interop_type_desc_func (obj, property_id);
 }
 
 const char *
@@ -5236,6 +5276,177 @@ ialias (omp_get_interop_name)
 ialias (omp_get_interop_type_desc)
 ialias (omp_get_interop_rc_desc)
 
+struct interop_data_t
+{
+  int device_num, n_init, n_use, n_destroy;
+  union {
+    struct interop_obj_t ***items;
+    struct interop_obj_t **item;
+  } init;
+  union {
+    struct interop_obj_t **items;
+    struct interop_obj_t *item;
+  } use;
+  union {
+    struct interop_obj_t ***items;
+    struct interop_obj_t **item;
+  } destroy;
+  union {
+    const int *vals;
+    int val;
+  } target_targetsync;
+  union {
+    const char **vals;
+    const char *val;
+  } prefer_type;
+};
+
+static void
+gomp_interop_internal (void *data)
+{
+  struct interop_data_t *args = (struct interop_data_t *) data;
+  struct gomp_device_descr *devicep;
+
+  /* Destroy objects to free resources.  */
+  for (int i = 0; i < args->n_destroy; i++)
+    {
+      struct interop_obj_t **obj;
+      if (args->n_destroy == 1)
+       obj = args->destroy.item;
+      else
+       obj = args->destroy.items[i];
+      if (*obj == NULL /* omp_interop_none */)
+       continue;
+      devicep = resolve_device ((*obj)->device_num, false);
+      if (devicep != NULL && devicep->interop_func)
+       devicep->interop_func (*obj, devicep->target_id,
+                              gomp_interop_flag_destroy, false, NULL);
+      free (*obj);
+      *obj = NULL;
+    }
+
+  /* Init streams next to give 'use' more time for completion.  */
+  if (args->n_init)
+    {
+      devicep = resolve_device (args->device_num, false);
+      for (int i = 0; i < args->n_init; i++)
+       {
+         struct interop_obj_t **obj;
+         bool targetsync;
+         const char *prefer_type;
+         if (args->n_init == 1)
+           {
+             obj = args->init.item;
+             targetsync = (args->target_targetsync.val
+                           & GOMP_INTEROP_FLAG_TARGETSYNC);
+             prefer_type = args->prefer_type.val;
+           }
+         else
+           {
+             obj = args->init.items[i];
+             targetsync = (args->target_targetsync.vals[i]
+                           & GOMP_INTEROP_FLAG_TARGETSYNC);
+             prefer_type = (args->prefer_type.vals
+                            ? args->prefer_type.vals[i] : NULL);
+           }
+         if (devicep == NULL || !devicep->interop_func)
+           {
+             *obj = NULL;
+             continue;
+           }
+         *obj =
+           (struct interop_obj_t *) calloc (1, sizeof (struct interop_obj_t));
+         devicep->interop_func (*obj, devicep->target_id,
+                                gomp_interop_flag_init, targetsync,
+                                prefer_type);
+       }
+    }
+
+  for (int i = 0; i < args->n_use; i++)
+    {
+      struct interop_obj_t *obj;
+      if (args->n_use == 1)
+       obj = args->use.item;
+      else
+       obj = args->use.items[i];
+      if (obj == NULL)
+       continue;
+      devicep = resolve_device (obj->device_num, false);
+      if (devicep != NULL && devicep->interop_func)
+       devicep->interop_func (obj, devicep->target_id,
+                              gomp_interop_flag_use, false, NULL);
+    }
+}
+
+void
+GOMP_interop (int device_num, int n_init, void *init,
+             const void *target_targetsync, const void *prefer_type,
+             int n_use, void *use, int n_destroy, void *destroy,
+             unsigned int nowait, void **depend)
+{
+  struct interop_data_t args;
+  args.device_num = device_num;
+  args.n_init = n_init;
+  args.n_use = n_use;
+  args.n_destroy = n_destroy;
+  if (n_init == 1)
+    {
+      args.init.item = (struct interop_obj_t **) init;
+      args.target_targetsync.val = (uintptr_t) target_targetsync;
+      args.prefer_type.val = (const char *) prefer_type;
+    }
+  else
+    {
+      args.init.items = (struct interop_obj_t ***) init;
+      args.target_targetsync.vals = (const int *) target_targetsync;
+      args.prefer_type.vals = (const char **) prefer_type;
+    }
+  if (n_use == 1)
+    args.use.item = (struct interop_obj_t *) use;
+  else
+    args.use.items = (struct interop_obj_t **) use;
+  if (n_destroy == 1)
+    args.destroy.item = (struct interop_obj_t **) destroy;
+  else
+    args.destroy.items = (struct interop_obj_t ***) destroy;
+
+  /* No need to create a task for 'init' as that should be fast. */
+  bool use_task = false;
+  if (nowait)
+    {
+      if (n_use == 1)
+       {
+         if (args.use.item)
+           use_task |= args.use.item->stream != NULL;
+       }
+      else
+       for (int i = 0; i < n_use && !use_task; i++)
+         if (args.use.items[i])
+           use_task |= args.use.items[i]->stream != NULL;
+
+      if (n_destroy == 1)
+       {
+         if (*args.destroy.item)
+           use_task |= (*args.destroy.item)->stream != NULL;
+       }
+      else
+       for (int i = 0; i < n_destroy && !use_task; i++)
+         if (*args.destroy.items[i])
+           use_task |= (*args.destroy.items[i])->stream != NULL;
+    }
+
+  if (use_task)
+    GOMP_task (gomp_interop_internal, &args, NULL, sizeof (args),
+              __alignof__ (args), true, depend ? GOMP_TASK_FLAG_DEPEND : 0,
+              depend, 0, NULL);
+  else
+    {
+      gomp_interop_internal (&args);
+      if (depend)
+       GOMP_taskwait_depend (depend);
+    }
+}
+
 static const char *
 gomp_get_uid_for_device (struct gomp_device_descr *devicep, int device_num)
 {
@@ -5344,6 +5555,14 @@ gomp_load_plugin_for_device (struct gomp_device_descr 
*device,
   DLSYM (host2dev);
   DLSYM_OPT (memcpy2d, memcpy2d);
   DLSYM_OPT (memcpy3d, memcpy3d);
+  if (DLSYM_OPT (interop, interop))
+    {
+      DLSYM (get_interop_int);
+      DLSYM (get_interop_ptr);
+      DLSYM (get_interop_str);
+      DLSYM (get_interop_type_desc);
+    }
+
   device->capabilities = device->get_caps_func ();
   if (device->capabilities & GOMP_OFFLOAD_CAP_OPENMP_400)
     {
diff --git a/libgomp/testsuite/libgomp.c-c++-common/interop-1.c 
b/libgomp/testsuite/libgomp.c-c++-common/interop-1.c
new file mode 100644
index 00000000000..149f3877b30
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/interop-1.c
@@ -0,0 +1,43 @@
+/* { dg-do run } */
+
+#include <omp.h>
+#include <stdlib.h>
+
+int
+main ()
+{
+  int dev = omp_get_num_devices ();
+  int x[6];
+  omp_interop_t obj1 = omp_interop_none;
+#pragma omp interop init(targetsync : obj1) depend(in : x) device(dev)
+  if (obj1 != omp_interop_none)
+    abort ();
+
+#pragma omp interop use(obj1)
+#pragma omp interop destroy(obj1) depend(out : x)
+  if (obj1 != omp_interop_none)
+    abort ();
+
+  omp_set_default_device (dev);
+  omp_interop_t obj2;
+
+#pragma omp interop init(                                                      
\
+    target, targetsync,                                                        
\
+      prefer_type({fr("hip"), attr("ompx_gnu_prio:1", "ompx_gnu_debug")},      
\
+                   {attr("ompx_gnu_nicest"), attr("ompx_something")}) : obj1, \
+      obj2) nowait
+  if (obj1 != omp_interop_none || obj2 != omp_interop_none)
+    abort ();
+#pragma omp interop use(obj1, obj2) nowait
+
+  omp_interop_t obj3 = __omp_interop_t_max__;
+
+#pragma omp interop init(target : obj3) use(obj2) destroy(obj1) nowait
+  if (obj1 != omp_interop_none || obj3 != omp_interop_none)
+    abort ();
+#pragma omp interop destroy(obj3, obj2) nowait
+  if (obj2 != omp_interop_none || obj3 != omp_interop_none)
+    abort ();
+
+  return 0;
+}
-- 
2.49.0


Reply via email to