[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