| Issue |
208086
|
| Summary |
[flang][OpenMP] Poor codegen for privatized arrays in parallel reduction loops (aliasing, descriptor overhead, vectorization)
|
| Labels |
flang
|
| Assignees |
cenewcombe
|
| Reporter |
cenewcombe
|
Privatizing a small, constant-shape local array for an OpenMP data-sharing clause
currently wraps it in a descriptor (box) with a per-thread heap allocation in the
privatizer `init` region. That has knock-on costs: alias analysis can no longer
see through the private, so HLFIR bufferization keeps per-iteration array
temporaries, and the loop doesn't vectorize.
Concretely, a loop like:
```fortran
module accel_mod
real(8), allocatable :: a(:,:), x(:,:), zii(:)
contains
subroutine accel(n)
integer :: n, i, j
real(8) :: xx(3), r, fc
!$omp parallel do private(xx, r, fc)
do i = 1, n
do j = 1, n
if (i /= j) then
xx(:) = x(:,i) - x(:,j)
r = sqrt(xx(1)**2 + xx(2)**2 + xx(3)**2)
fc = zii(j) * exp(-r) / r
a(:,i) = a(:,i) + fc*xx(:)
end if
end do
end do
end subroutine
end module
```
`xx(3)` is a compile-time-constant, trivial-element array, but it gets privatized as:
```mlir
omp.private {type = private} @..._private_box_3xf64 : !fir.box<!fir.array<3xf64>>
```
with a `fir.allocmem` in the `init` region (one heap allocation *per thread*). In
the loop body the private is reached through `fir.load` of the box, which
`fir::AliasAnalysis` can't relate to `a`, so it returns `MayAlias`. HLFIR
`OptimizedBufferization` then bails on the `a(:,i) = a(:,i) + fc*xx(:)` elemental and
emits a per-iteration `fir.allocmem`/`fir.freemem` temporary. Across many threads
those allocations serialize on the allocator and inflate barrier/spin time.
For comparison, the same `xx(3)` outside an OpenMP region is a plain
`fir.alloca !fir.array<3xf64>`, alias analysis proves `NoAlias`, and the temporary
is scalarized away.
A hand-written check confirms an unboxed constant-extent private survives
`FIRToLLVM` + `OpenMPToLLVMIRTranslation` cleanly (it becomes
`alloca [3 x double]` per thread), so the "box everything" workaround isn't
required for constant extents.
## Proposed fixes
1. **Alias analysis: recognize a private declare nested in `omp.loop_nest`.**
In `getSource` (`flang/lib/Optimizer/Analysis/AliasAnalysis.cpp`), the private
block argument is owned by the clause-carrying op (e.g. `omp.wsloop`), but the
`hlfir.declare` may be nested one region deeper (in the `omp.loop_nest`).
Resolving the OpenMP op from the block argument's owner rather than the
declare's immediate parent lets the private be recognized as an `Allocate`
source, so `alias(a, xx)` returns `NoAlias` and bufferization scalarizes the
temporary. This also helps pre-existing boxed nested privates.
2. **Lowering: privatize constant-shape trivial arrays unboxed.**
In `privatizeSymbol` (`flang/lib/Lower/Support/Utils.cpp`), only box arrays
that actually need it. A non-firstprivate array with constant extents and a
trivial element type is privatized as a plain `!fir.array` (no descriptor, no
per-thread heap alloc, no `init` region). Dynamic-extent, unknown-shape,
character, derived-type, and firstprivate arrays keep being boxed.
3. **Vectorization of the reduction loop.**
With (1) and (2) the per-iteration temporary is gone, but the loop still runs
scalar: the `a(:,i) = a(:,i) + fc*xx(:)` in-memory strided reduction isn't
recognized as a reduction by LLVM LoopVectorize, and the cost model overweights
the strided column-major gathers versus the cheap vectorizable `exp` call. So
the `exp` over the `j` loop stays scalar where other compilers evaluate it a
vector at a time. This is the largest change of the three and builds on the
first two (the array temporary has to be gone before the loop is even a
candidate); it touches LLVM LoopVectorize reduction recognition and the cost
model.
(1) and (2) are independent and each correct on their own; (1) is the pure
alias-precision improvement and should land first, after which (2)'s unboxed IR
is analyzed correctly from the start. (3) depends on both and comes last.
**Assisted-by:** Copilot
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs