Flang currently lacks support for volatile variables. For some cases, the compiler produces TODO error messages and others are ignored. Some of our tests are like the example from C.4 Clause 8 notes: The VOLATILE attribute (8.5.20) and require volatile variables.
To represent volatile entities, we discussed four solutions. The sub-sections have reasons why I think the first solution is the right one and the other three are not, and some implementation considerations.
1. Ref/box types can be volatile
-
Requires minimal changes. Explicit uses of
ReferenceTypecan remain, but take an extra bool argument for volatility, defaulting to false. -
Volatile refs and regular refs to the same element types will not compare equal.
-
Mishandling a volatile reference type will give us errors instead of silently dropping info (this applies to all the type-based solutions). If an op assumes it takes a non-volatile ref but the input is a volatile ref, we’ll get unrealized conversion casts in the IR instead of silently dropping volatility, as may happen if we represent volatility with an attribute on the ops instead of the type.
-
Memory effects of ops that can take volatile refs can be updated to give read/write effects on an abstract memory resource when the reference type is volatile so MLIR optimizations don’t make bad assumptions about reads/writes to/from volatile memory.
-
Represented as:
`ref` `<` type (`, volatile` $volatile^)? `>`Or if we extend this with an explicitly asynchronous type:
`ref` `<` type (`, volatile` $volatile^)? (`, async` $async^)? `>`
2. Duplicate all reference-like types with a volatile variant
- Something like
fir.volatile_ref<T>/fir.volatile_box<T> - This leads to an explosion in the type system. For example, will we do the same thing for async with
fir.volatile_and_async_ref<T>? What about optional? - Cumbersome - all explicit uses of
ReferenceTypewould need to handle aVolatileReferenceTypedifferently or use the convenience functions likefir::isa_ref_type. This might reduce the likelihood of mishandling volatile types but would be a huge amount of work.
3. Introduce a volatile type that thinly wraps another type
- Leads to ambiguities. When existing code gets the element type of a
fir.ref<fir.volatile<T>>, should it get the volatile type or the inner type? If the former, we would have to check for volatility everywhere we unwrap types before unwrapping another layer to get the “real” element type. This gets worse if/when we add asynchrony and provides no extra benefits, since the user of the type can check for volatility on the reference type instead of unwrapping.
4. Propagate attributes on ops with memory effects on a volatile entity
- Very simple to get started with, but attributes are often lost in transformations/conversions. Each op with memory effects would have to copy over the attributes.
- If an attribute is dropped in a conversion pass, we’ll get no errors and silently do the wrong thing. Flang does this currently - if you declare a local variable to be volatile, the fortran_attrs attribute on the hlfir.declare op will correctly contain volatile, but they will be dropped in hlfir to fir conversion.
- This is how the LLVM dialect handles volatility, but representing volatility in the type allows for more consistent propagation through the compiler, and lowering to LLVM dialect ops with volatile attributes is simple. FIRCG can check if the reference type is volatile when creating a load/store/memcopy/etc.
Most of these ideas are @jeanPerier 's and not my own.
I opened a draft pr for the first solution. Thanks in advance for any feedback!