Suppose I have a function which takes some pointer parameters - some non-const, through which it may write, and some const through which it only reads. Example:
void f(int * a, int const *b);
Suppose also that the function does not otherwise write to memory (i.e. doesn't use global variables, fixed addresses, recasting the const pointers as non-const and such tricks).
Now, is it sufficient (as per the C language standard), for achieving the benefits of restrict
for all reads within f()
, to only restrict
the output parameters? i.e. in the example, restrict a
but not b
?
A simple test (GodBolt) suggests such restriction should be sufficient. This source:
int f(int * restrict a, int const * b) {
a[0] += b[0];
return a[0] + b[0];
}
int all_restricted(int * restrict a, int const * restrict b) {
a[0] += b[0];
return a[0] + b[0];
}
int unrestricted(int * a, int const * b) {
a[0] += b[0];
return a[0] + b[0];
}
Produces the same object code for x86_64:
f:
mov eax, DWORD PTR [rsi]
mov edx, DWORD PTR [rdi]
add edx, eax
mov DWORD PTR [rdi], edx
add eax, edx
ret
all_restricted:
mov eax, DWORD PTR [rsi]
mov edx, DWORD PTR [rdi]
add edx, eax
mov DWORD PTR [rdi], edx
add eax, edx
ret
unrestricted:
mov eax, DWORD PTR [rsi]
add eax, DWORD PTR [rdi]
mov DWORD PTR [rdi], eax
add eax, DWORD PTR [rsi]
ret
but that's not a general guarantee.
No, it is not enough.
Suppose also that the function does not otherwise write to memory (i.e. doesn't use global variables, fixed addresses, recasting the const pointers as non-const and such tricks).
This supposition is insufficient. It would also be necessary that the compiler see the function does not otherwise write to memory that the pointer points to (including any memory that could be accessed via the pointer, such as b[18]
). For example, if the function f
calls bar(b);
, and the compiler cannot see bar
, then it cannot know whether memory that b
points to is modified during execution of f
, even if it is not.
Give this additional premise, that the compiler can see there are no modifications to any memory pointed to via b
, then it does not matter for optimization whether b
is declared with const
and/or restrict
: The compiler knows everything about the memory, and telling it anything more does not add information.
However, it is often not the case that code satisfies this premise. (And even when it does, it may be a nuisance for a programmer to be sure of it.) So let’s consider a case when we do not have the additional premise:
void f(int * restrict a, int const *b)
{
printf("%d\n", *b);
bar();
printf("%d\n", *b);
}
When bar
is called, the compiler does not know whether *b
is modified. Even though this function does not pass b
to bar
, bar
might access some external object that is *b
or has a pointer to where *b
is, and so bar
can change the object that is *b
. Therefore, the compiler must reload *b
from memory for the second printf
.
If instead we declare the function void f(int * restrict a, int const * restrict b)
, then restrict
asserts that, if *b
is modified during execution of f
(including indirectly, inside bar
), then every access to it will be via b
(directly, as in *b
, or indirectly, as through a pointer visibly copied or calculated from b
). Since the compiler can see bar
does not receive b
, it knows bar
does not contain any accesses to *b
that are based on b
, and therefore it may assume bar
does not change *b
.
Therefore, adding restrict
to a parameter that is a pointer to a const
-qualified type may enable some optimizations, even if all other parameters are also declared restrict
.