[OpenMP] Publish target-init team state with acq_rel barrier (#208701)

In __kmpc_target_init, thread 0 initializes the team state in LDS
(HasThreadState = 0, ThreadStates = nullptr) and then releases the other
threads with synchronize::threadsAligned(atomic::relaxed). Because the
relaxed ordering emits no memory fence, the initializing LDS stores are
not guaranteed to be visible to the worker threads before they proceed.
A worker thread can then read the uninitialized team state and produce
wrong results or crash. This is the flow that causes the race as I
understand it.

```
__kmpc_target_init                                    (Kernel.cpp:107)
  -> initializeRuntime                                (Kernel.cpp:38)
       -> state::init                                         (State.cpp:286)
            -> TeamState.init(IsSPMD)                 (State.cpp:295)
                 -> HasThreadState = false            (State.cpp:248)   // <-- the value later read stale
               ThreadStates = nullptr                 (State.cpp:296)
  -> synchronize::threadsAligned(atomic::relaxed)     (Kernel.cpp:115)  // <-- insufficient barrier:
     //     relaxed emits no fence, so the state stores above are not published to the other threads

        ... worker threads continue past the barrier ...

__kmpc_parallel_60                                    (Parallelism.cpp:143)
  if (OMP_UNLIKELY(!if_expr || state::HasThreadState || ...))   (Parallelism.cpp:158)
        ^ reads HasThreadState from LDS; a stale/unpublished value (!= 0)
          diverts the worker onto the wrong (serialized) path
```
The fix changes the barrier at Kernel.cpp:115 from atomic::relaxed to
atomic::acq_rel so that the memory fences are emitted.

Reproducing the problem requires an `if` clause with a non-constant
condition on the target region. The worker's read of HasThreadState
originates from the serialized-region check in __kmpc_parallel_60. When
the `if` condition is constant (or absent), the optimizer can prove that
branch is dead and eliminates the HasThreadState load entirely, so the
race has nothing to observe. A non-constant `if` keeps if_expr, and
hence the LDS read alive, exposing the problem.

I have added a Fortran offload regression test that launches a target
region many times to make the timing-dependent race likely to surface.

Fixes https://github.com/llvm/llvm-project/issues/208698.

Assisted-by: Cursor
GitOrigin-RevId: abd72b0eb7807a98c261a6cac35a969f954a76cd
1 file changed