[flang][OpenACC] Release declare-create allocatable on deallocate (#220541)
Example:
```fortran
module m
real(8), allocatable, save :: a(:)
!$acc declare create(a)
end module
program p
use m
do i = 1, 3
allocate(a(n))
!$acc update device(a)
deallocate(a) ! device buffer is never released: 8*n
end do ! bytes leak on every iteration
end program
```
For a declare-create allocatable the device copy's lifetime follows the
host allocation, so it has to be released before that allocation is
freed. Lowering wraps each Fortran DEALLOCATE in two hooks, differing
only in when they run:
```
call a_acc_declare_pre_dealloc() ! host storage still allocated
<host deallocate> ! storage freed
call a_acc_declare_post_dealloc() ! host storage already gone
```
Both hooks take the same `fir.address_of(@_QMmEa)`, the address of the
descriptor, so at first glance either could do the release. But the
mapping is keyed on the *data* address held inside that descriptor, not
on the descriptor itself. The descriptor's address is a fixed global and
identical in both hooks; its contents are not, because the deallocation
clears the base address. The pre hook therefore reads a live data
address that resolves to the mapping, while the post hook reads a
cleared one that resolves to nothing.
The hooks are attached accordingly, the pre one where the data address
is read while still valid, the post one where the emptied descriptor is
stored back:
```mlir
%4 = fir.box_addr %3 {acc.declare_action = <preDealloc = ...>}
fir.store %7 to %2#0 {acc.declare_action = <postDealloc = ...>}
```
For a module allocatable no pre hook was emitted at all and the release
was left to the post hook, which runs too late to resolve anything, so
every allocate/deallocate cycle leaks a device buffer.
Before, with no pre hook emitted:
```mlir
func.func @_QMmEa_acc_declare_post_dealloc()
attributes {acc.declare_action} {
%0 = fir.address_of(@_QMmEa) : !fir.ref<!fir.box<...>>
%1 = acc.getdeviceptr varPtr(%0 : ...) dataClause(acc_create)
structured(false) implicit(true) name("a") -> ...
acc.declare_exit dataOperands(%1 : ...)
return
}
```
After, the release moves to the pre hook and the post hook is gone:
```mlir
func.func @_QMmEa_acc_declare_pre_dealloc()
attributes {acc.declare_action} {
%0 = fir.address_of(@_QMmEa) : !fir.ref<!fir.box<...>>
%1 = acc.getdeviceptr varPtr(%0 : ...) dataClause(acc_create)
structured(false) name("a") -> ...
acc.declare_exit dataOperands(%1 : ...)
return
}
```
This keeps one decrement against the one post-alloc increment. Device
address per iteration of the case above:
| | iter 1 | iter 2 | iter 3 |
|---|---|---|---|
| before | `0x..2fa000` | `0x..302000` | `0x..30a000` |
| after | `0x..2fa000` | `0x..2fa000` | `0x..2fa000` |
A module variable now gets neither a post-dealloc recipe nor a
post-dealloc action, since it could do nothing at the point it runs.
Local variables keep theirs, including a local declared inside a module
subprogram. A using unit declares the pre-dealloc recipe, otherwise the
action attached there names a symbol that does not resolve and the
release is silently dropped for separate compilation.
`acc-declare.f90`, `acc-declare-unified.f90` and
`acc-declare-use-associated-allocatable.f90` asserted the post-dealloc
recipe and action for module variables; those expectations are removed.
GitOrigin-RevId: ffa30efb40250e555a23890eb1d22d317b5ba264
Flang is a ground-up implementation of a Fortran front end written in modern C++. It started off as the f18 project (https://github.com/flang-compiler/f18) with an aim to replace the previous flang project (https://github.com/flang-compiler/flang) and address its various deficiencies. F18 was subsequently accepted into the LLVM project and rechristened as Flang.
Please note that flang is not ready yet for production usage.
Read more about flang in the docs directory. Start with the compiler overview.
To better understand Fortran as a language and the specific grammar accepted by flang, read Fortran For C Programmers and flang's specifications of the Fortran grammar and the OpenMP grammar.
Treatment of language extensions is covered in this document.
To understand the compilers handling of intrinsics, see the discussion of intrinsics.
To understand how a flang program communicates with libraries at runtime, see the discussion of runtime descriptors.
If you're interested in contributing to the compiler, read the style guide and also review how flang uses modern C++ features.
If you are interested in writing new documentation, follow LLVM's Markdown style guide.
Consult the Getting Started with Flang for information on building and running flang.