Consider the following:
extern void bar(int *restrict);
void foo(int *restrict p) {
int tmp;
bar(&tmp);
*p = tmp;
}
Does C99 spec permit to optimize foo to the following?
extern void bar(int *restrict);
void foo(int *restrict p) {
bar(p);
}
I tried gcc, Clang and Intel Compiler in -O3 mode and neither generated code which reflects the above optimization. This lead me to suspect that this optimization breaks the spec. If it is not allowed, where does it say so in the spec?
Note: my question is inspired by this SO question
The answer is a definitive NO, this is not allowed.
Consider what happens if foo and bar are mutually recursive. For example, this implementation of bar
:
void bar(int *restrict p)
{
static int q;
if (p == &q) {
printf("pointers match!\n");
} else if (p == NULL) {
foo(&q);
}
}
bar
never dereferences p, so the restrict qualifier is irrelevant. It is obvious that the static variable q
cannot have the same address as the automatic variable tmp
in foo. Therefore, foo
cannot pass its parameter back to bar
, and the given optimization is not allowed.
For the compiler to do the optimization, it must be sure that regardless of how bar
is implemented, and how foo
is called, well-defined behavior will not change.
Since the implementation of bar
and the call to foo
are unknown to the compiler, when it compiles foo
, the theoretical existence of such a case is enough to prevent the optimization, even if it doesn't happen in reality.
Here's an example for such a situation. The important points are:
1. The parameter p
points to write-only memory (e.g. memory mapped I/O).
2. bar
is unsafe for use with a write-only pointer (maybe it writes it and then reads it back, expecting the same value).
The function foo
is safe for use with a write-only pointer, because it only writes p
. This is true even if bar
is unsafe, because bar
never gets p
. With the suggested optiomization, bar
does get p
, which may cause trouble.
Here's an example for the file containing bar
and the call to foo
.
static int increment;
void bar(int *restrict p) {
(*p)=0;
if (increment) (*p)++;
}
void foo(int *restrict p);
int main(int ac, char **av) {
int *p = get_io_addr(); /* Get a write-only memory mapped I/O address */
increment = atoi(av[1]);
foo(p);
return 0;
}
A brief reading of this SO question and this wikipedia enrty suggests that the restrict keyword can only have effect in the arguments of a function. However, reading the C99 standard, section 6.7.3.1 in particular, makes it clear that the restrict
applies to the whole context in which the restrict
is used. So by using
void foo(int *restrict p);
you are guaranteeing that the only reading of and writing into the memory block pointed to by p
will be via p
.
However, even with this information, when compiling foo
, the compiler has no idea what bar
will do with the information it is sent. For example, consider:
void bar (unsigned long long int *p) {
*p = ((unsigned long long int) p) % 2000;
}
The result is dependent on the value of the pointer set, which means that when compiling foo
the optimisation assumption you suggest cannot definitively be made, as the result will be different if the optimisation you suggest is made.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With