public inbox for fortran@gcc.gnu.org
 help / color / mirror / Atom feed
From: Thomas Schwinge <thomas@codesourcery.com>
To: Tobias Burnus <tobias@codesourcery.com>,
	Jakub Jelinek <jakub@redhat.com>,
	Julian Brown <julian@codesourcery.com>
Cc: <gcc-patches@gcc.gnu.org>, <fortran@gcc.gnu.org>
Subject: Re: [Patch] OpenMP: Handle descriptors in target's firstprivate [PR104949]
Date: Tue, 28 Feb 2023 11:56:01 +0100	[thread overview]
Message-ID: <87o7pe12ke.fsf@euler.schwinge.homeip.net> (raw)
In-Reply-To: <f61da0e4-1298-1808-026f-52a26d1278bd@codesourcery.com>

Hi!

I'm currently reviewing 'gomp_copy_host2dev', 'ephemeral' in a different
context, and a question came up here;
commit r13-706-g49d1a2f91325fa8cc011149e27e5093a988b3a49
"OpenMP: Handle descriptors in target's firstprivate [PR104949]":

On 2022-05-11T19:33:00+0200, Tobias Burnus <tobias@codesourcery.com> wrote:
> this patch handles (for target regions)
>    firstprivate(array_descriptor)
> by not only firstprivatizing the descriptor but also the data
> it points to. This is done by turning it in omp-low.cc the clause
> into
>    firstprivate(descr) firstprivate(descr.data)
> and then attaching the latter to the former. That works by
> adding an 'attach' after the last firstprivate (and checking
> for it in libgomp). The attached-to device address for a
> previous (here: the first) firstprivate is obtained by returning
> the device address inside the hostaddrs[i] alias omp_arr array,
> i.e. the compiler generates:
>    omp_arr.1 = &descr;  /* firstprivate */
>    omp_arr.2 = descr.data;  /* firstprivate */
>    omp_arr.3 = &omp_arr.1;  /* attach; bias: &desc.data-&desc */
> and libgomp then knows that the device address is in the
> pointer.

> Note: The code is not active for OpenACC. The existing code uses, e.g.,
> 'goto oacc_firstprivate' – thus, the new code would be
> partially active. I went for making it completely inactive for OpenACC
> by adding one '!is_gimple_omp_oacc'.

ACK.

> I bet that a deep copy would be
> also useful for OpenACC, but I have neither checked what the current
> code does nor what the OpenACC spec says about this.

Instead of adding corresponding handling to the OpenACC 'firstprivate'
special code paths later on, I suggest that we first address known issues
with OpenACC 'firstprivate' -- which probably may largely be achieved by
in fact removing those 'goto oacc_firstprivate's and other special code
paths?  For example, see <https://gcc.gnu.org/PR92036>
"OpenACC 'firstprivate' clause: initial value".

That means, the following code currently isn't active for OpenACC, and
given that OpenMP 'target' doesn't do asynchronous device execution
(meaning: not in the way/implementation of OpenACC 'async'), it thus
doesn't care about the 'ephemeral' argument to 'gomp_copy_host2dev', but
still, for correctness (and once that code gets used for OpenACC):

> 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.

> --- a/libgomp/target.c
> +++ b/libgomp/target.c
> @@ -1350,7 +1350,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);

Here, passing 'ephemeral <- false' is correct, as 'h <- hostaddrs[i]'
points to non-ephemeral data.

> +             /* 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);

Here, we modify 'hostaddrs[i]' (itself -- *not* the data that the
original 'hostaddrs[i]' points to), so the above 'gomp_copy_host2dev'
with 'ephemeral <- false' is still correct, right?

>               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);

However, 'h <- &target' here points to data in the local frame
('target'), which potentially goes out of scope before an asynchronous
'gomp_copy_host2dev' has completed.  Thus, don't we have to pass here
'ephemeral <- true' instead of 'ephemeral <- false'?  Or, actually
instead of '&target', pass '&hostaddrs[i]', which then again points to
non-ephemeral data?  Is the latter safe to do, or are we potentially
further down the line modifying the data that '&hostaddrs[i]' points to?
(I got a bit lost in the use of 'hostaddrs[i]' here.)

> +                 ++i;
> +               }
>               continue;
>             case GOMP_MAP_FIRSTPRIVATE_INT:
>             case GOMP_MAP_ZERO_LEN_ARRAY_SECTION:
> @@ -2517,6 +2534,11 @@ copy_firstprivate_data (char *tgt, size_t mapnum, void **hostaddrs,
>       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;
> +       }
>        }
>  }

For reference, the 'gomp_copy_host2dev' code that I highlighted above
still triggers for the following test cases only:

> --- /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

> --- /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

> --- /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


Grüße
 Thomas
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

  parent reply	other threads:[~2023-02-28 10:56 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-11 17:33 Tobias Burnus
2022-05-19 13:59 ` Jakub Jelinek
2022-05-23  9:00   ` Tobias Burnus
2023-02-28 10:56 ` Thomas Schwinge [this message]
2023-03-24 16:18   ` Add caveat/safeguard to OpenMP: Handle descriptors in target's firstprivate [PR104949] (was: [Patch] OpenMP: Handle descriptors in target's firstprivate [PR104949]) Thomas Schwinge

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87o7pe12ke.fsf@euler.schwinge.homeip.net \
    --to=thomas@codesourcery.com \
    --cc=fortran@gcc.gnu.org \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jakub@redhat.com \
    --cc=julian@codesourcery.com \
    --cc=tobias@codesourcery.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).