This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH]: Fix PR tree-optimization/21407


Dale Johannesen <dalej@apple.com> writes:

>> If i give you a pointer to a field, that would mean you could add or
>> subtract bytes and legally get at any other member of the structure,
>> containing structure, etc.
>> I was told by Geoff Keating, and others, that this is not legal in C.
>> Have I been misinformed?  That would be quite sad.
>
> There was quite a bit of discussion of this internal to Apple, and no
> agreement was reached.  Geoff strongly takes the position you say;
> Mike and I disagree.  The standard seems unclear.

There seem to be two different constructs being confused.

Construct 1:

 struct S
 {
   int a;
   int b;
 };

 void foo(int *x)
 {
   x[1] = 23;
 }

 void bar(struct S *s)
 {
   foo (&s->a);
 }

Construct 2:

 struct Base
 {
   int a;
   int b;
 };

 struct Derived
 {
   int c;
   int d;
   struct Base b;
   int e;
   int f;
 };

 void foo(struct Base *b)
 {
   struct Derived *d = 
     (struct Derived *) ((char *)b) - offsetof(struct Derived, b);

   d->c = 23;
 }

 void bar(struct Derived *d)
 {
   foo (&d->b);
 }

[In both cases, assume that the structure definitions are globally
visible, but 'foo' and 'bar' are in different translation units, and
we're not doing IMA.]

It is my opinion that construct 1 is not allowed by the standard - I
draw this opinion principally from 6.5.6p7,8: "&s->a" produces a
pointer which 'behaves the same as a pointer to the first element of
an array [of int] of length one', and foo() proceeds to access the
nonexistent element two of that array.

However, it is my opinion that construct 2 *is* allowed by the
standard.  Obviously nothing fishy is happening up till the first
statement of foo.  foo begins by converting b to a char*, which is an
escape hatch - everything aliases an array of char (6.5p7), and the
pointer is guaranteed to point to the first byte of Derived.b
(6.3.2.3p7).  We then subtract the offset in bytes of Derived.b from
the beginning of struct Derived (7.17p3) and convert back to a struct
Derived* - I read 6.3.2.3 as guaranteeing that this operation recovers
the original pointer passed to bar, which can then be legitimately
dereferenced.

On a practical note, I have never seen anyone use construct 1, but
construct 2 and/or variants appear in GCC's own code as well as in
other important bodies of software - as Mark says, this is how people
fake derived objects in C.  Thus, even if construct 2 is not allowed
by the letter of the standard, we cannot reasonably break it.

On another practical note, it appears impractical to distinguish the
two constructs from bar's point of view - in both cases, other members
of the structure are modified than the one that foo was given a
pointer to.  [One could rewrite foo_1 using the same construct as
foo_2, and its operation is then legitimate.]  I therefore come down
on the side of Mark, Dale, and Mike, in believing that the proposed
optimization is illegitimate.

zw


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]