Inferring non-aliasing solely from restrict pointer parameter declarations

109 Views Asked by At

The C language spec (n3096, 6.7.3.1/8) gives the following example

EXAMPLE 2 The function parameter declarations in the following example

void f(int n, int * restrict p, int * restrict q)
{
  while (n-- > 0)
    *p++ = *q++;
}

assert that, during each execution of the function, if an object is accessed through one of the pointer parameters, then it is not also accessed through the other. The translator can make this no-aliasing inference based on the parameter declarations alone, without analyzing the function body.

How can the translator infer this based on the parameter declarations alone?

For example, allow me to massage this example, preserving the parameter declarations, as follows.

int sum;
void f(int n, int * restrict p, int * restrict q)
{
  while (n-- > 0)
    sum += *p++ + *q++;
}

I argue it is now permissible for the same object to be accessed through both p and q, since this object is no longer modified. (Indeed, this seems to be the point of the subsequent EXAMPLE 3 in clause 10.)

My confusion is reinforced in EXAMPLES 5, 6, and 7 (clauses 13, 14, 15). One way to resolve my confusion would be to modify the text in clause 8,

"... if an object is modified and accessed ..."

along with similar changes in the subsequent examples. But to me this seems to change the semantics.

2

There are 2 best solutions below

1
On BEST ANSWER

It is a mistake in the example. Instead of saying the declarations assert that “if an object is accessed through one of the pointer parameters,” it should say “if an object is accessed through one of the pointer parameters and is modified during execution of the function.”

We can tell it is a mistake because the formal definition of restrict, in 6.7.3.1, does not impose any requirements on an object that is not modified (paragraph 4, “If… and X is also modified… then…”), so it is not possible that the declarations assert anything about merely accessing an object (that is accesses that only read the value and do not modify it).

0
On

I argue it is now permissible for the same object to be accessed through both p and q, since this object is no longer modified.

That's not all restrict does. It also guarantees that neither p nor q point at sum. Specifically that the only access of a variable's memory location happens through the restrict qualified pointer. This enables a bunch of modifications not related to modifying the pointed-at contents. Consider this:

int sum;
void f(int n, int * restrict p, int * restrict q)
{
  int tmp_p = *p;
  int tmp_q = *q;

  external_function();

  if(tmp_p == *p)
  {
    sum = tmp_p + tmp_q;
  }
  printf("%d %d\n", *p, *q);
}

restrict enables the following optimizations:

  • The compiler can assume that *p is untouched by the external function so *p need not get loaded from memory anew and the if statement can be optimized away since it is always true.
  • The compiler can assume that writing to sum changed neither *p nor *q so it need not reload them for memory during printing. In fact it can use the values of tmp_p and tmp_q and print those, in case those variables are already conveniently stored in registers etc.
  • The compiler is free to reorder a lot of things in the function, for example it may reorder the access of tmp_p = *p until after the external function call. It may however not reorder the sum = ... part across the function call, because the external function could be updating sum.