[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

aliasing again



Till Straumann <strauman at slac.stanford.edu> writes:
> Sergei Organov wrote:

[...]

>> -fstrict-aliasing should have no effect here, I think, as all the
>> pointers involved have the same type and are therefore allowed to
>> alias each other anyway.
>>
> *wrong* (for the x-th time) -- strict-aliasing *could* have an effect
> here.

Well, I still don't think it should have an effect here, even though I
expressed my reasonings incorrectly indeed.

> For the alias rule it is irrelevant if the two pointers are of the
> same type. What matters is that the *underlying object* matches the
> pointer.

Yes, the type of pointer must match the effective type of the underlying
object.

> In this case, 'p' would *NOT* point to a legal 'struct node',
> hence 'p' would be an illegal alias

[ Note: aliasing rules don't say anything about validity of an object. They talk
  about effective type of an object. ]

I don't see an evidence that 'p' can never point to an object of type
'struct node'. Probably you meant to say that it could be inferred that
both 'n' and 'p' in the last 2 lines can't *simultaneously* point to
objects of type 'struct node', provided '&n->prev == &p->next'. Well,
I'm afraid even that can't be inferred. Consider the following
counter-example:

// Repeat original code for convenience
struct node { struct node *next, *prev; };
void xtract(struct node *x)
{
    struct node *n, *p;
    n = x->next;
    p = x->prev;
    n->prev = p;
    p->next = n;
}

// The example:

struct xxxx  { struct node *a; struct node n; }
union u {
  struct node n;
  struct xxxx x;
};

union u u;

// We get the following layout for 'u':
//   | u.n.next | u.n.prev   |
//   | u.x.a    | u.x.n.next | u.x.n.prev |

u.x.a = NULL;
u.x.n.next = &u.n;
u.x.n.prev = &u.x.n;
xtract(&u.x.n)
// Call expansion:
//    n = u.x.n.next; => n = &u.n;
//    p = u.x.n.prev; => p = &u.x.n;
//    n->prev = p;    => u.n.prev = &u.x.n;
//    p->next = n;    => u.x.n.next = &u.n;

In this case both 'n' and 'p' will point to 2 different objects of type
'struct node' in the last 2 lines of the xtract(), yet 'n->prev' and
'p->next' will have the same address. Therefore, aliasing rules are not
violated here.

Now, one can argue that the code still violates union usage rules in the
standard, and he will be right. But unlike the standard, GCC explicitly
allows type punning through unions, and I think here lies the root of
GCC problem with this code. I believe GCC should not be allowed to
reorder last 2 lines provided type punning through unions is guaranteed
to work. Though, due to GCC guaranties about unions aren't specified
strictly enough, I'd have hard times to argue against an opposite
opinion, I'm afraid.

Disclaimer: the issues involved are indeed rather vague, so I won't be
surprised if I still miss something. Anyway, in my opinion such tricks
should simply be avoided.

-- Sergei.