Bug 89479 - __restrict on a pointer ignored when disambiguating against a call
Summary: __restrict on a pointer ignored when disambiguating against a call
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: tree-optimization (show other bugs)
Version: 8.3.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: alias, missed-optimization
: 94416 104574 (view as bug list)
Depends on:
Blocks: restrict
  Show dependency treegraph
 
Reported: 2019-02-23 22:39 UTC by Eyal Rozenberg
Modified: 2022-02-16 21:56 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2019-02-26 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Eyal Rozenberg 2019-02-23 22:39:19 UTC
(This is all illustrated at: https://godbolt.org/z/nz2YXE )

Let us make our language C++17. Consider the following function:

  int foo(const int* x, void g())
  {
      int result = *x;
      g();
      result += *x;
      return result;
  }

since we have no aliasing guarantees, we must assume the invocation of g() may change the value at address x, so we must perform two reads from x to compute the result - one before the call and one after.

If, however, we add __restrict__ specifier to x:

  int bar(const int* __restrict__ x, void g())
  {
      int result = *x;
      g();
      result += *x;
      return result;
  }

we may assume x "points to an unaliased integer" (as per https://gcc.gnu.org/onlinedocs/gcc/Restricted-Pointers.html ). That means we can read from address x just once, and double the value to get our result. I realize there's a subtle point here, which is whether being "unaliased" also applies to g()'s behavior. It is my understanding that it does.

Well, clang 7.0 understands things they way I do, and indeed optimizes one of the reads away in `bar()`. But - g++ 8.3 (and g++ "trunk", whatever that means on GodBolt) doesn't do so, and reads _twice_ from x both in `foo()` and in `bar()`.
Comment 1 Marc Glisse 2019-02-23 22:55:05 UTC
Seems similar enough.

*** This bug has been marked as a duplicate of bug 81009 ***
Comment 2 Eyal Rozenberg 2019-02-24 07:44:02 UTC
(In reply to Marc Glisse from comment #1)
> Seems similar enough.

With respect  - this is not about x being a const __restrict pointer; what I said (including the clang behavior) applies exactly the same when we remove the const. See: https://godbolt.org/z/hH643a (where the const is gone).
Comment 3 Marc Glisse 2019-02-24 08:15:53 UTC
Hmm indeed they are different (it would have been clearer to omit const in the initial testcase). I couldn't find an obvious duplicate in bug 49774.
Comment 4 Richard Biener 2019-02-26 09:01:18 UTC
I believe there's a dup somewhere.  Basically function calls are not
annotated with {base,clique} (our implementation detail exposing __restrict
to the IL).  To elaborate further to successfully mark a function call
with clique == 1 and base == 0 we have to prove the pointer marked restrict
doesn't escape the function through calls since I belive that for

int *p;
void g() { *p = 1; }
void f(int *q) { p = q; }

int foo(const int* x, void g())
  {
      f(x);
      int result = *x;
      g();
      result += *x;
      return result;
  }

the access in g is still based on x since the above is equivalent to

  int foo(const int* x, void g())
  {
      int *p = x;
      int result = *x;
      *p = 1;
      result += *x;
      return result;
  }

letting aside eventual issues with the const qualification of *x.
Comment 5 Eyal Rozenberg 2019-02-26 10:16:26 UTC
(In reply to Richard Biener from comment #4)
> exposing __restrict to the IL). 

Is "IL" an acronym for "Intermediate Language"? Remember many bug posters/readers are not GCC developers and don't know all the lingo.

> To elaborate further to successfully mark a function call
> with clique == 1 and base == 0 we have to prove the pointer marked restrict
> doesn't escape the function through calls

Certainly, calling g() could be just the same as writing to an alias of the x pointer. But - __restrict is how we guarantee this doesn't happen (or can be ignored) even when the compiler can't prove that's the case on its own. So I'm not sure I understand what you're suggesting with your comment. I suppose you could try and "disprove the __restrict" to give a warning, but other than that - why not just respect it?
Comment 6 Eyal Rozenberg 2019-02-26 10:57:04 UTC
Thanks to a friendly StackOverflow user, I should also report that (about) the same code produces the same compiler behavior disparity for proper C:

https://godbolt.org/z/kVYqp8

with the slight modification being `void g(void)` instead of `void g()` in the function signatures.
Comment 7 rguenther@suse.de 2019-02-26 14:03:07 UTC
On Tue, 26 Feb 2019, eyalroz at technion dot ac.il wrote:

> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89479
> 
> --- Comment #5 from Eyal Rozenberg <eyalroz at technion dot ac.il> ---
> (In reply to Richard Biener from comment #4)
> > exposing __restrict to the IL). 
> 
> Is "IL" an acronym for "Intermediate Language"? Remember many bug
> posters/readers are not GCC developers and don't know all the lingo.

Yes.

> > To elaborate further to successfully mark a function call
> > with clique == 1 and base == 0 we have to prove the pointer marked restrict
> > doesn't escape the function through calls
> 
> Certainly, calling g() could be just the same as writing to an alias of the x
> pointer. But - __restrict is how we guarantee this doesn't happen (or can be
> ignored) even when the compiler can't prove that's the case on its own. So I'm
> not sure I understand what you're suggesting with your comment. I suppose you
> could try and "disprove the __restrict" to give a warning, but other than that
> - why not just respect it?

Well, "respecting" it means encoding it in the intermediate language
which we don't at the moment for calls.
Comment 8 Eric Gallager 2019-05-23 05:04:38 UTC
(In reply to Eyal Rozenberg from comment #2)
> (In reply to Marc Glisse from comment #1)
> > Seems similar enough.
> 
> With respect  - this is not about x being a const __restrict pointer; what I
> said (including the clang behavior) applies exactly the same when we remove
> the const. See: https://godbolt.org/z/hH643a (where the const is gone).

OK, but even if it's not a dup, I still think it's related enough to go under "See Also"
Comment 9 Richard Biener 2020-03-31 07:49:44 UTC
*** Bug 94416 has been marked as a duplicate of this bug. ***
Comment 10 Andrew Pinski 2022-02-16 21:37:27 UTC
*** Bug 104574 has been marked as a duplicate of this bug. ***
Comment 11 Marc Nieper-Wißkirchen 2022-02-16 21:56:35 UTC
(In reply to Richard Biener from comment #1 of bug 94416)
> I think there's a duplicate somewhere.  We currently cannot encode "restrict"
> into the "accesses" implied by a call.
> 
> Note there's slight complication when g (b) eventually recurses into 'f'
> passing this 'b' as 'a'.  Recursion makes the interpretation of the
> lexically defined restrict concept a bit weird.
> 
> So I think this bug can be closed as duplicate of the "restrict and calls"
> bug.
> 
> *** This bug has been marked as a duplicate of bug 89479 ***

What do you mean by that the restrict concept is lexically defined? If I read the standard correctly, restrict makes an assumption about the dynamic extent of an executed block. This seems obvious from section 6.7.3.1 (of ISO C18) which speaks of the block of main in paragraph 2 and the lifetime of objects in paragraph 5.