[LLVMdev] Strange pointer aliasing behaviour
Eli Friedman
eli.friedman at gmail.com
Wed Jun 16 19:23:34 PDT 2010
On Wed, Jun 16, 2010 at 1:39 PM, Pierre C <lists at peufeu.com> wrote:
>
> I'm hitting a strange pointer aliasing "bug". Here is a test case :
>
> /* SOURCE CODE */
>
> #define little_list_size 8
>
> class LittleList1 {
> public:
> int _length;
> double _data[ little_list_size ];
>
> LittleList1( int length )
> {
> _length = length;
> for( int i=0; i<length; i++ )
> _data[i] = 0;
> }
> };
>
> class LittleList2 {
> public:
> int _length;
> double _data[ little_list_size ];
>
> LittleList2( int length )
> {
> _length = length;
> for( int i=0; i<_length; i++ )
> _data[i] = 0;
> }
> };
>
> int func1()
> {
> LittleList1 l(4);
> return l._length;
> }
>
> int func2()
> {
> LittleList2 l(4);
> return l._length;
> }
>
> /* END SOURCE CODE */
>
> The only difference between the 2 classes is in the constructor, in the
> line :
> for( int i=0; i<_length; i++ )
>
> One has "length" which is a function parameter, the other has "_length"
> which is the class member which was initialized in the same constructor.
>
> Now let's compile and optimize :
>
> llvm-g++ -emit-llvm -Winline -O3 -Iinclude -c test2.cpp -o test2.bc
> opt -S -O3 -print-alias-sets -count-aa test2.bc
>
> Result :
>
> %struct.LittleList1 = type { i32, [8 x double] }
>
> ********
> Here, func1() is perfectly optimized :
> ********
>
> define i32 @_Z5func1v() nounwind readnone {
> entry:
> ret i32 4
> }
>
>
> ********
> func2() should give the exact same result as func1, however ...
>
> Alias Set Tracker: 1 alias sets for 2 pointer values.
> AliasSet[0x0x8ca140,2] may alias, Mod/Ref Pointers: (i32* %0, 4),
> (double* %scevgep.i, 8)
>
> A spurious alias comes up between the 2 fields of the struct (which should
> in theory not happen).
> So, it reloads _length at each iteration, thus no optimization takes place
> and the code below is generated :
>
> ********
>
> define i32 @_Z5func2v() nounwind readnone {
> entry:
> %l = alloca %struct.LittleList1, align 8 ; <%struct.LittleList1*>
> [#uses=2]
> %0 = getelementptr inbounds %struct.LittleList1* %l, i64 0, i32 0 ;
> <i32*> [#uses=2]
> store i32 4, i32* %0, align 8
> br label %bb.i
>
> bb.i: ; preds = %bb.i, %entry
> %indvar.i = phi i64 [ %tmp, %bb.i ], [ 0, %entry ] ; <i64> [#uses=2]
> %tmp = add i64 %indvar.i, 1 ; <i64> [#uses=2]
> %tmp2.i = trunc i64 %tmp to i32 ; <i32> [#uses=1]
> %scevgep.i = getelementptr %struct.LittleList1* %l, i64 0, i32 1, i64
> %indvar.i ; <double*> [#uses=1]
> store double 0.000000e+00, double* %scevgep.i, align 8
> %1 = load i32* %0, align 8 ; <i32> [#uses=2]
> %2 = icmp sgt i32 %1, %tmp2.i ; <i1> [#uses=1]
> br i1 %2, label %bb.i, label %_ZN11LittleList2C1Ei.exit
>
> _ZN11LittleList2C1Ei.exit: ; preds = %bb.i
> ret i32 %1
>
> ********
>
> g++ correctly resolves this alias and both functions are compiled to
> "return 4".
> llvm-gcc (and opt) detect a spurious alias on func2() (all alias passes
> seem to do the same here) which prevents optimizations, and in other cases
> more complex than this simple test, it also prevents loop unrolling and
> many other optimizations.
>
> Can I do something to fix this ?
There are essentially two ways to "solve" this issue: one is
type-based alias analysis, i.e. assuming "double" and "int" don't
alias; LLVM doesn't implement this at the moment. The other is to
attempt to analyze the loop and prove that %indvar.i is never
negative; LLVM doesn't implement anything like this at the moment
either.
-Eli
More information about the llvm-dev
mailing list