Hi,

On Wed, 12 Oct 2011, Jakub Jelinek wrote:

> > Assignment 2 means that t->p points to s.p.  Assignment 3 changes t->p and 
> > s.p, but the change to s.p doesn't occur through a pointer based on t->p 
> > or any other restrict pointer, in fact it doesn't occur through any 
> > explicit initialization or assignment, but rather through in indirect 
> > access via a different pointer.  Hence the accesses to the same memory 
> > object at s.p[0] and t->p[0] were undefined because both accesses weren't 
> > through pointers based on each other.
> 
> Only the field p in the structure is restrict qualified, there is no
> restrict qualification on the other pointers (e.g. t is not restrict).
> Thus, it is valid that t points to s.  And, s.p[0] access is based on s.p
> as well as t->p and similarly t->p[0] access is based on s.p as well as
> t->p, in the sense of the ISO C99 restrict wording.

IMO reading the standard to allow an access to be 
based "on s.p _as well as_ t->p" and that this should result in any 
sensible behaviour regarding restrict is interpreting too much into it.  
Let's do away with the fields, trying to capture the core of the 
disagreement.  What you seem to be saying is that this code is 
well-defined and shouldn't return 1:

int foo (int * _a, int * _b)
{
  int * restrict a = _a;
  int * restrict b = _b;
  int * restrict *pa = wrap (&a);                                       
  *pa = _b;         // 1
  *a = 0;
  **pa = 1;
  return *a;
}

I think that would go straight against the intent of restrict.  I'd read 
the standard as making the above trick undefined.

> Because, if you change t->p (or s.p) at some point in between t->p = q; 
> and s.p[0]; (i.e. prior to the access) to point to a copy of the array, 
> both s.p and t->p change.

Yes, but the question is, if the very modification of t->p was valid to 
start with.  In my example above insn 1 is a funny way to write "a = _b", 
i.e. reassigning the already set restrict pointer a to the one that also 
is already in b.  Simplifying the above then leads to:

int foo (int * _a, int * _b)
{
  int * restrict a = _a;
  int * restrict b = _b;
  a = _b;
  *a = 0;
  *b = 1;
  return *a;
}

which I think is undefined because of the fourth clause (multiple 
modifying accesses to the same underlying object X need to go through one 
particular restrict chain).

Seen from another perspective your reading would introduce an 
inconsistency with composition.  Let's assume we have this function 
available:

int tail (int * restrict a, int * restrict b) {
  *a = 0;
  *b = 1;
  return *a;
}

Clearly we can optimize this into { *a=0;*b=1;return 0; } without 
looking at the context.  Now write the testcase or my example above in 
terms of that function:

int goo (int *p, int *q)
{
  struct S s, *t;
  s.a = 1;
  s.p = p;       // 1
  t = wrap(&s);  // 2 t=&s in effect, but GCC doesn't see this
  t->p = q;      // 3
  return tail (s.p, t->p);
}

Now we get the same behaviour of returning a zero.  Something must be 
undefined here, and it's not in tail itself.  It's either the call of 
tail, the implicit modification of s.p with writes to t->p or the 
existence of two separate restrict pointers of the same value.  I think 
the production of two separate equal-value restrict pointers via 
indirect modification is the undefinedness, and _if_ the standard can be 
read in a way that this is supposed to be valid then it needs to be 
clarified to not allow that anymore.

I believe the standard should say something to the effect of disallowing 
modifying restrict pointers after they are initialized/assigned to once.


Ciao,
Michael.

Reply via email to