[RFC PATCH] restrict_based_on_field attribute

Michael Matz matz@suse.de
Tue Oct 4 12:05:00 GMT 2011


Hi,

On Mon, 3 Oct 2011, Jakub Jelinek wrote:

> std::vector acts roughly as something having a restrict pointer field,
> i.e. two different std::vector objects will have the pointers pointing to
> a different array, unfortunately unlike e.g. std::valarray we have 3
> different pointers pointing into the array instead of 1, and we can't change
> it without breaking the ABI.  This patch adds an extension, where several
> pointer fields in the same structure can be marked as being a group for
> restrict purposes (so the ISO C99 restrict wording would for them not
> mandate that all accesses are made through pointers based on say _M_start,
> but through pointers based on any of the fields in the group (_M_start,
> _M_finish or _M_end_of_storage in the std::vector case).

Ugh, that's stretching our fragile to unsafe restrict support quite much.  
IMO beyond what we can reasonably be confident to not cause 
miscompilations.  Essentially what you're implicitely assuming is that the 
sources of restrict tags can ultimately be memory, and that this is 
somehow reliable.  It unfortunately isn't.

Keep in mind that the whole current restrict-tag support was designed to 
support fortrans specific needs where we can rely on the compiler 
generating code we most probably (!) won't miscompile later.  Further we 
interpret some of the part where ISO-C says nothing in our favor.  
Together this means that e.g. (intentionally _not_ ISO-C, but sort of 
middle-end speak):

foo (struct s)
{ restrict int *p1 = s.p;
  restrict int *p2 = s.p;
  *p1 = 0; *p2 = 1; return *p1;
}

depends on optimization.  We either generate two restrict tags for p1 and 
p2 (this currently happens when there's no CSE of s.p), or we generate 
only one for p1 and p2 (that's what currently happens with CSE).

Note that both interpretations make sense.  One tag makes sense to make 
the above function work as the user expected (returning 1, not 0).  Two 
tags make sense that this case could be optimized:

foo (struct s)
{ restrict int *p1 = s.p;
  restrict int *p2 = s.p;
  ... accesses of p1[2*i] and p2[2*i+1] intermixed ...
  ... we expect to be able to disambiguate both sets purely because of 
      restrict tags ...
}

We will generate new tags when the source of the load is _not_ restrict 
qualified (conversion that introduce restrict generate new tags).  We will 
generate only one tag when the load _is_ restrict qualified _and_ memory 
CSE has eliminated one of the loads.

In any case, right now the middle end sees memory-loads of restrict 
qualified pointers as source of new restrict tags (and depends on 
optimizations if that isn't what is wanted).  This all works more or less 
for fortran because the array->data pointer usually is loaded only once 
for one array, or at least later memory optimizations come and optimize 
away superflous loads.  For fortran at least we currently _rely_ on such 
memory optimizations to take place in case there are multiple accesses to 
one array (as each of them should better have the same restrict tag).  
Luckily if we disable such optimizations we still can't write 
miscompiled testcases because we would as well disable the transformations 
that make use of the invalid restrict info.  But we certainly should be 
able to create fortran testcases where the restrict tags clearly are 
wrong with disabled memory optimizations.

So, loads generate new tags, and we rely on optimizations that this 
doesn't cause miscompilations via invalid disambiguations, which works 
fine for the restricted set of cases coming from the fortran frontend.

You introduce even more freedom to this mud.  Effectively:

foo (struct s)
{ restrict int *p1 = s.p;
  restrict int *p2 = s.q; // same restrict tag as s.p
}

You're giving the user rope to specify the comment part.  For 
this to not cause miscompilations you absolutely rely on that request to 
not be merely a hint.  The problem is that there's no guarantee that it 
isn't lost:

foo_abstract (restrict *p1, restrict *p2)
{ ... p1 and p2 have different tags ... }
foo { foo_abstract (s.p, s.q); }

or

user does pointer to member games:

foo (struct s)
{ restrict **pp1 = &s.p;
  restrict **pp2 = &s.q;
  ... mem clobber ...
  restrict *p1 = *pp1;
  restrict *p2 = *pp2;  // generate new tag for p2
}

Or our own lowering for MEM_EXPR:

foo (struct s)
{ restrict *p1 = MEM[s, 0];
  restrict *p2 = MEM[s, 4]; // new tag for p2
}

Granted, you're trying to look at the type of s to reconstruct the 
field_decl, but that might be misleading when enough obfuscations are 
present.  For fortran it's impossible to generate any of the breaking 
situations above.

All in all the restrict tag support is extremely fragile when it comes to 
tag source from loads, and deep down even unsafe for generic cases, hence 
I don't think to increase its exposal into that generic direction is a 
good idea.


Ciao,
Michael.



More information about the Gcc-patches mailing list