Volatile and Inserted Loads/Stores on MMIO

The language reference says that LLVM optimizers must not change the number of volatile operations or change their order of execution relative to other volatile operations. However, it doesn’t say that optimizers can’t introduce non-volatile operations. Is there any way to write IR that would ensure the generated loads and stores exactly match the number and ordering of the loads and stores in the source IR? I’ve heard conflicting reports about this. I’m specifically interested in manipulating MMIO, where loads and stores may have side effects.

If it is in fact possible to prevent the insertion of loads/stores, does the presence of the “dereferenceable” attribute on pointers have any affect here? Will marking a pointer “dereferenceable” allow loads/stores to a volatile-only-accessed memory location that wouldn’t be allowed otherwise?

Context: this discussion originated in an issue on the Rust unsafe code guidelines issue tracker.

says “Any memory access must be done through a pointer value associated with an address range of the memory access, otherwise the behavior is undefined.” From LLVM’s perspective, we can refer to all the memory which can possibly be accessed according to those rules as “allocated memory”. A non-volatile memory access can only access allocated memory. MMIO registers are not allocated memory; they do not behave the way LLVM expects memory to behave. So a non-volatile load from an MMIO register has undefined behavior. And a pointer marked dereferenceable cannot point to an MMIO register, or the behavior is undefined. volatile accesses are special; they have target-specific semantics, so they can access MMIO registers even though that isn’t allowed for a non-volatile load. In practice, this means your code will do what you want as long as all MMIO accesses are volatile. -Eli

Thanks for the response! I take from that that “dereferencable” isn’t valid in reference to MMIO. It’s still not clear to me, however, what part of the spec guarantees that extra non-volatile loads and stores to MMIO won’t be inserted after llvm sees that a volatile load or store is guaranteed to be performed. Can you clarify that? Is that guarantee actually present? Additionally, is there something that clarifies the “special” behavior you refer to where volatile loads and stores need not operate on allocated memory in the same way that volatile loads and stores are expected to?

Thanks for the response! I take from that that “dereferencable” isn’t valid in reference to MMIO. It’s still not clear to me, however, what part of the spec guarantees that extra non-volatile loads and stores to MMIO won’t be inserted after llvm sees that a volatile load or store is guaranteed to be performed. Can you clarify that? Is that guarantee actually present? Additionally, is there something that clarifies the “special” behavior you refer to where volatile loads and stores need not operate on allocated memory in the same way that volatile loads and stores are expected to?

I don’t believe that the LangRef provides that guarantee. We probably should because, from a hardware perspective, there’s likely no difference between volatile and non-volatile accesses so adding more non-volatile accesses is equivalent, practically, to adding more volatile accesses. It’s also not immediately obvious to me that llvm::FindAvailablePtrLoadStore and llvm::isSafeToLoadUnconditionally skip volatile accesses when scanning, so at least in theory care is needed on this front. It’s not clear to me that this is needed. MMIO regions can be viewed as allocated by the system. -Hal