Issue 203711
Summary [flang][OpenMP][offload] static `declare target` module variable not unified across translation units
Labels flang
Assignees
Reporter sbryngelson
    ## Summary

A Fortran module variable with **static storage** (a scalar or a fixed-size array) marked `!$omp declare target` appears **not to be unified across translation units** on the device under OpenMP target offload.

A host→device update (`!$omp target update to` / `map(to:)`) performed in one translation unit updates only *that* TU's device copy. A `target` region compiled in a **different** TU reads its own device copy, which still holds the initial value. `allocatable` `declare target` variables are **not** affected. The failure is silent (wrong results, no diagnostic).

> **Provenance / scope:** reproduced with AMD's `amdflang` (downstream LLVM flang, ROCm 7.2.0) targeting `gfx90a` on OLCF Frontier. I do **not** have an upstream `flang` OpenMP-GPU-offload build available on that system, so I have **not** verified this against upstream `llvm-project` `flang`. Filing here in case the cause lies in `flang`'s `declare target` lowering (which amdflang derives from) rather than in AMD-specific code. Downstream report: ROCm/llvm-project#2890. If this is purely an AMD device-link issue, please feel free to close/redirect.

## Reproducer

Two source files. A single-file build (one compiler invocation) does **not** reproduce — the update and the kernel read must be in separately compiled objects.

**`repro_mod.f90`** — declares the variables and the kernel that reads them (TU 1):
```fortran
module repro_mod
   implicit none
   integer              :: s_sc       ! static scalar, declare target
   integer              :: s_ar(1)    ! static array,  declare target
   integer, allocatable :: a_ar(:)    ! allocatable,   declare target
   !$omp declare target(s_sc)
   !$omp declare target(s_ar)
   !$omp declare target(a_ar)
contains
   subroutine kernel(out)
      integer, intent(out) :: out(3)
      !$omp target map(from: out)
      out(1) = s_sc       ! expect 2
      out(2) = s_ar(1)    ! expect 2
      out(3) = a_ar(1)    ! expect 2
      !$omp end target
   end subroutine
end module repro_mod
```

**`repro_main.f90`** — sets the variables on the host, pushes them to the device, then calls the kernel (TU 2):
```fortran
program repro_main
   use repro_mod
   implicit none
   integer :: out(3)
   allocate (a_ar(1))
   s_sc = 2
   s_ar = 2
   a_ar = 2
   !$omp target enter data map(to: s_sc, s_ar, a_ar)
   call kernel(out)
   print '(a,i0)', 'static SCALAR s_sc   = ', out(1)
   print '(a,i0)', 'static ARRAY  s_ar(1)= ', out(2)
   print '(a,i0)', 'alloc  ARRAY  a_ar(1)= ', out(3)
   if (out(1) /= 2 .or. out(2) /= 2) error stop 1
end program repro_main
```

**Build and run** (as used with amdflang / ROCm 7.2.0):
```bash
FC=amdflang
CF="-fopenmp --offload-arch=gfx90a -O3 -fopenmp-assume-threads-oversubscription -fopenmp-assume-teams-oversubscription"
$FC $CF -c repro_mod.f90  -o repro_mod.o
$FC $CF -c repro_main.f90 -o repro_main.o
$FC -fopenmp --offload-arch=gfx90a -flto-partitions=16 repro_mod.o repro_main.o -o repro
OMP_TARGET_OFFLOAD=MANDATORY ./repro
```
(For an upstream `flang` + AMDGPU offload toolchain, substitute the equivalent `-fopenmp-targets`/`--offload-arch` flags.)

## Expected vs. actual (amdflang)

```
expected:  static SCALAR s_sc = 2   static ARRAY s_ar(1) = 2   alloc ARRAY a_ar(1) = 2
actual:    static SCALAR s_sc = 0   static ARRAY s_ar(1) = 0   alloc ARRAY a_ar(1) = 2
```

The program `error stop`s because the two static reads return `0`.

## Observations

- A static **scalar** is stale as well as a static array — storage class, not array descriptors or bounds.
- Identical at `-flto-partitions=1` and `-flto-partitions=16` — not LTO partitioning.
- Passing the static variable as a `map(to:)` subroutine argument does **not** help.
- The same source is correct with Cray `ftn` (CCE) and NVIDIA `nvfortran` offload, and was correct before the variable's update and read were split into different modules.

## Suspected cause (inference; not verified at the MLIR/LLVM-IR level)

Consistent with a static `declare target` variable being lowered to a device global resolved by symbol that the device link does not merge across objects (each TU keeps its own device copy), whereas allocatable data is reached through the runtime host→device mapping table (one global table, hence TU-independent). I have not confirmed this against the emitted IR; guidance on where to look in the `declare target` lowering would be appreciated.

## Workaround

Make the variable `allocatable`.

## Environment

- `amdflang` / ROCm 7.2.0; target `gfx90a` (AMD MI250X), OLCF Frontier; `cpe/25.09`, `PrgEnv-amd`
- OpenMP target offload, `OMP_TARGET_OFFLOAD=MANDATORY`

Found in [MFC](https://github.com/MFlowCode/MFC): a static `integer, dimension(2)` index-count array read from a GPU kernel in a different module than its `target update to` silently read as `0`, disabling a viscosity term — a silent wrong-answer regression on the AMD-flang GPU build only.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to