if we already know that compiler optimizations can cause issues in such cases, then why don’t we just turn off optimization entirely during compilation?
The guarantees and semantics of the C standard are independent of optimization. The standard does not offer additional guarantees or special semantics for non-optimized code.
This, this and this. The usual explanation that volatile somehow "bypasses optimization" is trying to be helpful, but really isn't. It's an unnecessarily deep compiler-internal detail, which is better left not mentioned, than oversimplified.
C language is, since late 1980's, a high-level abstract programming language defined upon the terms of observable behavior of the C abstract machine. The whole concept of high-level languages is that programmer writes a formal description (computer program in high-level language) about what
result the program is supposed to produce, and the compiler turns it into machine code, trying to do as good job as possible. Doing "good job" can be also called
optimizing, but it really isn't some separate process.
In other words, even if you try to disable all optimizations by modifying compiler parameters, it is still possible that broken code is still broken. Fix: write correct code. That means, using volatile qualifier if it is possible that variable will be modified outside of the usual code flow, or if you need to generate load and store instructions for every access, like you might do with peripheral registers.
Besides, optimizations are very necessary. Performance and size are both pretty important. A large number of real-world MCU projects will just become too big that they do not fit in flash memory, or too slow they can't satisfy the timing needs, if you try to compile them with something like -O0 (on GCC).
So by disabling optimizations, you can't guarantee broken code starts to work reliably (it might by luck), but on the other hand, you sacrifice a lot of performance. This is why it is usually a good idea to optimize either at -Os or -O2 (or even -O3).
Some people find that changing optimization settings is a good way to fight bugs, but I disagree. It is too coarse to even find bugs. And if lower optimization setting successfully hides a bug, then what? You still need to find it - develop good bughunting strategies instead, you will need them.
And indeed, volatile only guarantees memory accesses will be generated instead of "caching" (a very loose term) within the compiler. If you need atomicity, then that's a completely separate subject.