Side effect of volatile read/write of nullptr

I was watching this c++ lection (it’s in russian).
At around 16:10 the lector asked an open question:

Having this code:

int* foo()
{
    volatile auto a = nullptr;
    int* b = a;
    return b;
}

int main()
{}

Clang generates the following assembly for foo (-Ofast)

    mov     qword ptr [rsp - 8], 0 # volatile auto a = nullptr;
    xor     eax, eax
    ret

Meaning the compiler assumes there is no side effect for reading from a and basically removes int* b = a; part of the code.

GCC on the other hand generates a bit different code

    mov     QWORD PTR [rsp-8], 0 # volatile auto a = nullptr;
    mov     rax, QWORD PTR [rsp-8] # int* b = a;
    xor     eax, eax
    ret

Here compiler believes reading from a does produce the side effect and leaves everything as is.

The question is what is the correct behaviour according to C++20 standard?

>Solution :

The type of a will be volatile std::nullptr_t.

std::nullptr_t has no (or at least is not required to have any) internal state. It could behave like e.g. an empty class. Only the conversion behavior is specified for this type.

There is no need for std::nullptr_t to occupy any specific amount of memory (except the one byte to assure unique addresses) or to store an value in the memory it occupies. So there doesn’t need to be a 0 there to read. The assignment int* b = a; does not depend on any state or value stored in a, only on its type.

I would say both behaviors are correct. What exactly the meaning of the observable side effect of volatile access is is anyway implementation-defined and if std::nullptr_t has the layout of an empty class, then one wouldn’t expect any instruction loading/storing from/to it to be generated for the initialization and the implicit conversion in int* b = a; anyway. But if std::nullptr_t is implemented similar to a pointer which always has value 0, then it would be reasonable to expect a store and load.

Leave a Reply