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.