2025-04-25 Thomas Schwinge <tschwinge@baylibre.com>
+ Backported from trunk:
+ 2024-08-07 Julian Brown <julian@codesourcery.com>
+ Tobias Burnus <tobias@baylibre.com>
+
+ * builtins.def (DEF_GOMP_BUILTIN_COMPILER): Define
+ DEF_GOMP_BUILTIN_COMPILER to handle the non-prefix version.
+ * gimple-fold.cc (gimple_fold_builtin_omp_is_initial_device): New.
+ (gimple_fold_builtin): Call it.
+ * omp-builtins.def (BUILT_IN_OMP_IS_INITIAL_DEVICE): Define.
+ * tree.cc (get_file_function_name): Support names for on-target
+ constructor/destructor functions.
+
Revert:
2023-05-12 Julian Brown <julian@codesourcery.com>
(flag_openacc \
|| flag_openmp \
|| flag_tree_parallelize_loops > 1))
+#undef DEF_GOMP_BUILTIN_COMPILER
+#define DEF_GOMP_BUILTIN_COMPILER(ENUM, NAME, TYPE, ATTRS) \
+ DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \
+ flag_openmp, true, true, ATTRS, false, flag_openmp)
/* Builtin used by the implementation of GNU TM. These
functions are mapped to the actual implementation of the STM library. */
2025-04-25 Thomas Schwinge <tschwinge@baylibre.com>
+ Backported from trunk:
+ 2024-08-07 Julian Brown <julian@codesourcery.com>
+ Tobias Burnus <tobias@baylibre.com>
+
+ * decl2.cc (tree-inline.h): Include.
+ (static_init_fini_fns): Bump to four entries. Update comment.
+ (start_objects, start_partial_init_fini_fn): Add 'omp_target'
+ parameter. Support "declare target" decls. Update forward declaration.
+ (emit_partial_init_fini_fn): Add 'host_fn' parameter. Return tree for
+ the created function. Support "declare target".
+ (OMP_SSDF_IDENTIFIER): New macro.
+ (partition_vars_for_init_fini): Support partitioning "declare target"
+ variables also.
+ (generate_ctor_or_dtor_function): Add 'omp_target' parameter. Support
+ "declare target" decls.
+ (c_parse_final_cleanups): Support constructors/destructors on OpenMP
+ offload targets.
+
Revert:
2023-05-12 Julian Brown <julian@codesourcery.com>
#include "asan.h"
#include "optabs-query.h"
#include "omp-general.h"
+#include "tree-inline.h"
/* Id for dumping the raw trees. */
int raw_dump_id;
extern cpp_reader *parse_in;
-static tree start_objects (bool, unsigned, bool);
+static tree start_objects (bool, unsigned, bool, bool);
static tree finish_objects (bool, unsigned, tree, bool = true);
-static tree start_partial_init_fini_fn (bool, unsigned, unsigned);
+static tree start_partial_init_fini_fn (bool, unsigned, unsigned, bool);
static void finish_partial_init_fini_fn (tree);
-static void emit_partial_init_fini_fn (bool, unsigned, tree,
- unsigned, location_t);
+static tree emit_partial_init_fini_fn (bool, unsigned, tree,
+ unsigned, location_t, tree);
static void one_static_initialization_or_destruction (bool, tree, tree);
-static void generate_ctor_or_dtor_function (bool, unsigned, tree, location_t);
+static void generate_ctor_or_dtor_function (bool, unsigned, tree, location_t,
+ bool);
static tree prune_vars_needing_no_initialization (tree *);
static void write_out_vars (tree);
static void import_export_class (tree);
typedef hash_map<unsigned/*Priority*/, tree/*List*/,
priority_map_traits> priority_map_t;
-/* A pair of such hash tables, indexed by initp -- one for fini and
- one for init. The fini table is only ever used when !cxa_atexit. */
-static GTY(()) priority_map_t *static_init_fini_fns[2];
+/* Two pairs of such hash tables, for the host and an OpenMP offload device.
+ Each pair has one priority map for fini and one for init. The fini tables
+ are only ever used when !cxa_atexit. */
+static GTY(()) priority_map_t *static_init_fini_fns[4];
/* Nonzero if we're done parsing and into end-of-file activities.
2 if all templates have been instantiated.
/* Start a global constructor or destructor function. */
static tree
-start_objects (bool initp, unsigned priority, bool has_body)
+start_objects (bool initp, unsigned priority, bool has_body,
+ bool omp_target = false)
{
bool default_init = initp && priority == DEFAULT_INIT_PRIORITY;
bool is_module_init = default_init && module_global_init_needed ();
/* We use `I' to indicate initialization and `D' to indicate
destruction. */
- unsigned len = sprintf (type, "sub_%c", initp ? 'I' : 'D');
+ unsigned len;
+ if (omp_target)
+ /* Use "off_" signifying "offload" here. The name must be distinct
+ from the non-offload case. The format of the name is scanned in
+ tree.cc/get_file_function_name, so stick to the same length for
+ both name variants. */
+ len = sprintf (type, "off_%c", initp ? 'I' : 'D');
+ else
+ len = sprintf (type, "sub_%c", initp ? 'I' : 'D');
if (priority != DEFAULT_INIT_PRIORITY)
{
char joiner = '_';
tree fntype = build_function_type (void_type_node, void_list_node);
tree fndecl = build_lang_decl (FUNCTION_DECL, name, fntype);
+
+ if (omp_target)
+ {
+ DECL_ATTRIBUTES (fndecl)
+ = tree_cons (get_identifier ("omp declare target"), NULL_TREE,
+ DECL_ATTRIBUTES (fndecl));
+ DECL_ATTRIBUTES (fndecl)
+ = tree_cons (get_identifier ("omp declare target nohost"), NULL_TREE,
+ DECL_ATTRIBUTES (fndecl));
+ }
+
DECL_CONTEXT (fndecl) = FROB_CONTEXT (global_namespace);
if (is_module_init)
{
/* The name of the function we create to handle initializations and
destructions for objects with static storage duration. */
#define SSDF_IDENTIFIER "__static_initialization_and_destruction"
+#define OMP_SSDF_IDENTIFIER "__omp_target_static_init_and_destruction"
/* Begins the generation of the function that will handle all
initialization or destruction of objects with static storage
duration at PRIORITY.
- It is assumed that this function will only be called once. */
+ It is assumed that this function will be called once for the host, and once
+ for an OpenMP offload target. */
static tree
-start_partial_init_fini_fn (bool initp, unsigned priority, unsigned count)
+start_partial_init_fini_fn (bool initp, unsigned priority, unsigned count,
+ bool omp_target)
{
- char id[sizeof (SSDF_IDENTIFIER) + 1 /* '\0' */ + 32];
+ char id[MAX (sizeof (SSDF_IDENTIFIER), sizeof (OMP_SSDF_IDENTIFIER))
+ + 1 /* \0 */ + 32];
+ tree name;
/* Create the identifier for this function. It will be of the form
- SSDF_IDENTIFIER_<number>. */
- sprintf (id, "%s_%u", SSDF_IDENTIFIER, count);
-
+ SSDF_IDENTIFIER_<number> if not omp_target and otherwise
+ OMP_SSDF_IDENTIFIER_<number>. */
+ sprintf (id, "%s_%u", omp_target ? OMP_SSDF_IDENTIFIER : SSDF_IDENTIFIER,
+ count);
+ name = get_identifier (id);
tree type = build_function_type (void_type_node, void_list_node);
/* Create the FUNCTION_DECL itself. */
- tree fn = build_lang_decl (FUNCTION_DECL, get_identifier (id), type);
+ tree fn = build_lang_decl (FUNCTION_DECL, name, type);
TREE_PUBLIC (fn) = 0;
DECL_ARTIFICIAL (fn) = 1;
+ if (omp_target)
+ {
+ DECL_ATTRIBUTES (fn)
+ = tree_cons (get_identifier ("omp declare target"), NULL_TREE,
+ DECL_ATTRIBUTES (fn));
+ DECL_ATTRIBUTES (fn)
+ = tree_cons (get_identifier ("omp declare target nohost"), NULL_TREE,
+ DECL_ATTRIBUTES (fn));
+ }
+
+ int idx = initp + 2 * omp_target;
+
/* Put this function in the list of functions to be called from the
static constructors and destructors. */
- if (!static_init_fini_fns[initp])
- static_init_fini_fns[initp] = priority_map_t::create_ggc ();
- auto &slot = static_init_fini_fns[initp]->get_or_insert (priority);
+ if (!static_init_fini_fns[idx])
+ static_init_fini_fns[idx] = priority_map_t::create_ggc ();
+ auto &slot = static_init_fini_fns[idx]->get_or_insert (priority);
slot = tree_cons (fn, NULL_TREE, slot);
/* Put the function in the global scope. */
a TREE_LIST of VAR_DECL with static storage duration.
Whether initialization or destruction is performed is specified by INITP. */
-static void
+static tree
emit_partial_init_fini_fn (bool initp, unsigned priority, tree vars,
- unsigned counter, location_t locus)
+ unsigned counter, location_t locus, tree host_fn)
{
input_location = locus;
- tree body = start_partial_init_fini_fn (initp, priority, counter);
+ bool omp_target = (host_fn != NULL_TREE);
+ tree body = start_partial_init_fini_fn (initp, priority, counter, omp_target);
+ tree fndecl = current_function_decl;
+
+ tree nonhost_if_stmt = NULL_TREE;
+ if (omp_target)
+ {
+ nonhost_if_stmt = begin_if_stmt ();
+ /* We add an "omp declare target nohost" attribute, but (for
+ now) we still get a copy of the constructor/destructor on
+ the host. Make sure it does nothing unless we're on the
+ target device. */
+ tree fn = builtin_decl_explicit (BUILT_IN_OMP_IS_INITIAL_DEVICE);
+ tree initial_dev = build_call_expr (fn, 0);
+ tree target_dev_p
+ = cp_build_binary_op (input_location, NE_EXPR, initial_dev,
+ integer_one_node, tf_warning_or_error);
+ finish_if_stmt_cond (target_dev_p, nonhost_if_stmt);
+ }
for (tree node = vars; node; node = TREE_CHAIN (node))
- /* Do one initialization or destruction. */
- one_static_initialization_or_destruction (initp, TREE_VALUE (node),
- TREE_PURPOSE (node));
+ {
+ tree decl = TREE_VALUE (node);
+ tree init = TREE_PURPOSE (node);
+ /* We will emit 'init' twice, and it is modified in-place during
+ gimplification. Make a copy here. */
+ if (omp_target)
+ {
+ /* We've already emitted INIT in the host version of the ctor/dtor
+ function. We need to deep-copy it (including new versions of
+ local variables introduced, etc.) for use in the target
+ ctor/dtor function. */
+ copy_body_data id;
+ hash_map<tree, tree> decl_map;
+ memset (&id, 0, sizeof (id));
+ id.src_fn = host_fn;
+ id.dst_fn = current_function_decl;
+ id.src_cfun = DECL_STRUCT_FUNCTION (id.src_fn);
+ id.decl_map = &decl_map;
+ id.copy_decl = copy_decl_no_change;
+ id.transform_call_graph_edges = CB_CGE_DUPLICATE;
+ id.transform_new_cfg = true;
+ id.transform_return_to_modify = false;
+ id.eh_lp_nr = 0;
+ walk_tree (&init, copy_tree_body_r, &id, NULL);
+ }
+ /* Do one initialization or destruction. */
+ one_static_initialization_or_destruction (initp, decl, init);
+ }
+
+ if (omp_target)
+ {
+ /* Finish up nonhost if-stmt body. */
+ finish_then_clause (nonhost_if_stmt);
+ finish_if_stmt (nonhost_if_stmt);
+ }
/* Finish up the static storage duration function for this
round. */
input_location = locus;
finish_partial_init_fini_fn (body);
+
+ return fndecl;
}
/* VARS is a list of variables with static storage duration which may
This reverses the variable ordering. */
void
-partition_vars_for_init_fini (tree var_list, priority_map_t *(&parts)[2])
+partition_vars_for_init_fini (tree var_list, priority_map_t *(&parts)[4])
{
for (auto node = var_list; node; node = TREE_CHAIN (node))
{
auto &slot = parts[false]->get_or_insert (priority);
slot = tree_cons (NULL_TREE, decl, slot);
}
+
+ if (flag_openmp
+ && lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
+ {
+ priority_map_t **omp_parts = parts + 2;
+
+ if (init || (flag_use_cxa_atexit && has_cleanup))
+ {
+ // Add to initialization list.
+ if (!omp_parts[true])
+ omp_parts[true] = priority_map_t::create_ggc ();
+ auto &slot = omp_parts[true]->get_or_insert (priority);
+ slot = tree_cons (init, decl, slot);
+ }
+
+ if (!flag_use_cxa_atexit && has_cleanup)
+ {
+ // Add to finalization list.
+ if (!omp_parts[false])
+ omp_parts[false] = priority_map_t::create_ggc ();
+ auto &slot = omp_parts[false]->get_or_insert (priority);
+ slot = tree_cons (NULL_TREE, decl, slot);
+ }
+ }
}
}
static void
generate_ctor_or_dtor_function (bool initp, unsigned priority,
- tree fns, location_t locus)
+ tree fns, location_t locus, bool omp_target)
{
input_location = locus;
- tree body = start_objects (initp, priority, bool (fns));
+ tree body = start_objects (initp, priority, bool (fns), omp_target);
if (fns)
{
auto_vec<tree> consteval_vtables;
int retries = 0;
- unsigned ssdf_count = 0;
+ unsigned ssdf_count = 0, omp_ssdf_count = 0;
for (bool reconsider = true; reconsider; retries++)
{
reconsider = false;
write_out_vars (vars);
function_depth++; // Disable GC
- priority_map_t *parts[2] = {nullptr, nullptr};
+ priority_map_t *parts[4] = {nullptr, nullptr, nullptr, nullptr};
partition_vars_for_init_fini (vars, parts);
+ tree host_init_fini[2] = { NULL_TREE, NULL_TREE };
for (unsigned initp = 2; initp--;)
if (parts[initp])
// Partitioning kept the vars in reverse order.
// We only want that for dtors.
list = nreverse (list);
- emit_partial_init_fini_fn (initp, iter.first, list,
- ssdf_count++,
- locus_at_end_of_parsing);
+ host_init_fini[initp]
+ = emit_partial_init_fini_fn (initp, iter.first, list,
+ ssdf_count++,
+ locus_at_end_of_parsing,
+ NULL_TREE);
}
+
+ if (flag_openmp)
+ {
+ priority_map_t **omp_parts = parts + 2;
+ for (unsigned initp = 2; initp--;)
+ if (omp_parts[initp])
+ for (auto iter : *omp_parts[initp])
+ {
+ auto list = iter.second;
+ if (initp)
+ // Partitioning kept the vars in reverse order.
+ // We only want that for dtors.
+ list = nreverse (list);
+ emit_partial_init_fini_fn (initp, iter.first, list,
+ omp_ssdf_count++,
+ locus_at_end_of_parsing,
+ host_init_fini[initp]);
+ }
+ }
+
function_depth--; // Re-enable GC
/* All those initializations and finalizations might cause
for (auto iter : *static_init_fini_fns[true])
iter.second = nreverse (iter.second);
+ if (flag_openmp && static_init_fini_fns[2 + true])
+ for (auto iter : *static_init_fini_fns[2 + true])
+ iter.second = nreverse (iter.second);
+
/* Now we've instantiated all templates. Now we can escalate the functions
we squirreled away earlier. */
process_and_check_pending_immediate_escalating_fns ();
{
input_location = locus_at_end_of_parsing;
tree body = start_partial_init_fini_fn (true, DEFAULT_INIT_PRIORITY,
- ssdf_count++);
+ ssdf_count++, false);
/* For Objective-C++, we may need to initialize metadata found
in this module. This must be done _before_ any other static
initializations. */
static_init_fini_fns[true] = priority_map_t::create_ggc ();
if (static_init_fini_fns[true]->get_or_insert (DEFAULT_INIT_PRIORITY))
has_module_inits = true;
+
+ if (flag_openmp)
+ {
+ if (!static_init_fini_fns[2 + true])
+ static_init_fini_fns[2 + true] = priority_map_t::create_ggc ();
+ static_init_fini_fns[2 + true]->get_or_insert (DEFAULT_INIT_PRIORITY);
+ }
}
/* Generate initialization and destruction functions for all
priorities for which they are required. They have C-language
linkage. */
push_lang_context (lang_name_c);
- for (unsigned initp = 2; initp--;)
+ for (unsigned initp = 4; initp--;)
if (static_init_fini_fns[initp])
{
for (auto iter : *static_init_fini_fns[initp])
- generate_ctor_or_dtor_function (initp, iter.first, iter.second,
- locus_at_end_of_parsing);
+ generate_ctor_or_dtor_function (initp & 1, iter.first, iter.second,
+ locus_at_end_of_parsing,
+ (initp & 2) != 0);
static_init_fini_fns[initp] = nullptr;
}
pop_lang_context ();
+2025-04-25 Thomas Schwinge <tschwinge@baylibre.com>
+
+ Backported from trunk:
+ 2024-08-07 Julian Brown <julian@codesourcery.com>
+ Tobias Burnus <tobias@baylibre.com>
+
+ * gfortran.h (gfc_option_t): Add disable_omp_is_initial_device.
+ * lang.opt (fbuiltin-): Add.
+ * options.cc (gfc_handle_option): Handle
+ -fno-builtin-omp_is_initial_device.
+ * f95-lang.cc (gfc_init_builtin_functions): Handle
+ DEF_GOMP_BUILTIN_COMPILER.
+ * trans-decl.cc (gfc_get_extern_function_decl): Add code to use
+ DEF_GOMP_BUILTIN_COMPILER for 'omp_is_initial_device'.
+
2025-04-17 Kwok Cheung Yeung <kcyeung@baylibre.com>
* trans-openmp.cc (gfc_omp_deep_mapping_map): Add new argument for
attr);
#undef DEF_GOMP_BUILTIN
#define DEF_GOMP_BUILTIN(code, name, type, attr) /* ignore */
+#undef DEF_GOMP_BUILTIN_COMPILER
+#define DEF_GOMP_BUILTIN_COMPILER(code, name, type, attr) /* ignore */
#include "../omp-builtins.def"
#undef DEF_GOACC_BUILTIN
#undef DEF_GOACC_BUILTIN_COMPILER
#undef DEF_GOMP_BUILTIN
+#undef DEF_GOMP_BUILTIN_COMPILER
}
if (flag_openmp || flag_openmp_simd || flag_tree_parallelize_loops)
#define DEF_GOMP_BUILTIN(code, name, type, attr) \
gfc_define_builtin ("__builtin_" name, builtin_types[type], \
code, name, attr);
+#undef DEF_GOMP_BUILTIN_COMPILER
+#define DEF_GOMP_BUILTIN_COMPILER(code, name, type, attr) \
+ if (flag_openmp) \
+ gfc_define_builtin ("__builtin_" name, builtin_types[type], \
+ code, name, attr);
#include "../omp-builtins.def"
#undef DEF_GOACC_BUILTIN
#undef DEF_GOACC_BUILTIN_COMPILER
#undef DEF_GOMP_BUILTIN
+#undef DEF_GOMP_BUILTIN_COMPILER
tree gomp_alloc = builtin_decl_explicit (BUILT_IN_GOMP_ALLOC);
tree two = build_int_cst (integer_type_node, 2);
DECL_ATTRIBUTES (gomp_alloc)
int flag_init_logical;
int flag_init_character;
char flag_init_character_value;
+ int disable_omp_is_initial_device;
int fpe;
int fpe_summary;
fbuilding-libgfortran
Fortran Undocumented Var(flag_building_libgfortran)
+fbuiltin-
+Fortran Joined
+; Documented in C
+
fcheck-array-temporaries
Fortran
Produce a warning at runtime if a array temporary has been created for a procedure argument.
/* Set (or unset) the DEC extension flags. */
set_dec_flags (value);
break;
+
+ case OPT_fbuiltin_:
+ /* We only handle -fno-builtin-omp_is_initial_device. */
+ if (value)
+ return false; /* Not supported. */
+ if (!strcmp ("omp_is_initial_device", arg))
+ gfc_option.disable_omp_is_initial_device = true;
+ else
+ warning (0, "command-line option %<-fno-builtin-%s%> is not valid for "
+ "Fortran", arg);
+ break;
+
}
Fortran_handle_option_auto (&global_options, &global_options_set,
to know that. */
gcc_assert (!(sym->attr.entry || sym->attr.entry_master));
+ if (!gfc_option.disable_omp_is_initial_device
+ && flag_openmp && sym->attr.function && sym->ts.type == BT_LOGICAL
+ && !strcmp (sym->name, "omp_is_initial_device"))
+ {
+ sym->backend_decl
+ = builtin_decl_explicit (BUILT_IN_OMP_IS_INITIAL_DEVICE);
+ return sym->backend_decl;
+ }
+
if (sym->attr.proc_pointer)
return get_proc_pointer_decl (sym);
return false;
}
+static bool
+gimple_fold_builtin_omp_is_initial_device (gimple_stmt_iterator *gsi)
+{
+#if ACCEL_COMPILER
+ replace_call_with_value (gsi, integer_zero_node);
+ return true;
+#else
+ if (!ENABLE_OFFLOADING || symtab->state == EXPANSION)
+ {
+ replace_call_with_value (gsi, integer_one_node);
+ return true;
+ }
+#endif
+ return false;
+}
+
+
/* Fold a call to __builtin_acc_on_device. */
static bool
case BUILT_IN_ACC_ON_DEVICE:
return gimple_fold_builtin_acc_on_device (gsi,
gimple_call_arg (stmt, 0));
+ case BUILT_IN_OMP_IS_INITIAL_DEVICE:
+ return gimple_fold_builtin_omp_is_initial_device (gsi);
+
case BUILT_IN_REALLOC:
return gimple_fold_builtin_realloc (gsi);
DEF_GOACC_BUILTIN (ENUM, NAME, TYPE, ATTRS)
DEF_GOACC_BUILTIN_COMPILER (ENUM, NAME, TYPE, ATTRS)
DEF_GOMP_BUILTIN (ENUM, NAME, TYPE, ATTRS)
+ DEF_GOMP_BUILTIN_COMPILER (ENUM, NAME, TYPE, ATTRS)
See builtins.def for details. */
DEF_GOACC_BUILTIN_ONLY (BUILT_IN_GOACC_SINGLE_COPY_END, "GOACC_single_copy_end",
BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN_COMPILER (BUILT_IN_OMP_IS_INITIAL_DEVICE,
+ "omp_is_initial_device", BT_FN_INT,
+ ATTR_CONST_NOTHROW_LEAF_LIST)
DEF_GOMP_BUILTIN (BUILT_IN_OMP_GET_THREAD_NUM, "omp_get_thread_num",
BT_FN_INT, ATTR_NOTHROW_LEAF_LIST)
DEF_GOMP_BUILTIN (BUILT_IN_OMP_GET_NUM_THREADS, "omp_get_num_threads",
will be local to this file and the name is only necessary for
debugging purposes.
We also assign sub_I and sub_D sufixes to constructors called from
- the global static constructors. These are always local. */
+ the global static constructors. These are always local.
+ OpenMP "declare target" offloaded constructors/destructors use "off_I" and
+ "off_D" for the same purpose. */
else if (((type[0] == 'I' || type[0] == 'D') && targetm.have_ctors_dtors)
- || (startswith (type, "sub_")
+ || ((startswith (type, "sub_") || startswith (type, "off_"))
&& (type[4] == 'I' || type[4] == 'D')))
{
const char *file = main_input_filename;
2025-04-25 Thomas Schwinge <tschwinge@baylibre.com>
+ Backported from trunk:
+ 2024-08-07 Julian Brown <julian@codesourcery.com>
+ Tobias Burnus <tobias@baylibre.com>
+
+ * testsuite/libgomp.c++/static-aggr-constructor-destructor-1.C: New test.
+ * testsuite/libgomp.c++/static-aggr-constructor-destructor-2.C: New test.
+ * testsuite/libgomp.c++/static-aggr-constructor-destructor-3.C: New test.
+ * testsuite/libgomp.c-c++-common/target-is-initial-host.c: New test.
+ * testsuite/libgomp.c-c++-common/target-is-initial-host-2.c: New test.
+ * testsuite/libgomp.fortran/target-is-initial-host.f: New test.
+ * testsuite/libgomp.fortran/target-is-initial-host.f90: New test.
+ * testsuite/libgomp.fortran/target-is-initial-host-2.f90: New test.
+
Revert:
2023-05-12 Julian Brown <julian@codesourcery.com>
--- /dev/null
+// { dg-do run }
+// { dg-additional-options "-fdump-tree-gimple -fdump-tree-optimized" }
+// { dg-additional-options -foffload-options=-fdump-tree-optimized { target { offload_device_nvptx || offload_target_amdgcn } } }
+
+// { dg-final { scan-tree-dump-times "omp_is_initial_device" 1 "gimple" } }
+// { dg-final { scan-tree-dump-times "_GLOBAL__off_I_v1" 1 "gimple" } }
+// { dg-final { scan-tree-dump-times "__omp_target_static_init_and_destruction" 2 "gimple" } }
+// { dg-final { scan-tree-dump-times "__attribute__\\(\\(\[^\n\r]*omp declare target nohost" 2 "gimple" } }
+
+// { dg-final { scan-tree-dump-not "omp_is_initial_device" "optimized" } }
+// { dg-final { scan-tree-dump-not "__omp_target_static_init_and_destruction" "optimized" } }
+// FIXME: should be '-not' not '-times' 1:
+// { dg-final { scan-tree-dump-times "void _GLOBAL__off_I_v1" 1 "optimized" } }
+// { dg-final { scan-tree-dump-times "__attribute__\\(\\(\[^\n\r]*omp declare target nohost" 1 "optimized" } }
+
+// { dg-final { only_for_offload_target amdgcn-amdhsa scan-offload-tree-dump-not "omp_initial_device;" "optimized" { target offload_target_amdgcn } } }
+// { dg-final { only_for_offload_target amdgcn-amdhsa scan-offload-tree-dump "v1\\._x = 5;" "optimized" { target offload_target_amdgcn } } }
+// { dg-final { only_for_offload_target nvptx-none scan-offload-tree-dump-not "omp_initial_device;" "optimized" { target offload_target_nvptx } } }
+// { dg-final { only_for_offload_target nvptx-none scan-offload-tree-dump "v1\\._x = 5;" "optimized" { target offload_target_nvptx } } }
+
+#include <cassert>
+#include <omp.h>
+
+#pragma omp declare target
+
+struct str {
+ str(int x) : _x(x) { }
+ int add(str o) { return _x + o._x; }
+ int _x;
+} v1(5);
+
+#pragma omp end declare target
+
+void check_host()
+{
+ assert (v1._x == 5);
+}
+
+void check_devs()
+{
+ for (int dev = omp_initial_device; dev <= omp_get_num_devices(); dev++)
+ {
+ int res = 99, dev_num = 98;
+ #pragma omp target map(from: res, dev_num) device(dev)
+ {
+ res = v1._x;
+ dev_num = omp_get_device_num();
+ }
+ assert (res == 5);
+ if (dev == omp_initial_device)
+ assert (dev_num == omp_get_num_devices());
+ else
+ assert (dev_num == dev);
+ }
+}
+
+int main()
+{
+ int res = -1;
+ str v2(2);
+
+#pragma omp target map(from:res)
+ {
+ res = v1.add(v2);
+ }
+
+ assert (res == 7);
+ check_host();
+ check_devs();
+
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+// { dg-additional-options "-fdump-tree-gimple -fdump-tree-optimized" }
+// { dg-additional-options -foffload-options=-fdump-tree-optimized { target { offload_device_nvptx || offload_target_amdgcn } } }
+
+// { dg-final { scan-tree-dump-times "omp_is_initial_device" 1 "gimple" } }
+// { dg-final { scan-tree-dump-times "_GLOBAL__off_I_v1" 1 "gimple" } }
+// { dg-final { scan-tree-dump-times "__omp_target_static_init_and_destruction" 2 "gimple" } }
+// { dg-final { scan-tree-dump-times "__attribute__\\(\\(\[^\n\r]*omp declare target nohost" 2 "gimple" } }
+
+// { dg-final { scan-tree-dump-not "omp_is_initial_device" "optimized" } }
+// { dg-final { scan-tree-dump-not "__omp_target_static_init_and_destruction" "optimized" } }
+// FIXME: should be '-not' not '-times' 1:
+// { dg-final { scan-tree-dump-times "void _GLOBAL__off_I_" 1 "optimized" } }
+// { dg-final { scan-tree-dump-times "__attribute__\\(\\(\[^\n\r]*omp declare target nohost" 1 "optimized" } }
+
+// { dg-final { only_for_offload_target amdgcn-amdhsa scan-offload-tree-dump-not "omp_initial_device;" "optimized" { target offload_target_amdgcn } } }
+// { dg-final { only_for_offload_target amdgcn-amdhsa scan-offload-tree-dump "v1\\._x = 5;" "optimized" { target offload_target_amdgcn } } }
+// { dg-final { only_for_offload_target nvptx-none scan-offload-tree-dump-not "omp_initial_device;" "optimized" { target offload_target_nvptx } } }
+// { dg-final { only_for_offload_target nvptx-none scan-offload-tree-dump "v1\\._x = 5;" "optimized" { target offload_target_nvptx } } }
+
+
+#include <cassert>
+
+#pragma omp declare target
+
+template<typename T>
+struct str {
+ str(T x) : _x(x) { }
+ T add(str o) { return _x + o._x; }
+ T _x;
+};
+
+str<long> v1(5);
+
+#pragma omp end declare target
+
+int main()
+{
+ long res = -1;
+ str<long> v2(2);
+
+#pragma omp target map(from:res)
+ {
+ res = v1.add(v2);
+ }
+
+ assert (res == 7);
+
+ return 0;
+}
--- /dev/null
+// { dg-do run }
+
+#include <cassert>
+
+#pragma omp declare target
+
+struct item {
+ item(item *p, int v) : prev(p), val(v) { }
+ int get() { return prev ? prev->get() * val : val; }
+ item *prev;
+ int val;
+};
+
+/* This case demonstrates why constructing on the host and then copying to
+ the target would be less desirable. With on-target construction, "prev"
+ for each 'item' will be a device pointer, not a host pointer. */
+item hubert1(nullptr, 3);
+item hubert2(&hubert1, 5);
+item hubert3(&hubert2, 7);
+item hubert4(&hubert3, 11);
+
+#pragma omp end declare target
+
+int main()
+{
+ int res = -1;
+
+#pragma omp target map(from:res)
+ {
+ res = hubert4.get ();
+ }
+
+ assert (res == 1155);
+
+ return 0;
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-additional-options "-fno-builtin-omp_is_initial_device" } */
+
+/* Check whether 'omp_is_initial_device()' is NOT compile-time optimized. */
+
+/* { dg-additional-options "-fdump-tree-gimple -fdump-tree-optimized" } */
+/* { dg-additional-options -foffload-options=-fdump-tree-optimized { target { offload_device_nvptx || offload_target_amdgcn } } } */
+
+/* { dg-final { scan-tree-dump-times "omp_is_initial_device" 1 "gimple" } } */
+
+/* { dg-final { scan-tree-dump-times "omp_is_initial_device" 1 "optimized" } } */
+
+/* { dg-final { only_for_offload_target amdgcn-amdhsa scan-offload-tree-dump-times "omp_is_initial_device" 1 "optimized" { target offload_target_amdgcn } } } */
+/* { dg-final { only_for_offload_target nvptx-none scan-offload-tree-dump-times "omp_is_initial_device" 1 "optimized" { target offload_target_nvptx } } } */
+
+
+#include <omp.h>
+
+int
+main ()
+{
+ int is_initial, dev_num, initial;
+ initial = omp_get_initial_device();
+ for (int dev = omp_initial_device; dev <= omp_get_num_devices(); dev++)
+ {
+ is_initial = dev_num = 99;
+ #pragma omp target map(from: is_initial, dev_num) device(dev)
+ {
+ is_initial = omp_is_initial_device ();
+ dev_num = omp_get_device_num ();
+ }
+ if (dev == omp_initial_device || dev == initial)
+ {
+ if (dev_num != initial || is_initial != 1)
+ __builtin_abort ();
+ }
+ else
+ {
+ if (dev_num != dev || is_initial != 0)
+ __builtin_abort ();
+ }
+ }
+}
--- /dev/null
+/* { dg-do run } */
+
+/* Check whether 'omp_is_initial_device()' is properly compile-time optimized. */
+
+/* { dg-additional-options "-fdump-tree-gimple -fdump-tree-optimized" } */
+/* { dg-additional-options -foffload-options=-fdump-tree-optimized { target { offload_device_nvptx || offload_target_amdgcn } } } */
+
+/* { dg-final { scan-tree-dump-times "omp_is_initial_device" 1 "gimple" } } */
+
+/* { dg-final { scan-tree-dump-not "omp_is_initial_device" "optimized" } } */
+
+/* { dg-final { only_for_offload_target amdgcn-amdhsa scan-offload-tree-dump-not "omp_is_initial_device" "optimized" { target offload_target_amdgcn } } } */
+/* { dg-final { only_for_offload_target nvptx-none scan-offload-tree-dump-not "omp_is_initial_device" "optimized" { target offload_target_nvptx } } } */
+
+
+#include <omp.h>
+
+int
+main ()
+{
+ int is_initial, dev_num, initial;
+ initial = omp_get_initial_device();
+ for (int dev = omp_initial_device; dev <= omp_get_num_devices(); dev++)
+ {
+ is_initial = dev_num = 99;
+ #pragma omp target map(from: is_initial, dev_num) device(dev)
+ {
+ is_initial = omp_is_initial_device ();
+ dev_num = omp_get_device_num ();
+ }
+ if (dev == omp_initial_device || dev == initial)
+ {
+ if (dev_num != initial || is_initial != 1)
+ __builtin_abort ();
+ }
+ else
+ {
+ if (dev_num != dev || is_initial != 0)
+ __builtin_abort ();
+ }
+ }
+}
--- /dev/null
+! { dg-additional-options "-fno-builtin-omp_is_initial_device" }
+
+! Check whether 'omp_is_initial_device()' is NOT compile-time optimized. */
+
+! { dg-additional-options "-fdump-tree-gimple -fdump-tree-optimized" }
+! { dg-additional-options -foffload-options=-fdump-tree-optimized { target { offload_device_nvptx || offload_target_amdgcn } } }
+
+! { dg-final { scan-tree-dump-times "omp_is_initial_device" 1 "gimple" } }
+
+! { dg-final { scan-tree-dump-times "omp_is_initial_device" 1 "optimized" } }
+
+! { dg-final { only_for_offload_target amdgcn-amdhsa scan-offload-tree-dump-times "omp_is_initial_device" 1 "optimized" { target offload_target_amdgcn } } }
+! { dg-final { only_for_offload_target nvptx-none scan-offload-tree-dump-times "omp_is_initial_device" 1 "optimized" { target offload_target_nvptx } } }
+
+
+program main
+ use omp_lib
+ implicit none (type, external)
+ integer :: dev_num, initial, dev
+ logical :: is_initial
+
+ initial = omp_get_initial_device()
+ do dev = omp_initial_device, omp_get_num_devices()
+ dev_num = 99
+ !$omp target map(from: is_initial, dev_num) device(dev)
+ is_initial = omp_is_initial_device ()
+ dev_num = omp_get_device_num ()
+ !$omp end target
+ if (dev == omp_initial_device .or. dev == initial) then
+ if (dev_num /= initial .or. .not. is_initial) &
+ stop 1
+ else
+ if (dev_num /= dev .or. is_initial) &
+ stop 2
+ end if
+ end do
+end
--- /dev/null
+! Check whether 'omp_is_initial_device()' is properly compile-time optimized. */
+
+! { dg-additional-options "-fdump-tree-gimple -fdump-tree-optimized" }
+! { dg-additional-options -foffload-options=-fdump-tree-optimized { target { offload_device_nvptx || offload_target_amdgcn } } }
+
+! { dg-final { scan-tree-dump-times "omp_is_initial_device" 1 "gimple" } }
+
+! { dg-final { scan-tree-dump-not "omp_is_initial_device" "optimized" } }
+
+! { dg-final { only_for_offload_target amdgcn-amdhsa scan-offload-tree-dump-not "omp_is_initial_device" "optimized" { target offload_target_amdgcn } } }
+! { dg-final { only_for_offload_target nvptx-none scan-offload-tree-dump-not "omp_is_initial_device" "optimized" { target offload_target_nvptx } } }
+
+
+ program main
+ implicit none (type, external)
+ include "omp_lib.h"
+ integer :: dev_num, initial, dev
+ logical :: is_initial
+
+ initial = omp_get_initial_device()
+ do dev = omp_initial_device, omp_get_num_devices()
+ dev_num = 99
+!$omp target map(from: is_initial, dev_num) device(dev)
+ is_initial = omp_is_initial_device ()
+ dev_num = omp_get_device_num ()
+!$omp end target
+ if (dev == omp_initial_device .or. dev == initial) then
+ if (dev_num /= initial .or. .not. is_initial) &
+ & stop 1
+ else
+ if (dev_num /= dev .or. is_initial) &
+ & stop 2
+ end if
+ end do
+ end
--- /dev/null
+! Check whether 'omp_is_initial_device()' is properly compile-time optimized. */
+
+! { dg-additional-options "-fdump-tree-gimple -fdump-tree-optimized" }
+! { dg-additional-options -foffload-options=-fdump-tree-optimized { target { offload_device_nvptx || offload_target_amdgcn } } }
+
+! { dg-final { scan-tree-dump-times "omp_is_initial_device" 1 "gimple" } }
+
+! { dg-final { scan-tree-dump-not "omp_is_initial_device" "optimized" } }
+
+! { dg-final { only_for_offload_target amdgcn-amdhsa scan-offload-tree-dump-not "omp_is_initial_device" "optimized" { target offload_target_amdgcn } } }
+! { dg-final { only_for_offload_target nvptx-none scan-offload-tree-dump-not "omp_is_initial_device" "optimized" { target offload_target_nvptx } } }
+
+
+program main
+ use omp_lib
+ implicit none (type, external)
+ integer :: dev_num, initial, dev
+ logical :: is_initial
+
+ initial = omp_get_initial_device()
+ do dev = omp_initial_device, omp_get_num_devices()
+ dev_num = 99
+ !$omp target map(from: is_initial, dev_num) device(dev)
+ is_initial = omp_is_initial_device ()
+ dev_num = omp_get_device_num ()
+ !$omp end target
+ if (dev == omp_initial_device .or. dev == initial) then
+ if (dev_num /= initial .or. .not. is_initial) &
+ stop 1
+ else
+ if (dev_num /= dev .or. is_initial) &
+ stop 2
+ end if
+ end do
+end