6

Most definitions of restrict say that it's a promise from the programmer to the compiler that for the lifetime of the pointer, the pointer is the only way that object is accessed. This allows the compiler to optimize the output, because it knows that it will be accessed only by one pointer and thus can be changed only by it. Which if I understand correctly usually means that the program doesn't have to reload the value the pointer points to.

If this is correct then there should be some exceptions when the restrict keyword should be usable even if it goes against the intent of how it should be used.

One thing that comes to mind would be when the data the pointer points to never actually changes during the lifetime of the pointer. In such case there is no need to reload the data even if the pointers point to the same location, because they don't change in the lifetime of the pointers. E.g.:

int max(int *restrict a, int *restrict b) {
  return((*a > *b) ? *a : *b);
}

int main(void) {
  int num = 3;
  int max = max(&num, &num);
}

Is this a valid use of restrict even though it goes against how it was supposed to be used? Will using the restrict keyword like this result in undefined behaviour?

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
The Red Fox
  • 810
  • 6
  • 12
  • More precisely the contract is: for the the lifetime of the pointer, the pointee will not be *changed* by accesses through another pointer. If the pointees are not modified, this is trivially satisfied, but it also means that the `restrict` contributes nothing useful to the program and may as well be omitted. – Casey Aug 05 '13 at 14:45
  • Even if the thing pointed to by `restrict` will never modified, the `restrict` would let the compiler to "read ahead" in ways it could not do otherwise. – supercat Apr 29 '15 at 15:44

2 Answers2

4

As Eric says the in a comment that is now gone, the key phrase from the C99 draft standard 6.7.3.1 Formal definition of restrict is:

`If… X is also modified…`

this interpretation is supported by this example in 6.7.3.1/10:

void h(int n, int * restrict p, int * restrict q, int * restrict r)
{
  int i;
  for (i = 0; i < n; i++)
    p[i] = q[i] + r[i];
}

and the following comment with the code sample:

illustrate how an unmodified object can be aliased through two restricted pointers. In particular, if a and b are disjoint arrays, a call of the form h(100, a, b, b) has defined behavior, because array b is not modified within function h.

So it would seem that your specific example is defined behavior since you are not modifying a or b.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • Note: the text in N1570 6.7.3/8, and the text surrounding the example in 6.7.3.1/8, say that it is UB to just access (i.e. read). I guess we are concluding that those texts are defective; and the formal definition should be used. – M.M Apr 01 '15 at 13:01
  • @MattMcNabb I don't read `6.7.3/8` to say that and in `6.7.3.1/8` I believe we are meant to read it in the context of *during each execution of the function* which in this specific case involves a modification. – Shafik Yaghmour Apr 01 '15 at 13:07
  • 6.7.3/8 says "This association [...] requires that all accesses to that object use, directly or indirectly, the value of that particular pointer". Reading is an access, so if the associated object is read by a different pointer then the `restrict` association is violated. (According to that quote). – M.M Apr 01 '15 at 13:09
  • @MattMcNabb sure but it say *This association, defined in 6.7.3.1 below*, so I would take the more specific quote over the general quote. I can see how it seems to conflict, perhaps it is a defect, not sure I had not noticed that before. – Shafik Yaghmour Apr 01 '15 at 13:11
  • But the 6.7.3/8 quite is just wrong; the summary contradicts the specific description. It should say "unless the object is not modified" or something to that effect. – M.M Apr 01 '15 at 13:17
  • @MattMcNabb agreed, perhaps it was originally correct and through revisions they ended up out of sync but I agree they do seem to contradict each other. – Shafik Yaghmour Apr 01 '15 at 13:19
4

You may sometimes use restrict-qualified pointers to access the same objects as other pointers, but only if the pointed-to objects are not modified. Here are C 2011 (N1570) 6.7.3.1 paragraphs 1-3 and the first part of paragraph 4 interspersed with how they apply to the code in the question.

6.7.3.1 Formal definition of restrict

1 Let D be a declaration of an ordinary identifier that provides a means of designating an object P as a restrict-qualified pointer to type T.

So int * restrict a is such a declaration D. When max is called with max(&num, &num);, the object P is num (or, more formally, the object named by num), and T is int. Similarly, int * restrict b is another such declaration.

2 If D appears inside a block and does not have storage class extern, let B denote the block. If D appears in the list of parameter declarations of a function definition, let B denote the associated block. Otherwise, let B denote the block of main (or the block of whatever function is called at program startup in a freestanding environment).

These declarations appear in the parameter declarations of a function definition, so B is the block of the function definition, that is, the body of max.

3 In what follows, a pointer expression E is said to be based on object P if (at some sequence point in the execution of B prior to the evaluation of E) modifying P to point to a copy of the array object into which it formerly pointed would change the value of E.137) Note that ‘‘based’’ is defined only for expressions with pointer types.

The function max contains pointer expressions a and b, twice each, so these are each an instance of a pointer expression E. These expressions depend on the parameters a and b, respectively, because if we changed a to point to a copy of num instead of pointing to num, then a would have a different value (obviously), and similarly for b. (Although num is a scalar object, it acts like an array containing a single element for purposes of pointer arithmetic.)

4 During each execution of B, let L be any lvalue that has &L based on P. If L is used to access the value of the object X that it designates, and X is also modified (by any means), then the following requirements apply:…

During the execution of max, the lvalue *a has its address (&*a, which is a) based on P (a), so the lvalue *a is an instance of L. This lvalue is used to access num, so num is an instance of an object X. However num is never modified during the execution of max. Therefore, the requirements that follow do not apply. Similarly the lvalue *b refers to an object (num) that is never modified during the execution of max.

Therefore, the code in max does not violate the requirements for restrict, and its behavior is defined by the C standard.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312