Aliasing and pointer-interconvertability

299 Views Asked by At

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?

2

There are 2 best solutions below

7
463035818_is_not_an_ai On BEST ANSWER

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.

9
Michael Kenzel On

Here

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

the compiler has to assume that p may be a reference to x.k. Both p and x.k are lvalues of type int. Thus, they may be aliasing each other. Whether X is pointer-interconvertible with an int or not does not change the fact that p may be a reference to x.k.

Here

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

on the other hand, X<struct A> and X<struct B> are completely unrelated types. x and p cannot be references to the same object. Thus, x.k and p.k cannot denote the same subobject. The fact that X<struct A> as well as X<struct B> are both pointer-interconvertible with an int is, again, irrelevant…