On Sat, Jan 10, 2009 at 12:19 PM, Raoul Gough <raoulgo...@clara.co.uk> wrote: > I've been investigating a code-reordering question to do with g++ strict > alias analysis, and I suspect there is a deficiency in the way type-based > alias analysis works. I realise that's unlikely to be the case, so I'll try > to present a concise demonstration which uses (I think) well-defined C code. > The original problem (now long worked around) was in a much larger C++ > codebase and did result in a harmful reordering of code (although, that was > with g++ 3.4.3). Output below is from 4.3.2 > > Take these two example functions: > > extern int foo(int* p1, char* p2) > { > int result = *p1; > *p2 = 4; > return result; > } > > extern int bar(int* p1, double* p2) > { > int result = *p1; > *p2 = 4; > return result; > } > > Both of these read via one pointer, assign via another and return the first > value. Obviously, if the two pointers alias the same storage and the > compiler reorders the read and the write, the wrong value will result. In > the first case, it's obvious that p2 may alias p1, since it has type char*. > Indeed, -fdump-tree-salias from g++ 4.3.2, shows the following: > > > Alias information for int foo(int*, char*) > [...] > Symbol memory tags > > SMT.4, UID D.1674, int, is addressable, is global, score: 960006, direct > reads: 0, direct writes: 0, indirect reads: 1, indirect writes: 1, call > clobbered (is global var, is incoming pointer), may aliases: { SMT.5 } > SMT.5, UID D.1675, char, is addressable, is global, score: 960006, direct > reads: 0, direct writes: 0, indirect reads: 1, indirect writes: 1, call > clobbered (is global var, is incoming pointer), may aliases: { SMT.4 } > [...] > > > I guess this means the compiler knows that the int* and char* "may alias" > the same storage. Now for the second function, type based alias analysis > tells us that p1 and p2 cannot alias each other, because an int and a double > can't simultaneously occupy the same storage. So -fdump-tree-salias shows > the following: > > > Alias information for int bar(int*, double*) > [...] > Symbol memory tags > > SMT.18, UID D.1688, int, is addressable, is global, score: 320002, direct > reads: 0, direct writes: 0, indirect reads: 1, indirect writes: 0, call > clobbered (is global var, is incoming pointer) > SMT.19, UID D.1689, double, is addressable, is global, score: 640004, direct > reads: 0, direct writes: 0, indirect reads: 0, indirect writes: 1, call > clobbered (is global var, is incoming pointer) > [...] > > > This doesn't show any "may aliases" lists, so I assume this means the > compiler thinks it can reorder accesses via the pointers. I've included the > full salias output below, in case I'm missing something. So here's a > problematic example (which was first pointed out to me by James Kanze on > comp.std.c++): > > union A > { > int i_; > double d_; > }; > > void bar_bad() > { > A a; > a.i_ = 3; > assert(bar(&a.i_, &a.d_) == 3); > } > > Here we pass into bar a pointer to int and double at the same storage > location. So now, if the compiler goes ahead and reorders the read and write > in function bar, the assertion would fail. Note that this code does *not* > write one type into a union and read another. It writes an int, reads an int > and then writes a double. All that's happening is that the storage is being > *reused* for a different type. However, this re-use takes place within > function bar, which doesn't necessarily know about the union (especially if > it's in a different compilation unit). > > So, my question is, does the salias analysis indicate that the compiler > thinks it can reorder the operations in function bar?
There is a defect report about this and I'm sure numerous bugreports in bugzilla. The conclusion is that the above invokes undefined behavior because the accesses are not done through a union type, so the static memory typing rules of C apply. Richard.