On the redundancy of C99's restrict
Pascal Cuoq - 25th Jul 2012The restrict keyword in C99
C99 introduced a restrict keyword. The intention is to let the programmer specify the absence of alias between some inputs of a function ey is writing.
Consider the function:
int f1(int * restrict p int * restrict q)
{
*p = 1;
*q = 2;
return *p + *q;
}
Thanks to the restrict keyword GCC can compile function f1() into the following assembly code:
$ ~/gcc-172652/bin/gcc -O3 -std=c99 -S restr.c $ cat restr.s ... movl $1 (%rdi) movl $2 (%rsi) movl $3 %eax ret ...
Note that the -std=c99 is necessary. The code parsed as a C89 program is syntactically incorrect because restrict is not a keyword in C89.
In the generated x86_64 code %rdi is the first argument p %rsi is the second argument q the parentheses dereference and the dollar sign indicates constants. By convention register %eax contains the return value when the ret instruction is reached.
The above is pretty good code. Since the information is provided that pointers passed to function f1 for p and q never alias the compiler knows that at the point of the return *p must be 1 and therefore the returned expression evaluates to 3.
Contrast with the same function compiled without the restrict annotations say because all you have is a C89 compiler:
int f(int * p int * q)
{
*p = 1;
*q = 2;
return *p + *q;
}
...
movl $1 (%rdi)
movl $2 (%rsi)
movl (%rdi) %eax
addl $2 %eax
ret
This generated assembly code is not as good. The compiler knows that *q can only be 2 at the point of the return statement but it cannot be certain that *p contains 1 because the function could be passed the same address for both arguments. In order for the code to work in all cases the compiler decided to reload *p from memory for the addition.
My proposal
I claim that the restrict keyword in C99 both lacks expressive power and is redundant with constructs that already existed in C89. There was no need to encumber the language with a new construct especially a new keyword that breaks the compilation of existing code that uses a variable named restrict.
C89 already had undefined behavior a powerful tool for instructing the compiler that it does not have to generate code that handles special cases.
A function f2() that works under the same hypotheses as f1() can be written in C89:
int f2(int * p int * q)
{
(*p=*p) & (*q=*q);
*p = 1;
*q = 2;
return *p + *q;
}
The first statement in the body informs the compiler that f2() will never be called with aliasing arguments p and q. The compiler is then free to generate the same code as was generated for the restrict-using function f1(). It is completely legal for the compiler to assume that *p + *q at the return statement evaluates to 3 in the function above. GCC does not generate efficient code for the function f2() above but it could. It generates the same code as for the plain function f() correctly compiling (*p=*p) & (*q=*q); to nothing but failing to take advantage of the freedom it provides to generate a function that does not work when p and q alias.
Wrapping up
My proposal is more flexible than restrict allowing to specify fine-grained non-aliasing relations between specific pointers. It is slightly more verbose especially when there are many pointers but this is a normal downside of allowing to specify aliasing relations pointer by pointer. Note that the pairwise absence of alias between p q and r can be specified with the compact (*p=*p) & (*q=*q) & (*r=*r);
Compiler makers please support the (*p=*p) & (*q=*q); code annotation in your compilers.
