On the redundancy of C99's restrict

Pascal Cuoq - 25th Jul 2012

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

Pascal Cuoq
25th Jul 2012