> This example is also incomplete since result is not used. I'll assume > that it is returned from mk(). Correct. Sorry for the confusion. > Therefore, the compiler knows that the assignments to p[0] and p[1] can not modify > result. I understand this part. I would understand if say compiler reordered reads from result variable and stores to p[0] and p[1]. But I do not understand what allows compiler to consider these stores as dead --- they have to modify _something_ in the memory to which p points. In general compiler can't say anything about that location especially if it sees only body of init [lets consider non-inlined case to reduce amount of information available to compiler]. Maybe init was called like: uint32_t x[2]; init(reinterpret_cast<uint64_t*>(x), hi, lo); use(x[0]); use(x[1]); Is it also a violation of strict aliasing rules? We load and store values through uint32_t* pointers. But uint32_t* pointers are passed around hidden as a pointer of incompatible type uint64_t*. That is what happens in V8 all the time: we read and store values through pointers of type Object** (each object in heap is essentially an array of pointer to other objects), but we "hide" this Object** pointers inside pointers of type Foo*, where Foo is some type with nicely typed getters/setters which essentially cast this pointer to Object** pointer. -- Vyacheslav Egorov On Fri, Oct 1, 2010 at 8:38 PM, Ian Lance Taylor <iant@xxxxxxxxxx> wrote: > Vyacheslav Egorov <vegorov@xxxxxxxxxxxx> writes: > >> The question is why this conversions between incompatible pointer >> types allows GCC to remove memory stores to some random memory >> location it does not know anything about? >> >> In some article about strict aliasing I've read that compiler can >> remove the following stores: >> >> inline void init (uint64_t* value, uint32_t hi, uint32_t lo) { >> uint32_t* p = reinterpret_cast<uint32_t>(value); >> p[0] = lo; // can be optimized out when inlined to mk >> p[1] = hi; // can be optimized out when inlined to mk >> } >> >> uint64_t mk(uint32_t hi, uint32_t lo) { >> uint64_t result; >> init(&result, hi, lo); >> } >> >> But I cannot clearly see why compiler can do that nor I can understand >> why it would be beneficial to do so. And the main mystery for me here >> is how to prevent compiler from doing such optimizations. > > This example is also incomplete since result is not used. I'll assume > that it is returned from mk(). > > The C/C++ language standards say that a store via a pointer whose type > is uint32_t* can not modify a value whose type is uint64_t. Therefore, > the compiler knows that the assignments to p[0] and p[1] can not modify > result. So it can just leave result unchanged. Then it can see that > the assignments to p[0] and p[1] are not used, and so they can be > discarded. Finally, result is uninitialized, so the compiler does not > need to return any particular value. The effect is that compiler can > reduce mk() to a function which does absolutely nothing, and any caller > will simply get whatever value happens to be stored in the return > register when mk() is called. > > >>> (When your program has an aliasing violation, gcc does unpredictable >>> things. There is little benefit to speculating about precisely why it >>> did what it did.) >> >> Any optimization done by a compiler is always predictable [unless it >> is bug in optimizer], because compiler has to prove that his >> transformation is correct. > > You are right, of course. When I say unpredictable I mean that the > generated code will not follow any simple rules with respect to the > invalid source code. > >> I would like to understand what exactly compiler proved in this case. > > Hope this helps. > > Ian >