summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Burnus <tobias@codesourcery.com>2022-05-12 14:49:29 +0200
committerTobias Burnus <tobias@codesourcery.com>2022-05-12 14:49:29 +0200
commitedd7d04ccb4b5ead99cca6d6e20f5c9fa8f1286b (patch)
treea3e5f0f9f78d6649cc5fe3cef36fb93f0bccc954
parentee954d940c41614c75be11b9d1de9079c37044d7 (diff)
OpenMP: Handle descriptors in target's firstprivate [PR104949]
For allocatable/pointer arrays, a firstprivate to a device not only needs to privatize the descriptor but also the actual data. This is implemented as: firstprivate(x) firstprivate(x.data) attach(x [bias: &x.data-&x) where the address of x in device memory is saved in hostaddrs[i] by libgomp and the middle end actually passes hostaddrs[i]' to attach. OG11 version of the GCC 13/mainline patch submitted at https://gcc.gnu.org/pipermail/gcc-patches/2022-May/594582.html Note: Contrary to GCC 12+, OG11 does not support has_device_addr, which is the main difference to the posted patch. gcc/fortran/ChangeLog: PR fortran/104949 * f95-lang.c (LANG_HOOKS_OMP_ARRAY_SIZE): Redefine. * trans-openmp.c (gfc_omp_array_size): New. * trans.h (gfc_omp_array_size): New. gcc/ChangeLog: PR fortran/104949 * langhooks-def.h (lhd_omp_array_size): New. (LANG_HOOKS_OMP_ARRAY_SIZE): Define (LANG_HOOKS_DECLS): Add it. * langhooks.c (lhd_omp_array_size): New. * langhooks.h (struct lang_hooks_for_decls): Add hook. * omp-low.c (scan_sharing_clauses, lower_omp_target): Handle GOMP_MAP_FIRSTPRIVATE for array descriptors. libgomp/ChangeLog: PR fortran/104949 * target.c (gomp_map_vars_internal, copy_firstprivate_data): Support attach for GOMP_MAP_FIRSTPRIVATE. * testsuite/libgomp.fortran/target-firstprivate-1.f90: New test. * testsuite/libgomp.fortran/target-firstprivate-2.f90: New test. * testsuite/libgomp.fortran/target-firstprivate-3.f90: New test.
-rw-r--r--gcc/fortran/f95-lang.c2
-rw-r--r--gcc/fortran/trans-openmp.c42
-rw-r--r--gcc/fortran/trans.h1
-rw-r--r--gcc/langhooks-def.h3
-rw-r--r--gcc/langhooks.c8
-rw-r--r--gcc/langhooks.h5
-rw-r--r--gcc/omp-low.c87
-rw-r--r--libgomp/target.c24
-rw-r--r--libgomp/testsuite/libgomp.fortran/target-firstprivate-1.f9033
-rw-r--r--libgomp/testsuite/libgomp.fortran/target-firstprivate-2.f90113
-rw-r--r--libgomp/testsuite/libgomp.fortran/target-firstprivate-3.f9024
11 files changed, 341 insertions, 1 deletions
diff --git a/gcc/fortran/f95-lang.c b/gcc/fortran/f95-lang.c
index d23ad3a7520..d4dd9b7e127 100644
--- a/gcc/fortran/f95-lang.c
+++ b/gcc/fortran/f95-lang.c
@@ -114,6 +114,7 @@ static const struct attribute_spec gfc_attribute_table[] =
#undef LANG_HOOKS_TYPE_FOR_SIZE
#undef LANG_HOOKS_INIT_TS
#undef LANG_HOOKS_OMP_ARRAY_DATA
+#undef LANG_HOOKS_OMP_ARRAY_SIZE
#undef LANG_HOOKS_OMP_IS_ALLOCATABLE_OR_PTR
#undef LANG_HOOKS_OMP_CHECK_OPTIONAL_ARGUMENT
#undef LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE
@@ -155,6 +156,7 @@ static const struct attribute_spec gfc_attribute_table[] =
#define LANG_HOOKS_TYPE_FOR_SIZE gfc_type_for_size
#define LANG_HOOKS_INIT_TS gfc_init_ts
#define LANG_HOOKS_OMP_ARRAY_DATA gfc_omp_array_data
+#define LANG_HOOKS_OMP_ARRAY_SIZE gfc_omp_array_size
#define LANG_HOOKS_OMP_IS_ALLOCATABLE_OR_PTR gfc_omp_is_allocatable_or_ptr
#define LANG_HOOKS_OMP_CHECK_OPTIONAL_ARGUMENT gfc_omp_check_optional_argument
#define LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE gfc_omp_privatize_by_reference
diff --git a/gcc/fortran/trans-openmp.c b/gcc/fortran/trans-openmp.c
index 146aa748611..a51d8227c76 100644
--- a/gcc/fortran/trans-openmp.c
+++ b/gcc/fortran/trans-openmp.c
@@ -182,6 +182,48 @@ gfc_omp_array_data (tree decl, bool type_only)
return decl;
}
+/* Return the byte-size of the passed array descriptor. */
+
+tree
+gfc_omp_array_size (tree decl, gimple_seq *pre_p)
+{
+ stmtblock_t block;
+ if (POINTER_TYPE_P (TREE_TYPE (decl)))
+ decl = build_fold_indirect_ref (decl);
+ tree type = TREE_TYPE (decl);
+ gcc_assert (GFC_DESCRIPTOR_TYPE_P (type));
+ bool allocatable = (GFC_TYPE_ARRAY_AKIND (type) == GFC_ARRAY_ALLOCATABLE
+ || GFC_TYPE_ARRAY_AKIND (type) == GFC_ARRAY_POINTER
+ || GFC_TYPE_ARRAY_AKIND (type) == GFC_ARRAY_POINTER_CONT);
+ gfc_init_block (&block);
+ tree size = gfc_full_array_size (&block, decl,
+ GFC_TYPE_ARRAY_RANK (TREE_TYPE (decl)));
+ size = fold_convert (size_type_node, size);
+ tree elemsz = gfc_get_element_type (TREE_TYPE (decl));
+ if (TREE_CODE (elemsz) == ARRAY_TYPE && TYPE_STRING_FLAG (elemsz))
+ elemsz = gfc_conv_descriptor_elem_len (decl);
+ else
+ elemsz = TYPE_SIZE_UNIT (elemsz);
+ size = fold_build2 (MULT_EXPR, size_type_node, size, elemsz);
+ if (!allocatable)
+ gimplify_and_add (gfc_finish_block (&block), pre_p);
+ else
+ {
+ tree var = create_tmp_var (size_type_node);
+ gfc_add_expr_to_block (&block, build2 (MODIFY_EXPR, sizetype, var, size));
+ tree tmp = fold_build2_loc (input_location, NE_EXPR, boolean_type_node,
+ gfc_conv_descriptor_data_get (decl),
+ null_pointer_node);
+ tmp = build3_loc (input_location, COND_EXPR, void_type_node, tmp,
+ gfc_finish_block (&block),
+ build2 (MODIFY_EXPR, sizetype, var, size_zero_node));
+ gimplify_and_add (tmp, pre_p);
+ size = var;
+ }
+ return size;
+}
+
+
/* True if OpenMP should privatize what this DECL points to rather
than the DECL itself. */
diff --git a/gcc/fortran/trans.h b/gcc/fortran/trans.h
index a4398821ccd..d1888ae5c3d 100644
--- a/gcc/fortran/trans.h
+++ b/gcc/fortran/trans.h
@@ -816,6 +816,7 @@ bool gfc_get_array_descr_info (const_tree, struct array_descr_info *);
bool gfc_omp_is_allocatable_or_ptr (const_tree);
tree gfc_omp_check_optional_argument (tree, bool);
tree gfc_omp_array_data (tree, bool);
+tree gfc_omp_array_size (tree, gimple_seq *);
bool gfc_omp_privatize_by_reference (const_tree);
enum omp_clause_default_kind gfc_omp_predetermined_sharing (tree);
enum omp_clause_defaultmap_kind gfc_omp_predetermined_mapping (tree);
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index fc90018e08e..0f37dc1805d 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -86,6 +86,7 @@ extern bool lhd_omp_deep_mapping_p (const gimple *, tree);
extern tree lhd_omp_deep_mapping_cnt (const gimple *, tree, gimple_seq *);
extern void lhd_omp_deep_mapping (const gimple *, tree, unsigned HOST_WIDE_INT,
tree, tree, tree, tree, tree, gimple_seq *);
+extern tree lhd_omp_array_size (tree, gimple_seq *);
struct gimplify_omp_ctx;
extern void lhd_omp_firstprivatize_type_sizes (struct gimplify_omp_ctx *,
tree);
@@ -255,6 +256,7 @@ extern tree lhd_unit_size_without_reusable_padding (tree);
#define LANG_HOOKS_POST_COMPILATION_PARSING_CLEANUPS NULL
#define LANG_HOOKS_DECL_OK_FOR_SIBCALL lhd_decl_ok_for_sibcall
#define LANG_HOOKS_OMP_ARRAY_DATA hook_tree_tree_bool_null
+#define LANG_HOOKS_OMP_ARRAY_SIZE lhd_omp_array_size
#define LANG_HOOKS_OMP_IS_ALLOCATABLE_OR_PTR hook_bool_const_tree_false
#define LANG_HOOKS_OMP_CHECK_OPTIONAL_ARGUMENT hook_tree_tree_bool_null
#define LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE hook_bool_const_tree_false
@@ -291,6 +293,7 @@ extern tree lhd_unit_size_without_reusable_padding (tree);
LANG_HOOKS_POST_COMPILATION_PARSING_CLEANUPS, \
LANG_HOOKS_DECL_OK_FOR_SIBCALL, \
LANG_HOOKS_OMP_ARRAY_DATA, \
+ LANG_HOOKS_OMP_ARRAY_SIZE, \
LANG_HOOKS_OMP_IS_ALLOCATABLE_OR_PTR, \
LANG_HOOKS_OMP_CHECK_OPTIONAL_ARGUMENT, \
LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE, \
diff --git a/gcc/langhooks.c b/gcc/langhooks.c
index 2f7295ce3ec..afdd7ae35c7 100644
--- a/gcc/langhooks.c
+++ b/gcc/langhooks.c
@@ -638,6 +638,14 @@ lhd_omp_deep_mapping (const gimple *, tree, unsigned HOST_WIDE_INT, tree, tree,
{
}
+/* Return array size; cf. omp_array_data. */
+
+tree
+lhd_omp_array_size (tree, gimple_seq *)
+{
+ return NULL_TREE;
+}
+
/* Return true if DECL is a scalar variable (for the purpose of
implicit firstprivatization & mapping). Only if alloc_ptr_ok
are allocatables and pointers accepted. */
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index 302425e0bf9..5920903d9ae 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -231,6 +231,11 @@ struct lang_hooks_for_decls
is true, only the TREE_TYPE is returned without generating a new tree. */
tree (*omp_array_data) (tree, bool);
+ /* Return a tree for the actual data of an array descriptor - or NULL_TREE
+ if original tree is not an array descriptor. If the second argument
+ is true, only the TREE_TYPE is returned without generating a new tree. */
+ tree (*omp_array_size) (tree, gimple_seq *pre_p);
+
/* True if OpenMP should regard this DECL as being a scalar which has Fortran's
allocatable or pointer attribute. */
bool (*omp_is_allocatable_or_ptr) (const_tree);
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index ee6b4271447..68794c01c43 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -1685,6 +1685,15 @@ scan_sharing_clauses (tree clauses, omp_context *ctx,
install_var_field (decl, by_ref, 3, ctx);
}
install_var_local (decl, ctx);
+ /* For descr arrays on target: firstprivatize data + attach ptr. */
+ if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE
+ && is_gimple_omp_offloaded (ctx->stmt)
+ && !is_gimple_omp_oacc (ctx->stmt)
+ && lang_hooks.decls.omp_array_data (decl, true))
+ {
+ install_var_field (decl, false, 16 | 3, ctx);
+ install_var_field (decl, true, 8 | 3, ctx);
+ }
break;
case OMP_CLAUSE_USE_DEVICE_PTR:
@@ -13578,6 +13587,9 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
SET_DECL_VALUE_EXPR (new_var, x);
DECL_HAS_VALUE_EXPR_P (new_var) = 1;
}
+ /* Fortran array descriptors: firstprivate of data + attach. */
+ if (lang_hooks.decls.omp_array_data (var, true))
+ map_cnt += 2;
break;
case OMP_CLAUSE_PRIVATE:
@@ -14226,6 +14238,81 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
<= tree_to_uhwi (TYPE_MAX_VALUE (tkind_type)));
CONSTRUCTOR_APPEND_ELT (vkind, purpose,
build_int_cstu (tkind_type, tkind));
+ /* Fortran array descriptors: firstprivate of data + attach. */
+ if (lang_hooks.decls.omp_array_data (ovar, true))
+ {
+ tree not_null_lb, null_lb, after_lb;
+ tree var1, var2, size1, size2;
+ tree present = omp_check_optional_argument (ovar, true);
+ if (present)
+ {
+ location_t clause_loc = OMP_CLAUSE_LOCATION (c);
+ not_null_lb = create_artificial_label (clause_loc);
+ null_lb = create_artificial_label (clause_loc);
+ after_lb = create_artificial_label (clause_loc);
+ gimple_seq seq = NULL;
+ present = force_gimple_operand (present, &seq, true,
+ NULL_TREE);
+ gimple_seq_add_seq (&ilist, seq);
+ gimple_seq_add_stmt (&ilist,
+ gimple_build_cond_from_tree (present,
+ not_null_lb, null_lb));
+ gimple_seq_add_stmt (&ilist,
+ gimple_build_label (not_null_lb));
+ }
+ var1 = lang_hooks.decls.omp_array_data (var, false);
+ size1 = lang_hooks.decls.omp_array_size (var, &ilist);
+ var2 = build_fold_addr_expr (x);
+ if (!POINTER_TYPE_P (TREE_TYPE (var)))
+ var = build_fold_addr_expr (var);
+ size2 = fold_build2 (POINTER_DIFF_EXPR, ssizetype,
+ build_fold_addr_expr (var1), var);
+ size2 = fold_convert (sizetype, size2);
+ if (present)
+ {
+ tree tmp = create_tmp_var (TREE_TYPE (var1));
+ gimplify_assign (tmp, var1, &ilist);
+ var1 = tmp;
+ tmp = create_tmp_var (TREE_TYPE (var2));
+ gimplify_assign (tmp, var2, &ilist);
+ var2 = tmp;
+ tmp = create_tmp_var (TREE_TYPE (size1));
+ gimplify_assign (tmp, size1, &ilist);
+ size1 = tmp;
+ tmp = create_tmp_var (TREE_TYPE (size2));
+ gimplify_assign (tmp, size2, &ilist);
+ size2 = tmp;
+ gimple_seq_add_stmt (&ilist, gimple_build_goto (after_lb));
+ gimple_seq_add_stmt (&ilist, gimple_build_label (null_lb));
+ gimplify_assign (var1, null_pointer_node, &ilist);
+ gimplify_assign (var2, null_pointer_node, &ilist);
+ gimplify_assign (size1, size_zero_node, &ilist);
+ gimplify_assign (size2, size_zero_node, &ilist);
+ gimple_seq_add_stmt (&ilist, gimple_build_label (after_lb));
+ }
+ x = build_sender_ref ((splay_tree_key) &DECL_NAME (ovar), ctx);
+ gimplify_assign (x, var1, &ilist);
+ tkind = GOMP_MAP_FIRSTPRIVATE;
+ talign = DECL_ALIGN_UNIT (ovar);
+ talign = ceil_log2 (talign);
+ tkind |= talign << talign_shift;
+ gcc_checking_assert (tkind
+ <= tree_to_uhwi (
+ TYPE_MAX_VALUE (tkind_type)));
+ purpose = size_int (map_idx++);
+ CONSTRUCTOR_APPEND_ELT (vsize, purpose, size1);
+ if (TREE_CODE (size1) != INTEGER_CST)
+ TREE_STATIC (TREE_VEC_ELT (t, 1)) = 0;
+ CONSTRUCTOR_APPEND_ELT (vkind, purpose,
+ build_int_cstu (tkind_type, tkind));
+ x = build_sender_ref ((splay_tree_key) &DECL_UID (ovar), ctx);
+ gimplify_assign (x, var2, &ilist);
+ tkind = GOMP_MAP_ATTACH;
+ purpose = size_int (map_idx++);
+ CONSTRUCTOR_APPEND_ELT (vsize, purpose, size2);
+ CONSTRUCTOR_APPEND_ELT (vkind, purpose,
+ build_int_cstu (tkind_type, tkind));
+ }
break;
case OMP_CLAUSE_USE_DEVICE_PTR:
diff --git a/libgomp/target.c b/libgomp/target.c
index 77adb300ccd..182ae5769f3 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -1394,7 +1394,24 @@ gomp_map_vars_internal (struct gomp_device_descr *devicep,
gomp_copy_host2dev (devicep, aq,
(void *) (tgt->tgt_start + tgt_size),
(void *) hostaddrs[i], len, false, cbufp);
+ /* Save device address in hostaddr to permit latter availablity
+ when doing a deep-firstprivate with pointer attach. */
+ hostaddrs[i] = (void *) (tgt->tgt_start + tgt_size);
tgt_size += len;
+
+ /* If followed by GOMP_MAP_ATTACH, pointer assign this
+ firstprivate to hostaddrs[i+1], which is assumed to contain a
+ device address. */
+ if (i + 1 < mapnum
+ && (GOMP_MAP_ATTACH
+ == (typemask & get_kind (short_mapkind, kinds, i+1))))
+ {
+ uintptr_t target = (uintptr_t) hostaddrs[i];
+ void *devptr = *(void**) hostaddrs[i+1] + sizes[i+1];
+ gomp_copy_host2dev (devicep, aq, devptr, &target,
+ sizeof (void *), false, cbufp);
+ ++i;
+ }
continue;
case GOMP_MAP_FIRSTPRIVATE_INT:
case GOMP_MAP_ZERO_LEN_ARRAY_SECTION:
@@ -2674,13 +2691,18 @@ copy_firstprivate_data (char *tgt, size_t mapnum, void **hostaddrs,
tgt_size = 0;
size_t i;
for (i = 0; i < mapnum; i++)
- if ((kinds[i] & 0xff) == GOMP_MAP_FIRSTPRIVATE)
+ if ((kinds[i] & 0xff) == GOMP_MAP_FIRSTPRIVATE && hostaddrs[i] != NULL)
{
size_t align = (size_t) 1 << (kinds[i] >> 8);
tgt_size = (tgt_size + align - 1) & ~(align - 1);
memcpy (tgt + tgt_size, hostaddrs[i], sizes[i]);
hostaddrs[i] = tgt + tgt_size;
tgt_size = tgt_size + sizes[i];
+ if (i + 1 < mapnum && (kinds[i+1] & 0xff) == GOMP_MAP_ATTACH)
+ {
+ *(*(uintptr_t**) hostaddrs[i+1] + sizes[i+1]) = (uintptr_t) hostaddrs[i];
+ ++i;
+ }
}
}
diff --git a/libgomp/testsuite/libgomp.fortran/target-firstprivate-1.f90 b/libgomp/testsuite/libgomp.fortran/target-firstprivate-1.f90
new file mode 100644
index 00000000000..7b77992a21d
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-firstprivate-1.f90
@@ -0,0 +1,33 @@
+! PR fortran/104949
+
+implicit none (type,external)
+integer, allocatable :: A(:)
+A = [1,2,3,4,5,6]
+
+!$omp parallel firstprivate(A)
+!$omp master
+ if (any (A /= [1,2,3,4,5])) error stop
+ A(:) = [99,88,77,66,55]
+!$omp end master
+!$omp end parallel
+
+!$omp target firstprivate(A)
+ if (any (A /= [1,2,3,4,5])) error stop
+ A(:) = [99,88,77,66,55]
+!$omp end target
+if (any (A /= [1,2,3,4,5])) error stop
+
+!$omp parallel default(firstprivate)
+!$omp master
+ if (any (A /= [1,2,3,4,5])) error stop
+ A(:) = [99,88,77,66,55]
+!$omp end master
+!$omp end parallel
+if (any (A /= [1,2,3,4,5])) error stop
+
+!$omp target defaultmap(firstprivate)
+ if (any (A /= [1,2,3,4,5])) error stop
+ A(:) = [99,88,77,66,55]
+!$omp end target
+if (any (A /= [1,2,3,4,5])) error stop
+end
diff --git a/libgomp/testsuite/libgomp.fortran/target-firstprivate-2.f90 b/libgomp/testsuite/libgomp.fortran/target-firstprivate-2.f90
new file mode 100644
index 00000000000..d00b4070c11
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-firstprivate-2.f90
@@ -0,0 +1,113 @@
+! PR fortran/104949
+
+module m
+use omp_lib
+implicit none (type, external)
+
+contains
+subroutine one
+ integer, allocatable :: x(:)
+ integer :: i
+
+ do i = 1, omp_get_num_devices() + 1
+ !$omp target firstprivate(x)
+ if (allocated(x)) error stop
+ !$omp end target
+ if (allocated(x)) error stop
+ end do
+
+ do i = 1, omp_get_num_devices() + 1
+ !$omp target firstprivate(x, i)
+ if (allocated(x)) error stop
+ x = [10,20,30,40] + i
+ if (any (x /= [10,20,30,40] + i)) error stop
+ ! This leaks memory!
+ ! deallocate(x)
+ !$omp end target
+ if (allocated(x)) error stop
+ end do
+
+ x = [1,2,3,4]
+
+ do i = 1, omp_get_num_devices() + 1
+ !$omp target firstprivate(x, i)
+ if (i <= 0) error stop
+ if (.not.allocated(x)) error stop
+ if (size(x) /= 4) error stop
+ if (lbound(x,1) /= 1) error stop
+ if (any (x /= [1,2,3,4])) error stop
+ ! no reallocation, just malloced + assignment
+ x = [10,20,30,40] + i
+ if (any (x /= [10,20,30,40] + i)) error stop
+ ! This leaks memory!
+ ! deallocate(x)
+ !$omp end target
+ if (.not.allocated(x)) error stop
+ if (size(x) /= 4) error stop
+ if (lbound(x,1) /= 1) error stop
+ if (any (x /= [1,2,3,4])) error stop
+ end do
+ deallocate(x)
+end
+
+subroutine two
+ character(len=:), allocatable :: x(:)
+ character(len=5) :: str
+ integer :: i
+
+ str = "abcde" ! work around for PR fortran/91544
+ do i = 1, omp_get_num_devices() + 1
+ !$omp target firstprivate(x)
+ if (allocated(x)) error stop
+ !$omp end target
+ if (allocated(x)) error stop
+ end do
+
+ do i = 1, omp_get_num_devices() + 1
+ !$omp target firstprivate(x, i)
+ if (allocated(x)) error stop
+ ! no reallocation, just malloced + assignment
+ x = [character(len=2+i) :: str,"fhji","klmno"]
+ if (len(x) /= 2+i) error stop
+ if (any (x /= [character(len=2+i) :: str,"fhji","klmno"])) error stop
+ ! This leaks memory!
+ ! deallocate(x)
+ !$omp end target
+ if (allocated(x)) error stop
+ end do
+
+ x = [character(len=4) :: "ABCDE","FHJI","KLMNO"]
+
+ do i = 1, omp_get_num_devices() + 1
+ !$omp target firstprivate(x, i)
+ if (i <= 0) error stop
+ if (.not.allocated(x)) error stop
+ if (size(x) /= 3) error stop
+ if (lbound(x,1) /= 1) error stop
+ if (len(x) /= 4) error stop
+ if (any (x /= [character(len=4) :: "ABCDE","FHJI","KLMNO"])) error stop
+ !! Reallocation runs into the issue PR fortran/105538
+ !!
+ !!x = [character(len=2+i) :: str,"fhji","klmno"]
+ !!if (len(x) /= 2+i) error stop
+ !!if (any (x /= [character(len=2+i) :: str,"fhji","klmno"])) error stop
+ !! This leaks memory!
+ !! deallocate(x)
+ ! Just assign:
+ x = [character(len=4) :: "abcde","fhji","klmno"]
+ if (any (x /= [character(len=4) :: "abcde","fhji","klmno"])) error stop
+ !$omp end target
+ if (.not.allocated(x)) error stop
+ if (lbound(x,1) /= 1) error stop
+ if (size(x) /= 3) error stop
+ if (len(x) /= 4) error stop
+ if (any (x /= [character(len=4) :: "ABCDE","FHJI","KLMNO"])) error stop
+ end do
+ deallocate(x)
+end
+end module m
+
+use m
+call one
+call two
+end
diff --git a/libgomp/testsuite/libgomp.fortran/target-firstprivate-3.f90 b/libgomp/testsuite/libgomp.fortran/target-firstprivate-3.f90
new file mode 100644
index 00000000000..7406cdc4e41
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-firstprivate-3.f90
@@ -0,0 +1,24 @@
+implicit none
+ integer, allocatable :: x(:)
+ x = [1,2,3,4]
+ call foo(x)
+ if (any (x /= [1,2,3,4])) error stop
+ call foo()
+contains
+subroutine foo(c)
+ integer, allocatable, optional :: c(:)
+ logical :: is_present
+ is_present = present (c)
+ !$omp target firstprivate(c)
+ if (is_present) then
+ if (.not. allocated(c)) error stop
+ if (any (c /= [1,2,3,4])) error stop
+ c = [99,88,77,66]
+ if (any (c /= [99,88,77,66])) error stop
+ end if
+ !$omp end target
+ if (is_present) then
+ if (any (c /= [1,2,3,4])) error stop
+ end if
+end
+end