All of this is described in C11 5.1.2.3
Program execution
/--/
Accessing a volatile object, modifying an object, modifying a file, or calling a function
that does any of those operations are all side effects, which are changes in the state of
the execution environment. Evaluation of an expression in general includes both value
computations and initiation of side effects.
Sequenced before is an asymmetric, transitive, pair-wise relation between evaluations
executed by a single thread, which induces a partial order among those evaluations.
Given any two evaluations A and B, if A is sequenced before B, then the execution of A
shall precede the execution of B.
...
The presence of a sequence point between the evaluation of expressions A and B implies that every value computation and side effect associated with A is sequenced before every value computation and side effect associated with B.
/--/
An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).
/--/
The least requirements on a conforming implementation are:
— Accesses to volatile objects are evaluated strictly according to the rules of the abstract
machine.
This isn't exactly easy to interpret, but what it roughly means in plain English is: since the access of volatile objects is a side effect, the compiler is not allowed to optimize away such accesses, neither is it allowed to sequence them in a different order, which it would otherwise perhaps do in order to get better performance on a CPU with instruction cache/branch prediction, or just better performance because it had certain values conveniently stored in some CPU register.
(The C11 standard also explicitly states that volatile objects aren't guaranteed to be thread-safe, the value of a volatile object after a context switch is unspecified behavior.)
EDIT an example
Given the code
volatile int x;
volatile int y;
volatile int z;
x=i;
y=something;
z=i;
then the compiler is not allowed to re-order the instructions in the executable to
x=i;
z=i;
y=something
because the access to y must be sequenced before the access to z. There is a sequence point at the semi colon. But if the variables had not been volatile, it would be fine for the compiler to re-order them, if it could determine that it would not affect the outcome of the program.