[LLVMdev] Handling of pointer difference in llvm-gcc and clang

Duncan Sands baldrick at free.fr
Wed Aug 10 09:13:16 PDT 2011


Hi Stephan,

> We are developing a bounded model checker for C/C++ programs
> (http://baldur.iti.kit.edu/llbmc/) that operates on LLVM's intermediate
> representation.  While checking a C++ program that uses STL containers
> we noticed that llvm-gcc and clang handle pointer differences in
> disagreeing ways.
>
> Consider the following C function:
> int f(int *p, int *q)
> {
>       return q - p;
> }
>
> Here's the LLVM code generated by llvm-gcc (2.9):
> define i32 @f(i32* %p, i32* %q) nounwind readnone {
> entry:
>     %0 = ptrtoint i32* %q to i32
>     %1 = ptrtoint i32* %p to i32
>     %2 = sub nsw i32 %0, %1
>     %3 = ashr exact i32 %2, 2
>     ret i32 %3
> }
>
> And here is what clang (2.9) produces:
> define i32 @f(i32* %p, i32* %q) nounwind readnone {
>     %1 = ptrtoint i32* %q to i32
>     %2 = ptrtoint i32* %p to i32
>     %3 = sub i32 %1, %2
>     %4 = ashr exact i32 %3, 2
>     ret i32 %4
> }
>
> Thus, llvm-gcc added the nsw flag to the sub, whereas clang didn't.
>
> We think that clang is right and llvm-gcc is wrong:  it could be the
> case that p and q point into the same array, that q is 0x80000000, and
> that p is 0x7FFFFFFE.  Then the sub results in a signed overflow, i.e.,
> sub with nsw is a trap value.
>
> Is this a bug in llvm-gcc?

in llvm-gcc (and dragonegg) this is coming directly from GCC's gimple:

f (int * p, int * q)
{
   long int D.2718;
   long int D.2717;
   long int p.1;
   long int q.0;
   int D.2714;

<bb 2>:
   q.0_2 = (long int) q_1(D);
   p.1_4 = (long int) p_3(D);
   D.2717_5 = q.0_2 - p.1_4;
   D.2718_6 = D.2717_5 /[ex] 4;
   D.2714_7 = (int) D.2718_6;
   return D.2714_7;

}

Signed overflow in the difference of two long int (ptrdiff_t) values results in
undefined behaviour according to the GCC type system, which is where the nsw
flag comes from.

The C front-end generates this gimple in the pointer_diff routine.  The above is
basically a direct transcription of what pointer_diff does.

In short, I don't know if this is right or wrong; but if it is wrong it seems
to be a bug in GCC's C frontend.

Ciao, Duncan.



More information about the llvm-dev mailing list