Often optimizers will frustrate efforts to zero out sensitive data. It can be arbitrarily hard to make sure it happens. Your source code says "zero it", and the compiler, or even the CPU itself, says "nah, waste of time", and doesn't.
There's always ways to control this stuff adequately by setting the properties of memory ranges and the like. All CPU's have to support some equivalent of "volatile" because that's what memory-mapped hardware expects.
Compilers will often delete code that has volatile stores, if it thinks that would be OK. Not everybody agrees with this policy. But they are not who makes decisions about what compilers do.
There "are always ways", but not everybody always knows what they are, or even knows they need to know them. And, what you think ought to suffice too often does not.
How can a compiler remove a volatile store? There exists hardware where a store has a side effect, regardless of the value itself. For example, writing to a FIFO. That’s the entire point of volatile, no?
Right. In particular, a subsequent write to the same address may overwrite (part of) the cache line representing that bit of memory, pushing it to the back of the LRU line. This might happen over and over again, and that cache line then might not get flushed until the end of process's time slice. Anything out in memory backed by that cache line keeps its old value until the writeback finally happens.