| 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