From GCC docs:
Many functions do not examine any values except their arguments, and have no effects except the return value. Basically this is just slightly more strict class than the pure attribute above, since function is not allowed to read global memory.
My question is whether or not a global const
value counts as global memory. I assume that it does, but it seems odd that a value that I explicitly mark as unchanging could negate the possible optimization here.
For example:
int const ConstantModulusValue = 3;
int foo(int value) {
return foo % ConstantModulusValue;
}
The use of ConstantModulusValue
, to my understanding, means that this function should not be marked const
which, again, seems odd to me. Is there some danger in marking this const
tha t I don't see.?
These attributes allow the compiler to know whether it's safe to omit calls to the function without knowing how it's implemented.
pure attribute basically says that the function result only depends on the function arguments and the global state; additionally, the function itself does not mutate global state.
If you call a pure function twice, it's guaranteed to return the same result; however, if you mutate globally visible state between the calls, the guarantee no longer holds.
const attribute is stronger in that even if the global state is mutated, the function still should return the same result; thus it's safe to optimize the redundant calls to const function in more cases.
Reading global state should not be a problem if you can guarantee that the state does not change (note that marking the global as const does not always guarantee that).
As an example, consider this program:
int foo(int) __attribute__((pure));
int bar(int) __attribute__((const));
void unknown();
int test1(int a)
{
int x = foo(a);
int y = foo(a);
return x + y;
}
int test2(int a)
{
int x = bar(a);
int y = bar(a);
return x + y;
}
int test3(int a)
{
int x = foo(a);
unknown();
int y = foo(a);
return x + y;
}
int test4(int a)
{
int x = bar(a);
unknown();
int y = bar(a);
return x + y;
}
Compiling it with gcc 4.8.1 and analyzing the assembly reveals that test1()
and test2()
both only call the respective function once and then multiply the result by 2; test3()
does 3 calls - 2 calls to foo and 1 call to unknown; test4()
does a call to bar() followed by a call to unknown(), and returns the result of bar(), multiplied by 2.
This behavior matches the explanation above - unknown() can mutate global state, so compiler can't elide extra calls to foo(), but can elide extra calls to bar().