(5.1.2.3.2): Accessing a volatile object, ..., or calling a function that does any of those operations are all side effects...
(5.1.2.3.3): In the abstract machine, all expressions are evaluated as specified by the semantics. 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).
It does not look like this allows an implementation complete freedom in deciding what constitutes an "access". In fact, I'm pretty certain that 'volatile int i; i;' is required to access i exactly once. (There are plenty of optimizer bugs in this area, but these are bugs.)
(Note that the above has nothing to do with threads and everything with memory-mapped devices. Perhaps you were thinking of threads? These are not part of the C standard, though.)
That seems to me like giving 'complete freedom in deciding what constitutes an "access"'. Or almost.
Actually, chapter 4.10 of GCC 4.4 manual documents how is this defined by GCC and explicitly states, that discarding result of read of volatile object does not always cause access to such object.
What I did say is that Microsoft have chosen to implement volatile access in a way that is incompatible with other compilers. Their choice to put the barriers in for you is certainly well-intentioned, but really just complicates matters.
I don't see what Microsoft could do to unilaterally change meaning of something that is almost completely implementation-defined.