Granularity of restrict qualifier for overlapping pointers, types
Asked Answered
P

2

3

The whole point of restrict is to promise accesses through one pointer don't alias another. That said, there are examples where overlapping memory addresses wouldn't imply aliasing. For example:

int* arr_ptr0 = &arr[0];
int* arr_ptr1 = &arr[1];
for (int i=0;i<10;++i) {
    *arr_ptr0 = *arr_ptr1;
    arr_ptr0 += 2;
    arr_ptr1 += 2;
}

The thing is, these pointers actually do point to overlapping memory! For this particular example, guides like this say, e.g.:

It is valid . . . to point into the same array object, provided the range of elements accessed through one of the pointers does not overlap with the range of elements accessed through the other pointer.

My question is: What granularity is "elements"?

For example, suppose I have an array of type struct Foo. Do I really need to ensure that I don't access the same range of elements (Foos), even if the parts I access are disjoint? Here's a simple, scalar example:

struct Foo { int i; float f; };
void f(struct Foo*restrict foo0, struct Foo*restrict foo1) {
    foo0->i = 6;
    foo1->f = 19.0f;
}
void g(struct Foo* foo) {
    f(foo,foo); /* problem? */
}

You can run into similar issues with pointers to different types (e.g. char vs. int), but perhaps the structure example above is more clear.

Pill answered 19/9, 2014 at 20:0 Comment(2)
AFAIUI, yes, you really do need to ensure you don't access the same structure, independent of which part of the structure you access, via two different pointers. It's not even a question of 'same time'; it's 'the same function call'. So, if you access ptr1[a] .. ptr1[b] (a < b) and ptr2[c] .. ptr2[d] (c < d) and any of the addresses in the first range are the same as any of the address in the second range, the restrict constraint is violated.Scissure
@JonathanLeffler: It's not "same function call" but the execution lifetime of a particular block associated with the restrict-qualified pointer's lifetime.Nathalienathan
N
1

The relevant text of the standard is 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.

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).

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.

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: T shall not be const-qualified. Every other lvalue used to access the value of X shall also have its address based on P. Every access that modifies X shall be considered also to modify P, for the purposes of this subclause. If P is assigned the value of a pointer expression E that is based on another restricted pointer object P2, associated with block B2, then either the execution of B2 shall begin before the execution of B, or the execution of B2 shall end prior to the assignment. If these requirements are not met, then the behavior is undefined.

5 Here an execution of B means that portion of the execution of the program that would correspond to the lifetime of an object with scalar type and automatic storage duration associated with B.

Your first example (the interleaved array) is perfectly valid by my reading of the standard.

The second example with the struct is less clear, and depends on whether the use of the -> operator (your wrote . but meant ->) with foo0 (or foo1) means that *foo0 (or foo1) is "used to access the value of the object that it designates". This is not clear to me since the struct is not used as a value; only its members are.

Nathalienathan answered 19/9, 2014 at 20:16 Comment(3)
if you don't consider access to members as access to the aggregate, some parts of the C standard (eg restrict as we have seen, but also _Atomic) become very... let's say 'interesting'; in case of modifying access, it's arguably clear that member access implies accessing the aggregate: if the value of a member changes, then the value of the aggregate changes as well, implying it has been accessed by definition; assuming the purpose of the standard wasn't to drive C programmers completely insane, the same should hold for non-modifying access, but the jury on that is still out ;)Pinhead
hm.. scratch _Atomic from my argument as you're not allowed to access members of atomic structures anyway; the argument holds true for volatile, though; also note that the opposite case (accessing the aggregate implies accessing the member) gets an explicit mention in the effective typing rules (but is of course irrelevant)Pinhead
Wouldn't "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)" and "Every access that modifies X shall be considered also to modify P, for the purposes of this subclause. If P is assigned the value of a pointer expression E that is based on another restricted pointer object P2, associated with block B2, then either the execution of B2 shall begin before the execution of B..." imply a concurrent modification within the same block for the second example?Ionian
I
-1

The restrict keyword is strictly advisory to the compiler that the application won't modify the same addresses through another pointer not derived from it, within the type qualified scope.

Nothing actually restricts the application from doing so though. However, it's safe to assume modifying an address that's accessed through a restrict qualified pointer through something other than that restrict qualified pointer will result in undefined behavior (beware dragons).

Ionian answered 19/9, 2014 at 20:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.