[flang] VOLATILE attribute acquired by host association is ignored in lowering (loads/stores not volatile)
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
When a variable acquires the `VOLATILE` attribute **locally through host association** — an internal subprogram or a BLOCK construct whose specification part contains `VOLATILE :: name` naming a host variable (F2023 8.5.20 p2, 19.5.1.4 p1) — flang's semantics resolves it correctly (the `HostAssocDetails` symbol carries `VOLATILE`, `-fdebug-dump-symbols` shows e.g. `n, VOLATILE: HostAssoc => n`), but **lowering drops the volatility entirely**: the `hlfir.declare` gets no `fortran_attrs` and no volatile-qualified type, so LLVM receives ordinary loads/stores and optimizes them as non-volatile. At `-O2` a canonical polling loop is miscompiled into a load-free unconditional infinite loop.
The equivalent **use-associated** case is handled correctly, so this is specific to the host-association path.
### Reproducers
Internal subprogram (`repro-1-hostassoc.f90`):
```fortran
subroutine host
integer :: n = 0
call inner
contains
subroutine inner
volatile :: n
do while (n == 0)
end do
end subroutine
end subroutine
```
BLOCK construct (`repro-2-block.f90`):
```fortran
subroutine blk
integer :: n = 0
block
volatile :: n
do while (n == 0)
end do
end block
end subroutine
```
Controls: a directly declared `integer, volatile :: n` local is handled correctly, and so is the use-associated equivalent (`repro-3-useassoc.f90`):
```fortran
module m
integer :: n = 0
end module
subroutine poll
use m
volatile :: n
do while (n == 0)
end do
end subroutine
```
### Actual behavior
`flang -fc1 -emit-hlfir repro-1-hostassoc.f90`: zero volatile markers. The captured variable's declare inside the internal procedure is plain:
```mlir
func.func private @_QFhostPinner() attributes {fir.host_symbol = @_QPhost, ...} {
%2:2 = hlfir.declare %1 {uniq_name = "_QFhostEn"} : (!fir.ref) -> (!fir.ref, !fir.ref)
```
Same for the BLOCK case (zero volatile markers). Consequently at `flang -S -emit-llvm -O2` the polling loop loses its load entirely — for both reproducers:
```llvm
define void @host_() local_unnamed_addr #0 {
br label %1
1: ; preds = %1, %0
br label %1
}
```
A conforming program whose volatile variable is modified by external means (the entire purpose of F2023 8.5.20) hangs.
### Expected behavior
What the correctly-handled use-association case produces (`repro-3-useassoc.f90`):
```mlir
%2 = fir.volatile_cast %1 : (!fir.ref) -> !fir.ref
%3:2 = hlfir.declare %2 {fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEn"} : (!fir.ref) -> ...
```
and at `-O2` the loop keeps a volatile load:
```llvm
1: ; preds = %1, %0
%2 = load volatile i32, ptr @_QMmEn, align 4, !tbaa !4
%3 = icmp eq i32 %2, 0
br i1 %3, label %1, label %4
```
The host-association shapes should produce the same volatile-qualified accesses within the scoping unit that has the attribute (the host's own accesses correctly stay non-volatile).
### Analysis
(Note: this analysis was generated by AI, requires verification, don't just blindly implement it!)
The volatility of an `hlfir.declare` is driven purely by its `FortranVariableFlagsAttr`: `hlfir::DeclareOp::build`
(`flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp`, via `updateDeclaredInputTypeWithVolatility`) inserts the `fir.volatile_cast` and
volatile-qualifies the type when the flags contain `fortran_volatile`. That attr is computed by `Fortran::lower::translateSymbolAttributes`.
- `Fortran::lower::genDeclareSymbol` (`flang/lib/Lower/ConvertVariable.cpp`, the `fir::ExtendedValue` overload) passes **`sym.GetUltimate()`** to `translateSymbolAttributes`. `GetUltimate()` follows the `HostAssocDetails` link to the host symbol, which legitimately lacks `VOLATILE` — so the locally-acquired attribute is stripped. There is an in-tree FIXME at exactly this spot admitting the problem:
```cpp
// FIXME: Using the ultimate symbol for translating symbol attributes will
// lead to situations where the VOLATILE/ASYNCHRONOUS attributes are not
// propagated to the hlfir.declare (these attributes can be added when
// using module variables).
```
- The sibling static `genDeclareSymbol` overload in the same file (the `mlir::Value base` one) passes the **local** `sym` — which is why the use-association case works: the use-associated symbol's own `attrs()` carry `VOLATILE`.
- The designator lowering has the same pattern: `HlfirDesignatorBuilder` (`flang/lib/Lower/ConvertExprToHLFIR.cpp`) decides
volatility from `designatorNode.get().GetUltimate().attrs()`.
- The comment in `genSymbolType` (`flang/lib/Lower/ConvertType.cpp`, ~L248) still says building the type from the ultimate symbol "relies on the fact volatile and asynchronous are not reflected in fir types" — that premise is stale now that `!fir.ref` exists.
The same `GetUltimate()` stripping applies to a locally-acquired `ASYNCHRONOUS` attribute (per the FIXME), though that has no directly observable IR marker today.
Note: `-fdebug-dump-symbols` confirms semantics is right for both broken shapes (the BLOCK case matches the coverage in
`flang/test/Semantics/symbol05.f90`), so this is purely a lowering issue. A related-but-distinct semantics bug for *submodules* (where even the symbol identity is lost) is #208362; once that is fixed, submodule specification parts will presumably join the shapes affected by this lowering issue.
### Environment
```
flang version 23.0.0git (llvm-project commit d776ee4d4cae)
Target: x86_64-unknown-linux-gnu
Build config: +assertions
```
Contributor guide
Research direction
Start in flang/lib/Lower/ConvertVariable.cpp, especially genDeclareSymbol and its FIXME, then inspect HlfirDesignatorBuilder in flang/lib/Lower/ConvertExprToHLFIR.cpp and genSymbolType in flang/lib/Lower/ConvertType.cpp. Run the supplied host-association reproducers through HLFIR and optimized LLVM emission, comparing them with repro-3-useassoc.f90. Done means host-associated VOLATILE accesses retain volatile-qualified declarations and loads while host-scope accesses remain non-volatile.
Written by the indexing model from the issue text.
Assessment
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 65/100