c++pointer-aliasingpointer-conversion

Aliasing and pointer-interconvertability


Given the following code:

struct Tag {};
struct X {
//    Tag t; // if not commented it shouldn't be pointer-interconvertible
    int k;
};

int fn(const X& x, int& p) {
    int i = x.k;
    p = 2;
    return i + x.k;
}

The generated code is:

fn(X const&, int&):
        mov     eax, DWORD PTR [rdi]
        mov     DWORD PTR [rsi], 2
        add     eax, DWORD PTR [rdi]
        ret

Here the compiler assumes aliasing.

If member t is not present, the types X and int are pointer-interconvertible. As so, the compiler must generate code as if the references could alias.

But if member t is present, they should no longer be pointer-interconvertible and code for the non-aliased case should be generated. But in both cases the code is identical except the relative address of member k.

The assembler:

fn(X const&, int&):
        mov     eax, DWORD PTR [rdi+4]
        mov     DWORD PTR [rsi], 2
        add     eax, DWORD PTR [rdi+4]
        ret

As an counter-example

template<typename T>
struct X {int k; };

int fn(X<struct A>& x, X<struct B>& p) {
    int i = x.k;
    p.k = 2;
    return i + x.k;
}

in the above version the generated code assumes no aliasing, but the types are pointer-interconvertible.

fn(X<A>&, X<B>&):
        mov     eax, DWORD PTR [rdi]
        mov     DWORD PTR [rsi], 2
        add     eax, eax
        ret

Can anyone explain this?


Solution

  • Here

    int fn(const X& x, int& p) {
        int i = x.k;
        p = 2;
        return i + x.k;
    }
    

    X::k is int,p is a reference to int. p can be a reference to x.k.

    On the other hand, here:

    int fn(X<struct A>& x, X<struct B>& p) {
        int i = x.k;
        p.k = 2;
        return i + x.k;
    }
    

    X<struct A> and X<struct B> are distinct types. There is no way to have x and p or parts of it refer to the same object.

    But what if k is private and X has operator int() const returning k ?

    Then nothing changes. Sloppy speaking, you need a reference/pointer to get potential aliasing. For example

    struct G {};
    struct H { G* g; }
    
    void foo(G* a,H b);
    

    Here b.g and a can point to the same G (note that this is the case no matter if b is passed by value, reference or pointer). In your example...

    template<typename T>
    struct X {int k; };
    int fn(X<struct A>& x, X<struct B>& p)
    

    .. the only references are x and p. They refer to objects of different types, aka different objects.