Search code examples
c++linuxhardwarevolatile

C++ volatile: guaranteed 32-bit accesses?


In my Linux C++ project, I have a hardware memory region mapped somewhere in the physical address space which I access using uint32_t pointers, after doing a mmap.

The release build of the application crashes with a SIGBUS (bus error).

This is happening because the compiler optimizes accesses to the aforementioned hardware memory using 64-bit accesses, instead of sticking to 32-bit => bus error, the hardware memory can only be accessed using 32-bit reads/writes.

I marked the uint32_t pointer as volatile.

It works. For this one specific code portion at least. Because the compiler is told not to do reordering. And most of the time it would have to reorder to optimize.

I know that volatile controls when the compiler accesses the memory. The question is: does volatile also tell the compiler how to access the memory, i.e. to access it exactly as the programmer instructs? Am I guaranteed that the compiler will always stick to doing 32-bit accesses to volatile uint32_t buffers?

e.g. Does volatile also guarantee that the compiler will be accessing the 2 consecutive writes to the 2 consecutive 32-bit values in the following code-snippet using 32-bit reads/writes as well?

void aFunction(volatile uint32_t* hwmem_array)
{
    [...]

    // Are we guaranteed by volatile that the following 2 consecutive writes, in consecutive memory regions
    // are not merged into a single 64-bit write by the compiler?
    hwmem_array[0] = 0x11223344u;
    hwmem_array[1] = 0xaabbccddu;

    [...]
}

Solution

  • I think I answered my own question, please correct me if I'm wrong.

    C99 standard draft: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf

    Quotes:

    6 An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.3. Furthermore, at every sequence point the value last stored in the object shall agree with that prescribed by the abstract machine, except as modified by the unknown factors mentioned previously.

    Section 5.1.2.3:

    2 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 may produce side effects. At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place. (A summary of the sequence points is given in annex C.)

    5 The least requirements on a conforming implementation are: — At sequence points, volatile objects are stable in the sense that previous accesses are complete and subsequent accesses have not yet occurred.

    Annex C (informative) Sequence points:

    1 The following are the sequence points described in 5.1.2.3:

    […]

    — The end of a full expression: an initializer (6.7.8); the expression in an expression statement (6.8.3); the controlling expression of a selection statement (if or switch) (6.8.4); the controlling expression of a while or do statement (6.8.5); each of the expressions of a for statement (6.8.5.3); the expression in a return statement (6.8.6.4).

    So theoretically we are guaranteed that at the end of any expression involving a volatile object, the volatile object is written/read, as the compiler was instructed to do.