Because MMIO is made to look like it's really just memory (rather than a technical convenience) C programmers use the MMIO the same way they would the heap memory in the abstract machine. Sometimes the compiler will correctly intuit what needs to actually be emitted, sometimes the hardware they're actually talking to will compensate for what actually happens - other times it just "misbehaves" because this is not memory and so it doesn't behave like memory.