[flang-commits] [flang] [flang][cuda] Back `ALLOCATABLE` components with managed memory under `-gpu=mem:managed` (PR #223087)

Kareem Ergawy via flang-commits flang-commits at lists.llvm.org
Mon Sep 14 03:37:46 PDT 2026


ergawy wrote:

> For managed we implicitly add the attribute in semantic. We should do the same for components.

Thanks for taking a look Valentin. I tried that in this experimental branch: https://github.com/llvm/llvm-project/commits/users/ergawy/managed-components-semantic-experiment/.

To test these changes, I added logs to the compiler to see how it behaves when one component is allocatable and one is not. The test is the following:

```probe.c
#include <cuda_runtime.h>
#include <stdio.h>

void probe_ptr(const void *p, const char *tag) {
  struct cudaPointerAttributes a;
  cudaError_t e = cudaPointerGetAttributes(&a, p);
  if (e != cudaSuccess) {
    printf("  %-10s ptr=%p  cudaPointerGetAttributes FAILED: %d (%s)\n", tag, p,
           (int)e, cudaGetErrorString(e));
    /* Clear the sticky error so later CUDA calls still work. */
    cudaGetLastError();
    return;
  }
  const char *k = "?";
  switch (a.type) {
  case cudaMemoryTypeUnregistered:
    k = "UNREGISTERED (plain host malloc)";
    break;
  case cudaMemoryTypeHost:
    k = "HOST (pinned)";
    break;
  case cudaMemoryTypeDevice:
    k = "DEVICE";
    break;
  case cudaMemoryTypeManaged:
    k = "MANAGED";
    break;
  }
  printf("  %-10s ptr=%p  type=%d %-34s device=%d devPtr=%p hostPtr=%p\n", tag,
         p, (int)a.type, k, a.device, a.devicePointer, a.hostPointer);
}
```

```probe.f90
module m
  implicit none
  type :: t
     real :: fixed(4)             ! must stay UNREGISTERED (plain host)
     real, allocatable :: alc(:)  ! its data must be MANAGED
  end type t
end module m

program p
  use iso_c_binding
  use m
  implicit none

  interface
     subroutine probe_ptr(ptr, tag) bind(C, name="probe_ptr")
       import :: c_ptr, c_char
       type(c_ptr), value :: ptr
       character(kind=c_char), dimension(*) :: tag
     end subroutine probe_ptr
  end interface

  type(t), target :: obj

  allocate(obj%alc(64))

  call probe_ptr(c_loc(obj%fixed), "fixed -> want UNREGISTERED"//c_null_char)
  call probe_ptr(c_loc(obj%alc),   "alc   -> want MANAGED"//c_null_char)
end program p
```

---

Compiler output:

```shell
  >>>> ComponentDecl  type='t' comp='fixed' allocatable=0 pointer=0 existingAttr=<none> ==> left alone
  >>>> ComponentDecl  type='t' comp='alc'   allocatable=1 pointer=0 existingAttr=<none> ==> SET Managed
  >>>> needCUDAAlloc  object='obj' objectAttr=<none> type='t'
       ==> true, because component 'alc' is device-allocatable.
           The WHOLE object goes to cuf.alloc, including its non-allocatable components.
```

So eventually the whole object is marked for managed memory even though we only attribute `alc` in semantics.

And runtime logs show this is actually the case too:

```shell
  fixed -> want UNREGISTERED ptr=0x7ff46e000000  type=3 MANAGED                            device=0 devPtr=0x7ff46e000000 hostPtr=0x7ff46e000000
  alc   -> want MANAGED ptr=0x7ff442000080  type=3 MANAGED                            device=0 devPtr=0x7ff442000080 hostPtr=0x7ff442000080
```

---

I think this incorrect behavior. Therefore, I think the "allocatable-local" approach taken in this PR is a more proper one. Let me know if I missed something or misunderstood your suggestion.

https://github.com/llvm/llvm-project/pull/223087


More information about the flang-commits mailing list