I have a question regarding optimizations the compiler can potentially do.
The below code will speak for itself (this is an example):
typedef struct test
{
short i;
} s_test;
int function1(char *bin)
{
s_test foo;
lock(gmutex);
foo.i = *(int*)bin * 8;
unlock(gmutex);
sleep(5);
//
// Here anything can happen to *bin in another thread
// an inline example here could be: *(volatile int *)bin = 42;
//
int b = foo.i + sizeof(char*);
return (b > 1000);
}
Could the compiler ever replace the last lines with
return ((*(int*)bin * 8 + sizeof(char*)) > 1000);
It did not seem to be the case using -O2 or -O3 with gcc 4.4 but could it be the case with other compilers and with other compilation flags?
I don't think compiler will do such kind of optimization.
unlock(gmutex)
this is function, the compiler can't assume that value pointed by bin will be changed in unlock function or not.
for example, maybe bin comes from a globe. So optimization for bin can't cross the function call.
Your example is unnecessarily complicated because you are reading bin
through a different type than it is declared. Aliasing rules are quite complicated, char
is even special, I wouldn't comment on that.
Supposing your declaration would be int* bin
(and so you wouldn't have to cast the pointer) a compiler would not have the right to reorder statements across function calls, they form so-called sequence points. The value of *bin
before and after the call to unlock
could be different, so it has to load the value after the call.
Edit: as noted by slartibartfast, for this argument it is essential that unlock
is (or contains) a function call and isn't just a macro that resolves to a sequence of operations by the compiler.
As I said in the direct reply: your mutex isn't protecting anything IMHO. The char array which *bin points into can get modified during the int-access so depending on your machine you won't even get a consistent view on the memory you wanted to access. Back to your question: a compiler will not transform the source code to the sequence which you envisioned BUT it may very well produce machine language which in effect will behave the way your source code does. If it is able to inspect the functions lock, unlock and sleep (which seem to be macros anyway) and can deduce that there is no side effect to the involved memory locations AND there is no implementation defined meaning to calls to e.g. sleep() which would render temporary ("cached" although the standard doesn't use this term) values invalid then it is entitled to produce an instruction sequence like the one you gave. C (up to C99) is inherently single-threaded and the compiler can employ any optimization strategy it wants as long as the code behaves "as if" it would run on the ideal hypothetical C machine. The sequence points which Jens mentioned don't affect the correctness under single threaded conditions: the compiler could hold foo in a register during the whole function or it could even alias foo.i with the memory location pointed to by *bin, so this code is inherently dangerous (alhtough I think it will not exhibit this behaviour on most compilers).
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