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

Reply via email to