I am confused by the guarantees GCC makes about optimizing pure
functions (from online docs):
pure
Many functions have no effects except the return value and their return value depends only on the parameters and/or global variables. (...)
Interesting non-pure functions are functions with infinite loops or those depending on volatile memory or other system resources, that may change between two consecutive calls (such as
feof
in a multithreading environment).
And for const
:
const
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 below, since the function is not allowed to read global memory.
Note that a function that has pointer arguments and examines the data pointed to must not be declared
const
. Likewise, a function that calls a non-const function usually must not be const.
So, I tried creating a function which does accept a pointer parameter, and tried marking it pure
. However, I tried compiling this function using GCC online (I tried both const
and pure
):
typedef struct
{
int32_t start;
int32_t end;
}
Buffer;
inline __attribute__((pure,always_inline)) int32_t getLen(Buffer * b)
{
return b->end - b->start;
}
And noticed that GCC (at least the several online compiler versions I've tried):
Buffer*
parameter is pointing to a global value,const
instead of pure
, but presumably const
is ignored if there is a pointer argument?This is a good thing, because a global Buffer
might change by a different thread/interrupt at any time, while a local Buffer
is perfectly safe for optimizations.
But I am completely confused by the remarks regarding passing pointers. Is there a place where the behavior of the GCC is explicitly defined for pure
functions accepting pointer arguments?
The compiler optimizes to reduce the size of the binary instead of execution speed. If you do not specify an optimization option, gcc attempts to reduce the compilation time and to make debugging always yield the result expected from reading the source code.
GCC performs nearly all supported optimizations that do not involve a space-speed tradeoff. As compared to -O , this option increases both compilation time and the performance of the generated code.
The syntax is #pragma GCC optimize (option, ...) From the official source on GCC pragmas, this pragma allows you to set global optimization flags (specified by option ) for functions that come after it.
A counter-example of a non-pure function is the strcpy() function. This function takes two pointers as parameters.
Is there a place where the behavior of the GCC is explicitly defined for pure functions accepting pointer arguments?
The behavior is no different to pure functions that don't take pointer arguments; they can read any memory in the program, but can't write to memory or perform IO.
You've significantly confused things by writing your pure and const functions as inline; where the function body is available to the compiler at the call site it can work out for itself which operations the called function performs. The pure and const attributes are most useful where the function body is not visible since it will be defined in a separate compilation unit.
For example, consider the following collection of non-pure, pure and const functions:
__attribute__((__pure__)) int a();
__attribute__((__const__)) int b();
void c();
If we call a
twice in succession, with no operation interposing, we can collapse that to a single call, since a
is guaranteed to access global memory only to read it; even if another thread writes to global memory around the same time there is no way for a
to communicate with that thread so the compiler can assume that the write happens before or after both calls to a
:
int f() {
int i = a();
i += a();
return i; // optimized to "return a() * 2;"
}
If we call c
between the calls to a
we must assume that the return value of a
can be affected by the call to c
:
int g() {
int i = a();
c();
i += a();
return i; // no optimization possible
}
However, if instead of the pure a
we call the const b
, we can collapse that to a single call, since b
cannot read any memory that c
might write to:
int h() {
int i = b();
c();
i += b();
return i; // optimized to "c(); return b() * 2;"
}
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