Issue 203890
Summary [flang][OpenMP] `firstprivate` of a fixed-size array lowers copy-in via `_FortranAAssign`, spilling to scratch / failing to link on GPU offload
Labels flang
Assignees
Reporter sbryngelson
    ## Summary

On OpenMP GPU offload, a `firstprivate` clause carrying a small fixed-size array on a register-heavy `target teams distribute parallel do` kernel lowers the per-work-item copy-in through the Fortran runtime descriptor-assignment helper `_FortranAAssign`, emitted *into the target region*, instead of a plain value copy. The consequences on AMDGPU (gfx90a):

- On the released ROCm 7.2.0 flang (LLVM 22.0.0git) the device object fails to link: `ld.lld: error: undefined symbol: _FortranAAssign` (the runtime helper isn't on the device).
- On newer AMD flang drops (LLVM 23.0.0git) it links, but the helper is inlined into the kernel as a large scratch-spilling blob: ~35 KB/work-item scratch, AGPRs pinned at the hardware max, occupancy down to one wave/SIMD, and the kernel runs 30-50x slower.

Carrying the *same data* as scalars, or as a plain `private` array initialized from those scalars, does neither — it lowers to a clean register-resident copy.

## Caveat on scope

I could only verify on AMD's ROCm flang fork (ROCm 7.2.0 and AMD AFAR 23.1.0 / 23.2.0); I don't have an upstream-built flang with working AMDGPU offload to test against. The copy-in lowering that emits `_FortranAAssign` is shared flang OpenMP frontend code, so I expect this affects upstream flang offload as well, but I have not confirmed on a trunk build. Filing here because the lowering is not AMD-fork-specific; AMDGPU-side tracking and full traces are in [ROCm/llvm-project#2909](https://github.com/ROCm/llvm-project/issues/2909).

## Reproducer

One self-contained Fortran file, built five ways (one `-DVARIANT_*` each), plus build/run scripts and raw `LIBOMPTARGET_KERNEL_TRACE` logs:
https://github.com/sbryngelson/compiler-bugs/tree/main/amd/flang-firstprivate-array-occupancy

The kernel arithmetic is byte-identical across all five variants — a register-heavy blob (~90 private real scalars + a few small private arrays through a long dependent chain so nothing folds away). Only the clause carrying the two small integers differs.

## It's `firstprivate`-of-an-array, isolated

A 2x2 over {clause} x {how the array is indexed} separates "dynamic-index forces a spill (expected)" from "the firstprivate-array clause itself":

|                                        | read `a(i)` (dynamic)  | read `merge(a(1),a(2),..)` (constant) |
|----------------------------------------|------------------------|----------------------------------------|
| `firstprivate(a)`                      | **spills, 12% occ**    | **spills, 12% occ**                    |
| `private`, seeded from firstprivate scalars | **0 scratch, 50% occ** | scalars: 0 scratch, 50% occ       |

- A const-indexed `firstprivate` array still spills, so it's not the dynamic index.
- A dynamically-indexed `private` array is fine, so it's not the array storage or the indexing.
- The `private`-seeded-from-firstprivate-scalars variant expresses the *exact* semantics of `firstprivate(a)` by hand and lowers to clean code — so the back end handles the semantics fine; only the `firstprivate(<array>)` spelling takes the `_FortranAAssign` path.

## Measured (AMD AFAR 23.2.0, gfx90a, `LIBOMPTARGET_KERNEL_TRACE=1`)

```
variant                          ns/elem   scratch   AGPR  SGPR-spill  VGPR-spill  occ
baseline, no clause               0.135       0 B       0        0           0      50%
firstprivate(a)  [integer(2)]     6.330   35424 B     256     1155         451      12%
firstprivate(a1, a2)  [scalars]   0.196       0 B       0        0           0      50%
private(p)+firstprivate(scalars)  0.203       0 B       0        0           0      50%
```

`a` is `integer, dimension(2)` = 8 bytes.

## Question for flang/OpenMP folks

Should the `firstprivate` copy-in for a fixed-size (non-allocatable, non-pointer) array be a value copy rather than a `_FortranAAssign` runtime call? The runtime-assign path is what makes it undefined on the device under one toolchain and a scratch-spilling inlined blob under another. Happy to provide FIR/HLFIR/LLVM IR dumps or test a patch.

_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to