Bug 49367 - missed optimization with __restrict field
Summary: missed optimization with __restrict field
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: tree-optimization (show other bugs)
Version: 4.7.0
: P3 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: missed-optimization
Depends on:
Blocks: restrict
  Show dependency treegraph
 
Reported: 2011-06-10 17:20 UTC by Jason Merrill
Modified: 2019-02-23 22:37 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2011-06-12 11:32:54


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jason Merrill 2011-06-10 17:20:54 UTC
GCC fails to optimize away the call to g() in this C testcase.  It should recognize that since we've established that a1 is different from a2, their pointer fields must also be disjoint.  This is necessary to be able to improve optimization of C++ container classes, which currently have worse performance than raw restricted pointers.

typedef struct A
{
  int *__restrict p;
} A;

void g();

void f (A* a1, A* a2)
{
  if (a1 == a2)
    return;

  *a1->p = 0;
  *a2->p = 1;
  if (*a1->p != 0)
    g();
}

int main()
{
  A a,b;
  f (&a,&b);
}
Comment 1 Richard Biener 2011-06-12 11:32:54 UTC
As a1 and a2 are not restrict qualified they may point to the same object
and thus the "two" restrict pointers are based on each other.

Our pointer analysis isn't flow-sensitive so it doesn't see the disambiguating
test.
Comment 2 Jason Merrill 2011-06-13 18:11:04 UTC
(In reply to comment #1)
> As a1 and a2 are not restrict qualified they may point to the same object
> and thus the "two" restrict pointers are based on each other.

Marking them with restrict doesn't help:

typedef struct A
{
  int *__restrict p;
} A;

void g();

void f (A*__restrict a1, A*__restrict a2)
{
  *a1->p = 0;
  *a2->p = 1;
  if (*a1->p != 0)
    g();
}

int main()
{
  A a,b;
  f (&a,&b);
}

Saving the inside pointers into __restrict-qualified temporaries doesn't make it work, either:

typedef struct A
{
  int *__restrict p;
} A;

void g();

void f (A* a1, A* a2)
{
  if (a1 == a2)
    return;
  
  int *__restrict a1p = a1->p;
  int *__restrict a2p = a2->p;

  *a1p = 0;
  *a2p = 1;
  if (*a1p != 0)
    g();
}

int main()
{
  A a,b;
  f (&a,&b);
}

But at this point, if I remove the __restrict from the declaration of p, it works.

typedef struct A
{
  int * p;
} A;

void g();

void f (A* a1, A* a2)
{
  if (a1 == a2)
    return;
  
  int *__restrict a1p = a1->p;
  int *__restrict a2p = a2->p;

  *a1p = 0;
  *a2p = 1;
  if (*a1p != 0)
    g();
}

int main()
{
  A a,b;
  f (&a,&b);
}

It seems rather odd that the __restrict on p would prevent the optimization...
Comment 3 Richard Biener 2011-06-14 12:44:35 UTC
The last example is because with a restrict qualified loaded pointer the
later conversion to a restrict qualified pointer is thrown away.

As for the first example the only case that would work is by passing
the argument in a way that the FE sets DECL_BY_REFERENCE and uses a
restrict qualified pointer for it.  Which it does when the source passes
it by value I think.  That's the only case where we can assume something
about the pointed-to type.
Comment 4 Tom de Vries 2015-09-29 07:56:38 UTC
(In reply to Richard Biener from comment #3)
> As for the first example the only case that would work is by passing
> the argument in a way that the FE sets DECL_BY_REFERENCE and uses a
> restrict qualified pointer for it.  Which it does when the source passes
> it by value I think.  That's the only case where we can assume something
> about the pointed-to type.

The restricts in the first example of comment 2 are effective in current trunk, and the call to g is optimized away.