https://gcc.gnu.org/g:2468c79741f97c2aa0cbb4baaccb86fd4a150910

commit r17-2404-g2468c79741f97c2aa0cbb4baaccb86fd4a150910
Author: Tobias Burnus <[email protected]>
Date:   Wed Jul 15 08:43:26 2026 +0200

    OpenMP: Handle 'device_type(host)' on 'target'
    
    When 'device_type(host)' is specified on a target region, no offload code
    is generated for the outlined region. Additionally, it is honored for
    implicit 'declare target'.
    
    The code is executed as if 'device(omp_initial_device)' were present
    (i.e. also no issus with OMP_TARGET_OFFLOAD=mandatory).
    
    gcc/ChangeLog:
    
            * omp-expand.cc (expand_omp_target): For 'device_type(host)',
            invoke with device(omp_initial_device)'.
            * omp-low.cc (create_omp_child_function): Take host_only
            argument to prevent generating offload code.
            (scan_omp_parallel, scan_omp_task, scan_omp_target, scan_omp_teams,
            lower_omp_target): Update call.
            * omp-offload.cc (omp_discover_declare_target_tgt_fn_r,
            omp_discover_declare_target_fn_r): Do not add implicit 'declare
            target' to target regions with 'device_type(host)'.
    
    libgomp/ChangeLog:
    
            * libgomp.texi (Implementations status): Mark 'device_type'
            clause on target as implemented.
            * testsuite/libgomp.c/target-device-type-3.c: New test.
    
    gcc/testsuite/ChangeLog:
    
            * c-c++-common/gomp/target-device-type-1.c: Remove dg-sorry.
            * gfortran.dg/gomp/target-device-type-1.f90: Likewise.

Diff:
---
 gcc/omp-expand.cc                                  | 12 +++-
 gcc/omp-low.cc                                     | 26 ++++-----
 gcc/omp-offload.cc                                 | 10 +++-
 .../c-c++-common/gomp/target-device-type-1.c       |  2 +-
 .../gfortran.dg/gomp/target-device-type-1.f90      |  2 +-
 libgomp/libgomp.texi                               |  2 +-
 libgomp/testsuite/libgomp.c/target-device-type-3.c | 65 ++++++++++++++++++++++
 7 files changed, 96 insertions(+), 23 deletions(-)

diff --git a/gcc/omp-expand.cc b/gcc/omp-expand.cc
index 388f84eddff0..ca4d5d0db9e5 100644
--- a/gcc/omp-expand.cc
+++ b/gcc/omp-expand.cc
@@ -10014,6 +10014,7 @@ expand_omp_target (struct omp_region *region)
   tree clauses = gimple_omp_target_clauses (entry_stmt);
 
   bool is_ancestor = false;
+  bool is_host_only = false;
   child_fn = child_fn2 = NULL_TREE;
   child_cfun = NULL;
   if (offloaded)
@@ -10021,6 +10022,9 @@ expand_omp_target (struct omp_region *region)
       c = omp_find_clause (clauses, OMP_CLAUSE_DEVICE);
       if (ENABLE_OFFLOADING && c)
        is_ancestor = OMP_CLAUSE_DEVICE_ANCESTOR (c);
+      c = omp_find_clause (clauses, OMP_CLAUSE_DEVICE_TYPE);
+      if (c && OMP_CLAUSE_DEVICE_TYPE_KIND (c) == OMP_CLAUSE_DEVICE_TYPE_HOST)
+       is_host_only = true;
       child_fn = gimple_omp_target_child_fn (entry_stmt);
       child_cfun = DECL_STRUCT_FUNCTION (child_fn);
     }
@@ -10211,7 +10215,7 @@ expand_omp_target (struct omp_region *region)
        {
          if (in_lto_p)
            DECL_PRESERVE_P (child_fn) = 1;
-         if (!is_ancestor)
+         if (!is_ancestor && !is_host_only)
            vec_safe_push (offload_funcs, child_fn);
        }
 
@@ -10393,8 +10397,10 @@ expand_omp_target (struct omp_region *region)
     }
   else
     {
-      c = omp_find_clause (clauses, OMP_CLAUSE_DEVICE);
-      if (c)
+      if (is_host_only)
+       device = build_int_cst (integer_type_node,
+                               GOMP_DEVICE_HOST_FALLBACK - 1);
+      else if ((c = omp_find_clause (clauses, OMP_CLAUSE_DEVICE)) != NULL_TREE)
        {
          device = OMP_CLAUSE_DEVICE_ID (c);
          /* Ensure 'device' is of the correct type.  */
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index 99f73e501c02..ec421657c68b 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -2131,10 +2131,10 @@ omp_maybe_offloaded_ctx (omp_context *ctx)
 }
 
 /* Build a decl for the omp child function.  It'll not contain a body
-   yet, just the bare decl.  */
+   yet, just the bare decl. If HOST_ONLY, do not create a device version.  */
 
 static void
-create_omp_child_function (omp_context *ctx, bool task_copy)
+create_omp_child_function (omp_context *ctx, bool task_copy, bool host_only)
 {
   tree decl, type, name, t;
 
@@ -2189,7 +2189,7 @@ create_omp_child_function (omp_context *ctx, bool 
task_copy)
   DECL_FUNCTION_VERSIONED (decl)
     = DECL_FUNCTION_VERSIONED (current_function_decl);
 
-  if (omp_maybe_offloaded_ctx (ctx))
+  if (omp_maybe_offloaded_ctx (ctx) && !host_only)
     {
       cgraph_node::get_create (decl)->offloadable = 1;
       if (ENABLE_OFFLOADING)
@@ -2435,7 +2435,7 @@ scan_omp_parallel (gimple_stmt_iterator *gsi, omp_context 
*outer_ctx)
   DECL_NAMELESS (name) = 1;
   TYPE_NAME (ctx->record_type) = name;
   TYPE_ARTIFICIAL (ctx->record_type) = 1;
-  create_omp_child_function (ctx, false);
+  create_omp_child_function (ctx, false, false);
   gimple_omp_parallel_set_child_fn (stmt, ctx->cb.dst_fn);
 
   scan_sharing_clauses (gimple_omp_parallel_clauses (stmt), ctx);
@@ -2488,7 +2488,7 @@ scan_omp_task (gimple_stmt_iterator *gsi, omp_context 
*outer_ctx)
   DECL_NAMELESS (name) = 1;
   TYPE_NAME (ctx->record_type) = name;
   TYPE_ARTIFICIAL (ctx->record_type) = 1;
-  create_omp_child_function (ctx, false);
+  create_omp_child_function (ctx, false, false);
   gimple_omp_task_set_child_fn (stmt, ctx->cb.dst_fn);
 
   scan_sharing_clauses (gimple_omp_task_clauses (stmt), ctx);
@@ -2502,7 +2502,7 @@ scan_omp_task (gimple_stmt_iterator *gsi, omp_context 
*outer_ctx)
       DECL_NAMELESS (name) = 1;
       TYPE_NAME (ctx->srecord_type) = name;
       TYPE_ARTIFICIAL (ctx->srecord_type) = 1;
-      create_omp_child_function (ctx, true);
+      create_omp_child_function (ctx, true, false);
     }
 
   scan_omp (gimple_omp_body_ptr (stmt), ctx);
@@ -3209,7 +3209,10 @@ scan_omp_target (gomp_target *stmt, omp_context 
*outer_ctx)
 
   if (offloaded)
     {
-      create_omp_child_function (ctx, false);
+      tree c = omp_find_clause (clauses, OMP_CLAUSE_DEVICE_TYPE);
+      bool host_only
+       = c && OMP_CLAUSE_DEVICE_TYPE_KIND (c) == OMP_CLAUSE_DEVICE_TYPE_HOST;
+      create_omp_child_function (ctx, false, host_only);
       gimple_omp_target_set_child_fn (stmt, ctx->cb.dst_fn);
     }
 
@@ -3268,7 +3271,7 @@ scan_omp_teams (gomp_teams *stmt, omp_context *outer_ctx)
   DECL_NAMELESS (name) = 1;
   TYPE_NAME (ctx->record_type) = name;
   TYPE_ARTIFICIAL (ctx->record_type) = 1;
-  create_omp_child_function (ctx, false);
+  create_omp_child_function (ctx, false, false);
   gimple_omp_teams_set_child_fn (stmt, ctx->cb.dst_fn);
 
   scan_sharing_clauses (gimple_omp_teams_clauses (stmt), ctx);
@@ -13284,13 +13287,6 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, 
omp_context *ctx)
            DECL_HAS_VALUE_EXPR_P (new_var) = 1;
          }
        break;
-       case OMP_CLAUSE_DEVICE_TYPE:
-         /* FIXME: Ensure that 'nohost' also has not implied before that
-            'g->have_offload = true' or an implicit declare target.  */
-         if (OMP_CLAUSE_DEVICE_TYPE_KIND (c) == OMP_CLAUSE_DEVICE_TYPE_HOST)
-           sorry_at (OMP_CLAUSE_LOCATION (c),
-                     "the %<device_type(host)%> is not supported");
-         break;
       case OMP_CLAUSE_USES_ALLOCATORS:
        allocator = OMP_CLAUSE_USES_ALLOCATORS_ALLOCATOR (c);
        tree new_allocator = lookup_decl (allocator, ctx);
diff --git a/gcc/omp-offload.cc b/gcc/omp-offload.cc
index 7cd2a572b7c0..bbe13154d8f0 100644
--- a/gcc/omp-offload.cc
+++ b/gcc/omp-offload.cc
@@ -272,7 +272,10 @@ omp_discover_declare_target_tgt_fn_r (tree *tp, int 
*walk_subtrees, void *data)
   else if (TREE_CODE (*tp) == OMP_TARGET)
     {
       tree c = omp_find_clause (OMP_CLAUSES (*tp), OMP_CLAUSE_DEVICE);
-      if (c && OMP_CLAUSE_DEVICE_ANCESTOR (c))
+      tree c2 = omp_find_clause (OMP_CLAUSES (*tp), OMP_CLAUSE_DEVICE_TYPE);
+      if ((!c || !OMP_CLAUSE_DEVICE_ANCESTOR (c))
+         && (!c2 || (OMP_CLAUSE_DEVICE_TYPE_KIND (c2)
+                     != OMP_CLAUSE_DEVICE_TYPE_HOST)))
        *walk_subtrees = 0;
     }
   return NULL_TREE;
@@ -286,7 +289,10 @@ omp_discover_declare_target_fn_r (tree *tp, int 
*walk_subtrees, void *data)
   if (TREE_CODE (*tp) == OMP_TARGET)
     {
       tree c = omp_find_clause (OMP_CLAUSES (*tp), OMP_CLAUSE_DEVICE);
-      if (!c || !OMP_CLAUSE_DEVICE_ANCESTOR (c))
+      tree c2 = omp_find_clause (OMP_CLAUSES (*tp), OMP_CLAUSE_DEVICE_TYPE);
+      if ((!c || !OMP_CLAUSE_DEVICE_ANCESTOR (c))
+         && (!c2 || (OMP_CLAUSE_DEVICE_TYPE_KIND (c2)
+                     != OMP_CLAUSE_DEVICE_TYPE_HOST)))
        walk_tree_without_duplicates (&OMP_TARGET_BODY (*tp),
                                      omp_discover_declare_target_tgt_fn_r,
                                      data);
diff --git a/gcc/testsuite/c-c++-common/gomp/target-device-type-1.c 
b/gcc/testsuite/c-c++-common/gomp/target-device-type-1.c
index 758330436cd1..15817717daa9 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-device-type-1.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-device-type-1.c
@@ -13,7 +13,7 @@ void f ()
 #pragma omp target device_type ( nohost )
   ;
 
-#pragma omp target device_type ( host )  // { dg-message "sorry, 
unimplemented: the 'device_type\\(host\\)' is not supported" }
+#pragma omp target device_type ( host )
   ;
 
 }
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-device-type-1.f90 
b/gcc/testsuite/gfortran.dg/gomp/target-device-type-1.f90
index 164fc4f1fd60..fa1265f5ed21 100644
--- a/gcc/testsuite/gfortran.dg/gomp/target-device-type-1.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/target-device-type-1.f90
@@ -10,7 +10,7 @@
 !$omp target device_type ( nohost )
 !$omp end target
 
-!$omp target device_type ( host )  ! { dg-message "sorry, unimplemented: the 
'device_type\\(host\\)' is not supported" }
+!$omp target device_type ( host )
 !$omp end target
 
 end
diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi
index 63c5cf475b7b..d090e2e86d63 100644
--- a/libgomp/libgomp.texi
+++ b/libgomp/libgomp.texi
@@ -580,7 +580,7 @@ to address of matching mapped list item per 5.1, Sect. 
2.21.7.2 @tab N @tab
       @code{target_data}, @code{target_exit_data} and @code{target_update}
       @tab N @tab
 @item New @code{device_type} clause to the @code{target} directive
-      @tab P @tab @code{device_type(host)} unsupported
+      @tab Y @tab
 @item @code{target_data} as composite construct @tab N @tab
 @item @code{nowait} clause with reverse-offload @code{target} directives
       @tab N @tab
diff --git a/libgomp/testsuite/libgomp.c/target-device-type-3.c 
b/libgomp/testsuite/libgomp.c/target-device-type-3.c
new file mode 100644
index 000000000000..faedcabdf227
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/target-device-type-3.c
@@ -0,0 +1,65 @@
+/* { dg-do run } */
+/* { dg-additional-options -fdump-tree-ompexp } */
+/* { dg-additional-options -foffload-options=-fdump-tree-optimized-raw-asmname 
} */
+
+/* This code is supposed to run on the host independent of the current
+   default-device-var ICV - and it shall not create any device code for
+   neither the target region nor the called functions.  */
+
+// NOTE: With offloading,
+//   UNRESOLVED:
+// is expected for the "optimized" tests as those dumps are only enabled
+// for the no-host side.
+
+
+// Due to 'device_type(nohost)', the outlined region shall not be marked
+// as 'target entrypoint' - nor should 'ggg' become implicitly
+// 'declare target' -- check for the absence of those __attributes__:
+
+/* { dg-final { scan-tree-dump-times "omp declare target" 1 "ompexp" } } */
+/* { dg-final { scan-tree-dump-not "omp target entrypoint" "ompexp" } } */
+
+
+// Expect that GOMP_target explicitly invokes the host device, i.e.
+// -3 == (shifted) omp_initial_device (aka host fallback)
+
+/* { dg-final { scan-tree-dump "__builtin_GOMP_target_ext \\(-3," "ompexp" } } 
*/
+
+
+// On the device side, expect only hhh:
+
+/* { dg-final { scan-tree-dump "hhh" "optimized" { target offload_device } } } 
*/
+/* { dg-final { scan-tree-dump-not "ggg" "optimized" { target offload_device } 
} } */
+/* { dg-final { scan-tree-dump-not "_omp_fn" "optimized" { target 
offload_device } } } */
+
+
+#include <omp.h>
+
+// Use 'declare target' such that -fdump-tree-optimized creates a file.
+// Otherwise, there is dump file (only a stub assembly file)
+// That's the only purpose of this function.
+int hhh() { return 5; }
+#pragma omp declare target enter(hhh)
+
+
+int ggg() {
+  return 42;
+}
+
+int f(int *x) {
+  bool initial_dev = false;
+  #pragma omp target map(tofrom: initial_dev) device_type(host) 
+    {
+      initial_dev = omp_is_initial_device ();
+      (*x) = (*x) + 1 + ggg();
+    }
+  if (!initial_dev)
+    __builtin_abort ();
+  return *x;
+}
+
+int main() {
+  int i = 5;
+  if (f(&i) != 5 + 1 + 42)
+    __builtin_abort();
+}

Reply via email to