I am uncertain if passing a __restrict__
pointer to another function is always breaking the rules of restrict aliasing (assuming that other function reads or writes to it) or if it is more subtle
I am aware that it is not standard C++ and instead an extension of C99 that GCC supports. GCC's documentation doesn't really cover my question much: https://gcc.gnu.org/onlinedocs/gcc/Restricted-Pointers.html
CPP Reference has more notes regarding it (however talking about C since C99): https://en.cppreference.com/w/c/language/restrict
Notably the CPP Reference link states:
During each execution of a block in which a restricted pointer P is declared (typically each execution of a function body in which P is a function parameter), if some object that is accessible through P (directly or indirectly) is modified, by any means, then all accesses to that object (both reads and writes) in that block must occur through P (directly or indirectly), otherwise the behavior is undefined:
Also states:
Assignment from one restricted pointer to another is undefined behavior, except when assigning from a pointer to an object in some outer block to a pointer in some inner block (including using a restricted pointer argument when calling a function with a restricted pointer parameter) or when returning from a function (and otherwise when the block of the from-pointer ended):
Below I made some code to cover some cases I can imagine:
#include <cstring>
#include <iostream>
class Foo {
public:
void func1(char* __restrict__ ptr1) {
// 1. Does passing ptr1 to another method break the rules of restrict?
// I don't think it does because func2 only reads in its own scope
func2(ptr1);
// func3 writes
func3(ptr1);
ptr1 += 3;
// EDITED: 4. Is this read guaranteed to see the change from func2?
std::cout << ptr1 << std::endl;
// 3. Overlapping region that func3 wrote to
std::memcpy(ptr1, " overlap\0", 9);
}
void func2(char* const __restrict__ ptr2) { std::cout << ptr2 << std::endl; }
void func3(char* const __restrict__ ptr3) {
// 2. Does writing to ptr3 in this scope break the rules for ptr1 in
// func1? I am uncertain of this because from the point of view of func1
// the memory ptr1 points to changed without being accessed by ptr1
std::memcpy(ptr3, "after\0", 6);
}
};
int main() {
char buf[100];
std::memcpy(buf, "before\0", 7);
std::cout << buf << std::endl;
Foo foo;
foo.func1(buf);
std::cout << buf << std::endl;
return 0;
}
I am not sure if among cases 1, 2, and 3:
ptr1
is accessed through ptr2
and ptr3
)EDITED: Will case 4 always see the change func2
caused?
The exact definition of the C restrict
qualifier is in §6.7.4.2 of the ISO C23 standard. I am assuming that the GCC __restrict__
C++ extension is based on that definition.
According to that definition, the following are the answers to your 4 questions:
__restrict__
qualifier to a function does not violate the restrictions of that qualifier, even if the function parameter is declared as __restrict__
. The called function is also allowed to dereference the pointer parameter. So your posted code is well-defined.ptr3
is based on (i.e. depends on) the value of ptr1
, the __restrict__
qualifier of ptr1
is not being violated. So your posted code is well-defined.__restrict__
qualifier only imposes restrictions during the execution of the block in which it is declared. Since func3
is no longer running when the line std::memcpy(ptr1, " overlap\0", 9);
in func1
is executed, the __restrict__
qualifier of ptr3
in func3
is not being violated. So your posted code is well-defined.ptr2
is guaranteed to occur before the printing of the value of ptr1
, because the behavior of your program is well-defined and there is a sequence point after every full-expression.