From 97c640c1136c7af180c6063f4706cf7cbf23b1b4 Mon Sep 17 00:00:00 2001 From: Kwok Cheung Yeung Date: Sun, 7 Jun 2026 01:37:16 +0000 Subject: [PATCH] openmp, fortran: Add support for iterators in OpenMP 'target update' constructs (Fortran) This adds Fortran support for iterators in 'to' and 'from' clauses in the 'target update' OpenMP directive. gcc/fortran/ * dump-parse-tree.cc (show_omp_namelist): Add iterator support for OMP_LIST_TO and OMP_LIST_FROM. * match.cc (gfc_free_omp_namelist): Free namespace for OMP_LIST_TO and OMP_LIST_FROM. * openmp.cc (gfc_match_motion_var_list): Parse 'iterator' modifier. (resolve_omp_clauses): Resolve iterators for OMP_LIST_TO and OMP_LIST_FROM. * trans-openmp.cc (gfc_trans_omp_clauses): Handle iterators in OMP_LIST_TO and OMP_LIST_FROM clauses. Add expressions to iter_block rather than block. gcc/testsuite/ * gfortran.dg/gomp/target-update-iterators-1.f90: New. * gfortran.dg/gomp/target-update-iterators-2.f90: New. * gfortran.dg/gomp/target-update-iterators-3.f90: New. libgomp/ * testsuite/libgomp.fortran/target-update-iterators-1.f90: New. * testsuite/libgomp.fortran/target-update-iterators-2.f90: New. * testsuite/libgomp.fortran/target-update-iterators-3.f90: New. Co-authored-by: Andrew Stubbs Co-authored-by: Sandra Loosemore --- gcc/fortran/dump-parse-tree.cc | 7 +- gcc/fortran/match.cc | 3 +- gcc/fortran/openmp.cc | 83 +++++++++++++++++-- gcc/fortran/trans-openmp.cc | 49 +++++++++-- .../gomp/target-update-iterators-1.f90 | 33 ++++++++ .../gomp/target-update-iterators-2.f90 | 25 ++++++ .../gomp/target-update-iterators-3.f90 | 23 +++++ .../target-update-iterators-1.f90 | 68 +++++++++++++++ .../target-update-iterators-2.f90 | 63 ++++++++++++++ .../target-update-iterators-3.f90 | 78 +++++++++++++++++ 10 files changed, 420 insertions(+), 12 deletions(-) create mode 100644 gcc/testsuite/gfortran.dg/gomp/target-update-iterators-1.f90 create mode 100644 gcc/testsuite/gfortran.dg/gomp/target-update-iterators-2.f90 create mode 100644 gcc/testsuite/gfortran.dg/gomp/target-update-iterators-3.f90 create mode 100644 libgomp/testsuite/libgomp.fortran/target-update-iterators-1.f90 create mode 100644 libgomp/testsuite/libgomp.fortran/target-update-iterators-2.f90 create mode 100644 libgomp/testsuite/libgomp.fortran/target-update-iterators-3.f90 diff --git a/gcc/fortran/dump-parse-tree.cc b/gcc/fortran/dump-parse-tree.cc index 03e1ec0f78c..3d7a4d29511 100644 --- a/gcc/fortran/dump-parse-tree.cc +++ b/gcc/fortran/dump-parse-tree.cc @@ -1486,7 +1486,8 @@ show_omp_namelist (int list_type, gfc_omp_namelist *n) { gfc_current_ns = ns_curr; if (list_type == OMP_LIST_AFFINITY || list_type == OMP_LIST_DEPEND - || list_type == OMP_LIST_MAP) + || list_type == OMP_LIST_MAP + || list_type == OMP_LIST_TO || list_type == OMP_LIST_FROM) { gfc_current_ns = n->u2.ns ? n->u2.ns : ns_curr; if (n->u2.ns != ns_iter) @@ -1502,6 +1503,10 @@ show_omp_namelist (int list_type, gfc_omp_namelist *n) fputs ("DEPEND (", dumpfile); else if (list_type == OMP_LIST_MAP) fputs ("MAP (", dumpfile); + else if (list_type == OMP_LIST_TO) + fputs ("TO (", dumpfile); + else if (list_type == OMP_LIST_FROM) + fputs ("FROM (", dumpfile); else gcc_unreachable (); } diff --git a/gcc/fortran/match.cc b/gcc/fortran/match.cc index 95f1e8805cb..e8f2cd8580c 100644 --- a/gcc/fortran/match.cc +++ b/gcc/fortran/match.cc @@ -6516,7 +6516,8 @@ void gfc_free_omp_namelist (gfc_omp_namelist *name, enum gfc_omp_list_type list) { bool free_ns = (list == OMP_LIST_AFFINITY || list == OMP_LIST_DEPEND - || list == OMP_LIST_MAP); + || list == OMP_LIST_MAP + || list == OMP_LIST_TO || list == OMP_LIST_FROM); bool free_align_allocator = (list == OMP_LIST_ALLOCATE); bool free_mem_traits_space = (list == OMP_LIST_USES_ALLOCATORS); bool free_init = (list == OMP_LIST_INIT); diff --git a/gcc/fortran/openmp.cc b/gcc/fortran/openmp.cc index 384e7d7d48c..4fd681efece 100644 --- a/gcc/fortran/openmp.cc +++ b/gcc/fortran/openmp.cc @@ -1439,16 +1439,88 @@ gfc_match_motion_var_list (const char *str, gfc_omp_namelist **list, if (m != MATCH_YES) return m; - match m_present = gfc_match (" present : "); + gfc_namespace *ns_iter = NULL, *ns_curr = gfc_current_ns; + locus old_loc = gfc_current_locus; + int present_modifier = 0; + int iterator_modifier = 0; + locus second_present_locus = old_loc; + locus second_iterator_locus = old_loc; + bool saw_modifier = false; + + for (;;) + { + locus current_locus = gfc_current_locus; + if (gfc_match ("present ") == MATCH_YES) + { + if (present_modifier++ == 1) + second_present_locus = current_locus; + } + else if (gfc_match_iterator (&ns_iter, true) == MATCH_YES) + { + if (iterator_modifier++ == 1) + second_iterator_locus = current_locus; + } + else if (!saw_modifier) + break; + else + { + gfc_error ("Expected clause modifier at %C"); + return MATCH_ERROR; + } + + /* OpenMP 5.1 syntax mistakenly allowed commas to be optional + between and after modifiers in a clause. This was corrected + in 5.2 and later specifications: they're now required between + modifiers and a trailing comma is not permitted. We implement + the 5.2 syntax here. */ + saw_modifier = true; + if (gfc_match (" : ") == MATCH_YES) + break; + else if (gfc_match (", ") == MATCH_YES) + continue; + else + { + gfc_error ("Expected %<,%> or %<:%> after clause modifier at %C"); + return MATCH_ERROR; + } + } + + if (!saw_modifier) + { + gfc_current_locus = old_loc; + present_modifier = 0; + iterator_modifier = 0; + } + + if (present_modifier > 1) + { + gfc_error ("Too many % modifiers at %L", &second_present_locus); + return MATCH_ERROR; + } + if (iterator_modifier > 1) + { + gfc_error ("Too many % modifiers at %L", + &second_iterator_locus); + return MATCH_ERROR; + } + + if (ns_iter) + gfc_current_ns = ns_iter; m = gfc_match_omp_variable_list ("", list, false, NULL, headp, true, true); + gfc_current_ns = ns_curr; if (m != MATCH_YES) return m; - if (m_present == MATCH_YES) + gfc_omp_namelist *n; + for (n = **headp; n; n = n->next) { - gfc_omp_namelist *n; - for (n = **headp; n; n = n->next) + if (present_modifier) n->u.present_modifier = true; + if (iterator_modifier) + { + n->u2.ns = ns_iter; + ns_iter->refs++; + } } return MATCH_YES; } @@ -10048,7 +10120,8 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses, for (; n != NULL; n = n->next) { if ((list == OMP_LIST_DEPEND || list == OMP_LIST_AFFINITY - || list == OMP_LIST_MAP) + || list == OMP_LIST_MAP + || list == OMP_LIST_TO || list == OMP_LIST_FROM) && n->u2.ns && !n->u2.ns->resolved) { n->u2.ns->resolved = 1; diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc index c3ff30092c6..43697e99453 100644 --- a/gcc/fortran/trans-openmp.cc +++ b/gcc/fortran/trans-openmp.cc @@ -5270,12 +5270,40 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses, case OMP_LIST_TO: case OMP_LIST_FROM: case OMP_LIST_CACHE: + iterator = NULL_TREE; + prev = NULL; + prev_clauses = omp_clauses; for (; n != NULL; n = n->next) { if (!n->sym->attr.referenced && n->sym->attr.flavor != FL_PARAMETER) continue; + if (iterator && prev->u2.ns != n->u2.ns) + { + /* Finish previous iterator group. */ + BLOCK_SUBBLOCKS (tree_block) = gfc_finish_block (&iter_block); + TREE_VEC_ELT (iterator, 5) = tree_block; + for (tree c = omp_clauses; c != prev_clauses; + c = OMP_CLAUSE_CHAIN (c)) + OMP_CLAUSE_ITERATORS (c) = iterator; + prev_clauses = omp_clauses; + iterator = NULL_TREE; + } + if (n->u2.ns && (!prev || prev->u2.ns != n->u2.ns)) + { + /* Start a new iterator group. */ + gfc_init_block (&iter_block); + tree_block = make_node (BLOCK); + TREE_USED (tree_block) = 1; + BLOCK_VARS (tree_block) = NULL_TREE; + prev_clauses = omp_clauses; + iterator = handle_iterator (n->u2.ns, block, tree_block); + } + if (!iterator) + gfc_init_block (&iter_block); + prev = n; + switch (list) { case OMP_LIST_TO: @@ -5314,7 +5342,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses, ptr = build_fold_indirect_ref (ptr); OMP_CLAUSE_DECL (node) = ptr; OMP_CLAUSE_SIZE (node) - = gfc_full_array_size (block, decl, + = gfc_full_array_size (&iter_block, decl, GFC_TYPE_ARRAY_RANK (type)); tree elemsz = TYPE_SIZE_UNIT (gfc_get_element_type (type)); @@ -5339,7 +5367,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses, { gfc_conv_expr_reference (&se, n->expr); ptr = se.expr; - gfc_add_block_to_block (block, &se.pre); + gfc_add_block_to_block (&iter_block, &se.pre); OMP_CLAUSE_SIZE (node) = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ptr))); } @@ -5348,9 +5376,9 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses, gfc_conv_expr_descriptor (&se, n->expr); ptr = gfc_conv_array_data (se.expr); tree type = TREE_TYPE (se.expr); - gfc_add_block_to_block (block, &se.pre); + gfc_add_block_to_block (&iter_block, &se.pre); OMP_CLAUSE_SIZE (node) - = gfc_full_array_size (block, se.expr, + = gfc_full_array_size (&iter_block, se.expr, GFC_TYPE_ARRAY_RANK (type)); tree elemsz = TYPE_SIZE_UNIT (gfc_get_element_type (type)); @@ -5359,7 +5387,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses, = fold_build2 (MULT_EXPR, gfc_array_index_type, OMP_CLAUSE_SIZE (node), elemsz); } - gfc_add_block_to_block (block, &se.post); + gfc_add_block_to_block (&iter_block, &se.post); gcc_assert (POINTER_TYPE_P (TREE_TYPE (ptr))); OMP_CLAUSE_DECL (node) = build_fold_indirect_ref (ptr); } @@ -5367,8 +5395,19 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses, OMP_CLAUSE_MOTION_PRESENT (node) = 1; if (list == OMP_LIST_CACHE && n->u.map.readonly) OMP_CLAUSE__CACHE__READONLY (node) = 1; + if (!iterator) + gfc_add_block_to_block (block, &iter_block); omp_clauses = gfc_trans_add_clause (node, omp_clauses); } + if (iterator) + { + /* Finish last iterator group. */ + BLOCK_SUBBLOCKS (tree_block) = gfc_finish_block (&iter_block); + TREE_VEC_ELT (iterator, 5) = tree_block; + for (tree c = omp_clauses; c != prev_clauses; + c = OMP_CLAUSE_CHAIN (c)) + OMP_CLAUSE_ITERATORS (c) = iterator; + } break; case OMP_LIST_USES_ALLOCATORS: /* Ignore omp_null_allocator and pre-defined allocators as no diff --git a/gcc/testsuite/gfortran.dg/gomp/target-update-iterators-1.f90 b/gcc/testsuite/gfortran.dg/gomp/target-update-iterators-1.f90 new file mode 100644 index 00000000000..e9dc5a7de36 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/target-update-iterators-1.f90 @@ -0,0 +1,33 @@ +! { dg-do compile } +! { dg-options "-fopenmp" } + +program test + implicit none + + integer, parameter :: DIM1 = 17 + integer, parameter :: DIM2 = 39 + + type :: array_ptr + integer, pointer :: ptr(:) + end type + + type (array_ptr) :: x(DIM1), y(DIM1) + + !$omp target update to (iterator(i=1:DIM1): x(i)%ptr(:)) + + !$omp target update to (iterator(i=1:DIM1): x(i)%ptr(:DIM2), y(i)%ptr(:)) + + !$omp target update to (iterator(i=1:DIM1), present: x(i)%ptr(:)) + + !$omp target update to (, iterator(i=1:DIM1), present: x(i)%ptr(:)) ! { dg-error "Syntax error in OpenMP variable list at .1." } + + !$omp target update to (iterator(i=1:DIM1) present: x(i)%ptr(:)) ! { dg-error "Expected ',' or ':' after clause modifier at .1." } + + !$omp target update to (iterator(i=1:DIM1), present x(i)%ptr(:)) ! { dg-error "Expected ',' or ':' after clause modifier at .1." } + + !$omp target update to (iterator(i=1:DIM1), present, : x(i)%ptr(:)) ! { dg-error "Expected clause modifier at .1." } + + !$omp target update to (iterator(i=1:DIM1), iterator(j=i:DIM2): x(i)%ptr(j)) ! { dg-error "Too many 'iterator' modifiers at .1." } + + !$omp target update to (iterator(i=1:DIM1), something: x(i, j)) ! { dg-error "Expected clause modifier at .1." } +end program diff --git a/gcc/testsuite/gfortran.dg/gomp/target-update-iterators-2.f90 b/gcc/testsuite/gfortran.dg/gomp/target-update-iterators-2.f90 new file mode 100644 index 00000000000..91d07ef8fb7 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/target-update-iterators-2.f90 @@ -0,0 +1,25 @@ +! { dg-do compile } +! { dg-options "-fopenmp -fdump-tree-gimple" } + +program test + implicit none + + integer, parameter :: DIM1 = 100 + + type :: array_ptr + integer, pointer :: ptr(:) + end type + + type (array_ptr) :: x(DIM1), y(DIM1), z(DIM1) + + !$omp target update to(iterator(i=1:10): x) + !$omp target update from(iterator(i2=1:10, j2=1:20): x(i2)) + !$omp target update to(iterator(i3=1:10, j3=1:20, k3=1:30): x(i3+j3), y(j3+k3), z(k3+i3)) +end program + +! { dg-final { scan-tree-dump-times "update to\\\(x " 1 "gimple" } } +! { dg-final { scan-tree-dump-times "update from\\\(iterator\\\(integer\\\(kind=4\\\) i2=1:10:1, loop_label=" 1 "gimple" } } +! { dg-final { scan-tree-dump-times "to\\\(iterator\\\(integer\\\(kind=4\\\) j3=1:20:1, integer\\\(kind=4\\\) i3=1:10:1, loop_label=" 1 "gimple" } } +! { dg-final { scan-tree-dump-times "to\\\(iterator\\\(integer\\\(kind=4\\\) k3=1:30:1, integer\\\(kind=4\\\) j3=1:20:1, loop_label=" 1 "gimple" } } +! { dg-final { scan-tree-dump-times "to\\\(iterator\\\(integer\\\(kind=4\\\) k3=1:30:1, integer\\\(kind=4\\\) i3=1:10:1, loop_label=" 1 "gimple" } } + \ No newline at end of file diff --git a/gcc/testsuite/gfortran.dg/gomp/target-update-iterators-3.f90 b/gcc/testsuite/gfortran.dg/gomp/target-update-iterators-3.f90 new file mode 100644 index 00000000000..d9c92cf4679 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/target-update-iterators-3.f90 @@ -0,0 +1,23 @@ +! { dg-do compile } +! { dg-options "-fopenmp -fdump-tree-gimple" } + +program test + implicit none + + integer, parameter :: DIM1 = 17 + integer, parameter :: DIM2 = 39 + + type :: array_ptr + integer, pointer :: ptr(:) + end type + + type (array_ptr) :: x(DIM1, DIM2), y(DIM1, DIM2), z(DIM1) + + !$omp target update to (iterator(i=1:DIM1, j=1:DIM2): x(i, j)%ptr(:), y(i, j)%ptr(:)) + !$omp target update from (iterator(i=1:DIM1): z(i)%ptr(:)) +end program + +! { dg-final { scan-tree-dump-times "if \\(i <= 17\\) goto ; else goto ;" 2 "gimple" } } +! { dg-final { scan-tree-dump-times "if \\(j <= 39\\) goto ; else goto ;" 1 "gimple" } } +! { dg-final { scan-tree-dump-times "to\\(iterator\\(integer\\(kind=4\\) j=1:39:1, integer\\(kind=4\\) i=1:17:1, loop_label=, elems=omp_iter_data\\\.\[0-9\]+, index=D\\\.\[0-9\]+\\):MEM <\[^>\]+> \\\[\\\(\[^ \]+ \\\*\\\)D\\\.\[0-9\]+\\\]" 2 "gimple" } } +! { dg-final { scan-tree-dump-times "from\\(iterator\\(integer\\(kind=4\\) i=1:17:1, loop_label=, elems=omp_iter_data\\\.\[0-9\]+, index=D\\\.\[0-9\]+\\):MEM <\[^>\]+> \\\[\\\(\[^ \]+ \\\*\\\)D\\\.\[0-9\]+\\\]" 1 "gimple" } } diff --git a/libgomp/testsuite/libgomp.fortran/target-update-iterators-1.f90 b/libgomp/testsuite/libgomp.fortran/target-update-iterators-1.f90 new file mode 100644 index 00000000000..e9a13a3c737 --- /dev/null +++ b/libgomp/testsuite/libgomp.fortran/target-update-iterators-1.f90 @@ -0,0 +1,68 @@ +! { dg-do run } + +! Test target enter data and target update to the target using map +! iterators. + +program test + integer, parameter :: DIM1 = 8 + integer, parameter :: DIM2 = 15 + + type :: array_ptr + integer, pointer :: arr(:) + end type + + type (array_ptr) :: x(DIM1) + integer :: expected, sum, i, j + + expected = mkarray (x) + + !$omp target enter data map(to: x) + !$omp target enter data map(iterator(i=1:DIM1), to: x(i)%arr(:)) + !$omp target map(from: sum) + sum = 0 + do i = 1, DIM1 + do j = 1, DIM2 + sum = sum + x(i)%arr(j) + end do + end do + !$omp end target + + print *, sum, expected + if (sum .ne. expected) stop 1 + + expected = 0 + do i = 1, DIM1 + do j = 1, DIM2 + x(i)%arr(j) = x(i)%arr(j) * i * j + expected = expected + x(i)%arr(j) + end do + end do + + !$omp target update to(iterator(i=1:DIM1): x(i)%arr(:)) + + !$omp target map(from: sum) + sum = 0 + do i = 1, DIM1 + do j = 1, DIM2 + sum = sum + x(i)%arr(j) + end do + end do + !$omp end target + + if (sum .ne. expected) stop 2 +contains + integer function mkarray (x) + type (array_ptr), intent(inout) :: x(DIM1) + integer :: exp = 0 + + do i = 1, DIM1 + allocate (x(i)%arr(DIM2)) + do j = 1, DIM2 + x(i)%arr(j) = i * j + exp = exp + x(i)%arr(j) + end do + end do + + mkarray = exp + end function +end program diff --git a/libgomp/testsuite/libgomp.fortran/target-update-iterators-2.f90 b/libgomp/testsuite/libgomp.fortran/target-update-iterators-2.f90 new file mode 100644 index 00000000000..2e982bc032c --- /dev/null +++ b/libgomp/testsuite/libgomp.fortran/target-update-iterators-2.f90 @@ -0,0 +1,63 @@ +! { dg-do run } +! { dg-require-effective-target offload_device_nonshared_as } + +! Test target enter data and target update from the target using map +! iterators. + +program test + integer, parameter :: DIM1 = 8 + integer, parameter :: DIM2 = 15 + + type :: array_ptr + integer, pointer :: arr(:) + end type + + type (array_ptr) :: x(DIM1) + integer :: sum, expected + + call mkarray (x) + + !$omp target enter data map(to: x(:DIM1)) + !$omp target enter data map(iterator(i=1:DIM1), to: x(i)%arr(:)) + !$omp target map(from: expected) + expected = 0 + do i = 1, DIM1 + do j = 1, DIM2 + x(i)%arr(j) = (i + 1) * (j + 2) + expected = expected + x(i)%arr(j) + end do + end do + !$omp end target + + ! Host copy of x should remain unchanged. + sum = 0 + do i = 1, DIM1 + do j = 1, DIM2 + sum = sum + x(i)%arr(j) + end do + end do + if (sum .ne. 0) stop 1 + + !$omp target update from(iterator(i=1:DIM1): x(i)%arr(:)) + + ! Host copy should now be updated. + sum = 0 + do i = 1, DIM1 + do j = 1, DIM2 + sum = sum + x(i)%arr(j) + end do + end do + + if (sum .ne. expected) stop 2 +contains + subroutine mkarray (x) + type (array_ptr), intent(inout) :: x(DIM1) + + do i = 1, DIM1 + allocate (x(i)%arr(DIM2)) + do j = 1, DIM2 + x(i)%arr(j) = 0 + end do + end do + end subroutine +end program diff --git a/libgomp/testsuite/libgomp.fortran/target-update-iterators-3.f90 b/libgomp/testsuite/libgomp.fortran/target-update-iterators-3.f90 new file mode 100644 index 00000000000..54b2a6c37c1 --- /dev/null +++ b/libgomp/testsuite/libgomp.fortran/target-update-iterators-3.f90 @@ -0,0 +1,78 @@ +! { dg-do run } +! { dg-require-effective-target offload_device_nonshared_as } + +! Test target enter data and target update to the target using map +! iterators with a function. + +program test + implicit none + + integer, parameter :: DIM1 = 8 + integer, parameter :: DIM2 = 15 + + type :: array_ptr + integer, pointer :: arr(:) + end type + + type (array_ptr) :: x(DIM1) + integer :: x_new(DIM1, DIM2) + integer :: expected, sum, i, j + + call mkarray (x) + + !$omp target enter data map(to: x(:DIM1)) + !$omp target enter data map(iterator(i=1:DIM1), to: x(i)%arr(:)) + + ! Update x on host. + do i = 1, DIM1 + do j = 1, DIM2 + x_new(i, j) = x(i)%arr(j) + x(i)%arr(j) = (i + 1) * (j + 2); + end do + end do + + ! Update a subset of x on target. + !$omp target update to(iterator(i=1:DIM1/2): x(f (i))%arr(:)) + + !$omp target map(from: sum) + sum = 0 + do i = 1, DIM1 + do j = 1, DIM2 + sum = sum + x(i)%arr(j) + end do + end do + !$omp end target + + ! Calculate expected value on host. + do i = 1, DIM1/2 + do j = 1, DIM2 + x_new(f (i), j) = x(f (i))%arr(j) + end do + end do + + expected = 0 + do i = 1, DIM1 + do j = 1, DIM2 + expected = expected + x_new(i, j) + end do + end do + + if (sum .ne. expected) stop 1 +contains + subroutine mkarray (x) + type (array_ptr), intent(inout) :: x(DIM1) + + do i = 1, DIM1 + allocate (x(i)%arr(DIM2)) + do j = 1, DIM2 + x(i)%arr(j) = i * j + end do + end do + end subroutine + + integer function f (i) + integer, intent(in) :: i + + f = i * 2 + end function +end program -- 2.47.3