| Issue |
208588
|
| Summary |
[flang] VOLATILE attribute acquired by host association is ignored in lowering (loads/stores not volatile)
|
| Labels |
flang:fir-hlfir
|
| Assignees |
|
| Reporter |
eugeneepshteyn
|
## Summary
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<volatile>` 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<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
```
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<i32>) -> !fir.ref<i32, volatile>
%3:2 = hlfir.declare %2 {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "_QMmEn"} : (!fir.ref<i32, volatile>) -> ...
```
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
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<T, volatile>` 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
```
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs