llvm / llvm/llvm-project

[flang][OpenMP] The schedule clause is ignored for target offload

Open
#214,303 0 comments 0 reactions 0 assignees View on GitHub
flang:openmp
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

The `schedule` clause has no effect on a target construct in flang. Every schedule kind and chunk
size produces identical code, and the iteration-to-thread mapping is always that of
`schedule(static,1)`.

`schedule(static,C)` prescribes which thread runs which iteration, so the mapping is directly
observable. Recording `omp_get_thread_num()` per iteration with `num_teams(1) thread_limit(8)` and
32 iterations, on gfx90a:

| clause | clang, the C equivalent | flang |
|---|---|---|
| `schedule(static,1)` | `0 1 2 3 4 5 6 7 0 1 ...` | `0 1 2 3 4 5 6 7 0 1 ...` |
| `schedule(static,4)` | `0 0 0 0 1 1 1 1 ...` | `0 1 2 3 4 5 6 7 ...` |
| `schedule(static,8)` | `0 0 0 0 0 0 0 0 ...` | `0 1 2 3 4 5 6 7 ...` |
| none | `0 1 2 3 4 5 6 7 ...` | `0 1 2 3 4 5 6 7 ...` |

Deterministic: one unique output over 20 runs of each.

```fortran
program p
use omp_lib
implicit none
integer, parameter :: n = 32
integer :: tid(n), i
tid = -1
!$omp target teams distribute parallel do num_teams(1) thread_limit(8) schedule(static,4) map(tofrom:tid)
do i = 1, n
tid(i) = omp_get_thread_num()
end do
write(*,'(A,32I3)') " tid: ", tid
end program
```

At the IR level every variant emits a byte-identical call. `schedule(static,8)`,
`schedule(dynamic)`, `schedule(dynamic,4)`, `schedule(guided)`, `schedule(runtime)`,
`schedule(monotonic:static,4)`, `schedule(nonmonotonic:dynamic)`, `schedule(simd:static,4)`,
`dist_schedule(static,4)` and no clause at all all produce:

```
call void @__kmpc_distribute_for_static_loop_4u(..., i32 128, i32 %2, i32 0, i32 0, i8 0)
num_iters nthr blockchunk threadchunk
```

clang emits `__kmpc_dispatch_init_4` / `__kmpc_dispatch_next_4` for `dynamic` and `guided`.

## Cause

`OpenMPIRBuilder::applyWorkshareLoop` receives the schedule information and the device path drops it
(`llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp`):

```cpp
if (Config.isTargetDevice())
return applyWorkshareLoopTarget(DL, CLI, AllocaIP, LoopType, NoLoop);
```

`SchedKind`, `ChunkSize`, `HasDistSchedule`, `DistScheduleChunkSize` and the simd/monotonic
modifiers are all discarded, and `applyWorkshareLoopTarget` always passes `block_chunk = 0` and
`thread_chunk = 0`.

`HasOrderedClause` was dropped by the same line, which is #214257; #214263 fixes that one by falling
through to the generic path when `ordered` is present. The same approach may extend here, but a
non-default `schedule` on device is a performance decision rather than a correctness one, so it
seemed worth filing separately rather than widening that patch.

A side effect: `StaticLoopChunker::NormalizedLoopNestChunked` in the DeviceRTL is unreachable from
flang, since the chunk arguments are always zero.

## Scope

The chunk being ignored is deterministic and observable, as above. `schedule(dynamic)` also
collapsing to static is clear in the IR, but I could not demonstrate a runtime difference: on a
deliberately load-imbalanced loop clang's `dynamic` was no faster than its `static` on this GPU, so
I am not claiming a performance impact for that part.

Contributor guide

Open the contributing guide

Research direction

Start in llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp, reading applyWorkshareLoop and applyWorkshareLoopTarget, then compare the generated IR for the Fortran reproducer's schedule clauses. Done means device compilation preserves the relevant schedule and chunk information instead of always emitting zero chunks; verify this with the shown runtime mapping and IR call.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, fortran
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.